diff --git a/Assets/Scripts/Common/Dlls/Dungeons/ASCIIDisplay/Presenters/DungeonPresenter.cs b/Assets/Scripts/Common/Dlls/Dungeons/ASCIIDisplay/Presenters/DungeonPresenter.cs index d681ecaf..a8168bb1 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/ASCIIDisplay/Presenters/DungeonPresenter.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/ASCIIDisplay/Presenters/DungeonPresenter.cs @@ -18,6 +18,7 @@ public DungeonPresenter(IDrawingEngine drawingEngine, int left = 0, int top = 0) this.left = left; drawingEngine.CursorVisible = false; } + public void Print(Tile tile, PrintInfo pi) { @@ -25,6 +26,11 @@ public void Print(Tile tile, PrintInfo pi) var symbol = ' '; if (tile != null) { + if (pi.CustomDrawer != null) + { + pi.CustomDrawer(tile, drawingEngine); + return; + } color = tile.Color; if (pi.PrintNodeIndexes) { @@ -35,6 +41,10 @@ public void Print(Tile tile, PrintInfo pi) if (tile.Revealed) { symbol = tile.Symbol; + if (pi.SymbolToDraw!=null) + { + symbol = pi.SymbolToDraw(tile); + } } } drawingEngine.ForegroundColor = color; diff --git a/Assets/Scripts/Common/Dlls/Dungeons/ASCIIDisplay/Screen.cs b/Assets/Scripts/Common/Dlls/Dungeons/ASCIIDisplay/Screen.cs index a0d77891..abbd4af1 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/ASCIIDisplay/Screen.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/ASCIIDisplay/Screen.cs @@ -1,6 +1,7 @@ using Dungeons.ASCIIDisplay.Presenters; using Dungeons.TileContainers; using Dungeons.Tiles; +using System; using System.Collections.Generic; using System.Linq; @@ -16,7 +17,11 @@ public class Screen public int DungeonY { get; set; } public DungeonPresenter DungeonPresenter { get; set; } - public PrintInfo PrintInfo { get => printInfo; set => printInfo = value; } + public PrintInfo PrintInfo + { + get => printInfo; + set => printInfo = value; + } public Dictionary Lists { get => lists; set => lists = value; } public List ASCIIItems = new List(); @@ -57,7 +62,7 @@ private void RedrawTiles(List tiles) where T : Tile protected virtual void CreateLists() { Lists = new Dictionary(); - var usage = new ListPresenter(UsageListName, OriginX+55, OriginY, 30); + var usage = new ListPresenter(UsageListName, OriginX+75, OriginY, 30); var list = new List(); list.Add(new ListItem("R - reload")); list.Add(new ListItem("D - toggle node_indexes/symbols")); @@ -104,7 +109,7 @@ public virtual void Redraw(DungeonNode dungeon) DungeonPresenter.Redraw(dungeon, PrintInfo); DrawingEngine.SetCursorPosition(0, 0); } - + private void RedrawItems() { ASCIIItems.ForEach(i => diff --git a/Assets/Scripts/Common/Dlls/Dungeons/Core/GreediestConstructorBehavior.cs b/Assets/Scripts/Common/Dlls/Dungeons/Core/GreediestConstructorBehavior.cs index dc135f71..c806e3e0 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/Core/GreediestConstructorBehavior.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/Core/GreediestConstructorBehavior.cs @@ -2,6 +2,8 @@ using System; using System.Linq; using System.Reflection; +#pragma warning disable 8603 +#pragma warning disable 8602 namespace Dungeons.Core { diff --git a/Assets/Scripts/Common/Dlls/Dungeons/Core/RandHelper.cs b/Assets/Scripts/Common/Dlls/Dungeons/Core/RandHelper.cs index 9f31b53b..9bdfaf61 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/Core/RandHelper.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/Core/RandHelper.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; - +#pragma warning disable 8603 +#pragma warning disable 8602 namespace Dungeons.Core { diff --git a/Assets/Scripts/Common/Dlls/Dungeons/CorridorNodeLayouter.cs b/Assets/Scripts/Common/Dlls/Dungeons/CorridorNodeLayouter.cs index 84f3a1a7..6b31532f 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/CorridorNodeLayouter.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/CorridorNodeLayouter.cs @@ -138,8 +138,8 @@ private void CreateSecretRoom(DungeonNode level, List mazeNodes, ID var node = dungeonGenerator.CreateDungeonNodeInstance(); node.Secret = true; - var he = 9; - var wi = 9; + var he = 8; + var wi = 8; var entr = RandHelper.GetRandomDouble() > 0.5f ? EntranceSide.Bottom : EntranceSide.Top; diff --git a/Assets/Scripts/Common/Dlls/Dungeons/DefaultNodeLayouter.cs b/Assets/Scripts/Common/Dlls/Dungeons/DefaultNodeLayouter.cs index bff222bb..6432fe4d 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/DefaultNodeLayouter.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/DefaultNodeLayouter.cs @@ -112,7 +112,6 @@ protected virtual void LayoutNodes(DungeonNode level, List mazeNode if (!currentNode.Secret) lastNonSecretNode = currentNode; - bool shallBreak = currentNodeIndex == mazeNodes.Count - 1; AppendNodeInfo infoNext = new AppendNodeInfo(); diff --git a/Assets/Scripts/Common/Dlls/Dungeons/DungeonGenerator.cs b/Assets/Scripts/Common/Dlls/Dungeons/DungeonGenerator.cs index 1af32a00..bb3db23f 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/DungeonGenerator.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/DungeonGenerator.cs @@ -104,7 +104,7 @@ protected virtual DungeonNode CreateNode(int w, int h, GenerationInfo gi, int no DungeonNode dungeon = CreateDungeonNodeInstance(); dungeon.ChildIslandCreated += Dungeon_ChildIslandCreated; - if (dungeonLayouterKind == DungeonLayouterKind.Corridor && nodeIndex == (int)RoomPlacement.Center) + if (dungeonLayouterKind == Dungeons.DungeonLayouterKind.Corridor && nodeIndex == (int)RoomPlacement.Center) { w = GenerationInfo.MaxRoomSideSize; h = GenerationInfo.MaxRoomSideSize; @@ -150,7 +150,7 @@ protected virtual List CreateDungeonNodes(GenerationInfo info = nul if (!this.info.PreventSecretRoomGeneration) { - if (this.dungeonLayouterKind == DungeonLayouterKind.Default) + if (this.dungeonLayouterKind == Dungeons.DungeonLayouterKind.Default) { if (this.info.SecretRoomIndex >= 0) secretRoomIndex = this.info.SecretRoomIndex; @@ -232,12 +232,38 @@ public virtual DungeonLevel Generate(int levelIndex, GenerationInfo info = null, return level; } + static List everDungeonLayouterKinds = new List(); + static DungeonLayouterKind recentDungeonLayouterKind; + protected virtual DungeonLayouterKind CalcLayouterKind() { + DungeonLayouterKind lk; + + lk = GenerateDungeonLayouterKind(); + if (lk == recentDungeonLayouterKind)//help luck + { + lk = GenerateDungeonLayouterKind(); + } + if (everDungeonLayouterKinds.Count == 1 && lk == everDungeonLayouterKinds[0]) + { + lk = lk == DungeonLayouterKind.Corridor ? DungeonLayouterKind.Default : DungeonLayouterKind.Corridor; + } + + if (!everDungeonLayouterKinds.Contains(lk)) + everDungeonLayouterKinds.Add(lk); + + recentDungeonLayouterKind = lk; + return lk; + } + + static DungeonLayouterKind GenerateDungeonLayouterKind() + { + DungeonLayouterKind lk; if (RandHelper.GetRandomDouble() > 0.5f) - return DungeonLayouterKind.Corridor; + lk = DungeonLayouterKind.Corridor; else - return DungeonLayouterKind.Default; + lk = DungeonLayouterKind.Default; + return lk; } protected virtual void CreateDynamicTiles(List mazeNodes) diff --git a/Assets/Scripts/Common/Dlls/Dungeons/Dungeons.Core.csproj b/Assets/Scripts/Common/Dlls/Dungeons/Dungeons.Core.csproj index 45c3de26..71c58c48 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/Dungeons.Core.csproj +++ b/Assets/Scripts/Common/Dlls/Dungeons/Dungeons.Core.csproj @@ -8,11 +8,11 @@ - 1701;1702;8604;8629;8602;8618;8625;8600;8601;8603;8622;8765 + 1701;1702;8604;8629;8602;8618;8625;8600;8601 - 1701;1702;8604;8629;8602;8618;8625;8600;8601;8603;8622;8765 + 1701;1702;8604;8629;8602;8618;8625;8600;8601 diff --git a/Assets/Scripts/Common/Dlls/Dungeons/GenerationInfo.cs b/Assets/Scripts/Common/Dlls/Dungeons/GenerationInfo.cs index 8922973c..11a49bc4 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/GenerationInfo.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/GenerationInfo.cs @@ -1,6 +1,7 @@ using System; using System.Drawing; #pragma warning disable 8603 +#pragma warning disable 8602 namespace Dungeons { @@ -20,7 +21,7 @@ public class GenerationInfo : ICloneable public bool GenerateDecorations { get; set; } = true; public bool GenerateInterior { get; set; } = true; - public DungeonLayouterKind ForcedDungeonLayouterKind { get; set; }//= DungeonLayouterKind.Default; + public DungeonLayouterKind ForcedDungeonLayouterKind { get; set; }// = DungeonLayouterKind.Corridor; /// /// Normally true, can be set to false for issue testing purposes /// @@ -42,7 +43,7 @@ public class GenerationInfo : ICloneable public bool FirstNodeSmaller = false; public const int MinRoomSideSize = 16;//14 is too small - child island would not be created! - public const int MaxRoomSideSize = 20; + public const int MaxRoomSideSize = 24; public Size MinNodeSize = new Size(MinRoomSideSize, MinRoomSideSize); public Size MaxNodeSize = new Size(MaxRoomSideSize, MaxRoomSideSize); public Size ForcedChilldIslandSize = new Size(0, 0); diff --git a/Assets/Scripts/Common/Dlls/Dungeons/NodeInteriorGenerator.cs b/Assets/Scripts/Common/Dlls/Dungeons/NodeInteriorGenerator.cs index eebfe106..d5eb1b30 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/NodeInteriorGenerator.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/NodeInteriorGenerator.cs @@ -213,7 +213,7 @@ public void SetSideWalls() if (sideCounter < 3) { - dungeonNode.Container.GetInstance().LogError("sideCounter < 3 = "+ sideCounter + " side: " + side.Key + " NodeInd: "+ dungeonNode); + //dungeonNode.Container.GetInstance().LogError("sideCounter < 3 = "+ sideCounter + " side: " + side.Key + " NodeInd: "+ dungeonNode); } } } diff --git a/Assets/Scripts/Common/Dlls/Dungeons/TileContainers/DungeonNode.cs b/Assets/Scripts/Common/Dlls/Dungeons/TileContainers/DungeonNode.cs index 13ee9368..97efeed0 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/TileContainers/DungeonNode.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/TileContainers/DungeonNode.cs @@ -9,6 +9,9 @@ using System.Drawing; using System.Linq; using System.Xml.Serialization; +using Dungeons.ASCIIDisplay; + + #pragma warning disable 8603 namespace Dungeons { @@ -31,6 +34,9 @@ public class PrintInfo public bool PrintNodeIndexes = false; public int OriginX { get; set; } public int OriginY { get; set; } + + public Func SymbolToDraw; + public Action CustomDrawer; } @@ -188,6 +194,21 @@ public bool GetHiddenTilesAlreadyAdded(string key) return hiddenTilesAlreadyAdded[key]; } + protected virtual bool ShallEnsureCorrectY(Dungeons.Tiles.Tile tile) + { + return false; + } + + public Dungeons.Tiles.Tile EnsureCorrectY(Dungeons.Tiles.Tile tile) + { + var maxY = Height - 1; + if (tile.point.Y >= maxY) + { + tile = GetEmptyTiles().Where(i => i.point.Y < maxY).ToList().FirstOrDefault(); + } + + return tile; + } public void SetAlreadyAdded(string key, bool alreadyAdded) { hiddenTilesAlreadyAdded[key] = alreadyAdded; @@ -267,26 +288,14 @@ internal List GenerateLayoutDoors(EntranceSide side, int nextNodeIndex, b List wall = sides[side]; if (secret) { - var count = sides[side].Count; - var diff = GenerationInfo.MaxRoomSideSize - GenerationInfo.MinRoomSideSize; - int counter = 0; - bool added = false; - while (counter < 100) - { - counter++; - var index = Enumerable.Range(diff, (count - diff*2)-2).ToList().GetRandomElem(); - //var allowed = AreDoorAllowedToPutOn(wall[index]); - //if (!allowed) - //{ - // continue; - //} - var door = CreateDoor(wall[index], side) as IDoor; - door.Secret = true; - res.Add(door); - added = true; - break; - } - DebugHelper.Assert(added); + var wallSize = sides[side].Count; + var index = wallSize / 2; + var moveBy = (int)RandHelper.GetRandomFloatInRange(0,4); + index += moveBy * RandHelper.GetRandomFloat() > 0.5f ? 1 : -1; + var door = CreateDoor(wall[index], side) as IDoor; + door.Secret = true; + res.Add(door); + return res; } @@ -470,6 +479,8 @@ public void SetTilesNodeIndex() public Tile GetNeighborTile(Tile tile, TileNeighborhood neighborhood) { + if(tile == null) + return null; var pt = GetNeighborPoint(tile, neighborhood); return GetTile(pt); } @@ -549,7 +560,8 @@ public virtual List GetEmptyTiles return emptyTiles; } - public Tile GetRandomEmptyTile(EmptyCheckContext emptyCheckContext, GenerationConstraints constraints = null, bool canBeNextToDoors = true, int? nodeIndex = null) + public Tile GetRandomEmptyTile(EmptyCheckContext emptyCheckContext, GenerationConstraints constraints = null, + bool canBeNextToDoors = true, int? nodeIndex = null) { var emptyTiles = GetEmptyTiles(constraints, canBeNextToDoors, emptyCheckContext : emptyCheckContext); @@ -1047,7 +1059,7 @@ bool AreDoorAllowedToPutOn(Tile tile) return true; var neibs = GetNeighborTiles(tile); - var forbid = neibs.Where(i => i is Wall).Count() >= 3 || neibs.Where(i => i == null).Any(); + var forbid = neibs.Count < 4 || neibs.Where(i => i is Wall).Count() >= 3 || neibs.Where(i => i == null).Any(); return !forbid; } @@ -1322,11 +1334,18 @@ public virtual List GetAllTiles() #nullable enable public virtual Tile? SetTileAtRandomPosition(Tile tile, bool matchNodeIndex = true, EmptyCheckContext emptyCheckContext = EmptyCheckContext.Unset) { + var node = matchNodeIndex == true ? (int?)NodeIndex : null; var empty = this.GetRandomEmptyTile(emptyCheckContext, nodeIndex: node); + if (empty == null) return null; + if (ShallEnsureCorrectY(tile)) + { + empty = EnsureCorrectY(empty); + } + var set = SetTile(tile, empty.point); return set ? tile : null; diff --git a/Assets/Scripts/Common/Dlls/Dungeons/Tiles/Tile.cs b/Assets/Scripts/Common/Dlls/Dungeons/Tiles/Tile.cs index ca48713a..45748721 100644 --- a/Assets/Scripts/Common/Dlls/Dungeons/Tiles/Tile.cs +++ b/Assets/Scripts/Common/Dlls/Dungeons/Tiles/Tile.cs @@ -23,16 +23,11 @@ public class Constants public class Tile { //members public for speed purposes - public Point point; - //public Point point - //{ - // get { return _point; } - // set { _point = value; } - //} + private char symbol = Constants.SymbolBackground; - public string name; - private string displayedName; + public string name = ""; + protected string displayedName = ""; public string mapName; #if DEBUG_PROPS @@ -42,8 +37,13 @@ public string tag1 get { return _tag1; } set { + if (_tag1.Contains("grave")) + { + int k = 0; + k++; + } _tag1 = value; - if (value == "wall1_1_thin_horiz") + if (value == "fallen_one") { if(point.Y == 0) { @@ -58,8 +58,25 @@ public string tag1 } } } //custom purpose field + public Point _point; + public Point point + { + get { return _point; } + set + { + if (value.X == 0 && value.Y == 0) + { + int k = 0; + k++; + } + _point = value; + + } + } + #else - public string tag1 = "";//custom purpose field + public string tag1 = "";//custom purpose field + public Point point; #endif public string tag2 = "";//custom purpose field @@ -142,12 +159,17 @@ public Tile(Point point, char symbol = Constants.SymbolBackground) this.Symbol = symbol; } - string GetDefaultName() + protected virtual string GetDefaultName() { return GetType().Name; } public string GetNameFromTag1() + { + return GetNameFromTag1(tag1); + } + + public string GetNameFromTag1(string tag1) { var str = tag1.Replace("_ch", ""); str = str.Replace("_", " "); @@ -156,6 +178,7 @@ public string GetNameFromTag1() public virtual void SetNameFromTag1() { + DisplayedName = ""; Name = GetNameFromTag1(); } @@ -204,7 +227,7 @@ public bool IsAtValidPoint } [JsonIgnore] - public bool IsEmpty { get { return Symbol == Constants.SymbolBackground; } } + public bool IsEmpty { get { return symbol == Constants.SymbolBackground; } } //[JsonIgnore] item name was lost public virtual string Name @@ -218,12 +241,31 @@ public virtual string Name name = value.Trim();//call GetCapitalized(value) ?; //if (Symbol != Constants.SymbolBackground) { - if (string.IsNullOrEmpty(displayedName) || GetDefaultName() == displayedName || displayedName == "Unset") - DisplayedName = name; + if (DisplayedNameNeedsToBeSet()) + DisplayedName = EnsureLevelNotInAssetName(name); } } } + public string EnsureLevelNotInAssetName(string asset) + { + if (asset.Contains("_level")) + { + asset = asset.Substring(0, asset.IndexOf("_level")); + } + else if (asset.Contains(" level")) + { + asset = asset.Substring(0, asset.IndexOf(" level")); + } + + return asset; + } + + protected virtual bool DisplayedNameNeedsToBeSet() + { + return string.IsNullOrEmpty(displayedName) || displayedName == "Unset" || GetDefaultName() == displayedName; + } + public static string GetCapitalized(string val) { return val.GetCapitalized(); @@ -261,6 +303,16 @@ public virtual string DisplayedName } set { + //if (displayedName == "Bloody Mary") + //{ + // int k = 0; + // k++; + //} + //if(value == "Ice scepter1") + //{ + // int k = 0; + // k++; + //} displayedName = value; } } @@ -273,15 +325,15 @@ public bool IsAtSamePosition(Tile other) #if DEBUG_PROPS public static bool IncludeDebugDetailsInToString = true; #else - public static bool IncludeDebugDetailsInToString = false; + public static bool IncludeDebugDetailsInToString = true; #endif public override string ToString() { - string res = GetType().ToString() + " " + Name; + string res = GetType().ToString() + " " + Name + " " + tag1; if (IncludeDebugDetailsInToString) - res += " " + Symbol + " " + DungeonNodeIndex + " " + point + " " + tag1 + " " + GetHashCode(); + res += " " + symbol + " " + DungeonNodeIndex + " " + point + " " + tag1 + " " + GetHashCode(); return res; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Abilities/AbilitiesSet.cs b/Assets/Scripts/Common/Dlls/Roguelike/Abilities/AbilitiesSet.cs index b6f382d6..8fe56f7b 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Abilities/AbilitiesSet.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Abilities/AbilitiesSet.cs @@ -15,6 +15,16 @@ public AbilitiesSet() { EnsureItems(); } + + public static AbilitiesSet CreateEmpty() + { + var set = new AbilitiesSet(); + set.passiveAbilities.Clear(); + set.activeAbilities.Clear(); + return set; + + } + public bool IsActive(AbilityKind kind) { return activeAbilities.Where(i => i.Kind == kind).Any(); diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Abilities/Ability.cs b/Assets/Scripts/Common/Dlls/Roguelike/Abilities/Ability.cs index 73d2c8fc..4e5d20ba 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Abilities/Ability.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Abilities/Ability.cs @@ -42,7 +42,7 @@ public abstract class Ability : IDescriptable, IHotbarItem { protected string primaryStatDescription; public int Level { get; set; } - + public List Stats { get; set; } = new List(); public int MaxLevel = 5; @@ -56,7 +56,7 @@ public abstract class Ability : IDescriptable, IHotbarItem public int CoolDownCounter { get; set; } public bool UsesCoolDownCounter { get; set; } = false; public int MaxCollDownCounter { get; set; } = 5; - public bool AutoApply { get; set; } + //public bool AutoApply { get; set; } public bool TurnsIntoLastingEffect { get; set; } public Ability() @@ -323,6 +323,13 @@ public string Name get; set; } + public bool IsBowLike + { + get { + return Kind == AbilityKind.PerfectHit || Kind == AbilityKind.PiercingArrow || Kind == AbilityKind.ArrowVolley; + } + + } public Tuple GetNextLevelStats() { diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Abilities/AbilityLastingEffectSrc.cs b/Assets/Scripts/Common/Dlls/Roguelike/Abilities/AbilityLastingEffectSrc.cs index bc452134..a14cbe26 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Abilities/AbilityLastingEffectSrc.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Abilities/AbilityLastingEffectSrc.cs @@ -13,22 +13,35 @@ public class AbilityLastingEffectSrc : ILastingEffectSrc { Ability ability; int index; + + public AbilityLastingEffectSrc(Ability ab, int index) { ability = ab; this.index = index; StatKindPercentage = new PercentageFactor(0); StatKindEffective = new EffectiveFactor(0); + if (ab.Stats[index].Unit == EntityStatUnit.Percentage) StatKindPercentage = new PercentageFactor(ab.PrimaryStat.Factor); - else if(ab.Stats[index].Unit == EntityStatUnit.Absolute) + else if (ab.Stats[index].Unit == EntityStatUnit.Absolute) StatKindEffective = new EffectiveFactor(ab.PrimaryStat.Factor); } public AbilityKind AbilityKind { get => ability.Kind; } public int Duration { get; set; } = 3; - public EntityStatKind StatKind { get => ability.Stats[index].Kind; set => ability.PrimaryStat.Kind = value; } + public EntityStatKind StatKind + { + get + { + return ability.Stats[index].Kind; + } + set + { + ability.PrimaryStat.Kind = value; + } + } public PercentageFactor StatKindPercentage { get; private set; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Abilities/ActiveAbility.cs b/Assets/Scripts/Common/Dlls/Roguelike/Abilities/ActiveAbility.cs index fb74733c..ffa8f681 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Abilities/ActiveAbility.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Abilities/ActiveAbility.cs @@ -9,7 +9,8 @@ namespace Roguelike.Abilities { - public enum AbilityState { Unset, Working, CoolingDown } + //public enum AbilityVisualState { Unset, Selected, Highlighted, Disabled } + public enum AbilityState { Unset, Unusable, Activated, Working, CoolingDown } public enum AbilityProperty { Unset, Range, Duration, Durability} /// @@ -99,6 +100,7 @@ public override AbilityKind Kind case AbilityKind.PerfectHit: psk = EntityStatKind.PerfectHitDamage; ask = EntityStatKind.PerfectHitChanceToHit; + MaxLevel = 10; break; case AbilityKind.IronSkin: @@ -189,22 +191,22 @@ public override AbilityKind Kind AuxStat.Unit = EntityStatUnit.Percentage; } - if (//kind == AbilityKind.Rage || - kind == AbilityKind.IronSkin //|| + if (kind == AbilityKind.Rage || + kind == AbilityKind.IronSkin //kind == AbilityKind.OpenWound || - //kind == AbilityKind.ElementalVengeance + //kind == AbilityKind.ElementalVengeance || //kind == AbilityKind.ZealAttack ) { - RunAtTurnStart = true; + RunAtActivation = true; //AutoApply = true; //Activated = true; } } } - public bool RunAtTurnStart { get; internal set; } - + public bool RunAtActivation { get; internal set; } + public bool RunAtTurnStart => RunAtActivation; public override float CalcFactor(int index, int level) { AbilityKind kind = Kind; @@ -268,7 +270,7 @@ public override float CalcFactor(int index, int level) //ask = EntityStatKind.NumberOfPiercedVictims; if (primary) { - var percentToDo = new int[] { 0, 5, 10, 15, 20, 25 }; + var percentToDo = new int[] { 0, 10, 20, 30, 40, 50 }; factor = percentToDo[level]; } else @@ -348,12 +350,12 @@ public override float CalcFactor(int index, int level) case AbilityKind.PerfectHit: if (primary) { - var victims = new int[] { 0, 10, 15, 35, 50, 70 };//PerfectHitDamage % + var victims = new int[] { 0, 15, 30, 45, 55, 70, 85, 100, 115, 130, 150 };//PerfectHitDamage % factor = victims[level]; } else { - var extraDamages = new int[] { 0, 5, 10, 15, 20, 25 };//PerfectHitChanceToHit % + var extraDamages = new int[] { 0, 05, 07, 10, 12, 14, 17, 20, 22, 24, 25 };//PerfectHitChanceToHit % factor = extraDamages[level]; } break; diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Abstract/IGame.cs b/Assets/Scripts/Common/Dlls/Roguelike/Abstract/IGame.cs index 1fdb80e0..abf2bae6 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Abstract/IGame.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Abstract/IGame.cs @@ -37,6 +37,7 @@ public GameManager GameManager public Container Container { get; set; } public Hero Hero { get { return GameManager.Hero; } } public abstract Dungeons.TileContainers.DungeonNode GenerateDungeon(); - public static string Version { get; } = "0.6.7"; + public static string Version { get; } = "0.8.7"; + public static bool VersionNeedsNewWorld { get; } = false; } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Inventory/IInventoryOwner.cs b/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Inventory/IInventoryOwner.cs index 6d8b2618..6418dfd5 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Inventory/IInventoryOwner.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Inventory/IInventoryOwner.cs @@ -8,5 +8,7 @@ public interface IInventoryOwner int GetPrice(Loot loot); int Gold { get; set; } bool GetGoldWhenSellingTo(IInventoryOwner other); + + bool IsSellable(Loot loot); } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Projectiles/IProjectile.cs b/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Projectiles/IProjectile.cs index a4577124..9a624c83 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Projectiles/IProjectile.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Projectiles/IProjectile.cs @@ -8,11 +8,6 @@ public interface IProjectile : Dungeons.Tiles.Abstract.IProjectile { [JsonIgnore] LivingEntity Caller { get; set; } - - - - AbilityKind ActiveAbilitySrc { get; set; } - - + AbilityKind ActiveAbilitySrc { get; } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Spells/ISpell.cs b/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Spells/ISpell.cs index 49d42926..b127f044 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Spells/ISpell.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Abstract/Spells/ISpell.cs @@ -24,6 +24,8 @@ public interface ISpell : Dungeons.Tiles.Abstract.ISpell SpellStatsDescription CreateSpellStatsDescription(bool currentLevel); int NextLevelMagicNeeded { get; } + + bool SendByGod { get; set; } //EntityStatKind[] GetEntityStatKinds(); } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Attributes/EntityStat.cs b/Assets/Scripts/Common/Dlls/Roguelike/Attributes/EntityStat.cs index d1cba283..2e5fba3c 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Attributes/EntityStat.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Attributes/EntityStat.cs @@ -16,11 +16,9 @@ public class EntityStat [JsonIgnore] public bool Hidden { get; set; } - - [JsonIgnore] + public bool CanBeBelowZero { get; set; } = false; - - [JsonIgnore] + public bool UseSign { get; set; } public EntityStat() : this(EntityStatKind.Unset, 0) @@ -203,6 +201,12 @@ public static float Round(float value) return value.Rounded(); } + public static int GetRoundedStat(float cv) + { + cv = Round(cv); + return (int)(Math.Ceiling(cv)); + } + public string GetFormattedCurrentValue(float cv) { var val = cv.ToString(); @@ -210,8 +214,7 @@ public string GetFormattedCurrentValue(float cv) val += " " + "%"; else { - cv = Round(cv); - int intVal = (int)(Math.Ceiling(cv)); + int intVal = GetRoundedStat(cv); val = intVal.ToString(); } return val; diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Attributes/EntityStats.cs b/Assets/Scripts/Common/Dlls/Roguelike/Attributes/EntityStats.cs index bafc4afc..fe0d8269 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Attributes/EntityStats.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Attributes/EntityStats.cs @@ -39,7 +39,9 @@ public enum EntityStatKind Strength = 1, Health = 2, Magic = 3, Defense = 4, Dexterity = 5, Mana = 10, - + Virility = 11, + + ResistFire = 30, ResistCold = 32, ResistPoison = 34, ResistLighting = 36, diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Calculated/AttackDescription.cs b/Assets/Scripts/Common/Dlls/Roguelike/Calculated/AttackDescription.cs index f6b9a621..ccf38ae8 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Calculated/AttackDescription.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Calculated/AttackDescription.cs @@ -30,14 +30,16 @@ public class AttackDescription public AttackDescription(LivingEntity ent, bool withVariation = true, AttackKind attackKind = AttackKind.Unset,//if uset it will be based on current weapon/active fi - OffensiveSpell spell = null) + OffensiveSpell spell = null, + ProjectileFightItem pfi = null) { AttackKind = attackKind; fightItem = null; - Calc(ent, withVariation, ref attackKind, spell); + Calc(ent, withVariation, ref attackKind, spell, pfi); } - private void Calc(LivingEntity ent, bool withVariation, ref AttackKind attackKind, OffensiveSpell spell) + private void Calc(LivingEntity ent, bool withVariation, ref AttackKind attackKind, + OffensiveSpell spell, ProjectileFightItem pfi = null) { try { @@ -65,7 +67,11 @@ private void Calc(LivingEntity ent, bool withVariation, ref AttackKind attackKin fightItem = GetActiveFightItem(ent); fi = fightItem; if (fightItem == null) - return; + { + fightItem = pfi; + if (fightItem == null) + return; + } } if (attackKind == AttackKind.WeaponElementalProjectile) { diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Calculated/NextLevelCalculator.cs b/Assets/Scripts/Common/Dlls/Roguelike/Calculated/NextLevelCalculator.cs new file mode 100644 index 00000000..eaff00e7 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Calculated/NextLevelCalculator.cs @@ -0,0 +1,57 @@ +using Roguelike.Generators; +using Roguelike.Tiles.LivingEntities; +using System.Collections.Generic; +using System.Linq; + +namespace Roguelike.Calculated +{ + internal class NextLevelCalculator + { + Dictionary levelToExpThreshold = new Dictionary(); + + public double NextLevelExperience { get; private set; } + public double PrevLevelExperience { get; private set; } + public int LevelUpPoints { get; private set; } + AdvancedLivingEntity entity; + + public NextLevelCalculator(AdvancedLivingEntity entity) + { + this.entity = entity; + levelToExpThreshold[1] = GenerationInfo.FirstNextLevelExperienceThreshold; + double nextLevelExp = levelToExpThreshold[1]; + for (int i = 2; i < 100; i++) + { + levelToExpThreshold[i] = CalcNextLevel(nextLevelExp, i); + nextLevelExp = levelToExpThreshold[i]; + } + } + + double CalcNextLevel(double nextLevelExperience, int nextLevel) + { + double nextExp = FactorCalculator.AddFactor((int)nextLevelExperience, 60); + + if (nextLevel <= 5) + { + nextExp = nextExp * 1.3; + } + return nextExp; + } + + public int GetNextLevel(int currentLevel) + { + var prevLevel = levelToExpThreshold.First(); + foreach (var item in levelToExpThreshold) + { + if (entity.Experience < item.Value) + { + NextLevelExperience = item.Value; + PrevLevelExperience = prevLevel.Value; + LevelUpPoints = (item.Key - currentLevel)*GenerationInfo.LevelUpPoints; + return item.Key; + } + prevLevel = item; + } + return 100; + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Calculated/NextLevelCalculator.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Calculated/NextLevelCalculator.cs.meta new file mode 100644 index 00000000..481e02a2 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Calculated/NextLevelCalculator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ad1fafa16851ad1468abcc1081209bd9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/CraftWorker.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/CraftWorker.cs new file mode 100644 index 00000000..b982eae8 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/CraftWorker.cs @@ -0,0 +1,100 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System; +using System.Collections.Generic; +using System.Linq; +using Dungeons.Core; +using Roguelike.Crafting.Workers; +using System.Diagnostics; +using Roguelike.Core.Crafting.Workers; + +namespace Roguelike.Crafting +{ + public class RecipeRequiredItem + { + public Type Type { get; set; } + public int MinCount { get; set; } = 1; + + public override string ToString() + { + return base.ToString() + "Type: "+ Type; + } + + } + + public class RecipeRequiredItems + { + List items = new List(); + + public List Items { get => items; set => items = value; } + } + + public abstract class CraftWorker : LootCrafterTools + { + static List recipeKinds; + + protected List lootToConvert; + protected List eqs; + protected List enchanters; + protected List stacked; + protected ILootCrafter lootCrafter; + + public List usedSrcItems = new List(); + + public RecipeKind Kind { get; set; } + public List LootToConvert + { + get => lootToConvert; + } + public List Eqs { get => eqs; } + + static CraftWorker() + { + recipeKinds = EnumHelper.GetEnumValues(true); + recipeKinds.Remove(RecipeKind.Custom); + } + + public CraftingResult ReturnCanDo(CraftingResult previewResult) + { + return previewResult == null ? new CraftingResult(this.lootToConvert): previewResult; + } + + public CraftWorker() + { + } + + public static bool IsAdvisorSupportsBulk(RecipeKind rk) + { + if (rk == RecipeKind.EnchantEquipment) + return false; + if (rk == RecipeKind.TransformGem || rk == RecipeKind.TwoEq) + return false; + return true; + } + + public CraftWorker(List lootToConvert, ILootCrafter lootCrafter) + { + Init(lootToConvert, lootCrafter); + } + + public virtual void Init(List lootToConvert, ILootCrafter lootCrafter) + { + this.lootCrafter = lootCrafter; + this.lootToConvert = lootToConvert; + eqs = Filter(lootToConvert); + enchanters = Filter(lootToConvert); + stacked = Filter(lootToConvert); + } + public override void ReportError(string errorMessage) + { + Debug.WriteLine("!!!" + errorMessage + ", "+Kind); + } + + public abstract CraftingResult CanDo(); + + public abstract CraftingResult Do(); + + public abstract RecipeRequiredItems GetRequiredItems(); + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/CraftWorker.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/CraftWorker.cs.meta new file mode 100644 index 00000000..1ee3ee5f --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/CraftWorker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e8f6a67e18545d4eb7d95a41033f591 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/CraftingResult.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/CraftingResult.cs index 9f0e1f82..eef66a64 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/CraftingResult.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/CraftingResult.cs @@ -1,4 +1,5 @@ using Roguelike.Tiles; +using Roguelike.Tiles.Looting; using System; using System.Collections.Generic; using System.Linq; @@ -11,29 +12,38 @@ namespace Roguelike.Crafting /// public class CraftingResult { - public string Message { get; set; } - public List LootItems { get; set; } + public RecipeKind UsedKind { get; set; } + public string Message { get; set; } = ""; + public List OutLootItems { get; set; } = new List(); + public List UsedInputItems { get; set; } = new List(); - public bool Success { get { return LootItems != null && LootItems.Any(); } } + + public bool Success { get { return OutLootItems != null && OutLootItems.Any(); } } /// /// Normally true but in rare cases when Eq is enhanced/fixed (e.g.Magical weapon recharge) false /// public bool DeleteCraftedLoot { get; set; } = true; + public bool AddOutLoot { get; set; } public CraftingResult(List lootItems) { - this.LootItems = lootItems; + this.OutLootItems = lootItems; } public Loot FirstOrDefault() { - return LootItems.FirstOrDefault(); + return OutLootItems.FirstOrDefault(); } public T FirstOrDefault() where T : Loot { - return LootItems.FirstOrDefault() as T; + return OutLootItems.FirstOrDefault() as T; + } + + public override string ToString() + { + return base.ToString() + ", "+Message; } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/LootConverter.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/LootConverter.cs index a6f7a5dd..16adf959 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/LootConverter.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/LootConverter.cs @@ -1,15 +1,14 @@ using Dungeons.Core; using Roguelike.Attributes; -using Roguelike.Extensions; +using Roguelike.Crafting.Workers; using Roguelike.Generators; using Roguelike.LootFactories; using Roguelike.Tiles; -using Roguelike.Tiles.LivingEntities; using Roguelike.Tiles.Looting; using SimpleInjector; using System; using System.Collections.Generic; -using System.IO; +using System.Diagnostics; using System.Linq; namespace Roguelike.Crafting @@ -17,41 +16,24 @@ namespace Roguelike.Crafting public interface ILootCrafter { CraftingResult Craft(Recipe recipe, List lootToCraft); - } + bool ApplyEnchant(Enchanter enchant, Equipment eq, out string err); - public abstract class LootCrafterBase : ILootCrafter + LootGenerator GetLootGenerator(); + Type GetMinedLootType(); + } + + public abstract class LootCrafterBase : LootCrafterTools, ILootCrafter { - static CraftingResult error = new CraftingResult(null); + public abstract bool ApplyEnchant(Enchanter enchant, Equipment eq, out string err); + + public abstract CraftingResult Craft(Recipe recipe, List lootToCraft); - /// - /// - /// - /// - /// Normally true but in rare cases when Eq is enhanced/fixed (e.g. Magical weapon recharge) false - /// - protected CraftingResult ReturnCraftedLoot(List loot, bool deleteCraftedLoot = true) - { - return new CraftingResult(loot) { DeleteCraftedLoot = deleteCraftedLoot }; - } + public abstract LootGenerator GetLootGenerator(); - /// - /// - /// - /// - /// Normally true but in rare cases when Eq is enhanced/fixed (e.g. Magical weapon recharge) false - /// - protected CraftingResult ReturnCraftedLoot(Loot loot, bool deleteCraftedLoot = true) + public virtual Type GetMinedLootType() { - if (loot == null)//ups - return ReturnCraftingError("Improper ingredients"); - return ReturnCraftedLoot(new List() { loot }, deleteCraftedLoot); - } - - protected CraftingResult ReturnCraftingError(string errorMessage) - { - error.Message = errorMessage; - return error; + throw new NotImplementedException(); } } @@ -59,66 +41,41 @@ public class LootCrafter : LootCrafterBase { Container container; EquipmentFactory equipmentFactory; - const string InvalidIngredients = "Invalid ingredients"; + public override LootGenerator GetLootGenerator() + { + return container.GetInstance(); + } public LootCrafter(Container container) { this.container = container; equipmentFactory = container.GetInstance(); } - protected List Filter(List lootToConvert) + protected virtual EquipmentKind GetEquipmentKindForGemApply(Equipment eq) { - return lootToConvert.Where(i => i is T).Cast().ToList(); + return eq.EquipmentKind; } - public bool ApplyEnchant(Enchanter enchant, Equipment eq, out string err) + public override bool ApplyEnchant(Enchanter enchant, Equipment eq, out string err) { return enchant.ApplyTo(eq, () => { return GetEquipmentKindForGemApply(eq); }, out err); } - - protected T FilterOne(List lootToConvert) - { - return lootToConvert.Where(i => i is T).Cast().FirstOrDefault(); - } - - int GetStackedCount(List lootToConvert, string name = "") where T : StackedLoot - { - if(name.Any()) - { - lootToConvert = lootToConvert.Where(i => i.Name == name).ToList(); - } - var stacked = Filter(lootToConvert).FirstOrDefault(); - return stacked != null ? stacked.Count : 0; - } - - List GetToadstools(List lootToConvert) - { - return lootToConvert.Where(i => i.IsToadstool()).Cast().ToList(); - } - - int GetToadstoolsCount(List lootToConvert) - { - var toadstools = lootToConvert.Where(i => i.IsToadstool()).ToList(); - return GetStackedCount(toadstools); - } - + + protected List orgLootToConvert; + protected MagicDust magicDust; + protected Recipe orgRecipe; public override CraftingResult Craft(Recipe recipe, List lootToConvert) { if (recipe == null) return ReturnCraftingError("Recipe not set"); - if (recipe.MagicDustRequired > 0) - { - var magicDust = lootToConvert.Where(i => i is MagicDust).Cast().FirstOrDefault(); - if (magicDust == null || magicDust.Count < recipe.MagicDustRequired) - { - return ReturnCraftingError("Invalid amount of Magic Dust"); - } - } + orgRecipe = recipe; + magicDust = FilterOne(lootToConvert); + orgLootToConvert = lootToConvert.ToList(); lootToConvert = lootToConvert.Where(i => !(i is MagicDust)).ToList(); - var eqs = lootToConvert.Where(i => i is Equipment).Cast().ToList(); - + var eqs = Filter(lootToConvert); + if (eqs.Any(i => i.Class == EquipmentClass.Unique)) { string error; @@ -128,179 +85,46 @@ public override CraftingResult Craft(Recipe recipe, List lootToConvert) } if (lootToConvert.Any()) { - var sulfCount = GetStackedCount(lootToConvert, "Sulfur"); - var hoochCount = GetStackedCount(lootToConvert); + return Convert(recipe, lootToConvert); + } - if (lootToConvert.Count == 2 && recipe.Kind == RecipeKind.Custom) - { - return HandleCustomRecipe(lootToConvert); - } - else if (recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.UnEnchantEquipment) - { - return UnEnchantEquipment(lootToConvert); - } - else if (recipe.Kind == RecipeKind.CraftSpecialPotion && lootToConvert.Count == 2) - { - var healthPotion = lootToConvert.Where(i => i.IsPotionKind(PotionKind.Health)).FirstOrDefault(); - var manaPotion = lootToConvert.Where(i => i.IsPotionKind(PotionKind.Mana)).FirstOrDefault(); - if (healthPotion != null || manaPotion != null) - { - var toad = lootToConvert.Where(i => i.IsToadstool()); - if (toad != null) - { - Potion pot = null; - if (healthPotion != null) - pot = new SpecialPotion(SpecialPotionKind.Strength, SpecialPotionSize.Small); - else - pot = new SpecialPotion(SpecialPotionKind.Magic, SpecialPotionSize.Small); - return ReturnCraftedLoot(pot); - } - } - } - else if (recipe.Kind == RecipeKind.RechargeMagicalWeapon) - { - return RechargeMagicalWeapon(lootToConvert); - } - else if (recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.AntidotePotion) - { - return AntidotePotion(lootToConvert); - } - else if (recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.Pendant) - { - return Pendant(lootToConvert); - } - else if (recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.EnchantEquipment) - { - return EnchantEq(lootToConvert); - } - else if ((recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.ExplosiveCocktail) && - sulfCount > 0 && hoochCount > 0) - { - if (sulfCount != hoochCount) - return ReturnCraftingError("Number of ingradients must be the same (except for Magic Dust)"); - return ReturnCraftedLoot(new ProjectileFightItem(FightItemKind.ExplosiveCocktail, null)); - } + return ReturnCraftingError(InvalidIngredients); + } - var allGems = lootToConvert.All(i => i is Gem); - if ((recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.ThreeGems) && allGems && lootToConvert.Count == 1 && - GetStackedCount(lootToConvert) == 3) - { - return HandleAllGems(lootToConvert); - } + protected virtual CraftingResult Convert(Recipe recipe, List lootToConvert) + { + var fac = new WorkersFactory(lootToConvert, this, this.container); + var previewRes = ReturnCraftingError("Unsupported operation"); + var worker = fac.GetWorker(recipe.Kind); - var hpCount = lootToConvert.Where(i => i.IsPotionKind(PotionKind.Health)).Count(); - var mpCount = lootToConvert.Where(i => i.IsPotionKind(PotionKind.Mana)).Count(); - var toadstools = lootToConvert.Where(i => i.IsToadstool()).ToList(); - var toadstoolsCount = GetStackedCount(toadstools); - if ((recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.TransformPotion) && - (lootToConvert.Count == 2 && toadstoolsCount == 1 && (hpCount == 1 || mpCount == 1))) - { - //if ((lootToConvert[0] as Potion).Count == 1)//TODO allow many conv (use many M Dust) - var potion = lootToConvert.Where(i => i.IsPotion()).Single(); - - if (potion.AsPotion().Kind == PotionKind.Mana) - { - if (toadstools.Single().AsToadstool().MushroomKind == MushroomKind.RedToadstool) - return ReturnCraftedLoot(new Potion(PotionKind.Health)); - } - else - { - if (toadstools.Single().AsToadstool().MushroomKind == MushroomKind.BlueToadstool) - return ReturnCraftedLoot(new Potion(PotionKind.Mana)); - } - } + if (worker != null) + { + if (worker.Kind != recipe.Kind) + recipe = new Recipe(worker.Kind); - if (recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.Toadstools2Potion) + if (magicDust == null || magicDust.Count < orgRecipe.MagicDustRequired) { - var allToadstool = lootToConvert.All(i => i.IsToadstool()); - if (allToadstool) - { - var toadstool = lootToConvert[0].AsToadstool(); - if (toadstool != null && toadstool.Count == 3) - { - return Toadstools2Potion(toadstool); - } - } + return ReturnCraftingError("Invalid amount of Magic Dust");// + worker.Kind); - do not show it to user } - } - if (recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.OneEq) - { - if (lootToConvert.Count == 1) + previewRes = worker.CanDo(); + if (previewRes.Success) { - var srcEq = lootToConvert[0] as Equipment; - if (srcEq != null) + Debug.WriteLine("Do " + worker.Kind); + var did = worker.Do(); + if (did.Success) { - return CraftOneEq(srcEq); - } - } - } - else if (lootToConvert[0] is Gem && (recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.TransformGem)) - { - return TransformGem(lootToConvert); - } - else if (recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.Arrows || recipe.Kind == RecipeKind.Bolts) - { - return ConvertBoltsOrArrows(recipe, lootToConvert); - } - else if (recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.NiesiolowskiSoup) - { - return CraftNiesiolowskiSoup(recipe, lootToConvert); - } - else if (recipe.Kind == RecipeKind.Custom || recipe.Kind == RecipeKind.TwoEq) - { - if (lootToConvert.Count == 2) - { - if(eqs.Count == 2) - return CraftTwoEq(eqs); - if (lootToConvert.Any(i => i is KeyHalf)) - { - var parts = Filter(lootToConvert); - if (parts.Count != 2 || !parts[0].Matches(parts[1])) + did.UsedKind = worker.Kind; + if (did.UsedInputItems == null) { - return ReturnCraftingError("Two matching parts of a key are needed"); + var reqItems = worker.GetRequiredItems(); + did.UsedInputItems = orgLootToConvert.Where(i => reqItems.Items.Any(i => i.Type == i.GetType())).ToList(); } - - return ReturnCraftedLoot(new Key() { KeyName = (lootToConvert[0] as KeyHalf).KeyName, Kind = KeyKind.BossRoom }); ; ; - } - - if (lootToConvert.Any(i => i is KeyMold)) - { - return ConvertMold(recipe, lootToConvert); - - } - } - } - else if (recipe.Kind == RecipeKind.Custom && eqs.Count == 1) - { - if (eqs[0].EquipmentKind == EquipmentKind.Weapon) - { - var spsCount = GetStackedCount(lootToConvert); - if (spsCount == lootToConvert.Count - 1 && spsCount <= 2) - { - return ReturnStealingEq(eqs, Filter(lootToConvert)); } + return did; } } - else if (recipe.Kind == RecipeKind.Custom && lootToConvert.Count == 2) - { - //turn one Toadstool kind into other (using Potion) - var toadstoolsCount = GetToadstoolsCount(lootToConvert); - var potions = lootToConvert.Where(i => i.IsPotion()).ToList(); - if (toadstoolsCount == 1 && potions.Count == 1) - { - var tk = GetToadstools(lootToConvert)[0].MushroomKind; - var pk = (potions[0].AsPotion()).Kind; - - if (tk == MushroomKind.RedToadstool && pk == PotionKind.Mana) - return ReturnCraftedLoot(new Mushroom(MushroomKind.BlueToadstool)); - else if (tk == MushroomKind.BlueToadstool && pk == PotionKind.Health) - return ReturnCraftedLoot(new Mushroom(MushroomKind.RedToadstool)); - } - } - - - return ReturnCraftingError(InvalidIngredients); + return previewRes; } protected virtual CraftingResult ConvertMold(Recipe recipe, List lootToConvert) @@ -308,198 +132,13 @@ protected virtual CraftingResult ConvertMold(Recipe recipe, List lootToCon return ReturnCraftingError("TODO"); } - private CraftingResult UnEnchantEquipment(List lootToConvert) - { - var eqsToUncraft = Filter(lootToConvert); - if (eqsToUncraft.Count != 1 || eqsToUncraft[0].Enchants.Count == 0) - return ReturnCraftingError("One enchanted piece of equipment is required"); - var eq = eqsToUncraft[0]; - var enchs = eqsToUncraft[0].Enchants.Select(i => i.Enchanter).ToList(); - enchs.ForEach(i => i.Count = 1); - foreach (var ench in eqsToUncraft[0].Enchants) - { - foreach (var stat in ench.StatKinds) - { - eq.RemoveMagicStat(stat, ench.StatValue); - } - } - eq.Enchants = new List(); - - var lootItems = new List() { eqsToUncraft[0] }; - lootItems.AddRange(enchs); - return ReturnCraftedLoot(lootItems);//deleteCraftedLoot:false - } - - private CraftingResult TransformGem(List lootToConvert) - { - var srcGem = lootToConvert[0] as Gem; - var destKind = RandHelper.GetRandomEnumValue(new GemKind[] { srcGem.GemKind, GemKind.Unset }); - var destGem = new Gem(destKind); - destGem.EnchanterSize = srcGem.EnchanterSize; - destGem.SetProps(); - return ReturnCraftedLoot(destGem); - } - - private CraftingResult Toadstools2Potion(Mushroom toadstool) - { - Potion potion = null; - if (toadstool.MushroomKind == MushroomKind.BlueToadstool) - potion = new Potion(PotionKind.Mana); - else - potion = new Potion(PotionKind.Health); - return ReturnCraftedLoot(potion); - } - - private CraftingResult EnchantEq(List lootToConvert) - { - var equips = lootToConvert.Where(i => i is Equipment); - var equipsCount = equips.Count(); - if (equipsCount == 0) - return ReturnCraftingError("Equipment is needed by the Recipe"); - if (equipsCount > 1) - return ReturnCraftingError("One equipment is needed by the Recipe"); - var enchanters = lootToConvert.Where(i => !(i is Equipment)).ToList(); - if (enchanters.Any(i => !(i is Enchanter))) - { - return ReturnCraftingError("Only enchanting items (gems, claws,...)are alowed by the Recipe"); - } - var eq = equips.ElementAt(0) as Equipment; - if (!eq.Enchantable) - return ReturnCraftingError("Equipment is not " + Translations.Strings.Enchantable.ToLower()); - var freeSlots = eq.EnchantSlots - eq.Enchants.Count; - if (freeSlots < enchanters.Count()) - return ReturnCraftingError("Too many enchantables added"); - string err; - foreach (var ench in enchanters.Cast()) - { - //if (!ench.ApplyTo(eq, () => { return GetEquipmentKindForGemApply(eq); }, out err)) - if (!ApplyEnchant(ench, eq, out err)) - return ReturnCraftingError(InvalidIngredients); - } - - return ReturnCraftedLoot(eq, false); - } - - private CraftingResult Pendant(List lootToConvert) - { - var cords = GetStackedCount(lootToConvert); - if (cords == 0) - return ReturnCraftingError("Cord is needed by the Recipe"); - - var amulet = Equipment.CreatePendant(); - return ReturnCraftedLoot(amulet); - } - - private CraftingResult AntidotePotion(List lootToConvert) - { - var plants = GetStackedCount(lootToConvert); - if (plants != 1 || Filter(lootToConvert).Where(i => i.Kind == PlantKind.Thistle).Count() != 1) - return ReturnCraftingError("One thistle is needed by the Recipe"); - - var hoohCount = GetStackedCount(lootToConvert); - if (hoohCount != 1) - return ReturnCraftingError("One hooch is needed by the Recipe"); - - return ReturnCraftedLoot(new Potion(PotionKind.Antidote), true); - } - - private CraftingResult RechargeMagicalWeapon(List lootToConvert) - { - var equips = lootToConvert.Where(i => i is Weapon wpn0 && wpn0.IsMagician).Cast().ToList(); - var equipsCount = equips.Count(); - if (equipsCount != 1) - return ReturnCraftingError("One charge emitting weapon is needed by the Recipe"); - - var wpn = equips[0]; - //foreach (var wpn in equips) - { - (wpn.SpellSource as WeaponSpellSource).Restore(); - wpn.UpdateMagicWeaponDesc(); - } - return ReturnCraftedLoot(wpn, false); - } - - private CraftingResult CraftNiesiolowskiSoup(Recipe recipe, List lootToConvert) - { - var sorrel = Filter(lootToConvert).Where(i => i.Kind == PlantKind.Sorrel).FirstOrDefault(); - if (sorrel == null) - ReturnCraftingError("Sorrel not available"); - - var plum = Filter(lootToConvert).Where(i => i.Kind == FoodKind.Plum).FirstOrDefault(); - if (plum == null) - ReturnCraftingError("Plum not available"); - - var count = GetCraftedStackedCount(lootToConvert); - - return ReturnCraftedLoot(new Food() { Kind = FoodKind.NiesiolowskiSoup, Count = count }); - } - - protected int GetCraftedStackedCount(List lootToConvert) - { - return lootToConvert.Cast().Min(i => i.Count); - } - protected virtual CraftingResult ConvertBoltsOrArrows(Recipe recipe, List lootToConvert) { return ReturnCraftingError("TODO"); } + - protected virtual EquipmentKind GetEquipmentKindForGemApply(Equipment eq) - { - return eq.EquipmentKind; - } - - private CraftingResult HandleCustomRecipe(List lootToConvert) - { - if (lootToConvert.Count == 2) - { - //if ( - // (lootToConvert[0].IsToadstool() && lootToConvert[1].IsPotion(PotionKind.Health)) || - // (lootToConvert[1].IsToadstool() && lootToConvert[0].IsPotion(PotionKind.Health)) || - // (lootToConvert[0].IsToadstool() && lootToConvert[1].IsPotion(PotionKind.Mana)) || - // (lootToConvert[1].IsToadstool() && lootToConvert[0].IsPotion(PotionKind.Mana)) - // ) - //{ - // var srcLoot = lootToConvert[0].IsToadstool() ? lootToConvert[0] : lootToConvert[1]; - // var destLoot = lootToConvert[0].IsToadstool() ? lootToConvert[1] : lootToConvert[0]; - // var crafted = srcLoot.CreateCrafted(destLoot); - // if(crafted!=null) - // return ReturnCraftedLoot(crafted); - //} - ////////////////////////////////////////////////////////////////////////////// - if (lootToConvert[0] is Gem && lootToConvert[1] is Equipment || - lootToConvert[1] is Gem && lootToConvert[0] is Equipment - ) - { - var gem = lootToConvert[0] is Gem ? lootToConvert[0] as Gem : lootToConvert[1] as Gem; - var eq = lootToConvert[0] is Equipment ? lootToConvert[0] as Equipment : lootToConvert[1] as Equipment; - var err = ""; - if(ApplyEnchant(gem, eq, out err)) - { - return ReturnCraftedLoot(eq); - } - else - return ReturnCraftingError(err); - } - ////////////////////////////////////////////////////////////////////////////// - //if (lootToConvert[0].IsCraftableWith(lootToConvert[1]) || - // lootToConvert[1].IsCraftableWith(lootToConvert[0]) - // ) - //{ - // if (lootToConvert[0].IsCraftableWith(lootToConvert[1])) - // return ReturnCraftedLoot(lootToConvert[0].CreateCrafted(lootToConvert[1])); - // else - // return ReturnCraftedLoot(lootToConvert[1].CreateCrafted(lootToConvert[0])); - //} - - return ReturnCraftingError("Improper ingredients"); - } - else - return ReturnCraftingError("Invalid amount of ingradients"); - } - - - private CraftingResult ReturnStealingEq(List eqs, List sps) + private CraftingResult ReturnStealingEq(List eqs, List sps) { if (eqs[0].ExtendedInfo.Stats.GetFactor(EntityStatKind.LifeStealing) > 10 || eqs[0].ExtendedInfo.Stats.GetFactor(EntityStatKind.ManaStealing) > 10) @@ -532,136 +171,5 @@ private CraftingResult ReturnStealingEq(List eqs, List return ReturnCraftedLoot(eqs[0]); } - private CraftingResult CraftTwoEq(List eqs) - { - var eq1 = eqs[0]; - var eq2 = eqs[1]; - if (eq1.EquipmentKind != eq2.EquipmentKind) - return ReturnCraftingError("Equipment for crafting must be of the same type"); - - if (eq1.WasCraftedBy(RecipeKind.TwoEq) || eq2.WasCraftedBy(RecipeKind.TwoEq)) - return ReturnCraftingError("Can not craft equipment which was already crafted in pairs"); - var destEq = eq1.Price > eq2.Price ? eq1 : eq2; - - var srcEq = destEq == eq1 ? eq2 : eq1; - var srcHadEmptyEnch = srcEq.Enchantable && !srcEq.MaxEnchantsReached(); - var destHadEmptyEnch = destEq.Enchantable && !destEq.MaxEnchantsReached(); - - float priceInc = 0; - var enhPr = GetEnhStatValue(destEq.PrimaryStatValue, destEq.Price, srcEq.Price); - destEq.PrimaryStatValue += enhPr; - priceInc += destEq.GetPriceForFactor(destEq.PrimaryStatKind, enhPr); - - var destStats = destEq.GetMagicStats(); - var srcStats = srcEq.GetMagicStats(); - var srcDiffStats1 = srcStats.Where(i => !destStats.Any(j => j.Key == i.Key)).ToList(); - - if (destStats.Count < 3 && srcDiffStats1.Any()) - { - var countToAdd = 3 - destStats.Count; - foreach (var statToAdd in srcDiffStats1) - { - if (destEq.Class == EquipmentClass.Plain) - destEq.Class = EquipmentClass.Magic; - destEq.SetMagicStat(statToAdd.Key, statToAdd.Value); - countToAdd--; - priceInc += destEq.GetPriceForFactor(statToAdd.Key, (int)statToAdd.Value.Factor); - if (countToAdd == 0) - break; - } - } - else - { - foreach (var destStat in destStats) - { - var enh = GetEnhStatValue(destStat.Value.Factor, destEq.Price, srcEq.Price); - destStat.Value.Factor += enh; - priceInc += destEq.GetPriceForFactor(destStat.Key, (int)enh); - destEq.SetMagicStat(destStat.Key, destStat.Value); - } - } - - if (srcHadEmptyEnch || destHadEmptyEnch) - { - if (destEq.GetMagicStats().Count < 3 && !destEq.Enchantable) - destEq.MakeEnchantable(); - } - destEq.WasCrafted = true; - destEq.CraftingRecipe = RecipeKind.TwoEq; - - //I noticed price is too high comparing to unique items, maybe price should be calculated from scratch ? - //priceInc /= 2; - - destEq.Price += (int)priceInc; - - return ReturnCraftedLoot(destEq); - } - - int GetEnhStatValue(float currentVal, float betterEqPrice, float worseEqPrice) - { - float factor = 10; - factor += factor * (worseEqPrice / betterEqPrice); - if (factor > 16) - factor = 16; - var enh = currentVal * factor / 100f; - if (enh < 1) - enh = 1; - - return (int)Math.Ceiling(enh); - } - - private CraftingResult CraftOneEq(Equipment srcEq) - { - if (srcEq.WasCraftedBy(RecipeKind.TwoEq)) - return ReturnCraftingError("Can not craft equipment which was already crafted in pairs"); ; - - var srcLootKind = srcEq.EquipmentKind; - var lks = Equipment.GetPossibleLootKindsForCrafting().ToList(); - var destLk = RandHelper.GetRandomElem(lks, new EquipmentKind[] { srcLootKind }); - var lootGenerator = container.GetInstance(); - var destEq = lootGenerator.GetRandomEquipment(destLk, srcEq.MinDropDungeonLevel, null); - if (srcEq.Class == EquipmentClass.Magic) - { - destEq.SetClass(EquipmentClass.Magic, srcEq.MinDropDungeonLevel, null, srcEq.IsSecondMagicLevel); - } - var srcStatsCount = srcEq.GetMagicStats().Count; - if (srcStatsCount > destEq.GetMagicStats().Count) - { - var diff = srcStatsCount - destEq.GetMagicStats().Count; - for (int i = 0; i < diff; i++) - { - destEq.AddRandomMagicStat(); - } - } - if (srcEq.Enchantable) - { - destEq.MakeEnchantable(); - } - destEq.WasCrafted = true; - destEq.CraftingRecipe = RecipeKind.OneEq; - return ReturnCraftedLoot(destEq); - } - - private CraftingResult HandleAllGems(List lootToConvert) - { - if (lootToConvert.Count != 1) - return ReturnCraftingError("Invalid amount of gems"); - List gems = Filter(lootToConvert); - var allSameKind = gems.All(i => i.GemKind == gems[0].GemKind); - var allSameSize = gems.All(i => i.EnchanterSize == gems[0].EnchanterSize); - if (allSameKind && allSameSize) - { - if (gems[0].EnchanterSize == EnchanterSize.Big) - return ReturnCraftingError("Big gems can not be crafted"); - var gem = new Gem(); - gem.GemKind = gems[0].GemKind; - gem.EnchanterSize = gems[0].EnchanterSize == EnchanterSize.Small ? EnchanterSize.Medium : EnchanterSize.Big; - gem.SetProps(); - return ReturnCraftedLoot(gem); - } - - return ReturnCraftingError("All gems must be of the same size and type"); - } - } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/LootCrafterTools.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/LootCrafterTools.cs new file mode 100644 index 00000000..72419683 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/LootCrafterTools.cs @@ -0,0 +1,129 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Roguelike.Extensions; +using System.Diagnostics; +//using System.Reflection.Metadata; + +namespace Roguelike.Crafting +{ + public class LootCrafterTools + { + protected const string InvalidIngredients = "Invalid ingredients"; + + + protected int GetEnhStatValue(float currentVal, float betterEqPrice, float worseEqPrice) + { + float factor = 10; + factor += factor * (worseEqPrice / betterEqPrice); + if (factor > 16) + factor = 16; + var enh = currentVal * factor / 100f; + if (enh < 1) + enh = 1; + + return (int)Math.Ceiling(enh); + } + protected bool IsMatchingRecipe(List lootToConvert, RecipeKind kind) + { + if (lootToConvert.Count == 2) + { + if (lootToConvert[0].GetMatchingRecipe(lootToConvert[1]) == kind) + return true; + } + return false; + } + + public List Filter(List lootToConvert) + { + return lootToConvert.Where(i => i is T).Cast().ToList(); + } + protected T FilterOne(List lootToConvert) + { + return lootToConvert.Where(i => i is T).Cast().FirstOrDefault(); + } + + protected int GetStackedCount(List lootToConvert, string name = "") where T : StackedLoot + { + if (name.Any()) + { + lootToConvert = lootToConvert.Where(i => i.Name == name).ToList(); + } + var stacked = Filter(lootToConvert).FirstOrDefault(); + return stacked != null ? stacked.Count : 0; + } + + protected List GetToadstools(List lootToConvert) + { + return lootToConvert.Where(i => i.IsToadstool()).Cast().ToList(); + } + + protected int GetToadstoolsCount(List lootToConvert) + { + var toadstools = lootToConvert.Where(i => i.IsToadstool()).ToList(); + return GetStackedCount(toadstools); + } + + public virtual CraftingResult ReturnCraftingError(string errorMessage) + { + var error = new CraftingResult(new List()); + error.Message = errorMessage; + ReportError(errorMessage); + return error; + } + + public virtual void ReportError(string errorMessage) + { + Debug.WriteLine("!!!" + errorMessage); + } + + public Loot GetMaxStackedCount(Loot[] lootToConvert) + { + var nn = Filter(lootToConvert.ToList()); + return nn.Where(i=> i.Count == nn.Max(i=>i.Count)).FirstOrDefault(); + } + + public int GetCraftedStackedCount(Loot[] lootToConvert) + { + return GetCraftedStackedCount(lootToConvert.ToList()); + } + public int GetCraftedStackedCount(int[] lootToConvertCounts) + { + return lootToConvertCounts.Min(); + } + + public int GetCraftedStackedCount(List lootToConvert) + { + return lootToConvert.Cast().Where(i=> i != null).Min(i => i.Count); + } + + /// + /// + /// + /// + /// Normally true but in rare cases when Eq is enhanced/fixed (e.g. Magical weapon recharge) false + /// + public CraftingResult ReturnCraftedLoot(Loot loot, List usedSrcItems = null, bool deleteCraftedLoot = true) + { + if (loot == null)//ups + return ReturnCraftingError("Improper ingredients"); + + return ReturnCraftedLoot(new List() { loot }, usedSrcItems, deleteCraftedLoot); + } + + /// + /// + /// + /// + /// Normally true but in rare cases when Eq is enhanced/fixed (e.g. Magical weapon recharge) false + /// + protected CraftingResult ReturnCraftedLoot(List loot, List usedSrcItems = null, bool deleteCraftedLoot = true) + { + return new CraftingResult(loot) { DeleteCraftedLoot = deleteCraftedLoot, UsedInputItems = usedSrcItems }; + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/LootCrafterTools.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/LootCrafterTools.cs.meta new file mode 100644 index 00000000..1eaedc7e --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/LootCrafterTools.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: de4ccadb87e230a46922c063ddc84671 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/RecipeUsingAdvisor.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/RecipeUsingAdvisor.cs new file mode 100644 index 00000000..10a143f3 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/RecipeUsingAdvisor.cs @@ -0,0 +1,207 @@ +using Dungeons.Core; +using Roguelike.Extensions; +using Roguelike.Tiles; +using Roguelike.Tiles.LivingEntities; +using Roguelike.Tiles.Looting; +using SimpleInjector; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace Roguelike.Crafting +{ + public class RecipeUsingAdvisor : LootCrafterTools + { + Container container; + WorkersFactory wf; + ILootCrafter lootCrafter; + + public RecipeUsingAdvisor(Container container, ILootCrafter lootCrafter) + { + this.container = container; + this.lootCrafter = lootCrafter; + wf = new WorkersFactory(new List { }, lootCrafter, container); + } + + //public RecipeKind[] GetPossibleKinds(Hero hero) + //{ + // var recipeKinds = EnumHelper.GetEnumValues(true); + // recipeKinds.Remove(RecipeKind.Custom); + // var recipeKindsToUse = recipeKinds.ToList(); + // foreach (var rk in recipeKinds) + // { + // if(!CanUseRecipe(rk, hero)) + // recipeKindsToUse.Remove(RecipeKind.Custom); + // } + // return recipeKindsToUse.ToArray(); + //} + + public bool CanCopyLootToCraftPanel(RecipeKind rk, Hero hero) + { + CraftWorker worker = GetWorker(rk); + if(worker == null) + return false; + + bool forCopyLootToCraftPanel = true; + if (GetRecipeRequiredLoot(rk, hero, CraftWorker.IsAdvisorSupportsBulk(rk), forCopyLootToCraftPanel).Any()) + { + return true; + } + + return false; + } + + public bool CanUseRecipe(RecipeKind rk, Hero hero) + { + CraftWorker worker = GetWorker(rk); + if (worker == null) + return false; + + bool forCopyLootToCraftPanel = false; + if (GetRecipeRequiredLoot(rk, hero, CraftWorker.IsAdvisorSupportsBulk(rk), forCopyLootToCraftPanel).Any()) + { + return true; + } + + return false; + } + + public List GetRecipeRequiredLoot(RecipeKind rk, Hero hero, bool bulkCraft, bool forCopyLootToCraftPanel) + { + var worker = GetWorker(rk); + if (worker == null) + return new List(); + + var invItems = forCopyLootToCraftPanel ? hero.Inventory.Items : hero.Crafting.InvItems.Inventory.Items; + List res = new List(); + + if (rk == RecipeKind.TransformPotion || + rk == RecipeKind.Toadstools2Potion) + { + var canDoRes = CanRunWorker(worker, invItems); + if (canDoRes.Success) + res = canDoRes.UsedInputItems; + } + else + { + var requiredItem = worker.GetRequiredItems(); + res = GetLoot(rk, bulkCraft, worker, invItems, requiredItem); + var mightMatch = res.Count >= requiredItem.Items.Count; + if (mightMatch) + { + if (!CanRunWorker(worker, res).Success) + res.Clear(); + } + else + res.Clear(); + } + + if (res.Any()) + { + var recipe = new Recipe(worker.Kind); + var md = FilterOne(hero.Crafting.InvItems.Inventory.Items); + if (md == null || md.Count < recipe.MagicDustRequired) + { + //var md = FilterOne(hero.Crafting.InvItems.Inventory.Items); + res.Clear(); + Debug.WriteLine("recipe "+ rk+ " due to lack of md!"); + } + else + res.Add(md); + } + return res; + } + + private List GetLoot + ( + RecipeKind rk, bool bulkCraft, CraftWorker worker, List invItems, + RecipeRequiredItems requiredItems + ) + { + var res = new List(); + foreach (var nextItemsDesc in requiredItems.Items) + { + var invLootItems = invItems.Where(i => + (i.GetType() == nextItemsDesc.Type || i.GetType().IsSubclassOf(nextItemsDesc.Type)) && + !res.Contains(i) && + i.IsMatchingRecipe(rk) + ).ToList(); + if (invLootItems.Any()) + { + if (!bulkCraft) + { + if (nextItemsDesc.Type == typeof(Equipment)) + { + worker.Init(invLootItems, lootCrafter); + var eq = worker.Eqs.FirstOrDefault(); + if (eq != null) + res.Add(eq); + } + else + { + var loot = invLootItems.First(); + res.Add(loot); + //bug!!!! + //if (loot is StackedLoot sl) + //{ + // sl.Count = nextItemsDesc.MinCount; + //} + } + } + else + { + var toAdd = invLootItems; + if (nextItemsDesc.Type == typeof(Equipment)) + { + if (rk == RecipeKind.OneEq || rk == RecipeKind.TwoEq) + { + toAdd = Filter(toAdd).Where(i => i.Class != EquipmentClass.Unique).Cast().ToList(); + } + } + res.AddRange(invLootItems); + } + } + else + Debug.WriteLine("Not found inv item for " + nextItemsDesc); + } + + return res; + } + + public CraftWorker GetWorker(RecipeKind rk) + { + return wf.GetWorker(rk); + } + + private void PrepareForTranform(List invItems, List res) + { + var pot = res.Where(i => i.IsPotion()).FirstOrDefault() as Potion; + var mush = FilterOne(res); + if (pot != null) + { + Potion otherPot = null; + if (pot.Kind == PotionKind.Health || (mush != null && mush.MushroomKind == MushroomKind.RedToadstool)) + { + otherPot = invItems.Where(i => i.IsPotionKind(PotionKind.Mana)).FirstOrDefault().AsPotion(); + + } + else if (pot.Kind == PotionKind.Mana || (mush != null && mush.MushroomKind == MushroomKind.BlueToadstool)) + { + otherPot = invItems.Where(i => i.IsPotionKind(PotionKind.Health)).FirstOrDefault().AsPotion(); + + } + if (otherPot != null) + { + res.Remove(pot); + res.Add(otherPot); + } + } + } + + private CraftingResult CanRunWorker(CraftWorker worker, List res) + { + worker.Init(res, lootCrafter); + return worker.CanDo(); + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/RecipeUsingAdvisor.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/RecipeUsingAdvisor.cs.meta new file mode 100644 index 00000000..78d76650 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/RecipeUsingAdvisor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3e71008a8e48a4148920b8008686e1e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers.meta new file mode 100644 index 00000000..0a1e1b6d --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c0220e990b2a6d04787fc443718c524a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/AntidotePotion.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/AntidotePotion.cs new file mode 100644 index 00000000..437a18ff --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/AntidotePotion.cs @@ -0,0 +1,50 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System.Collections.Generic; +using System.Linq; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class AntidotePotion : CraftWorker + { + Plant plant; + Hooch hooch; + internal AntidotePotion(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.AntidotePotion; + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Plant) }; + //var i2 = new RecipeRequiredItem() { Type = typeof(MagicDust), MinCount = 1 }; + var i3 = new RecipeRequiredItem() { Type = typeof(Hooch) }; + ri.Items.Add(i1); + //ri.Items.Add(i2); + ri.Items.Add(i3); + return ri; + } + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + plant = Filter(lootToConvert).Where(i => i.Kind == PlantKind.Thistle).SingleOrDefault(); + if (plant ==null) + return ReturnCraftingError("Thistle is required by the Recipe"); + + hooch = FilterOne(lootToConvert); + if (hooch == null) + return ReturnCraftingError("Hooch is required by the Recipe"); + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + var ct = GetCraftedStackedCount(new [] { plant.Count, hooch.Count }); + return ReturnCraftedLoot(new Potion(PotionKind.Antidote) { Count = ct }, new List() { plant, hooch }, true); + + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/AntidotePotion.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/AntidotePotion.cs.meta new file mode 100644 index 00000000..5f544c85 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/AntidotePotion.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f62fa63cde6329e4d89ba90e0255467b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ArrowsAndBolts.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ArrowsAndBolts.cs new file mode 100644 index 00000000..20f09151 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ArrowsAndBolts.cs @@ -0,0 +1,74 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Roguelike.Crafting.Workers +{ + public class Arrows : CraftWorker + { + public Arrows() + { + Kind = RecipeKind.Arrows; + } + public Arrows(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.Arrows; + } + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + + return ReturnCraftingError(InvalidIngredients); + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + return ri; + } + + } + + public class Bolts : CraftWorker + { + public Bolts() + { + Kind = RecipeKind.Bolts; + } + + public Bolts(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.Bolts; + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + return ri; + } + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + + return ReturnCraftingError(InvalidIngredients); + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ArrowsAndBolts.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ArrowsAndBolts.cs.meta new file mode 100644 index 00000000..ab4a51ed --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ArrowsAndBolts.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: db426a1042d131947be0374f61f00f88 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/EnchantEquipment.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/EnchantEquipment.cs new file mode 100644 index 00000000..613510b0 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/EnchantEquipment.cs @@ -0,0 +1,83 @@ + +using Roguelike.Tiles; +using Roguelike.Tiles.Looting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Roguelike.Crafting.Workers +{ + internal class EnchantEquipment : CraftWorker + { + Equipment toCraft; + + internal EnchantEquipment(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.EnchantEquipment; + + } + + public override void Init(List lootToConvert, ILootCrafter lootCrafter) + { + base.Init(lootToConvert, lootCrafter); + eqs = eqs.Where(i => i.Enchantable && (i.EnchantSlots - i.Enchants.Count) > 0).ToList(); + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Equipment) }; + var i4 = new RecipeRequiredItem() { Type = typeof(Enchanter) }; + ri.Items.Add(i1); + ri.Items.Add(i4); + return ri; + } + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + toCraft = eqs.FirstOrDefault(); + if (toCraft == null) + previewResult = ReturnCraftingError("One enchantable piece of equipment is required by the recipe"); + else + { + //var nonEq = lootToConvert.Where(i => !(i is Equipment)).ToList(); + //if (nonEq.Any(i => !(i is Enchanter))) + //{ + // previewResult = ReturnCraftingError("Only enchanting items (gems, claws,...) are allowed by the recipe"); + //} + //else + { + if (!enchanters.Any()) + { + previewResult = ReturnCraftingError("Enchanting item is required"); + } + // else + // { + // if (freeSlots == 0) + // previewResult = ReturnCraftingError("No free slots available in the "); + //} + } + + } + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + var eq = toCraft; + string err; + foreach (var ench in enchanters.Cast()) + { + if (!lootCrafter.ApplyEnchant(ench, eq, out err)) + return ReturnCraftingError(InvalidIngredients); + else + usedSrcItems.Add(ench); + } + + return ReturnCraftedLoot(eq, usedSrcItems, false); + + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/EnchantEquipment.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/EnchantEquipment.cs.meta new file mode 100644 index 00000000..98668a6b --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/EnchantEquipment.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d43b060d5e6cf1e4bb548c354336509e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ExplosiveCocktail.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ExplosiveCocktail.cs new file mode 100644 index 00000000..5339ac71 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ExplosiveCocktail.cs @@ -0,0 +1,47 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System.Collections.Generic; + +namespace Roguelike.Crafting.Workers +{ + public class ExplosiveCocktail : CraftWorker + { + //StackedLoot sulfur; + //Hooch hooch; + + public ExplosiveCocktail() + { + Kind = RecipeKind.ExplosiveCocktail; + } + public ExplosiveCocktail(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.ExplosiveCocktail; + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = lootCrafter.GetMinedLootType() };//Sulfur + // var i2 = new RecipeRequiredItem() { Type = typeof(MagicDust), MinCount = 1 }; + var i3 = new RecipeRequiredItem() { Type = typeof(Hooch) }; + ri.Items.Add(i1); + //ri.Items.Add(i2); + ri.Items.Add(i3); + return ri; + } + + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + return null; + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ExplosiveCocktail.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ExplosiveCocktail.cs.meta new file mode 100644 index 00000000..e58561b8 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ExplosiveCocktail.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57bd9d4be02d688408412d00286c67f0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/NiesiolowskiSoup.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/NiesiolowskiSoup.cs new file mode 100644 index 00000000..0f778a4b --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/NiesiolowskiSoup.cs @@ -0,0 +1,53 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class NiesiolowskiSoup : CraftWorker + { + Plant sorrel; + Food plum; + + internal NiesiolowskiSoup(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.NiesiolowskiSoup; + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Plant)}; + var i2 = new RecipeRequiredItem() { Type = typeof(Food) }; + ri.Items.Add(i1); + ri.Items.Add(i2); + return ri; + } + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + sorrel = Filter(lootToConvert).Where(i => i.Kind == PlantKind.Sorrel).FirstOrDefault(); + if (sorrel == null) + return ReturnCraftingError("Sorrel not available"); + + plum = Filter(lootToConvert).Where(i => i.Kind == FoodKind.Plum).FirstOrDefault(); + if (plum == null) + return ReturnCraftingError("Plum not available"); + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + var count = GetCraftedStackedCount(lootToConvert); + usedSrcItems.Add(sorrel); + usedSrcItems.Add(plum); + //sorrel.Count -= count; + //plum.Count -= count; + return ReturnCraftedLoot(new Food() { Kind = FoodKind.NiesiolowskiSoup, Count = count }, usedSrcItems); + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/NiesiolowskiSoup.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/NiesiolowskiSoup.cs.meta new file mode 100644 index 00000000..ce6dadf1 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/NiesiolowskiSoup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 957b4afb18e88c841b7f645ddad342d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/OneEq.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/OneEq.cs new file mode 100644 index 00000000..b93a30d9 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/OneEq.cs @@ -0,0 +1,86 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Dungeons.Core; +using Roguelike.Generators; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class OneEq : CraftWorker + { + internal OneEq(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.OneEq; + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Equipment) }; + + ri.Items.Add(i1); + return ri; + } + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + if (!eqs.Any()) + previewResult = ReturnCraftingError("At least on equipment item is required"); + + else if (!eqs.Any(i => CanBeCrafted(i))) + previewResult = ReturnCraftingError("Can not craft equipment which was already crafted in pairs"); + + + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + var res = new List(); + //var usedSrcItems = new List(); + foreach (var srcEq in eqs) + { + if (!CanBeCrafted(srcEq)) + continue; + var srcLootKind = srcEq.EquipmentKind; + var lks = Equipment.GetPossibleLootKindsForCrafting().ToList(); + var destLk = RandHelper.GetRandomElem(lks, new EquipmentKind[] { srcLootKind }); + var lootGenerator = lootCrafter.GetLootGenerator(); + var destEq = lootGenerator.GetRandomEquipment(destLk, srcEq.MinDropDungeonLevel, null); + if (srcEq.Class == EquipmentClass.Magic) + { + destEq.SetClass(EquipmentClass.Magic, srcEq.MinDropDungeonLevel, null, srcEq.IsSecondMagicLevel); + } + var srcStatsCount = srcEq.GetMagicStats().Count; + if (srcStatsCount > destEq.GetMagicStats().Count) + { + var diff = srcStatsCount - destEq.GetMagicStats().Count; + for (int i = 0; i < diff; i++) + { + destEq.AddRandomMagicStat(); + } + } + if (srcEq.Enchantable) + { + destEq.MakeEnchantable(); + } + destEq.WasCrafted = true; + destEq.CraftingRecipe = RecipeKind.OneEq; + res.Add(destEq); + usedSrcItems.Add(srcEq); + } + return ReturnCraftedLoot(res, usedSrcItems); + } + + private bool CanBeCrafted(Equipment srcEq) + { + return !srcEq.WasCraftedBy(RecipeKind.TwoEq); + } + + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/OneEq.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/OneEq.cs.meta new file mode 100644 index 00000000..21c43aaf --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/OneEq.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 51ea58f0a94b51043b5729fd18a96ea8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/RechargeMagicalWeapon.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/RechargeMagicalWeapon.cs new file mode 100644 index 00000000..f279b5ce --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/RechargeMagicalWeapon.cs @@ -0,0 +1,48 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System.Collections.Generic; +using System.Linq; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class RechargeMagicalWeapon : CraftWorker + { + internal RechargeMagicalWeapon(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.RechargeMagicalWeapon; + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Weapon), MinCount = 1 }; + //var i3 = new RecipeRequiredItem() { Type = typeof(MagicDust), MinCount = 1 }; + ri.Items.Add(i1); + //ri.Items.Add(i3); + return ri; + } + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + var equips = lootToConvert.Where(i => i is Weapon wpn0 && wpn0.IsMagician).Cast().ToList(); + var equipsCount = equips.Count(); + if (equipsCount != 1) + return ReturnCraftingError("One charge emitting weapon is needed by the Recipe"); + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + var equips = lootToConvert.Where(i => i is Weapon wpn0 && wpn0.IsMagician).Cast().ToList(); + var wpn = equips[0]; + //foreach (var wpn in equips) + { + (wpn.SpellSource as WeaponSpellSource).RestoreCharges(); + wpn.UpdateMagicWeaponDesc(); + } + return ReturnCraftedLoot(wpn, null, false); + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/RechargeMagicalWeapon.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/RechargeMagicalWeapon.cs.meta new file mode 100644 index 00000000..dfd8433e --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/RechargeMagicalWeapon.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 863c8727baeb4d24ea9bf85f1a9a70f3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/SpecialPotion.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/SpecialPotion.cs new file mode 100644 index 00000000..c6a13d6a --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/SpecialPotion.cs @@ -0,0 +1,70 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Roguelike.Extensions; +using Roguelike.Crafting.Workers; +using Roguelike.Probability; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class SpecialPotion : CraftWorker + { + StackedLoot hps; + StackedLoot mps; + StackedLoot toads; + internal SpecialPotion(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.CraftSpecialPotion; + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Potion) }; + var i2 = new RecipeRequiredItem() { Type = typeof(Mushroom) }; + ri.Items.Add(i1); + ri.Items.Add(i2); + return ri; + } + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + hps = stacked.Where(i => i.IsPotionKind(PotionKind.Health)).FirstOrDefault(); + mps = stacked.Where(i => i.IsPotionKind(PotionKind.Mana)).FirstOrDefault(); + if (hps == null && mps == null) + previewResult = ReturnCraftingError("At least one Mana or Health potion is required"); + else + { + toads = stacked.Where(i => i.IsBoletus()).FirstOrDefault(); + if (toads == null) + previewResult = ReturnCraftingError("At least one Boletus is required"); + } + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + Potion pot = null; + var maxLoot = GetMaxStackedCount(lootToConvert.Where(i=>i.IsPotion()).ToArray()); + if (maxLoot == hps) + { + var count = GetCraftedStackedCount(new Loot[] { hps, toads }); + pot = new Roguelike.Tiles.Looting.SpecialPotion(SpecialPotionKind.Strength, SpecialPotionSize.Small) { Count = count }; + usedSrcItems.Add(hps); + } + else + { + var count = GetCraftedStackedCount(new Loot[] { mps, toads }); + usedSrcItems.Add(mps); + pot = new Roguelike.Tiles.Looting.SpecialPotion(SpecialPotionKind.Magic, SpecialPotionSize.Small) { Count = count }; + } + usedSrcItems.Add(toads); + return ReturnCraftedLoot(pot, usedSrcItems); + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/SpecialPotion.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/SpecialPotion.cs.meta new file mode 100644 index 00000000..7b180a54 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/SpecialPotion.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b01cc6bfb830984589a97a6aa9291b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ThreeGems.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ThreeGems.cs new file mode 100644 index 00000000..706bfeae --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ThreeGems.cs @@ -0,0 +1,82 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class ThreeGems : CraftWorker + { + Gem toCraft = null; + + internal ThreeGems(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.ThreeGems; + } + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Gem) , MinCount = 3}; + ri.Items.Add(i1); + + return ri; + } + + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + var gemsKinds = Filter(lootToConvert).Where(i=>i.EnchanterSize != EnchanterSize.Big).GroupBy(i=>i.GemKind).Where(i=>i.Count() >=0); + var notEnoughResources = "At least three gems of the same kind and size are required"; + if(gemsKinds.Count() == 0) + return ReturnCraftingError(notEnoughResources); + + toCraft = null; + foreach (var gemKind in gemsKinds) + { + var groupsBySize = gemKind.GroupBy(i => i.EnchanterSize).ToList();//.Where(i=>i.Count()>=3).FirstOrDefault(); + foreach (var groupBySize in groupsBySize) + { + foreach (var size in groupsBySize) + { + var si = size.ElementAt(0); + if (si.Count >= 3) + { + toCraft = groupBySize.First(); + break; + } + } + if (toCraft != null) + break; + } + if (toCraft != null) + break; + } + if(toCraft == null) + return ReturnCraftingError(notEnoughResources); + // return ReturnCraftingError("Big gems can not be crafted"); + + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + var gem = new Gem(toCraft.GameLevel); + gem.GemKind = toCraft.GemKind; + gem.EnchanterSize = toCraft.EnchanterSize == EnchanterSize.Small ? EnchanterSize.Medium : EnchanterSize.Big; + gem.SetProps(); + gem.Count = toCraft.Count / 3; + toCraft.Count = toCraft.Count % 3; + + if(toCraft.Count == 0) + return ReturnCraftedLoot(gem, new List() { toCraft }); + else + return ReturnCraftedLoot(gem); + + + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ThreeGems.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ThreeGems.cs.meta new file mode 100644 index 00000000..c6f8a3a1 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/ThreeGems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 97a27b64b98931941895b5e4f8830c89 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/Toadstools2Potion.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/Toadstools2Potion.cs new file mode 100644 index 00000000..16ef0ec8 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/Toadstools2Potion.cs @@ -0,0 +1,78 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System.Collections.Generic; +using System.Linq; +using Roguelike.Extensions; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class Toadstools2Potion : CraftWorker + { + int srcCount = 0; + const int countReq = 3; + internal Toadstools2Potion(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.Toadstools2Potion; + } + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Mushroom), MinCount = 3 }; + ri.Items.Add(i1); + return ri; + } + + Mushroom fromCanDo; + public override CraftingResult CanDo() + { + var toadstools = lootToConvert.Where(i => i.IsToadstool()).Cast().GroupBy(i=>i.MushroomKind).ToList(); + int matches = 0; + + foreach (var ts in toadstools) + { + var mushOfKind = ts.ElementAt(0); + if (mushOfKind.Count >= countReq) + { + matches++; + fromCanDo = mushOfKind; + break; + } + + } + //var matches = toadstools.Where(i=>i.Count() >= 3).ToList(); + if(matches == 0) + return ReturnCraftingError("At least three toadstools (of the same kind) are required"); + //;///(lootToConvert, "Sulfur"); + //if (allToadstool) + //{ + // var toadstool = lootToConvert[0].AsToadstool(); + // if (toadstool == null || toadstool.Count < 3) + // { + // return ReturnCraftingError("At least three toadstools are required"); + // } + //} + //else + // return ReturnCraftingError("Only toadstools are allowed"); + + srcCount = fromCanDo.Count; + + return new CraftingResult(lootToConvert) { UsedInputItems = new List() { fromCanDo } }; + } + + public override CraftingResult Do() + { + fromCanDo.Count = fromCanDo.Count % countReq;// (fromCanDo.Count/ countReq)* countReq; + var toadstool = fromCanDo; + Potion potion = null; + if (toadstool.MushroomKind == MushroomKind.BlueToadstool) + potion = new Potion(PotionKind.Mana) { Count = srcCount/ countReq }; + else + potion = new Potion(PotionKind.Health) { Count = srcCount/ countReq }; + + var src = new List() { toadstool }; + var res = ReturnCraftedLoot(potion, src, false); + return res; + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/Toadstools2Potion.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/Toadstools2Potion.cs.meta new file mode 100644 index 00000000..562afa48 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/Toadstools2Potion.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 84fb04f0b0b06c449b92cdcab24ab444 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformGem.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformGem.cs new file mode 100644 index 00000000..fd0f23d5 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformGem.cs @@ -0,0 +1,55 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System.Collections.Generic; +using Dungeons.Core; +using System.Linq; +using System.Diagnostics; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class TransformGem : CraftWorker + { + internal TransformGem(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.TransformGem; + } + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Gem) }; + + ri.Items.Add(i1); + + return ri; + } + + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + var srcGems = Filter(lootToConvert); + if (srcGems.Count != 1) + return ReturnCraftingError("One gem is required"); + + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + var res = new List(); + var srcGems = Filter(lootToConvert); + foreach (var srcGem in srcGems) + { + var destKind = RandHelper.GetRandomEnumValue(new GemKind[] { srcGem.GemKind, GemKind.Unset }); + var destGem = new Gem(destKind); + destGem.EnchanterSize = srcGem.EnchanterSize; + destGem.SetProps(); + res.Add(destGem); + destGem.Count = srcGem.Count; + Debug.WriteLine("Added gem: "+ destGem); + } + return ReturnCraftedLoot(res, srcGems.Cast().ToList()); + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformGem.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformGem.cs.meta new file mode 100644 index 00000000..65ad8a7e --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformGem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d953bffb27f7fbd45a27a23c42f86d02 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformPotion.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformPotion.cs new file mode 100644 index 00000000..878e10b3 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformPotion.cs @@ -0,0 +1,112 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System.Collections.Generic; +using System.Linq; +using Roguelike.Extensions; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class TransformPotion : CraftWorker + { + //Potion hps; + Potion srcPotion; + Mushroom srcToad; + internal TransformPotion(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.TransformPotion; + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Potion) }; + //var i2 = new RecipeRequiredItem() { Type = typeof(MagicDust), MinCount = 1 }; + var i3 = new RecipeRequiredItem() { Type = typeof(Mushroom) }; + + ri.Items.Add(i1); + //ri.Items.Add(i2); + ri.Items.Add(i3); + return ri; + } + + bool CanCraftHP() + { + srcPotion = stacked.Where(i => i.IsPotionKind(PotionKind.Mana)).Cast().FirstOrDefault(); + if (srcPotion == null) + return false; + + srcToad = lootToConvert.Where(i => i.IsToadstool()).Cast().Where(i => i.MushroomKind == MushroomKind.RedToadstool).FirstOrDefault(); + if (srcToad == null) + return false; + return true; + } + + bool CanCraftMP() + { + srcPotion = stacked.Where(i => i.IsPotionKind(PotionKind.Health)).Cast().FirstOrDefault(); + if (srcPotion == null) + return false; + + srcToad = lootToConvert.Where(i => i.IsToadstool()).Cast().Where(i=> i.MushroomKind == MushroomKind.BlueToadstool).FirstOrDefault(); + if (srcToad == null) + return false; + return true; + } + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + if(!CanCraftHP() && !CanCraftMP()) + return ReturnCraftingError(InvalidIngredients); + + // new Loot[] { potion, toad }; + + //hps = stacked.Where(i => i.IsPotionKind(PotionKind.Health)).Cast().FirstOrDefault(); + //mps = stacked.Where(i => i.IsPotionKind(PotionKind.Mana)).Cast().FirstOrDefault(); + //if (hps == null && mps == null) + // previewResult = ReturnCraftingError("At least one Mana or Health potion is required"); + + //if (hps != null && mps != null) + // return ReturnCraftingError("Source potions must be of one kind"); + + //var toadstools = lootToConvert.Where(i => i.IsToadstool()).Cast().ToList(); + //if (!toadstools.Any()) + //{ + // return ReturnCraftingError("Toadstool is required"); + //} + + //var ts = toadstools.GroupBy(i => i.MushroomKind); + ////if (ts.Count() > 1) + ////{ + //// return ReturnCraftingError("Toadstools must be of one kind"); + ////} + //if (hps != null && mush.MushroomKind == MushroomKind.RedToadstool || + // mps !=null && mush.MushroomKind == MushroomKind.BlueToadstool) + //return ReturnCraftingError("Invalid toadstool kind provided"); + //toad = mush; + previewResult = new CraftingResult(lootToConvert) { UsedInputItems = new List{ srcPotion, srcToad } }; + return ReturnCanDo(previewResult); + + } + + public override CraftingResult Do() + { + var toadstoolsCount = srcToad.Count; + + var inputLoot = new Loot[] { srcPotion, srcToad }; + var ct = GetCraftedStackedCount(inputLoot); + if (srcPotion.AsPotion().Kind == PotionKind.Mana) + { + if (srcToad.MushroomKind == MushroomKind.RedToadstool) + return ReturnCraftedLoot(new Potion(PotionKind.Health) { Count = ct }, inputLoot.ToList()); + } + else + { + if (srcToad.MushroomKind == MushroomKind.BlueToadstool) + return ReturnCraftedLoot(new Potion(PotionKind.Mana) { Count = ct }, inputLoot.ToList()); + } + return ReturnCraftingError(InvalidIngredients); + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformPotion.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformPotion.cs.meta new file mode 100644 index 00000000..16810238 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TransformPotion.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 35e85b5bd77181a4790dec7b8896a5db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TwoEquipments.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TwoEquipments.cs new file mode 100644 index 00000000..fdbafcd7 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TwoEquipments.cs @@ -0,0 +1,144 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class TwoEquipments : CraftWorker + { + internal TwoEquipments(List lootToConvert, ILootCrafter lootCrafter) : base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.TwoEq; + } + + public override void Init(List lootToConvert, ILootCrafter lootCrafter) + { + base.Init(lootToConvert, lootCrafter); + //TODO use code from CanDo() + //eqs = eqs.Where(i => i.WasCrafted && i.Enchants.Count > 0).ToList(); + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Equipment), MinCount = 1 }; + var i2 = new RecipeRequiredItem() { Type = typeof(Equipment), MinCount = 1 }; + ri.Items.Add(i1); + ri.Items.Add(i2); + return ri; + } + + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + if (eqs.Count == 2) + { + var eq1 = eqs[0]; + var eq2 = eqs[1]; + if (eq1.EquipmentKind != eq2.EquipmentKind) + previewResult = ReturnCraftingError("Equipment for crafting must be of the same type"); + + else if (eq1.WasCraftedBy(RecipeKind.TwoEq) || eq2.WasCraftedBy(RecipeKind.TwoEq)) + previewResult = ReturnCraftingError("Can not craft equipment which was already crafted in pairs"); + } + else if (lootToConvert.Any(i => i is KeyHalf)) + { + var parts = Filter(lootToConvert); + if (parts.Count != 2 || !parts[0].Matches(parts[1])) + { + previewResult = ReturnCraftingError("Two matching parts of a key are needed"); + } + //return ReturnCraftedLoot(new Key() { KeyName = (lootToConvert[0] as KeyHalf).KeyName, Kind = KeyKind.BossRoom }); ; ; + } + + //else if (lootToConvert.Any(i => i is KeyMold)) + //{ + // //return ConvertMold(recipe, lootToConvert); + //} + else + previewResult = ReturnCraftingError(InvalidIngredients); + + return ReturnCanDo(previewResult); + } + + public override CraftingResult Do() + { + var previewResult = ReturnCraftingError(InvalidIngredients); + if (eqs.Count == 2) + previewResult = CraftTwoEq(this.eqs); + // return CraftTwoEq(eqs); + else if (lootToConvert.Any(i => i is KeyHalf)) + { + previewResult = ReturnCraftedLoot(new Key() { KeyName = (lootToConvert[0] as KeyHalf).KeyName, Kind = KeyKind.BossRoom }); + } + + return previewResult; + } + + private CraftingResult CraftTwoEq(List eqs) + { + var eq1 = eqs[0]; + var eq2 = eqs[1]; + + var destEq = eq1.Price > eq2.Price ? eq1 : eq2; + + var srcEq = destEq == eq1 ? eq2 : eq1; + var srcHadEmptyEnch = srcEq.Enchantable && !srcEq.MaxEnchantsReached(); + var destHadEmptyEnch = destEq.Enchantable && !destEq.MaxEnchantsReached(); + + float priceInc = 0; + var enhPr = GetEnhStatValue(destEq.PrimaryStatValue, destEq.Price, srcEq.Price); + destEq.PrimaryStatValue += enhPr; + priceInc += destEq.GetPriceForFactor(destEq.PrimaryStatKind, enhPr); + + var destStats = destEq.GetMagicStats(); + var srcStats = srcEq.GetMagicStats(); + var srcDiffStats1 = srcStats.Where(i => !destStats.Any(j => j.Key == i.Key)).ToList(); + + if (destStats.Count < 3 && srcDiffStats1.Any()) + { + var countToAdd = 3 - destStats.Count; + foreach (var statToAdd in srcDiffStats1) + { + if (destEq.Class == EquipmentClass.Plain) + destEq.Class = EquipmentClass.Magic; + destEq.SetMagicStat(statToAdd.Key, statToAdd.Value); + countToAdd--; + priceInc += destEq.GetPriceForFactor(statToAdd.Key, (int)statToAdd.Value.Factor); + if (countToAdd == 0) + break; + } + } + else + { + foreach (var destStat in destStats) + { + var enh = GetEnhStatValue(destStat.Value.Factor, destEq.Price, srcEq.Price); + destStat.Value.Factor += enh; + priceInc += destEq.GetPriceForFactor(destStat.Key, (int)enh); + destEq.SetMagicStat(destStat.Key, destStat.Value); + } + } + + if (srcHadEmptyEnch || destHadEmptyEnch) + { + if (destEq.GetMagicStats().Count < 3 && !destEq.Enchantable) + destEq.MakeEnchantable(); + } + destEq.WasCrafted = true; + destEq.CraftingRecipe = RecipeKind.TwoEq; + + //I noticed price is too high comparing to unique items, maybe price should be calculated from scratch ? + //priceInc /= 2; + + destEq.Price += (int)priceInc; + + return ReturnCraftedLoot(destEq, eqs.Cast().ToList()); + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TwoEquipments.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TwoEquipments.cs.meta new file mode 100644 index 00000000..50c8c69c --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/TwoEquipments.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f0894e5e5c843894aa3266fdc74765fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/UnEnchantEquipment.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/UnEnchantEquipment.cs new file mode 100644 index 00000000..6c1af749 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/UnEnchantEquipment.cs @@ -0,0 +1,73 @@ +using Roguelike.Crafting; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Roguelike.Core.Crafting.Workers +{ + internal class UnEnchantEquipment : CraftWorker + { + Equipment eq; + internal UnEnchantEquipment(List lootToConvert, ILootCrafter lootCrafter) : + base(lootToConvert, lootCrafter) + { + Kind = RecipeKind.UnEnchantEquipment; + } + + public override void Init(List lootToConvert, ILootCrafter lootCrafter) + { + base.Init(lootToConvert, lootCrafter); + } + + public override RecipeRequiredItems GetRequiredItems() + { + var ri = new RecipeRequiredItems(); + var i1 = new RecipeRequiredItem() { Type = typeof(Equipment) }; + ri.Items.Add(i1); + + return ri; + } + + public override CraftingResult CanDo() + { + CraftingResult previewResult = null; + if (eqs.Count != 1 || eqs[0].Enchants.Count == 0) + return GetError("One enchanted piece of equipment is required"); + else if(lootToConvert.Any(i => !(i is Equipment) )) + return GetError("Only equipment is allowed for this recipe"); + + eq = eqs[0]; + return ReturnCanDo(previewResult); + } + + private CraftingResult GetError(string err) + { + return ReturnCraftingError(err); + } + + + public override CraftingResult Do() + { + var enchs = eq.Enchants.Select(i => i.Enchanter).ToList(); + enchs.ForEach(i => i.Count = 1); + foreach (var ench in eq.Enchants) + { + foreach (var stat in ench.StatKinds) + { + eq.RemoveMagicStat(stat, ench.StatValue); + } + } + eq.Enchants = new List(); + + var lootItems = new List() { eq }; + lootItems.AddRange(enchs); + usedSrcItems = new List();//nothing to remove + var res = ReturnCraftedLoot(lootItems, usedSrcItems, false); + res.AddOutLoot = true; + return res; + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/UnEnchantEquipment.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/UnEnchantEquipment.cs.meta new file mode 100644 index 00000000..33e253fd --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/Workers/UnEnchantEquipment.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 300cd01d5c2f8744e9c18aa0ee95658a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/WorkersFactory.cs b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/WorkersFactory.cs new file mode 100644 index 00000000..6d103bbc --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/WorkersFactory.cs @@ -0,0 +1,86 @@ +using Dungeons.Core; +using Roguelike.Core.Crafting.Workers; +using Roguelike.Crafting.Workers; +using Roguelike.Factors; +using Roguelike.Tiles; +using Roguelike.Tiles.Looting; +using SimpleInjector; +using System.Collections.Generic; +using System.Linq; + +namespace Roguelike.Crafting +{ + internal class WorkersFactory + { + static List recipeKinds; + Dictionary factory = new Dictionary(); + List lootToConvert; + + static WorkersFactory() + { + recipeKinds = EnumHelper.GetEnumValues(true); + recipeKinds.Remove(RecipeKind.Custom); + } + + public WorkersFactory(List lootToConvert, ILootCrafter lootCrafter, Container container) + { + this.lootToConvert = lootToConvert; + factory[RecipeKind.EnchantEquipment] = new EnchantEquipment(lootToConvert, lootCrafter); + factory[RecipeKind.TwoEq] = new TwoEquipments(lootToConvert, lootCrafter); + factory[RecipeKind.UnEnchantEquipment] = new UnEnchantEquipment(lootToConvert, lootCrafter); + factory[RecipeKind.OneEq] = new OneEq(lootToConvert, lootCrafter); + factory[RecipeKind.CraftSpecialPotion] = new Core.Crafting.Workers.SpecialPotion(lootToConvert, lootCrafter); + factory[RecipeKind.Arrows] = container.GetInstance(); + factory[RecipeKind.Arrows].Init(lootToConvert, lootCrafter); + factory[RecipeKind.Bolts] = container.GetInstance(); + factory[RecipeKind.Bolts].Init(lootToConvert, lootCrafter); + factory[RecipeKind.NiesiolowskiSoup] = new NiesiolowskiSoup(lootToConvert, lootCrafter); + factory[RecipeKind.ThreeGems] = new ThreeGems(lootToConvert, lootCrafter); + factory[RecipeKind.TransformPotion] = new TransformPotion(lootToConvert, lootCrafter); + factory[RecipeKind.TransformGem] = new TransformGem(lootToConvert, lootCrafter); + factory[RecipeKind.Toadstools2Potion] = new Toadstools2Potion(lootToConvert, lootCrafter); + factory[RecipeKind.ExplosiveCocktail] = container.GetInstance (); + factory[RecipeKind.ExplosiveCocktail].Init(lootToConvert, lootCrafter); + + factory[RecipeKind.RechargeMagicalWeapon] = new RechargeMagicalWeapon(lootToConvert, lootCrafter); + factory[RecipeKind.AntidotePotion] = new AntidotePotion(lootToConvert, lootCrafter); + } + + void EnsureWorker(RecipeKind kind) + { + + } + + internal CraftWorker GetWorker(RecipeKind kind) + { + if (kind == RecipeKind.Custom) + { + List matches = new List(); + recipeKinds.ForEach(i => { + var workerReal = GetWorker(i); + if (workerReal != null) + { + if(workerReal.CanDo().Success) + matches.Add(workerReal); + } + }); + var ench = matches.FirstOrDefault(i => i.Kind == RecipeKind.EnchantEquipment); + if (ench != null) + return ench; + + ench = matches.FirstOrDefault(i => i.Kind == RecipeKind.TwoEq); + if (ench != null) + return ench; + + return matches.GetRandomElem(); + + } + var worker = factory.ContainsKey(kind) ? factory[kind] : null; + if (worker != null) + { + return worker; + } + return null; + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Crafting/WorkersFactory.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/WorkersFactory.cs.meta new file mode 100644 index 00000000..d683cef0 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Crafting/WorkersFactory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 14d063b5ebf20e74facc2eaabf1162da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussPanel.cs b/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussPanel.cs index 12429148..05471634 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussPanel.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussPanel.cs @@ -1,4 +1,6 @@ using Dungeons.Core; +using Roguelike.Quests; +using Roguelike.Tiles.Abstract; using Roguelike.Tiles.LivingEntities; using Roguelike.UI.Models; using System; @@ -10,19 +12,48 @@ public class DiscussPanel : Roguelike.Abstract.Discussions.IDiscussPanel { protected INPC npc; GenericListModel boundTopics = new GenericListModel(); + + public INPC NPC => npc; - public GenericListModel BoundTopics { get => boundTopics; set => boundTopics = value; } + public GenericListModel BoundTopics + { + get => boundTopics; + set => boundTopics = value; + } public event EventHandler DiscussionOptionChosen; public event EventHandler Hidden; - protected Roguelike.Tiles.LivingEntities.AdvancedLivingEntity AdvancedLivingEntity + //protected INPC AdvancedLivingEntity + //{ + // get{ return npc; } + //} + + public DiscussPanel() { - get{ return npc as Roguelike.Tiles.LivingEntities.AdvancedLivingEntity; } } - public DiscussPanel() + public virtual void Reset() { + npc.Discussion.Reset(); + BindTopics(npc.Discussion.MainItem, npc); + } + + protected virtual bool PreventPanelRebind(Roguelike.Discussions.DiscussionTopic topic) + { + return false; + } + + protected virtual bool ShallHideOn(DiscussionTopic topic, KnownSentenceKind knownSentenceKind) + { + if (topic.ClosesPanel) + return true; + + if (knownSentenceKind == KnownSentenceKind.AllyAccepted && topic.Right.Id == "NoWorry") + return false; + + return (knownSentenceKind == KnownSentenceKind.Bye || knownSentenceKind == KnownSentenceKind.LetsTrade || + knownSentenceKind == KnownSentenceKind.QuestAccepted || knownSentenceKind == KnownSentenceKind.AllyAccepted) ; } public virtual bool ChooseDiscussionTopic(DiscussionTopic topic) @@ -32,20 +63,25 @@ public virtual bool ChooseDiscussionTopic(DiscussionTopic topic) { BindTopics(topic.Parent, npc); } - else if (knownSentenceKind == KnownSentenceKind.Bye || knownSentenceKind == KnownSentenceKind.LetsTrade || + else if (topic.ClosesPanel || knownSentenceKind == KnownSentenceKind.Bye || knownSentenceKind == KnownSentenceKind.LetsTrade || knownSentenceKind == KnownSentenceKind.QuestAccepted || knownSentenceKind == KnownSentenceKind.AllyAccepted) { - Hide(); - } - else if (knownSentenceKind == KnownSentenceKind.SellHound) - { + if(ShallHideOn(topic, knownSentenceKind)) + Hide(); + else if(topic.Right.Id == "NoWorry")//TODO! + BindTopics(topic, npc); + + if (PreventPanelRebind(topic)) + BindTopics(topic, npc); } + else { var itemToBind = topic; - - if (knownSentenceKind == KnownSentenceKind.WorkingOnQuest|| - knownSentenceKind == KnownSentenceKind.AwaitingReward|| + //var qs = GetQuestStatus(topic); + + if (knownSentenceKind == KnownSentenceKind.QuestAccepted|| + knownSentenceKind == KnownSentenceKind.AwaitingReward || knownSentenceKind == KnownSentenceKind.RewardSkipped|| knownSentenceKind == KnownSentenceKind.Cheating|| knownSentenceKind == KnownSentenceKind.AwaitingRewardAfterRewardDeny) @@ -59,7 +95,7 @@ public virtual bool ChooseDiscussionTopic(DiscussionTopic topic) else if (knownSentenceKind == KnownSentenceKind.Cheating) { npc.Discussion.EmitCheating(topic, npc); - if (AdvancedLivingEntity.RelationToHero.CheatingCounter >= 2) + if (npc.RelationToHero.CheatingCounter >= 2) { Hide(); //RelationChanged.Raise(this, merch.RelationToHero.Kind); @@ -67,7 +103,8 @@ public virtual bool ChooseDiscussionTopic(DiscussionTopic topic) } //itemToBind = itemToBind.Parent.Parent; - itemToBind = npc.Discussion.MainItem; + if (ShallBindMainTopic(itemToBind)) + itemToBind = npc.Discussion.MainItem; } BindTopics(itemToBind, npc); @@ -77,6 +114,16 @@ public virtual bool ChooseDiscussionTopic(DiscussionTopic topic) return true; } + protected virtual bool ShallBindMainTopic(DiscussionTopic topic) + { + return !(npc.Name == "Zyndram" && topic.RightKnownSentenceKind == KnownSentenceKind.Cheating); + } + + protected virtual QuestStatus GetQuestStatus(DiscussionTopic topic) + { + return QuestStatus.Unset; + } + protected virtual void RewardHero(Roguelike.Tiles.LivingEntities.INPC npc, DiscussionTopic topic) { @@ -87,10 +134,13 @@ public void Bind(INPC leftEntity, AdvancedLivingEntity rightEntity, GenericListM throw new NotImplementedException(); } - public virtual GenericListModel BindTopics(DiscussionTopic parentTopic, Roguelike.Tiles.LivingEntities.INPC npc) + public virtual GenericListModel BindTopics(DiscussionTopic parentTopic, + INPC npc) { this.npc = npc; boundTopics.Clear(); + + parentTopic.EnsureBack(); foreach (var topic in parentTopic.Topics) { boundTopics.Add(new GenericListItemModel(topic, topic.Right.Id, topic.Right.Body+ topic.RightSuffix)); @@ -117,6 +167,8 @@ public GenericListItemModel GetTopicModel(DiscussionTopic topic public virtual void Hide() { + if(npc != null) + npc.SetHasUrgentTopic(false); if(Hidden!=null) Hidden(this, EventArgs.Empty); } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Discussions/Discussion.cs b/Assets/Scripts/Common/Dlls/Roguelike/Discussions/Discussion.cs index 4be375b1..44fc8e69 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Discussions/Discussion.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Discussions/Discussion.cs @@ -1,39 +1,38 @@ -using Newtonsoft.Json; -using Roguelike.Extensions; +using Dungeons.Core; +using Newtonsoft.Json; using Roguelike.Managers; +using Roguelike.Serialization; using Roguelike.Tiles.LivingEntities; using SimpleInjector; -using System.IO; using System.Diagnostics; namespace Roguelike.Discussions { - public enum KnownSentenceKind { Unset, WhatsUp, LetsTrade, Bye, SellHound, Back, QuestAccepted, QuestProgress, WorkingOnQuest, AwaitingReward, - Cheating, DidIt , RewardDeny, AwaitingRewardAfterRewardDeny, RewardSkipped, AllyAccepted, AllyRejected + public enum KnownSentenceKind { Unset, WhatsUp, LetsTrade, Bye, SellHound, Back, QuestAccepted, QuestRejected, QuestProgress, + WorkingOnQuest, AwaitingReward, + Cheating, DidIt , RewardDeny, AwaitingRewardAfterRewardDeny, + RewardSkipped, AllyAccepted, AllyRejected, AskedToAddAlcohol, AgreeToAddAlcohol, RefusedToAddAlcohol } - public class Discussion + public class Discussion : IPersistable { protected Container container; public string EntityName { get; set; } - DiscussionTopic mainItem; + protected DiscussionTopic mainItem; + //[JsonConstructor] public Discussion(Container container) { this.container = container; mainItem = CreateMainItem(); } - [JsonConstructor] - public Discussion() { } - protected virtual DiscussionTopic CreateMainItem() { return container.GetInstance(); } - public static void CreateMerchantResponseOptions(DiscussionTopic item, bool allowBuyHound) { item.AddTopic(KnownSentenceKind.LetsTrade); @@ -41,6 +40,12 @@ public static void CreateMerchantResponseOptions(DiscussionTopic item, bool allo item.AddTopic(KnownSentenceKind.SellHound, " (" + Merchant.HoundPrice + " gold)", "I'm afraid you can not afford it."); } + public void Reset() + { + SetMainItem(CreateMainItem()); + } + + public void SetMainItem(DiscussionTopic topic) { topic.AddTopic(KnownSentenceKind.Bye); @@ -56,34 +61,15 @@ public DiscussionTopic MainItem [JsonIgnore] public Container Container { get => container; set => container = value; } - public string ToJson() - { - JsonSerializerSettings settings = new JsonSerializerSettings - { - PreserveReferencesHandling = PreserveReferencesHandling.Objects - }; - string jsonData = JsonConvert.SerializeObject(this, settings); - return jsonData; - } - - public void FromJson(string json) - { - Discussion discussion = JsonConvert.DeserializeObject(json); - EntityName = discussion.EntityName; - mainItem = discussion.mainItem; - } - - public void ToJsonFile() + public void ToXml() { - JsonSerializerSettings settings = new JsonSerializerSettings - { - PreserveReferencesHandling = PreserveReferencesHandling.Objects - }; try { - string jsonData = JsonConvert.SerializeObject(this, settings); - string path = EntityName + ".json"; - File.WriteAllText(path, jsonData); + var writer = new System.Xml.Serialization.XmlSerializer(typeof(Discussion)); + var path = EntityName + ".xml"; + var file = System.IO.File.Create(path); + writer.Serialize(file, this); + file.Close(); } catch (System.Exception ex) { @@ -94,13 +80,15 @@ public void ToJsonFile() } } - public static Discussion FromJsonFile(string entityName) + public static Discussion FromXml(string entityName) { try { Discussion disc = null; - var jsonData = File.ReadAllText(entityName + ".json"); - disc = JsonConvert.DeserializeObject(jsonData); + var reader = new System.Xml.Serialization.XmlSerializer(typeof(Discussion)); + var file = new System.IO.StreamReader(entityName + ".xml"); + disc = (Discussion)reader.Deserialize(file); + file.Close(); return disc; } catch (System.Exception ex) @@ -116,7 +104,7 @@ public virtual void EmitCheating(DiscussionTopic item, INPC inpc) { } - public virtual bool AcceptQuest(QuestManager qm, string questKind) + public virtual bool AcceptQuest(QuestManager qm, string questKind, string questPrincipalName) { return false; } @@ -140,5 +128,26 @@ private void SetParentsForTopic(DiscussionTopic parent) SetParentsForTopic(topic); } } + + public bool RemoveTopic(KnownSentenceKind ksk) + { + var topic = MainItem.GetTopic(ksk); + if (topic != null) + return RemoveTopic(topic); + + return false; + } + + public bool RemoveTopic(DiscussionTopic topic) + { + if (topic == null) + { + container.GetInstance().LogError("RemoveTopic null!"+ topic); + return false; + } + return topic.Parent.Topics.Remove(topic); + } + + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussionNpcInfo.cs b/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussionNpcInfo.cs index 99c5bc38..8350a4a5 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussionNpcInfo.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussionNpcInfo.cs @@ -1,6 +1,4 @@ -using Roguelike.Discussions; -using System.Collections; -using System.Collections.Generic; +using Roguelike.Tiles.Interactive; namespace Roguelike.Discussions { @@ -8,6 +6,9 @@ public abstract class DiscussionNpcInfo { string npc; protected SimpleInjector.Container container; + public const string Great = "Great!"; + public const string WIPID = "WorkingOnIt"; + public const string WIPText = "I'm working on it"; public DiscussionNpcInfo(SimpleInjector.Container container, string npc) { @@ -15,13 +16,15 @@ public DiscussionNpcInfo(SimpleInjector.Container container, string npc) this.container = container; } - public DiscussionTopic GetTopicByLeftRight(string rightId, string leftId) + public DiscussionTopic GetTopicByLeftRight(string rightId, string leftId = null) { string left, right; GetRightLeft(rightId, out left, out right); var topic = container.GetInstance (); topic.Init(right, left); //var topic = new DiscussionTopic(container, right, left); + if (string.IsNullOrEmpty(leftId)) + leftId = rightId; topic.Left.Id = leftId; topic.Right.Id = rightId; if (topic.Right.Id == KnownSentenceKind.WhatsUp.ToString()) @@ -58,18 +61,17 @@ private void SetNpcClipTimes(string rightId, DiscussionTopic topic) topic.Left.VoiceClipTimeTo = end; } - //public DiscussionTopic GetTopic(string rightId, KnownSentenceKind rightKnown, QuestKind questKind, bool skipReward = false) - //{ - // string left, right; - // GetRightLeft(rightId, out left, out right); - // var topic = new DiscussionTopic(container, right, rightKnown, questKind, skipReward: skipReward); - // topic.Right.Id = rightId; - // SetClipTimes(rightId, topic); - // return topic; - //} - - protected abstract void GetRightLeft(string rightId, out string left, out string right); - public abstract void GetHeroClipTimes(string clipId, out string fileName, out float start, out float end); + protected virtual /*abstract*/ void GetRightLeft(string rightId, out string left, out string right) + { + left = ""; + right = ""; + } + public virtual /*abstract*/ void GetHeroClipTimes(string clipId, out string fileName, out float start, out float end) + { + fileName = ""; + start = 0; + end = 0; + } public virtual void GetNpcClipTimes(string clipId, out string fileName, out float start, out float end) { fileName = ""; diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussionTopic.cs b/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussionTopic.cs index fd81c711..41362950 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussionTopic.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Discussions/DiscussionTopic.cs @@ -1,16 +1,16 @@ using Dungeons.Core; -using Newtonsoft.Json; -using Roguelike.Extensions; using SimpleInjector; using System; using System.Collections.Generic; using System.Linq; -using System.Xml.Serialization; namespace Roguelike.Discussions { public class DiscussionTopic { + public bool BreakableVoice = true; + public bool CustomLeft; + public bool ClosesPanel { get; set; } public bool SkipReward { get; set; } public DiscussionSentence Right { get; set; } = new DiscussionSentence(); public DiscussionSentence Left { get; set; } = new DiscussionSentence(); @@ -32,7 +32,17 @@ public KnownSentenceKind RightKnownSentenceKind public bool HasBack() { - return Topics.Any(i => i.Right.Body == "Back"); + return HasTopicByBody("Back"); + } + public bool HasBye() + { + return HasTopicByBody("Bye"); + } + + + public bool HasTopicByBody(string body) + { + return Topics.Any(i => i.Right.Body == body); } public override string ToString() @@ -41,7 +51,6 @@ public override string ToString() return res; } - [JsonConstructor] public DiscussionTopic(Container container) { this.container = container; @@ -53,7 +62,7 @@ public DiscussionTopic(Container container, KnownSentenceKind right, string left { Init(right, left, allowBuyHound , addMerchantItems); } - + public DiscussionTopic(Container container, string right, string left, bool allowBuyHound = false, bool addMerchantItems = merchantItemsAtAllLevels) : this(container) { @@ -85,6 +94,7 @@ private void Init(bool allowBuyHound, bool addMerchantItems) if (addMerchantItems) Discussion.CreateMerchantResponseOptions(this, allowBuyHound); } + public DiscussionTopic(string right, KnownSentenceKind leftKnownSentenceKind, bool allowBuyHound = false) { Right = new DiscussionSentence(right); @@ -121,16 +131,21 @@ public bool ShowableOnDiscussionList } } - public DiscussionTopic GetTopic(string topic) + public DiscussionTopic GetTopic(string topicBody) + { + return Topics.FirstOrDefault(i => i.Right.Body == topicBody); + } + + public DiscussionTopic GetTopicById(string topicId) { - return Topics.FirstOrDefault(i => i.Right.Body == topic); + return Topics.FirstOrDefault(i => i.Right.Id == topicId); } public DiscussionTopic GetTopic(KnownSentenceKind kind) { return Topics.SingleOrDefault(i => i.RightKnownSentenceKind == kind); } - + public bool HasTopics(string topic) { return GetTopic(topic) != null; @@ -145,9 +160,19 @@ public void EnsureBack() { if (!HasBack()) { - var back = CreateBack(this.parent); - Topics.Add(back); + if (parent != null) + { + var back = CreateBack(this.parent); + Topics.Add(back); + } + else if (!HasBye()) + { + var bye = container.GetInstance(); + bye.Init(KnownSentenceKind.Bye, KnownSentenceKind.Bye.ToString()); + Topics.Add(bye); + } } + } public DiscussionTopic CreateBack(DiscussionTopic parent) @@ -159,12 +184,14 @@ public DiscussionTopic CreateBack(DiscussionTopic parent) } - public void InsertTopic(DiscussionTopic subItem, bool atBegining = true) + public DiscussionTopic InsertTopic(DiscussionTopic subItem, bool atBegining = true) { //TODO prevent duplicates if (HasTopics(subItem.Right.Body)) - container.GetInstance().LogError("HasTopics! "+ subItem.Right.Body); - + { + container.GetInstance().LogError("already HasTopic! " + subItem.Right.Body); + return subItem; + } if (subItem == this) throw new Exception("subItem == this!"+this); subItem.parent = this; @@ -184,6 +211,8 @@ public void InsertTopic(DiscussionTopic subItem, bool atBegining = true) else Topics.Add(subItem); } + + return subItem; } public void InsertTopic(string right, KnownSentenceKind knownSentenceKind) @@ -197,7 +226,5 @@ public void InsertTopic(string right, string left, bool addMerchantItems = merch item.Init(right, left, allowBuyHound, addMerchantItems); InsertTopic(item, true); } - - } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Effects/EffectTypeConverter.cs b/Assets/Scripts/Common/Dlls/Roguelike/Effects/EffectTypeConverter.cs index 26c198ae..3815a8e5 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Effects/EffectTypeConverter.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Effects/EffectTypeConverter.cs @@ -1,9 +1,62 @@ using Roguelike.Attributes; +using Roguelike.Spells; +using Roguelike.Tiles.Looting; namespace Roguelike.Effects { - class EffectTypeConverter + public static class EffectTypeConverter { + public static EffectType GetEffectType(this ProjectileFightItem pfi) + { + switch (pfi.FightItemKind) + { + case FightItemKind.Unset: + break; + case FightItemKind.ExplosiveCocktail: + case FightItemKind.ThrowingTorch: + return EffectType.Firing; + + case FightItemKind.ThrowingKnife: + case FightItemKind.HunterTrap: + return EffectType.Bleeding; + case FightItemKind.Stone: + break; + + case FightItemKind.PlainArrow: + break; + case FightItemKind.IronArrow: + break; + case FightItemKind.SteelArrow: + break; + case FightItemKind.PlainBolt: + break; + case FightItemKind.IronBolt: + break; + case FightItemKind.SteelBolt: + break; + case FightItemKind.PoisonArrow: + case FightItemKind.PoisonBolt: + case FightItemKind.PoisonCocktail: + return EffectType.Poisoned; + case FightItemKind.IceArrow: + case FightItemKind.IceBolt: + return EffectType.Frozen; + case FightItemKind.FireArrow: + case FightItemKind.FireBolt: + return EffectType.Firing; + case FightItemKind.WeightedNet: + break; + case FightItemKind.CannonBall: + break; + case FightItemKind.Smoke: + break; + default: + break; + } + + return EffectType.Unset; + } + public static EffectType Convert(EntityStatKind esk) { switch (esk) diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Effects/LastingEffect.cs b/Assets/Scripts/Common/Dlls/Roguelike/Effects/LastingEffect.cs index 26c6e8ab..033cef53 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Effects/LastingEffect.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Effects/LastingEffect.cs @@ -1,4 +1,5 @@ using Dungeons.Tiles; +using Dungeons.Tiles.Abstract; using Newtonsoft.Json; using Roguelike.Abilities; using Roguelike.Abstract.Effects; @@ -15,6 +16,8 @@ namespace Roguelike.Effects { + public enum EffectKind { Unset, Positive, Neutral, Negative} + public enum EffectType { Unset = 0, @@ -44,6 +47,7 @@ public enum EffectType //OpenWound = 110 } + public interface ILastingEffectOwner { string Name { get; set; } @@ -95,6 +99,7 @@ public class LastingEffect { public const int DefaultPendingTurns = 3; + [JsonIgnore] public ILastingEffectSrc LastingEffectSrc { get; set; } public EffectApplication Application { get; set; } @@ -229,6 +234,9 @@ public string GetDescription(bool shortOne) if (Type == EffectType.Frighten) return "Frighten (Pending Turns: " + PendingTurns+")"; + if (Type == EffectType.Stunned) + return "Stunned (Pending Turns: " + PendingTurns + ")"; + if (AbilityKind == AbilityKind.ElementalVengeance) return "Elemental Attacks: "+ EffectiveFactor.ToString(); @@ -304,10 +312,22 @@ public override string ToString() return Type + ", " + Description; } - public LivingEntityAction CreateAction(LastingEffect le) + //public LivingEntityAction CreateAction(LastingEffect le, bool removed) + //{ + // var lea = new LivingEntityAction(removed ? LivingEntityActionKind.EffectFinished : LivingEntityActionKind.ExperiencedEffect); + // lea.InvolvedEntity = this.livingEntity; + // lea.EffectType = le.Type; + // var targetName = livingEntity.Name.ToString(); + + // lea.Info = targetName + " " + le.Description; + // lea.Level = ActionLevel.Important; + // return lea; + //} + + public LivingEntityAction CreateAction(LastingEffect le, bool removed, LivingEntity owner) { - var target = (Owner as LivingEntity); - var lea = new LivingEntityAction(LivingEntityActionKind.ExperiencedEffect); + var target = owner;// (Owner as LivingEntity); + var lea = new LivingEntityAction(removed ? LivingEntityActionKind.EffectFinished : LivingEntityActionKind.ExperiencedEffect); lea.InvolvedEntity = target; lea.EffectType = le.Type; //var targetName = target.Name.ToString(); @@ -323,17 +343,15 @@ private string CreateActionInfo(LastingEffect le, LivingEntity target) if (Origin == EffectOrigin.SelfCasted) { - expected = ownerName; - expected += " casted:"; + expected = ownerName + " casted:"; } - else + else if (Origin == EffectOrigin.OtherCasted) { + expected = "Spell was casted on " + ownerName; } - - if (Origin == EffectOrigin.OtherCasted) + else { - //expected += Type.ToDescription(); - expected += "Spell was casted on " + ownerName; + expected = ownerName; } var res = expected; if (res.Any()) diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Effects/LastingEffectsSet.cs b/Assets/Scripts/Common/Dlls/Roguelike/Effects/LastingEffectsSet.cs index baea4e8a..8b433810 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Effects/LastingEffectsSet.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Effects/LastingEffectsSet.cs @@ -1,5 +1,6 @@ using Dungeons.Core; using Dungeons.Tiles; +using Dungeons.Tiles.Abstract; using Newtonsoft.Json; using Roguelike.Abilities; using Roguelike.Abstract.Effects; @@ -21,7 +22,17 @@ public class LastingEffectsSet { List lastingEffects = new List(); public List LastingEffects { get => lastingEffects; set => lastingEffects = value; } + LivingEntity livingEntity; + [JsonIgnore] + public LivingEntity LivingEntity + { + get { return livingEntity; } + set { + livingEntity = value; + } + } + static EntityStatKind[] resists = new EntityStatKind[] { EntityStatKind.ResistCold, EntityStatKind.ResistFire, EntityStatKind.ResistPoison, EntityStatKind.ResistLighting }; public event EventHandler LastingEffectStarted; public event EventHandler LastingEffectApplied; @@ -38,7 +49,8 @@ public EventsManager EventsManager { get { - return livingEntity.EventsManager; + return Container.GetInstance(); + //return livingEntity.EventsManager; } } @@ -47,13 +59,16 @@ bool AddLastingEffect(LastingEffect le) if (livingEntity.IsImmuned(le.Type)) return false; - //le.PendingTurns = 50; LastingEffects.Add(le); this.livingEntity.IncreateLastingEffectCounter(le.Type); if (le.Type != EffectType.Stunned) { - ApplyLastingEffect(le, true); + if (!ApplyLastingEffect(le, true)) + { + LastingEffects.Remove(le); + return false; + } if (le.Application != EffectApplication.EachTurn) { HandleStatSubtraction(le, true); @@ -78,9 +93,14 @@ public LastingEffect GetByType(EffectType type) return LastingEffects.Where(i => i.Type == type).FirstOrDefault(); } + public LastingEffect GetByAbilityKind(AbilityKind ak) + { + return LastingEffects.Where(i => i.AbilityKind == ak).FirstOrDefault();//not Single as ElementalVengeance adds 3 + } + public bool HasEffect(AbilityKind ak) { - return LastingEffects.Where(i => i.AbilityKind == ak).FirstOrDefault() != null; + return GetByAbilityKind(ak) != null; } public bool HasEffect(EffectType type) @@ -195,7 +215,7 @@ public bool IsHealthGone() return livingEntity.IsHealthGone(); } - private void ApplyLastingEffect(LastingEffect le, bool newOne) + private bool ApplyLastingEffect(LastingEffect le, bool newOne) { //if(Container != null) //Container.GetInstance().LogInfo(livingEntity + " ApplyLastingEffect: " + le); @@ -204,7 +224,8 @@ private void ApplyLastingEffect(LastingEffect le, bool newOne) if (le.Application == EffectApplication.EachTurn) { - HandleStatSubtraction(le, true); + if (!HandleStatSubtraction(le, true)) + return false; } LastingEffectApplied?.Invoke(this, le); @@ -215,21 +236,25 @@ private void ApplyLastingEffect(LastingEffect le, bool newOne) if (le.Sibling != null) ApplyLastingEffect(le.Sibling, newOne); } + return true; + } - private void HandleStatSubtraction(LastingEffect le, bool add) + private bool HandleStatSubtraction(LastingEffect le, bool add) { var value = le.EffectiveFactor.Value; if (!le.ChangesStats(le.Type)) - return; + return false; if (le.Type == EffectType.TornApart) value = livingEntity.Stats.Health; if (le.StatKind != EntityStatKind.Unset) { - livingEntity.Stats.ChangeStatDynamicValue(le.StatKind, add ? value : -value); + var applied = livingEntity.Stats.ChangeStatDynamicValue(le.StatKind, add ? value : -value); + if (!applied) + return false; if( le.Type != EffectType.ManaShield && ( @@ -239,25 +264,17 @@ private void HandleStatSubtraction(LastingEffect le, bool add) ) AppendEffectAction(le, !add); } + + return true; } private void AppendEffectAction(LastingEffect le, bool removed) { - LivingEntityAction lea = CreateAction(le, removed); + LivingEntityAction lea = le.CreateAction(le, removed, this.LivingEntity); AppendAction(lea); } - public LivingEntityAction CreateAction(LastingEffect le, bool removed) - { - var lea = new LivingEntityAction(removed ? LivingEntityActionKind.EffectFinished : LivingEntityActionKind.ExperiencedEffect); - lea.InvolvedEntity = this.livingEntity; - lea.EffectType = le.Type; - var targetName = livingEntity.Name.ToString(); - - lea.Info = targetName + " " + le.Description; - lea.Level = ActionLevel.Important; - return lea; - } + public virtual void RemoveLastingEffect(LastingEffect le) { @@ -289,7 +306,7 @@ public virtual void RemoveLastingEffect(LastingEffect le) if (le.AbilityKind != AbilityKind.Unset) { - livingEntity.HandleActiveAbilityEffectDone(le.AbilityKind, true); + livingEntity.StartAbilityCooling(le.AbilityKind); } if (LastingEffectDone != null) @@ -382,15 +399,31 @@ public LastingEffect TryAddLastingEffectOnHit(float hitAmount, LivingEntity atta return TryAddLastingEffect(effectInfo, attacker); } - public static int GetPendingTurns(EffectType et) + public int GetPendingTurns(EffectType et, LivingEntity attacker = null) { + if (attacker is Hero hero) + { + if (et == EffectType.WebTrap) + { + var ab = attacker.GetActiveAbility(AbilityKind.WeightedNet); + var stats = ab.GetEntityStats(true); + var dur = stats.Where(i => i.Kind == EntityStatKind.WeightedNetDuration).FirstOrDefault(); + if (dur != null) + { + var val = LastingEffect.DefaultPendingTurns + (int)dur.Factor; ; + return val; + } + int k = 0; + k++; + } + } return LastingEffect.DefaultPendingTurns; } public LastingEffect EnsureEffect(EffectType et, float inflictedDamage, LivingEntity attacker = null, int turnLasting = -1, Tile source = null) { - var effectInfo = CalcLastingEffDamage(et, inflictedDamage, null); - if(turnLasting > 0) + var effectInfo = CalcLastingEffDamage(et, inflictedDamage, null, attacker); + if(turnLasting > 0 && turnLasting > effectInfo.Turns)//HACK! effectInfo.Turns = turnLasting; if(attacker!=null) @@ -402,14 +435,14 @@ public LastingEffect EnsureEffect(EffectType et, float inflictedDamage, LivingEn private void ProlongEffect(float inflictedDamage, LastingEffect le, int turns = 0) { var effectInfo = CalcLastingEffDamage(le.Type, inflictedDamage, null); - le.PendingTurns = turns > 0 ? turns : LastingEffectsSet.GetPendingTurns(le.Type); + le.PendingTurns = turns > 0 ? turns : GetPendingTurns(le.Type); le.PercentageFactor = effectInfo.PercentageFactor; le.EffectiveFactor = effectInfo.EffectiveFactor; } - public LastingEffectCalcInfo CalcLastingEffDamage(EffectType et, float amount, FightItem fi = null) + public LastingEffectCalcInfo CalcLastingEffDamage(EffectType et, float amount, FightItem fi = null, LivingEntity attacker = null) { - return CreateLastingEffectCalcInfo(et, amount, 0, GetPendingTurns(et)); + return CreateLastingEffectCalcInfo(et, amount, 0, GetPendingTurns(et, attacker)); } public LastingEffectCalcInfo CalcLastingEffectInfo(EffectType eff, ILastingEffectSrc src) @@ -441,15 +474,6 @@ public virtual LastingEffect AddPercentageLastingEffect(EffectType eff, ILasting } var le = AddLastingEffect(calcEffectValue, origin, effectSrc, src.StatKind, false); - //bool handle = false; - //if (eff == EffectType.Rage || eff == EffectType.Weaken || eff == EffectType.IronSkin || eff == EffectType.Inaccuracy) - //{ - // //if (!onlyProlong) - // //{ - // // //le.CalcInfo = calcEffectValue; - // // handle = true; - // //} - //} //else if (eff == EffectType.ResistAll) //{ // ////effValue must be adjusted not to be over 100 @@ -477,14 +501,6 @@ public virtual LastingEffect AddPercentageLastingEffect(EffectType eff, ILasting // eff == EffectType.ManaShield) //{ //} - - //else - // Assert(false, "AddLastingEffect - unhandeled eff = " + eff); - - //if (handle) - //{ - // HandleSpecialFightStat(le, true); - //} return le; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Events/GameEvent.cs b/Assets/Scripts/Common/Dlls/Roguelike/Events/GameEvent.cs index fa612977..0744e6ac 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Events/GameEvent.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Events/GameEvent.cs @@ -1,5 +1,5 @@ -using Algorithms; -using Dungeons.Tiles; +using Dungeons.Tiles; +using Roguelike.Abilities; using Roguelike.Abstract.Tiles; using Roguelike.Attributes; using Roguelike.Effects; @@ -8,6 +8,7 @@ using Roguelike.Managers; using Roguelike.Policies; using Roguelike.Spells; +using Roguelike.State; using Roguelike.TileContainers; using Roguelike.Tiles; using Roguelike.Tiles.LivingEntities; @@ -26,11 +27,14 @@ public enum ActionLevel public class GameEvent { Guid guid; + + public string TurnHint { get; set; } = ""; public string Info { get; set; } = ""; public ActionLevel Level { get; set; } public int Index { get; set; } public Guid Guid { get => guid; set => guid = value; } public bool ShowHint { get; set; } + public bool Silent { get; internal set; } public virtual string GetSound() { return ""; } @@ -56,9 +60,10 @@ public override string ToString() public class GameStateAction : GameEvent { public enum ActionType { Load, Save, NextLevel, PrevLevel, GameFinished, DemoFinished, EnteredLevel, ContextSwitched, HitGameOneEntry, Assert, - AutoQuickSaveStarting, AutoQuickSaveFinished } + AutoQuickSaveStarting, AutoQuickSaveFinished, SaveFinished } public ActionType Type { get; set; } public AbstractGameLevel InvolvedNode { get => involvedNode; set => involvedNode = value; } + public Tile InvolvedTile { get; internal set; } AbstractGameLevel involvedNode; } @@ -83,7 +88,7 @@ public class ShortcutsBarAction : GameEvent public ShortcutsBarActionKind Kind { get; set; } } - public enum InventoryActionKind { ItemAdded, ItemRemoved, DragDropDone, NotEnoughRoom, ItemTooExpensive } + public enum InventoryActionKind { ItemAdded, ItemRemoved, DragDropDone, NotEnoughRoom, ItemTooExpensive, NeedsRebind } public enum InventoryActionDetailedKind { Unset, Collected, TradedDragDrop } public class InventoryAction : GameEvent @@ -107,7 +112,7 @@ public class ResourceNeededAction : GameEvent /// /// ///////////////////////////////////////////// /// - public enum AllyActionKind { Unset, Engaged, Created, Died } + public enum AllyActionKind { Unset, Engaged, Created, Died, Released } public class AllyAction : GameEvent { public IAlly InvolvedTile { get; set; } @@ -117,7 +122,7 @@ public class AllyAction : GameEvent /// /// ///////////////////////////////////////////// /// - public enum NPCActionKind { Unset, Engaged, Died } + public enum NPCActionKind { Unset, Engaged, Died, NotToLooseVigor } public class NPCAction : GameEvent { public INPC InvolvedTile { get; set; } @@ -143,6 +148,21 @@ public class InteractiveTileAction : GameEvent public InteractiveTileAction() { } } + public class NpcAction : GameEvent + { + public DestPointDesc DestPointDesc { get; set; } + public INPC InvolvedTile { get; set; } + + public Tile MoveOnPathTarget { get; set; } + + public NpcAction(INPC involvedTile, DestPointDesc destPointDesc, Tile moveOnPathTarget) + { + InvolvedTile = involvedTile; + DestPointDesc = destPointDesc; + MoveOnPathTarget = moveOnPathTarget; + } + } + /// /// /////////////// /// @@ -186,7 +206,9 @@ public SurfaceAction(Surface sur, SurfaceActionKind kind) } - public enum HeroActionKind { ChangedLevel, Moved, HitWall, HitPrivateChest, HitLockedChest }; + public enum HeroActionKind { ChangedLevel, Moved, HitWall, HitPrivateChest, HitLockedChest, EnteredInteractive , + LeftInteractive + }; public class HeroAction : GameEvent { public Tile InvolvedTile { get; set; } @@ -213,21 +235,43 @@ public class TilesRevealedAction : GameEvent public enum EntityCommandKind { - Unset, RaiseMyFriends, SorroundHim + Unset, Resurrect, SorroundHim, SenseVictimWeakResist, MakeFakeClones, TeleportAway, + TeleportCloser } - public enum EnemyActionKind { Moved, /*Died,*/ AttackingHero, ChasingPlayer, SendComand, SpecialAction, Teleported }; + public enum EnemyActionKind { Moved, /*Died,*/ AttackingHero, ChasingPlayer, SendComand }; public class EnemyAction : GameEvent { public EnemyActionKind Kind; - public EntityCommandKind CommandKind; + public CommandUseInfo Command { get; set; } + //public EntityCommandKind CommandKind; public Enemy Enemy { get; set; } + + public EnemyAction(CommandUseInfo command) + { + Command = command; + } + } + + public class AbilityStateChangedEvent : GameEvent + { + public AbilityKind AbilityKind { get; set; } + public AbilityState AbilityState { get; set; } + + public LivingEntity AbilityUser { get; set; } + + //public AbilityVisualState AbilityVisualState { get; set; } + + public override string ToString() + { + return base.ToString() + " " + AbilityKind + " " + AbilityState;// + " " + AbilityVisualState; + } } public class SoundRequestAction : GameEvent @@ -239,7 +283,12 @@ public enum LivingEntityActionKind { LeveledUp, Moved, Died, GainedDamage, ExperiencedEffect, EffectFinished, Trapped, Interacted, Missed, UsedSpell, FailedToCastSpell, GodsTurn, GodsPowerReleased, UsedPortal, Teleported, AppendedToLevel, - StateChanged, UsedAbility, MadeBattleOrder, SummonedBuddy, SwappedPos + StateChanged, + /*UsedAbility, */ + MadeBattleOrder, + SummonedBuddy, + SwappedPos, + SendCommand } public class PolicyAppliedAction : GameEvent @@ -269,15 +318,14 @@ public LivingEntityAction(LivingEntityActionKind kind) { this.Kind = kind; } - + public EntityCommandKind CommandKind { get; set; } public EffectType EffectType { get; set; } public MovePolicy MovePolicy { get; set; } public string Sound { get; set; } public LivingEntity InvolvedEntity { get; set; } - - public System.Drawing.Point targetEntityPosition { get; set; } //For evasion + public LivingEntity AttackerEntity { get; set; } public LivingEntityActionKind Kind { get; set; } public double InvolvedValue { get; set; } @@ -286,6 +334,8 @@ public LivingEntityAction(LivingEntityActionKind kind) public SpellKind UsedSpellKind { get; set; } public Dungeons.Tiles.Abstract.IHitable Missed { get; internal set; } public AttackKind AttackKind { get; internal set; } + public CommandUseInfo Cmd { get; internal set; } + } public class TilesAppendedEvent : GameEvent diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Extensions/EnumExtensions.cs b/Assets/Scripts/Common/Dlls/Roguelike/Extensions/EnumExtensions.cs index 33e538d2..9bbb27bd 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Extensions/EnumExtensions.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Extensions/EnumExtensions.cs @@ -9,8 +9,10 @@ public static bool IsGod(this Roguelike.Spells.SpellKind sk) if (sk == Roguelike.Spells.SpellKind.Dziewanna || sk == Roguelike.Spells.SpellKind.Swarog || sk == Roguelike.Spells.SpellKind.Swiatowit || - sk == Roguelike.Spells.SpellKind.Perun - ) + sk == Roguelike.Spells.SpellKind.Perun || + sk == Roguelike.Spells.SpellKind.Jarowit || + sk == Roguelike.Spells.SpellKind.Wales + ) return true; return false; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Extensions/LootExtensions.cs b/Assets/Scripts/Common/Dlls/Roguelike/Extensions/LootExtensions.cs index 886d5c9d..ca607d82 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Extensions/LootExtensions.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Extensions/LootExtensions.cs @@ -1,4 +1,6 @@ using Roguelike.Attributes; +using Roguelike.Crafting.Workers; +using Roguelike.Spells; using Roguelike.Tiles; using Roguelike.Tiles.Looting; @@ -25,6 +27,18 @@ public static AttackKind ToAttackKind(this EntityStatKind esk) return AttackKind.Unset; } + public static SpellKind GetSpellKind(this EntityStatKind esk) + { + if (esk == EntityStatKind.ResistCold) + return SpellKind.IceBall; + else if (esk == EntityStatKind.ResistFire) + return SpellKind.FireBall; + else if (esk == EntityStatKind.ResistPoison) + return SpellKind.PoisonBall; + + return SpellKind.Unset; + } + public static bool IsAttackStat(this EntityStatKind esk) { return esk == EntityStatKind.MeleeAttack || esk == EntityStatKind.PhysicalProjectilesAttack || @@ -87,12 +101,27 @@ public static bool IsToadstool(this Loot loot) return mash.MushroomKind == MushroomKind.BlueToadstool || mash.MushroomKind == MushroomKind.RedToadstool; } + // + public static bool IsBoletus(this Loot loot) + { + var mash = loot as Mushroom; + if (mash == null) + return false; + return mash.MushroomKind == MushroomKind.Boletus; + } + public static bool IsPotion(this Loot loot) { var potion = loot as Potion; return potion != null; } + public static bool IsAlcohol(this Loot loot) + { + var potion = loot as Hooch; + return potion != null; + } + public static bool IsPotionKind(this Loot loot, PotionKind kind) { var potion = loot as Potion; @@ -118,6 +147,14 @@ public static bool IsBowLikeAmmunition(this FightItemKind fightItemKind) return Weapon.IsBowLikeAmmoKind(fightItemKind); } + public static bool IsCausingElementalVengeance(this FightItemKind fightItemKind) + { + return Weapon.IsBowLikeAmmoKind(fightItemKind) && + fightItemKind.ToString().Contains("Fire") || + fightItemKind.ToString().Contains("Ice") || + fightItemKind.ToString().Contains("Poison"); + } + public static bool IsBowAmmoKind(this FightItemKind kind) { return Weapon.IsBowAmmoKind(kind); diff --git a/Assets/Scripts/Common/Dlls/Roguelike/GameContext.cs b/Assets/Scripts/Common/Dlls/Roguelike/GameContext.cs index 1d9631b4..3befbc5e 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/GameContext.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/GameContext.cs @@ -22,7 +22,7 @@ namespace Roguelike { public enum GameContextSwitchKind { DungeonSwitched, NewGame, GameLoaded, Teleported } - public enum TurnOwner { Unset, Hero, Allies, Enemies, Animals} + public enum TurnOwner { Unset, Hero, Allies, Enemies, Animals, Npcs} public class HeroPlacementResult { @@ -79,6 +79,7 @@ public GameContext(Container container) turnCounts[TurnOwner.Allies] = 0; turnCounts[TurnOwner.Enemies] = 0; turnCounts[TurnOwner.Animals] = 0; + turnCounts[TurnOwner.Npcs] = 0; } public bool CanUseScroll(LivingEntity caster, SpellSource spellSource, ISpell spell, ref string preventReason) @@ -104,7 +105,7 @@ public bool CanUseScroll(LivingEntity caster, SpellSource spellSource, ISpell sp return true; } - bool UseAsyncContextSwitching = true; + public bool UseAsyncContextSwitching = true; //////////////////////////////////////////////////////////////////////////////////////////////// public virtual bool SwitchTo ( @@ -145,7 +146,7 @@ public virtual bool SwitchTo //swap active node CurrentNode = node; - CurrentNode.OnHeroPlaced(Hero); + CurrentNode.OnHeroPlaced(Hero, context); EmitContextSwitched(context); Logger.LogInfo("--SwitchTo ends: " + node + " " + tr.TotalSeconds); @@ -312,10 +313,11 @@ public void IncreaseActions(TurnOwner caller, Policy policy = null) { if (turnOwner != caller) { - if (!ShalLReportTurnOwner(policy)) - return;//TODO + //TODO + //if (!ShalLReportTurnOwner(policy)) + // return;//TODO - Logger.LogError("TurnOwner mismatch! ", true); + //Logger.LogError("TurnOwner mismatch! ", true); return; } @@ -349,23 +351,25 @@ void DoMoveToNextTurnOwner() turnCounts[TurnOwner.Allies]++; TurnOwner = TurnOwner.Enemies; } + else if (turnOwner == TurnOwner.Enemies) { TurnActionsCount[TurnOwner.Enemies] = 0; turnCounts[TurnOwner.Enemies]++; TurnOwner = TurnOwner.Animals; } - //else if (turnOwner == TurnOwner.Animals) - //{ - // TurnActionsCount[TurnOwner.Enemies] = 0; - // turnCounts[TurnOwner.Enemies]++; - // TurnOwner = TurnOwner.Animals; - //} - else + else if (turnOwner == TurnOwner.Animals) { - DebugHelper.Assert(turnOwner == TurnOwner.Animals); + TurnActionsCount[TurnOwner.Animals] = 0; turnCounts[TurnOwner.Animals]++; + TurnOwner = TurnOwner.Npcs; + } + else + { + DebugHelper.Assert(turnOwner == TurnOwner.Npcs); + TurnActionsCount[TurnOwner.Npcs] = 0; + turnCounts[TurnOwner.Npcs]++; TurnOwner = TurnOwner.Hero; } @@ -408,6 +412,7 @@ public TurnOwner TurnOwner Hero.ApplyLastingEffects(); Hero.ReduceHealthDueToSurface(CurrentNode); + EventsManager.GotoNextTurnHint(); } if (!Hero.Alive) { @@ -419,6 +424,10 @@ public TurnOwner TurnOwner //logger.LogInfo("TurnOwner to =>" + turnOwner); } } + + /// + /// if true mean game has changed the turn owner and waits for it to make a turn + /// public bool PendingTurnOwnerApply { get => pendingTurnOwnerApply; set => pendingTurnOwnerApply = value; } public bool AutoTurnManagement { get => autoTurnManagement; set => autoTurnManagement = value; } public Dictionary TurnActionsCount { get => turnActionsCount; set => turnActionsCount = value; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Generators/BaseTilesGenerator.cs b/Assets/Scripts/Common/Dlls/Roguelike/Generators/BaseTilesGenerator.cs index 9ace84ad..bd8bf4ad 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Generators/BaseTilesGenerator.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Generators/BaseTilesGenerator.cs @@ -31,17 +31,17 @@ protected virtual List GetEnemyNames(Dungeons.TileContainers.DungeonNode return Filter(enemyNames); } - public virtual Enemy CreateEnemyInstance(SimpleInjector.Container container, string enemyName, bool reveal, bool setLevel) + public virtual Enemy CreateEnemyInstance(SimpleInjector.Container container, string enamyTag, bool reveal, bool setLevel) { var enemy = container.GetInstance(); enemy.Container = container; - enemy.tag1 = enemyName; - enemy.Name = enemyName; + enemy.tag1 = enamyTag; + enemy.SetNameFromTag1(); if(setLevel) SetILootSourceLevel(enemy, null); - if (EnemySymbols.EnemiesToSymbols.ContainsKey(enemy.Name)) - enemy.Symbol = EnemySymbols.EnemiesToSymbols[enemy.Name]; + if (EnemySymbols.EnemiesToSymbols.ContainsKey(enamyTag)) + enemy.Symbol = EnemySymbols.EnemiesToSymbols[enamyTag]; if (enemy.tag1.Contains("bandit")) { @@ -57,13 +57,20 @@ public void SetILootSourceLevel(Dungeons.TileContainers.DungeonNode node, Hero h //these call are fast, 2 ms with grid 200x200 lootSources.AddRange(node.GetTiles()); lootSources.AddRange(node.GetTiles()); - lootSources.AddRange(node.GetTiles()); + SetILootSourceLevel(lootSources, hero); + + lootSources = new List(); + var ens = node.GetTiles(); + lootSources.AddRange(ens); SetILootSourceLevel(lootSources, hero); } public virtual void SetILootSourceLevel(List lss, Hero hero) { - lss.ForEach(i => SetILootSourceLevel(i, hero)); + lss.ForEach(i => + { + SetILootSourceLevel(i, hero); + }); } protected virtual List CreateEnemiesPack(GenerationInfo gi, string enemyName) diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Generators/GenerationInfo.cs b/Assets/Scripts/Common/Dlls/Roguelike/Generators/GenerationInfo.cs index 4cf2915d..942435bb 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Generators/GenerationInfo.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Generators/GenerationInfo.cs @@ -40,11 +40,11 @@ public GenerationInfo() #if TEST_ON //MakeEmpty(); - //ForcedNumberOfEnemiesInRoom = 0; - GenerateEnemies = false; + //ForcedNumberOfEnemiesInRoom = 1; + //GenerateEnemies = false; NumberOfRooms = 2; //MaxLevelIndex = 0; - DefaultForcedDungeonLayouterKind = Dungeons.DungeonLayouterKind.Corridor; + DefaultForcedDungeonLayouterKind = DungeonLayouterKind.Default; ForcedKeyPuzzle = KeyPuzzle.LeverSet; #endif @@ -67,7 +67,8 @@ public GenerationInfo() public const int LevelUpPoints = 5; public const float NextExperienceIncrease = .5f; public const int FirstNextLevelExperienceThreshold = 250; - + public const int AbilityPointLevelUpIncrease = 3; + public const float EnemyStatsIncreasePerLevel = .15f; public bool GenerateEnemies { get; set; } = DebugInfo.GenerateEnemies && !ForceEmpty; public bool GenerateLoot { get; set; } = true && !ForceEmpty; @@ -82,8 +83,8 @@ public GenerationInfo() public int MaxBarrelsPerRoom = 5; public int MaxLootPerRoom = 2; - public static int MaxMerchantMagicDust = 4; - public static int MaxMerchantHooch = 4; + public static int MaxMerchantMagicDust = 8; + public static int MaxMerchantHooch = 8; public static float ChangeToGetEnchantableItem = 0.2f; public static float MaxMagicAttackDistance = 6; public static Difficulty Difficulty = Difficulty.Normal; diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Generators/LevelGenerator.cs b/Assets/Scripts/Common/Dlls/Roguelike/Generators/LevelGenerator.cs index 3386f2e4..9a33f10a 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Generators/LevelGenerator.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Generators/LevelGenerator.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; namespace Roguelike.Generators @@ -28,6 +29,10 @@ public class LevelGenerator : Dungeons.DungeonGenerator public LevelGenerator(Container container) : base(container) { Logger = container.GetInstance(); + + var cc = CultureInfo.CurrentCulture; + int k = 0; + k++; } public LeverSet LeverSet { get => leverSet; protected set => leverSet = value; } @@ -136,20 +141,23 @@ protected virtual void GenerateStairsDown(DungeonNode maze) var tile = maze.GetRandomEmptyTile(DungeonNode.EmptyCheckContext.DropLoot);//should do job if (tile != null) { + tile = maze.EnsureCorrectY(tile); + var set = maze.SetTile(stairs, tile.point); if (stairs.IsFromChildIsland()) { Logger.LogInfo("stairs.IsFromChildIsland! "); } - if(!set) + if (!set) Logger.LogError("failed to set stairs down at: " + tile.point); - Logger.LogInfo("stairs down set at "+ tile.point); + Logger.LogInfo("stairs down set at " + tile.point); } else Logger.LogError("no room for stairs, maze: " + maze); } + protected override void OnChildIslandCreated(ChildIslandCreationInfo e) { base.OnChildIslandCreated(e); @@ -159,7 +167,7 @@ protected override void OnChildIslandCreated(ChildIslandCreationInfo e) protected virtual Stairs CreateStairsUp(int nodeIndex) { - return new Stairs(Container) { StairsKind = StairsKind.LevelUp, Symbol = '<' }; + return new Stairs(Container, StairsKind.LevelUp); } public override DungeonNode CreateDungeonNodeInstance() @@ -261,5 +269,12 @@ protected override void DoPostGenerationJobs(DungeonLevel level) gl.GenerateOilSpread((int)RandHelper.GetRandomFloatInRange(1, maxSpreads)); } } + + protected override DungeonLayouterKind CalcLayouterKind() + { + if (GenerationInfo.DefaultForcedDungeonLayouterKind != DungeonLayouterKind.Unset) + return GenerationInfo.DefaultForcedDungeonLayouterKind; + return base.CalcLayouterKind(); + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Generators/LootGenerator.cs b/Assets/Scripts/Common/Dlls/Roguelike/Generators/LootGenerator.cs index e82a9aee..d24fb650 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Generators/LootGenerator.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Generators/LootGenerator.cs @@ -12,7 +12,6 @@ using SimpleInjector; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; namespace Roguelike.Generators @@ -144,37 +143,37 @@ protected virtual void PrepareLoot(Loot loot) } - public virtual Loot GetLootByAsset(string tileName) + public virtual Loot GetLootByAsset(string tileAsset) { Loot loot; - if (uniqueLoot.ContainsKey(tileName)) - loot = uniqueLoot[tileName]; + if (uniqueLoot.ContainsKey(tileAsset)) + loot = uniqueLoot[tileAsset]; else - loot = LootFactory.GetByName(tileName); + loot = LootFactory.GetByAsset(tileAsset); - if (tileName == "cap") + if (tileAsset == "cap") { var arm = new Armor(); arm.EquipmentKind = Roguelike.Tiles.EquipmentKind.Helmet; arm.Defense = 5; - arm.tag1 = tileName; + arm.tag1 = tileAsset; arm.SetLevelIndex(1); return arm; } - tileName = tileName.ToLower(); + tileAsset = tileAsset.ToLower(); if (loot == null) { var wpn = new Weapon(); loot = wpn; - if (tileName == "rusty_sword") + if (tileAsset == "rusty_sword") { wpn.Kind = Weapon.WeaponKind.Sword; wpn.tag1 = "rusty_sword"; wpn.Name = "Rusty sword"; wpn.SetLevelIndex(1); } - else if (tileName == "sickle") + else if (tileAsset == "sickle") { wpn.Kind = Weapon.WeaponKind.Axe; wpn.tag1 = "sickle"; @@ -182,7 +181,7 @@ public virtual Loot GetLootByAsset(string tileName) wpn.SetLevelIndex(1); loot = wpn; } - else if (tileName == "axe") + else if (tileAsset == "axe") { wpn.Kind = Weapon.WeaponKind.Axe; wpn.tag1 = "axe"; @@ -191,7 +190,7 @@ public virtual Loot GetLootByAsset(string tileName) loot = wpn; } - else if (tileName == "gladius") + else if (tileAsset == "gladius") { wpn.Kind = Weapon.WeaponKind.Sword; wpn.tag1 = "gladius"; @@ -201,7 +200,7 @@ public virtual Loot GetLootByAsset(string tileName) } - else if (tileName == "hammer") + else if (tileAsset == "hammer") { wpn.Kind = Weapon.WeaponKind.Bashing; wpn.tag1 = "hammer"; @@ -209,7 +208,7 @@ public virtual Loot GetLootByAsset(string tileName) wpn.Price *= 2; wpn.SetLevelIndex(4); } - else if (tileName == "broad_sword") + else if (tileAsset == "broad_sword") { wpn.Kind = Weapon.WeaponKind.Sword; wpn.tag1 = "broad_sword"; @@ -217,7 +216,7 @@ public virtual Loot GetLootByAsset(string tileName) wpn.Price *= 2; wpn.SetLevelIndex(6); } - else if (tileName == "war_dagger") + else if (tileAsset == "war_dagger") { wpn.Kind = Weapon.WeaponKind.Dagger; wpn.tag1 = "war_dagger"; @@ -226,7 +225,7 @@ public virtual Loot GetLootByAsset(string tileName) wpn.SetLevelIndex(5); } - else if (tileName == "scepter") + else if (tileAsset == "scepter") { wpn.Kind = Weapon.WeaponKind.Scepter; wpn.tag1 = "scepter"; @@ -235,7 +234,7 @@ public virtual Loot GetLootByAsset(string tileName) wpn.SetLevelIndex(1); } - else if (tileName == "staff") + else if (tileAsset == "staff") { wpn.Kind = Weapon.WeaponKind.Staff; wpn.tag1 = "staff"; @@ -244,7 +243,7 @@ public virtual Loot GetLootByAsset(string tileName) wpn.SetLevelIndex(1); } - else if (tileName == "wand") + else if (tileAsset == "wand") { wpn.Kind = Weapon.WeaponKind.Wand; wpn.tag1 = "wand"; @@ -253,7 +252,7 @@ public virtual Loot GetLootByAsset(string tileName) wpn.SetLevelIndex(1); } - else if (tileName == "bow") + else if (tileAsset == "bow") { wpn.Kind = Weapon.WeaponKind.Bow; wpn.tag1 = "bow"; @@ -263,7 +262,7 @@ public virtual Loot GetLootByAsset(string tileName) wpn.SetLevelIndex(1); } - else if (tileName == "crossbow") + else if (tileAsset == "crossbow") { wpn.Kind = Weapon.WeaponKind.Crossbow; wpn.tag1 = "crossbow"; @@ -296,78 +295,108 @@ public virtual Equipment GetRandomEquipment(int maxEqLevel, LootAbility ab) public virtual Equipment GetRandomEquipment(EquipmentKind kind, int level, LootAbility ab = null) { - var eqClass = EquipmentClass.Plain; - if (ab != null && ab.ExtraChanceToGetMagicLoot > RandHelper.GetRandomDouble()) - eqClass = EquipmentClass.Magic; - var eq = LootFactory.EquipmentFactory.GetRandom(kind, level, eqClass); - //EnasureLevelIndex(eq);//level must be given by factory! - return eq; + try + { + var eqClass = EquipmentClass.Plain; + if (ab != null && ab.ExtraChanceToGetMagicLoot > RandHelper.GetRandomDouble()) + eqClass = EquipmentClass.Magic; + var eq = LootFactory.EquipmentFactory.GetRandom(kind, level, eqClass); + //EnasureLevelIndex(eq);//level must be given by factory! + return eq; + } + catch (Exception ex) + { + Container.GetInstance().LogError(ex); + return GetErrorEqPh(); + } + } + + private Equipment GetErrorEqPh() + { + //must return null = much of code rely on it now + return null;// LootFactory.GetByName("rusty_sword") as Equipment; } bool debug = false; internal Loot TryGetRandomLootByDiceRoll(LootSourceKind lsk, int maxEqLevel, LootAbility ab) { - if(debug) - return GetRandomEquipment(EquipmentKind.Weapon, maxEqLevel); - - //return null; - LootKind lootKind = LootKind.Unset; - if ( - lsk == LootSourceKind.DeluxeGoldChest || - lsk == LootSourceKind.GoldChest - ) + try { - lootKind = LootKind.Equipment; - } - else if (lsk == LootSourceKind.PlainChest) - return GetRandomLoot(maxEqLevel);//some cheap loot - else - lootKind = Probability.RollDiceForKind(lsk, ab); + if (debug) + return GetRandomEquipment(EquipmentKind.Weapon, maxEqLevel); + + //return null; + LootKind lootKind = LootKind.Unset; + if ( + lsk == LootSourceKind.DeluxeGoldChest || + lsk == LootSourceKind.GoldChest + ) + { + lootKind = LootKind.Equipment; + } + else if (lsk == LootSourceKind.PlainChest) + return GetRandomLoot(maxEqLevel);//some cheap loot + else + lootKind = Probability.RollDiceForKind(lsk, ab); - if (lootKind == LootKind.Equipment) - { - var eqClass = Probability.RollDice(lsk, ab); - if (eqClass != EquipmentClass.Unset) - { - var item = GetRandomEquipment(eqClass, maxEqLevel); - //if (item is Equipment eq) - // { - // EnsureMaterialFromLootSource(eq); - // if (item.LevelIndex < maxEqLevel) - // { - // //int k = 0; - // //k++; - // } - // } - return item; + if (lootKind == LootKind.Equipment) + { + var eqClass = Probability.RollDice(lsk, ab); + if (eqClass != EquipmentClass.Unset) + { + var item = GetRandomEquipment(eqClass, maxEqLevel); + //if (item is Equipment eq) + // { + // EnsureMaterialFromLootSource(eq); + // if (item.LevelIndex < maxEqLevel) + // { + // //int k = 0; + // //k++; + // } + // } + return item; + } } - } - if (lootKind == LootKind.Unset) - return null; + if (lootKind == LootKind.Unset) + return null; - return GetRandomLoot(lootKind, maxEqLevel); + return GetRandomLoot(lootKind, maxEqLevel); + } + catch (Exception ex) + { + Container.GetInstance().LogError(ex); + return new MagicDust(); + } } protected virtual Equipment GetRandomEquipment(EquipmentClass eqClass, int level) { - var randedEnum = GetPossibleEqKind(); - - LootFactory.EquipmentFactory.lootHistory = this.lootHistory; - var generatedEq = LootFactory.EquipmentFactory.GetRandom(randedEnum, level, eqClass); - if ((generatedEq == null || generatedEq.Class != EquipmentClass.Unique) && eqClass == EquipmentClass.Unique) + try { - var values = GetEqKinds(); - foreach (var kind in values) + var randedEnum = GetPossibleEqKind(); + + LootFactory.EquipmentFactory.lootHistory = this.lootHistory; + var generatedEq = LootFactory.EquipmentFactory.GetRandom(randedEnum, level, eqClass); + if (generatedEq == null || (generatedEq.Class != EquipmentClass.Unique && eqClass == EquipmentClass.Unique)) { - generatedEq = LootFactory.EquipmentFactory.GetRandom(kind, level, eqClass); - if (generatedEq != null) - break; + var values = GetEqKinds(); + foreach (var kind in values) + { + generatedEq = LootFactory.EquipmentFactory.GetRandom(kind, level, eqClass); + if (generatedEq != null) + break; + } } - } - return generatedEq; + return generatedEq; + } + catch (Exception ex) + { + Container.GetInstance().LogError(ex); + return GetErrorEqPh(); + } } public static List GetEqKinds() @@ -404,6 +433,21 @@ public virtual Loot GetBestLoot(EnemyPowerKind powerKind, int level, LootHistory } } var eq = GetRandomEquipment(eqClass, level); + + if (powerKind == EnemyPowerKind.Champion || powerKind == EnemyPowerKind.Boss) + { + if (eq.Class == EquipmentClass.Plain && !eq.Enchantable) + enchant = true; + else if (eq.Class == EquipmentClass.Magic) + { + eq.PromoteToSecondMagicClass(); + if (eq.Class == EquipmentClass.Magic) + { + int k = 0; + k++; + } + } + } if (enchant) eq.MakeEnchantable(2); return eq; @@ -421,87 +465,94 @@ private EquipmentKind GetPossibleEqKind() public virtual Loot GetRandomJewellery() { - return LootFactory.EquipmentFactory.GetRandom(EquipmentKind.Amulet, -1); + return LootFactory.EquipmentFactory.GetRandom(EquipmentKind.Amulet, 1); } public virtual Loot GetRandomRing() { - return LootFactory.EquipmentFactory.GetRandom(EquipmentKind.Ring, -1); + return LootFactory.EquipmentFactory.GetRandom(EquipmentKind.Ring, 1); } static string[] GemTags; public virtual Loot GetRandomLoot(LootKind kind, int level) { - Loot res = null; - - if (kind == LootKind.Gold) - res = new Gold(); - else if (kind == LootKind.Equipment) - res = GetRandomEquipment(EquipmentClass.Plain, level); - else if (kind == LootKind.Potion) - res = GetRandomPotion(); - else if (kind == LootKind.Food) - { - var enumVal = RandHelper.GetRandomEnumValue(); - if (enumVal == FoodKind.Mushroom) - res = new Mushroom(RandHelper.GetRandomEnumValue()); - else - res = new Food(RandHelper.GetRandomEnumValue(new[] { FoodKind.Unset, FoodKind.Mushroom }));//Mushroom is a diff type - } - else if (kind == LootKind.Plant) - res = new Plant(RandHelper.GetRandomEnumValue()); - else if (kind == LootKind.Scroll) + try { - var scroll = LootFactory.ScrollsFactory.GetRandom(level) as Scroll; - var rand = RandHelper.GetRandomDouble(); - - if ((scroll.Kind == Spells.SpellKind.Portal && rand > 0.2f) //no need for so many of them - || (scroll.Kind != Spells.SpellKind.Identify && rand > 0.6f)) //these are fine + Loot res = null; + + if (kind == LootKind.Gold) + res = new Gold(); + else if (kind == LootKind.Equipment) + res = GetRandomEquipment(EquipmentClass.Plain, level); + else if (kind == LootKind.Potion) + res = GetRandomPotion(); + else if (kind == LootKind.Food) { - var newScroll = LootFactory.ScrollsFactory.GetRandom(level) as Scroll; - //if (newScroll.Kind != Spells.SpellKind.Portal || scroll.Kind == Spells.SpellKind.Portal) - scroll = newScroll; + var enumVal = RandHelper.GetRandomEnumValue(); + if (enumVal == FoodKind.Mushroom) + res = new Mushroom(RandHelper.GetRandomEnumValue()); + else + res = new Food(RandHelper.GetRandomEnumValue(new[] { FoodKind.Unset, FoodKind.Mushroom }));//Mushroom is a diff type } + else if (kind == LootKind.Plant) + res = new Plant(RandHelper.GetRandomEnumValue()); + else if (kind == LootKind.Scroll) + { + var scroll = LootFactory.ScrollsFactory.GetRandom(level) as Scroll; + var rand = RandHelper.GetRandomDouble(); - res = scroll; - } - else if (kind == LootKind.Book) - { - res = LootFactory.BooksFactory.GetRandom(level) as Book; - } - else if (kind == LootKind.Gem) - { - res = GetRandomEnchanter(level, false); - //var lootName = RandHelper.GetRandomElem(GemTags.ToArray()); - //res = GetLootByName(lootName); - } - else if (kind == LootKind.HunterTrophy) - { - res = GetRandomEnchanter(level, true); - } - else if (kind == LootKind.Recipe) - { - res = GetRandRecipe(); - } - else if (kind == LootKind.FightItem) - { - res = LootFactory.MiscLootFactory.GetRandomFightItem(level); - } - else if (kind == LootKind.Other) - { - var rand = RandHelper.GetRandomDouble(); - if (rand > 0.5f) - res = new MagicDust(); + if ((scroll.Kind == Spells.SpellKind.Portal && rand > 0.2f) //no need for so many of them + || (scroll.Kind != Spells.SpellKind.Identify && rand > 0.6f)) //these are fine + { + var newScroll = LootFactory.ScrollsFactory.GetRandom(level) as Scroll; + //if (newScroll.Kind != Spells.SpellKind.Portal || scroll.Kind == Spells.SpellKind.Portal) + scroll = newScroll; + } + + res = scroll; + } + else if (kind == LootKind.Book) + { + res = LootFactory.BooksFactory.GetRandom(level) as Book; + } + else if (kind == LootKind.Gem) + { + res = GetRandomEnchanter(level, false); + //var lootName = RandHelper.GetRandomElem(GemTags.ToArray()); + } + else if (kind == LootKind.HunterTrophy) + { + res = GetRandomEnchanter(level, true); + } + else if (kind == LootKind.Recipe) + { + res = GetRandRecipe(); + } + else if (kind == LootKind.FightItem) + { + res = LootFactory.MiscLootFactory.GetRandomFightItem(level); + } + else if (kind == LootKind.Other) + { + var rand = RandHelper.GetRandomDouble(); + if (rand > 0.5f) + res = new MagicDust(); + else + res = new Hooch(); + } else - res = new Hooch(); + { + DebugHelper.Assert(false); + } + PrepareLoot(res); + return res; } - else + catch (Exception ex) { - DebugHelper.Assert(false); + Container.GetInstance().LogError(ex); + return new MagicDust(); } - - return res; } List alreadyGen = new List(); @@ -595,6 +646,10 @@ public virtual Loot GetRandomLoot(int level, LootKind skip = LootKind.Unset) return loot; } + public List GetWeapons(Weapon.WeaponKind crossbow, int level) + { + return LootFactory.EquipmentFactory.GetWeapons(crossbow, level); + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Generators/RoomContentGenerator.cs b/Assets/Scripts/Common/Dlls/Roguelike/Generators/RoomContentGenerator.cs index aa941464..18273147 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Generators/RoomContentGenerator.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Generators/RoomContentGenerator.cs @@ -78,7 +78,7 @@ GenerationInfo gi node.ContentGenerated = true; } - protected Enemy CreateBoss(string name, char symbol, bool addBossToTag1) + protected virtual Enemy CreateBoss(string name, char symbol, bool addBossToTag1) { var enemy = CreateEnemyInstance(container, name, false, true); if(addBossToTag1) @@ -222,8 +222,8 @@ protected virtual void GenerateLoot() node.SetTileAtRandomPosition(new ProjectileFightItem(FightItemKind.ThrowingKnife) { Count = RandHelper.GetRandomInt(4) }); if (RandHelper.GetRandomDouble() > .6) node.SetTileAtRandomPosition(new ProjectileFightItem(FightItemKind.ThrowingTorch) { Count = RandHelper.GetRandomInt(4) }); - if (RandHelper.GetRandomDouble() > .9) - node.SetTileAtRandomPosition(new ProjectileFightItem(FightItemKind.CannonBall) { Count = RandHelper.GetRandomInt(4) }); + //if (RandHelper.GetRandomDouble() > .9) + // node.SetTileAtRandomPosition(new ProjectileFightItem(FightItemKind.CannonBall) { Count = RandHelper.GetRandomInt(4) }); //node.SetTileAtRandomPosition(new Hooch() { Count = RandHelper.GetRandomInt(4) }); @@ -257,6 +257,7 @@ protected virtual bool PlaceEnemy(Enemy enemy, Dungeons.TileContainers.DungeonNo protected virtual bool PlaceEnemy(Enemy enemy, Dungeons.TileContainers.DungeonNode node, Point pt) { + var res = node.SetTile(enemy, pt); if (res) { @@ -310,7 +311,7 @@ void Log(string log) //Debug.WriteLine(log); } - protected void CreateEnemiesPack(int packIndex, string enemyName, bool addBoss = false) + protected virtual void CreateEnemiesPack(int packIndex, string enemyName, bool addBoss = false) { Log("CreateEnemiesPack start packIndex: " + packIndex + ", ChildIsland: " + gi.ChildIsland + ", NodeIndex: " + node.NodeIndex + " ChempsCount: "+ gi.GeneratedInfo.ChempionsCount); @@ -406,7 +407,9 @@ protected virtual void PlaceEnemiesPack(List packEnemies, bool keepDistFr emptyCells.RemoveAll(i => i.point == enemyPoint); foreach (var en in packEnemies) { - PlaceEnemy(en, node, enemyPoint.Value); + var emp = node.GetTile(enemyPoint.Value); + emp = node.EnsureCorrectY(emp); + PlaceEnemy(en, node, emp.point); var empty = node.GetClosestEmpty(en, emptyCells); if (empty == null) { @@ -464,7 +467,7 @@ protected override List Filter(List enemyNames) return selEnemyNames; } - public void EnsureBoss() + public virtual void EnsureBoss() { if (!string.IsNullOrEmpty(LevelBossName)) { diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Generators/TileContainers/DungeonNode.cs b/Assets/Scripts/Common/Dlls/Roguelike/Generators/TileContainers/DungeonNode.cs index c2c3cdf6..2a41d150 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Generators/TileContainers/DungeonNode.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Generators/TileContainers/DungeonNode.cs @@ -24,7 +24,11 @@ public DungeonNode(Container c) : base(c) { } - + //in the mill they were outside the room + protected override bool ShallEnsureCorrectY(Dungeons.Tiles.Tile tile) + { + return tile.IsDynamic(); + } public override bool SetTile(Tile tile, Point point, bool resetOldTile = true, bool revealReseted = true, bool autoSetTileDungeonIndex = true, bool reportError = true) @@ -76,8 +80,16 @@ protected override bool ShallReveal(int row, int col) return reveal; } - public override Tile SetTileAtRandomPosition(Tile tile, bool matchNodeIndex = true, EmptyCheckContext emptyCheckContext = EmptyCheckContext.Unset) + public override Tile SetTileAtRandomPosition + ( + Tile tile, + bool matchNodeIndex = true, + EmptyCheckContext emptyCheckContext = EmptyCheckContext.Unset + ) { + if (tile is Loot) + emptyCheckContext = EmptyCheckContext.DropLoot; + var tileSet = base.SetTileAtRandomPosition(tile, matchNodeIndex, emptyCheckContext); if (tileSet != null) { diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Help/HintItem.cs b/Assets/Scripts/Common/Dlls/Roguelike/Help/HintItem.cs index f231ec26..6f649b31 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Help/HintItem.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Help/HintItem.cs @@ -3,8 +3,9 @@ public enum HintKind { Unset, LootCollectShortcut, BulkLootCollectShortcut, ShowCraftingPanel, HeroLevelTooLow, CanNotPutOnUnidentified, - LootHightlightShortcut, UseProjectile, UseElementalWeaponProjectile, SwapActiveWeapon, SwapActiveHotBar, PreviewSwapActiveHotBar, - FoundPlace, SecretLevel, EnchantEquipment + LootHightlightShortcut, UseProjectile, UseElementalWeaponProjectile, SwapActiveWeapon, SwapActiveHotBarByUI, PreviewSwapActiveHotBar, + FoundPlace, SecretLevel, EnchantEquipment, WatchoutEnemyLevel, TalkToNPCs, AbilitiesPassive, AbilitiesActive, AbilitiesSpells, + EquipAlly, QuicklySell } /// diff --git a/Assets/Scripts/Common/Dlls/Roguelike/History/HintHistory.cs b/Assets/Scripts/Common/Dlls/Roguelike/History/HintHistory.cs index 160c0179..9aa9ffc4 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/History/HintHistory.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/History/HintHistory.cs @@ -11,7 +11,6 @@ namespace Hints { public class HintHistory { - //public static Dictionary Messages = new Dictionary(); private List hints = new List(); //[Json] @@ -33,16 +32,42 @@ static HintHistory() public HintHistory() { Hints.Add(new HintItem() { Info = "Press 'Left Alt' to see collectable/interactive items.", Kind = HintKind.LootHightlightShortcut }); - Hints.Add(new HintItem() { Info = "Press 'G' to collect a loot under your position.", Kind = HintKind.LootCollectShortcut }); + Hints.Add(new HintItem() { Info = "Press 'G' to collect a loot under your position.\r\nKeys can be rebinded in Options.", Kind = HintKind.LootCollectShortcut }); Hints.Add(new HintItem() { Info = "Use 'G' to collect nearby loot items.", Kind = HintKind.BulkLootCollectShortcut }); Hints.Add(new HintItem() { Info = "Recipe has been collected. Press 'R' to open Crafting Panel and see it's description.", Kind = HintKind.ShowCraftingPanel }); - Hints.Add(new HintItem() { Info = "Hero level too low to use an item", Kind = HintKind.HeroLevelTooLow }); - Hints.Add(new HintItem() { Info = "Can not put on unidentified item", Kind = HintKind.CanNotPutOnUnidentified }); + Hints.Add(new HintItem() { Info = "Hero level too low to use an item.", Kind = HintKind.HeroLevelTooLow }); + Hints.Add(new HintItem() { Info = "Can not put on unidentified item.", Kind = HintKind.CanNotPutOnUnidentified }); Hints.Add(new HintItem() { Info = "TODO", Kind = HintKind.UseProjectile }); Hints.Add(new HintItem() { Info = "TODO", Kind = HintKind.UseElementalWeaponProjectile }); Hints.Add(new HintItem() { Info = "swapping an active weapon/shield set Press 'X' to .", Kind = HintKind.SwapActiveWeapon }); Hints.Add(new HintItem() { Info = "", Kind = HintKind.SecretLevel}); - Hints.Add(new HintItem() { Info = "You collected a gem, it can be used to enchant your equipment.\r\nPress 'I' to open Inventory Panel and drop the gem on a weapon.", Kind = HintKind.EnchantEquipment }); + Hints.Add(new HintItem() { Info = "Before attacking an enemy, check his stats to make sure you want to attack it.", Kind = HintKind.WatchoutEnemyLevel }); + Hints.Add(new HintItem() { Info = "You can use a gem to enchant your equipment.\r\nPress 'I' to open Inventory Panel and drop the gem on a weapon.", Kind = HintKind.EnchantEquipment }); + + Hints.Add(new HintItem() { Info = "Talk to NPCs to get quests.", Kind = HintKind.TalkToNPCs }); + Hints.Add(new HintItem() { Info = "Press Left Control + Right Mouse Button to quickly sell an item.", Kind = HintKind.QuicklySell }); + Hints.Add(new HintItem() { Info = "Passive abilities are used by the hero automatically (each turn/randomly"+ + "\r\n - depend on a kind). Just invest action points and enjoy them." + , Kind = HintKind.AbilitiesPassive }); + + Hints.Add(new HintItem() + { + Info = "Active abilities must be assigned to the hot bar.\r\nThe hero controls when they are used (activates/deactivates them)." + + "\r\nAfter usage a cooldown is applicated." + , + Kind = HintKind.AbilitiesActive + }); + + Hints.Add(new HintItem() + { + Info = "Spell abilities apply to scrolls and books but not to projectiles casted\r\n by weapons (staffs, wands, scepters)." + + "That means a fireball emitted from a staff\r\nwill have different power that the one casted by scroll/book." + + , + Kind = HintKind.AbilitiesSpells + }); + + Hints.Add(new HintItem() { Info = "Open Ally's panel to equip it (default key: L)", Kind = HintKind.EquipAlly }); } string BuildDesc(HintKind kind, int keyCode, Func codeFormatter = null) diff --git a/Assets/Scripts/Common/Dlls/Roguelike/History/History.cs b/Assets/Scripts/Common/Dlls/Roguelike/History/History.cs index 4d4b4f46..d616819b 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/History/History.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/History/History.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; namespace Roguelike @@ -19,7 +20,27 @@ public bool WasEngaged(string tag1) public void SetEngaged(string tag1) { - Engaged.Add(new HistoryItem() { Tag1 = tag1 }); + if(GetEngaged(tag1) is null) + Engaged.Add(new HistoryItem() { Tag1 = tag1 }); + } + + public HistoryItem GetEngaged(string v) + { + var item = Engaged.Where(i => i.Tag1 == v).FirstOrDefault(); + return item; + } + + public void RemoveEngaged(string v) + { + var item = GetEngaged(v); + if (item!=null) + Engaged.Remove(item); + } + + public void EnsureEngaged(string v) + { + if (!WasEngaged(v)) + SetEngaged(v); } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/History/LivingEntityHistory.cs b/Assets/Scripts/Common/Dlls/Roguelike/History/LivingEntityHistory.cs index 1a1f2212..634d8d47 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/History/LivingEntityHistory.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/History/LivingEntityHistory.cs @@ -7,50 +7,51 @@ namespace Roguelike.History { - public class LivingEntityHistoryItem : HistoryItem + public class LivingEntityHistoryItem : HistoryItem + { + public LivingEntityHistoryItem(LivingEntity dead) { - public LivingEntityHistoryItem() - { - } - - public LivingEntityHistoryItem(LivingEntity dead) - { - Name = dead.name; - Tag1 = dead.tag1; - Herd = dead.Herd; - if (dead is Enemy en) - Power = en.PowerKind; - Level = dead.Level; - } - - public string Herd { get; set; } - public Tiles.LivingEntities.EnemyPowerKind Power { get; set; } - public int Level { get; set; } + Name = dead.name; + Tag1 = dead.tag1; + Herd = dead.Herd; + if(dead is Enemy en) + Power = en.PowerKind; + Level = dead.Level; } - public class LivingEntityHistory - { - public List Items { get; set; } = new List(); + public string Herd { get; set; } + public Tiles.LivingEntities.EnemyPowerKind Power { get; set; } + public int Level { get; set; } + } + + public class LivingEntityHistory + { + public List Items { get; set; } = new List(); - public int CountByTag1(string tag1) - { - return Items.Where(i => i.Tag1 == tag1).Count(); - } + public int CountByTag1(string tag1) + { + return Items.Where(i => i.Tag1 == tag1).Count(); + } - public int CountByName(string name) - { - return Items.Where(i => i.Name == name).Count(); - } + public int CountByName(string name) + { + return Items.Where(i => i.Name == name).Count(); + } - public int CountByHerd(string herd) - { - return Items.Where(i => i.Herd == herd).Count(); - } + public int CountByHerd(string herd) + { + return Items.Where(i => i.Herd == herd).Count(); + } - public void AddItem(LivingEntityHistoryItem lh) - { - Items.Add(lh); - } + public void AddItem(LivingEntityHistoryItem lh) + { + Items.Add(lh); + } + public int CountEnemy() + { + return Items.Where(i => i.Power != EnemyPowerKind.Unset).Count(); } + + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/LootContainers/Crafting.cs b/Assets/Scripts/Common/Dlls/Roguelike/LootContainers/Crafting.cs index 6091ca3a..09fe348c 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/LootContainers/Crafting.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/LootContainers/Crafting.cs @@ -11,11 +11,6 @@ public class InventoryOwner : IInventoryOwner public Inventory Inventory { get; set; } public int Gold { get; set; } - //public bool InventoryAcceptsItem(Loot loot) - //{ - // return true; - //} - public virtual bool GetGoldWhenSellingTo(IInventoryOwner other) { return false; @@ -25,6 +20,11 @@ public int GetPrice(Loot loot) { throw new NotImplementedException(); } + + public bool IsSellable(Loot loot) + { + return loot.IsSellable(); + } } public class Crafting diff --git a/Assets/Scripts/Common/Dlls/Roguelike/LootContainers/Inventory.cs b/Assets/Scripts/Common/Dlls/Roguelike/LootContainers/Inventory.cs index 7e92a08a..9a690693 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/LootContainers/Inventory.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/LootContainers/Inventory.cs @@ -1,9 +1,11 @@ using Dungeons.ASCIIDisplay; using Dungeons.ASCIIDisplay.Presenters; +using Dungeons.Core; using Newtonsoft.Json; using Roguelike.Events; using Roguelike.Managers; using Roguelike.Tiles; +using Roguelike.Tiles.Abstract; using Roguelike.Tiles.LivingEntities; using Roguelike.Tiles.Looting; using SimpleInjector; @@ -55,7 +57,7 @@ public class Inventory : IInventory public int Capacity { get; set; }//how many items there can be? [JsonIgnore] - public AdvancedLivingEntity Owner { get; set; } + public IAdvancedEntity Owner { get; set; } public InvBasketKind InvBasketKind { get; set; } [JsonIgnore] @@ -68,7 +70,7 @@ public Inventory(Container container) { this.Container = container; //Assert(this.Container != null); - Capacity = 64; + Capacity = 80;//Look out, merchant share the same inv in UI - shall also have same digit here! } public List ToASCIIList() @@ -171,7 +173,7 @@ protected virtual StackedLoot GetStackedItem(Loot loot) { return Items.FirstOrDefault(i => i == loot) as StackedLoot; } - + public const string DuplItem = "Add(Loot item) duplicate item "; public virtual bool Add ( Loot item, @@ -187,6 +189,11 @@ public virtual bool Add if (stackedInInv != null) { exist = true; + if (itemStacked.Name != item.Name) + { + Assert(false, "itemStacked.Name != item.Name, stacked: " + itemStacked + ", item:" +item ); + return false; + } } else exist = Items.Contains(item); @@ -225,7 +232,8 @@ public virtual bool Add else { //var sameID = Items.FirstOrDefault(i => i.Id == item.Id); - Assert(false, "Add(Loot item) duplicate item " + item); + //TODO sometimes a ring is not removed from UI layer :O + Assert(false, DuplItem + item, item); //throw new Exception("Add(Loot item) duplicate item " + item); } } @@ -244,11 +252,16 @@ private void AppendAction(GameEvent ac) Dungeons.DebugHelper.Assert(false, Owner + " AppendAction EventsManager == null"); } - public void Assert(bool assert, string info = "assert failed") + public void Assert(bool assert, string info = "assert failed", Dungeons.Tiles.Tile involvedTile = null) { if (!assert) { - AppendAction(new Events.GameStateAction() { Type = Events.GameStateAction.ActionType.Assert, Info = info }); + AppendAction(new Events.GameStateAction() + { + Type = Events.GameStateAction.ActionType.Assert, + Info = info, + InvolvedTile = involvedTile + }); } } @@ -278,7 +291,7 @@ public virtual Loot Remove(Loot item, RemoveItemArg arg = null)//int stackedCoun { stackedItem.Count = 0; itemToRemove = item; - + } else SetStackCount(stackedItem, stackedItemCount); @@ -287,10 +300,16 @@ public virtual Loot Remove(Loot item, RemoveItemArg arg = null)//int stackedCoun res = true; } else + { Assert(false); + return null; + } } else + { Assert(false); + return null; + } } if (itemToRemove != null) @@ -348,7 +367,7 @@ public Container Container internal bool CanAddLoot(Loot loot) { - var roomLeft = Capacity > Items.Count || (loot is StackedLoot stacked && stacked.Count > 0); + var roomLeft = Capacity > Items.Count || (loot is StackedLoot stacked && GetStackedCount(stacked) > 0); //if (!roomLeft) // denyReason = "not enouth room"; return roomLeft; diff --git a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/AbstractLootFactory.cs b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/AbstractLootFactory.cs index 4933ad45..a6d9c3cb 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/AbstractLootFactory.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/AbstractLootFactory.cs @@ -11,276 +11,74 @@ namespace Roguelike.LootFactories { public abstract class AbstractLootFactory { - protected T GetRandom(Dictionary> factory) + protected static T PrepareLoot(string tagPart, T loot) where T : Loot { - var index = RandHelper.GetRandomInt(factory.Count); - var lootCreator = factory.ElementAt(index); - return lootCreator.Value(lootCreator.Key); - } - - protected abstract void Create(); - public abstract Loot GetRandom(int level);// where T : Loot; - public abstract Loot GetByName(string name); - public abstract Loot GetByAsset(string tagPart); - protected Container container; - - public AbstractLootFactory(Container container) - { - this.container = container; - Create(); - } - - public virtual IEnumerable GetAll() - { - return new List(); - } - } - - public abstract class EquipmentTypeFactory : AbstractLootFactory - { - protected Dictionary> factory = new Dictionary>(); - protected Dictionary prototypes = new Dictionary(); - - public EquipmentTypeFactory(Container container) : base(container) - { - } - protected void CreatePrototypes() - { - foreach (var sh in factory.Keys) - { - if (factory.ContainsKey(sh)) - { - var res = factory[sh](sh); - prototypes.Add(sh, res); - } - else - ReportError("!factory.ContainsKey(sh): " + sh); - } - } - - protected void ReportError(string error) - { - container.GetInstance().LogError(error); - } - - public List GetUniqueItems(int level) - { - return prototypes.Where(i => i.Value.Class == Roguelike.Tiles.EquipmentClass.Unique && i.Value.LevelIndex <= level).Select(i => i.Value).ToList(); - } - - public override Loot GetRandom(int level) - { - var lootProto = GetRandromFromPrototype(level); - if (lootProto != null) - return lootProto; - - Equipment loot = GetRandomFromAll(); + if (string.IsNullOrEmpty(loot.tag1)) + loot.tag1 = tagPart; return loot; } - protected Equipment GetRandomFromAll() + protected T GetRandom(Dictionary> factory) where T : Loot { var index = RandHelper.GetRandomInt(factory.Count); var lootCreator = factory.ElementAt(index); var loot = lootCreator.Value(lootCreator.Key); - return loot; - } - - protected void EnsureMaterialFromLootSource(Equipment eq, EquipmentMaterial mat) - { - if (!eq.IsMaterialAware()) - return; - // int level = eq.LevelIndex; - //if (level > MaterialProps.IronDropLootSrcLevel) - { - eq.SetMaterial(mat); - } + return PrepareLoot(lootCreator.Key, loot); } - - protected virtual Loot GetRandromFromPrototype(int level) + protected T GetByAsset(Dictionary> factory, string tag) where T : Loot { - if (prototypes.Any()) - { - var eq = GetLootAtLevel(level); - if (eq.Item2 == EquipmentMaterial.Bronze && level > 7) - { - // if (LevelIndex > 7) - { - int k = 0; - k++; - } - } - return CreateItem(level, eq); - } - return null; + return GetByTag(factory, tag); } - protected Loot CreateItem(int level, Tuple eqDesc) + protected T GetByTag(Dictionary> factory, string tag) where T : Loot { - if (eqDesc != null && eqDesc.Item1.Any()) + var tile = factory.FirstOrDefault(i => i.Key.ToLower() == tag.ToLower()); + if (tile.Key != null) { - var eqCreator = factory[eqDesc.Item1]; - var eqDone = eqCreator(eqDesc.Item1); - if (eqDesc.Item2 != EquipmentMaterial.Unset) - EnsureMaterialFromLootSource(eqDone, eqDesc.Item2); - - if (eqDone is Weapon wpn && wpn.IsMagician && wpn.Class != EquipmentClass.Unique) - { - wpn.SetLevelIndex(level);//TODO - } - return eqDone; + T loot = tile.Value(tag); + PrepareLoot(tag, loot); + return loot; } - return null; } - List GetPlainsAtLevel(int level, bool materialAware, ref bool fakeDecreaseOfLevel) - { - var plains = prototypes.Values.Where(i => i.Class == EquipmentClass.Plain).ToList(); - - var plainsAtLevel = GetPlainsAtLevel(level, materialAware, plains); - if (level > 1) - { - fakeDecreaseOfLevel = false; - while (!plainsAtLevel.Any())// && && RandHelper.GetRandomDouble() < 0.5f) - { - level--; - plainsAtLevel = GetPlainsAtLevel(level, materialAware, plains); - fakeDecreaseOfLevel = true; - } - } - return plainsAtLevel; - } - - private static List GetPlainsAtLevel(int level, bool materialAware, List plains) + protected int GetStackableDefaultCount() { - var plainsAtLevel = plains.Where(i => i.LevelIndex == level).ToList(); - if (materialAware) - plainsAtLevel = plainsAtLevel.Where(i => i.IsMaterialAware()).ToList(); - return plainsAtLevel; + var rand = Enumerable.Range(2, 5).ToList().GetRandomElem(); + return rand; } - protected virtual Tuple GetLootAtLevel(int level) - { - EquipmentMaterial mat = EquipmentMaterial.Unset; - bool fakeDecreaseOfLevel = false; - var plainsAtLevel = GetPlainsAtLevel(level, false, ref fakeDecreaseOfLevel); - if (level > 1) - plainsAtLevel.AddRange(GetPlainsAtLevel(level-1, false, ref fakeDecreaseOfLevel));//give a chance to get material aware - var eq = plainsAtLevel.GetRandomElem(); - var orgLevel = level; - if (eq.Name == "Hammer") - { - int k = 0; - k++; - } - //Debug.WriteLine("GetLootAtLevel "+ eq + " start"); - if (eq.IsMaterialAware()) - { - GetMaterial(ref level, ref mat, fakeDecreaseOfLevel); - //Debug.WriteLine("GetMaterial " + mat + " level != orgLevel "+ (level != orgLevel)); - //if (level != orgLevel) - //{ - // var newEq = plains.Where(i => i.LevelIndex == level).Where(i => i.IsMaterialAware()).ToList().GetRandomElem(); - // if (newEq == null) - // { - // mat = EquipmentMaterial.Unset; - // level = orgLevel; - - // GetMaterial(ref level, ref mat, fakeDecreaseOfLevel); - // Debug.WriteLine("GetMaterial rewert!"); - // } - // else - // { - // eq = newEq; - // } - //} - } - - - // Debug.WriteLine("GetLootAtLevel " + eq + " end"); - return ReturnEq(mat, eq); - } - - private static void GetMaterial(ref int level, ref EquipmentMaterial mat, bool fakeDecreaseOfLevel) - { - - //bool upgMaterial = false; - float nextMatThreshold = 0.4f; - var lvl = level; - if (fakeDecreaseOfLevel) - lvl++; - if (lvl > MaterialProps.SteelDropLootSrcLevel) - { - mat = EquipmentMaterial.Iron; - if (RandHelper.GetRandomDouble() > nextMatThreshold)//make it more unpredictable - { - mat = EquipmentMaterial.Steel; - //upgMaterial = true; - } - } - else if (lvl > MaterialProps.IronDropLootSrcLevel) - { - if (RandHelper.GetRandomDouble() > nextMatThreshold) - { - mat = EquipmentMaterial.Iron; - //upgMaterial = true; - } - } - //TODO ? too complicated.. - //if (upgMaterial) - // level--; - } - - protected Tuple GetRandomFromList(int level, EquipmentMaterial mat, List plains) - { - - var eqsAtLevel = plains.Where(i => i.LevelIndex == level).ToList(); - - var eq = eqsAtLevel.GetRandomElem(); - return ReturnEq(mat, eq); - } + protected abstract void Create(); + public abstract Loot GetRandom(int level);// where T : Loot; + //public abstract Loot GetByName(string name); + public abstract Loot GetByAsset(string tagPart); + protected Container container; - private static Tuple ReturnEq(EquipmentMaterial mat, Equipment eq) + public AbstractLootFactory(Container container) { - Tuple res = null; - if (eq != null) - { - if (eq.IsMaterialAware()) - { - if (mat == EquipmentMaterial.Unset) - { - mat = EquipmentMaterial.Bronze; - } - } - res = new Tuple(eq.tag1, mat); - } - - return res; + this.container = container; + Create(); } - public override Loot GetByName(string name) + public virtual IEnumerable GetAll() { - return GetByAsset(name); + return new List(); } + } - public override Loot GetByAsset(string tagPart) - { - var tile = factory.FirstOrDefault(i => i.Key.ToLower() == tagPart.ToLower()); - if (tile.Key != null) - return tile.Value(tagPart); + public class PrototypeValue + { + public int Level { get; set; } + public bool IsMaterialAware { get; set; } + public EquipmentMaterial Material { get; set; } - return null; - } + public string Tag { get; set; } - public override IEnumerable GetAll() - { - List loot = new List(); - foreach (var lc in factory) - loot.Add(lc.Value(lc.Key)); + public EquipmentKind Kind { get; set; } - return loot; - } + public Weapon.WeaponKind WeaponKind { get; set; } + } - }} + +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/BooksFactory.cs b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/BooksFactory.cs index c5f2a0ca..98c3e853 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/BooksFactory.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/BooksFactory.cs @@ -23,7 +23,7 @@ protected override void Create() var book = new Book(); book.tag1 = tag; book.Kind = Scroll.DiscoverKindFromName(tag); - //scroll.Count = Enumerable.Range(1, 3).ToList().GetRandomElem(); + return book; }; var names = new[] { "fire_ball_book", "ice_ball_book", "poison_ball_book", @@ -39,25 +39,18 @@ public override Loot GetRandom(int level) return GetRandom(factory); } - public override Loot GetByName(string name) - { - return GetByAsset(name); - } - public override Loot GetByAsset(string tagPart) { - var tile = factory.FirstOrDefault(i => i.Key == tagPart); - if (tile.Key != null) - return tile.Value(tagPart); - - return null; + return GetByAsset(factory, tagPart); } public Loot GetByKind(Spells.SpellKind kind) { var tile = factory.FirstOrDefault(i => Scroll.DiscoverKindFromName(i.Key) == kind); if (tile.Key != null) - return tile.Value(tile.Key); + { + return PrepareLoot(tile.Key, tile.Value(tile.Key)); + } return null; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/EquipmentFactory.cs b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/EquipmentFactory.cs index f7b780b9..034091ca 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/EquipmentFactory.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/EquipmentFactory.cs @@ -1,6 +1,5 @@ using Dungeons.Core; using Roguelike.Attributes; -using Roguelike.Generators; using Roguelike.History; using Roguelike.Tiles; using Roguelike.Tiles.Looting; @@ -26,7 +25,7 @@ public class Props public class MaterialProps { - public const int BashingWeaponBaseDamage = 2; + public const int BashingWeaponBaseDamage = 3; public const int BronzeSwordBaseDamage = 3; public const int BronzeDaggerBaseDamage = 2; public const int BronzeAxeBaseDamage = 3; @@ -75,16 +74,16 @@ public override Loot GetByAsset(string tagPart) return null; } - public override Roguelike.Tiles.Loot GetByName(string assetName) - { - foreach (var kv in lootCreators) - { - var tile = kv.Value.GetByName(assetName); - if (tile != null) - return tile; - } - return null; - } + //public override Roguelike.Tiles.Loot GetByName(string assetName) + //{ + // foreach (var kv in lootCreators) + // { + // var tile = kv.Value.GetByName(assetName); + // if (tile != null) + // return tile; + // } + // return null; + //} public void SetFactory(EquipmentKind ek, EquipmentTypeFactory factory) { @@ -111,7 +110,8 @@ public virtual Equipment GetRandom(EquipmentKind kind, int maxEqLevel, Equipment { if (this.lootCreators.Any()) { - eq = this.lootCreators[EquipmentKind.Weapon].GetUniqueItems(maxEqLevel).FirstOrDefault(); + var eqTag = this.lootCreators[EquipmentKind.Weapon].GetUniqueItems(maxEqLevel).FirstOrDefault(); + eq = GetByAsset(eqTag) as Equipment; } if (eq == null) { @@ -155,9 +155,9 @@ public virtual Equipment GetRandom(EquipmentKind kind, int maxEqLevel, Equipment break; } - if (eq != null && eqClass != EquipmentClass.Plain) + if (eq != null) { - if (eqClass == EquipmentClass.Plain) + if (eq.Class == EquipmentClass.Plain && eqClass != EquipmentClass.Plain) { MakeMagic(eqClass, eq); } @@ -173,12 +173,8 @@ protected void MakeMagic(EquipmentClass eqClass, Equipment eq) return; if (eq.IsPlain()) eq.MakeMagic(); - if (!eq.IsSecondMagicLevel && eqClass == EquipmentClass.MagicSecLevel) - { - var stats = eq.GetPossibleMagicStats(); - var stat = RandHelper.GetRandomElem(stats); - eq.MakeMagicSecLevel(stat.Key, 3);//TODO ! - } + if (eqClass == EquipmentClass.MagicSecLevel) + eq.PromoteToSecondMagicClass(); } public virtual Equipment GetRandomArmor() @@ -203,6 +199,28 @@ public virtual Weapon GetRandomWeapon() return item; } + public virtual List GetWeapons(Weapon.WeaponKind kind, int level) + { + return new List(); + } + + public virtual Weapon GetRandomWeapon(Weapon.WeaponKind kind) + { + var item = new Weapon(); + if (kind == Weapon.WeaponKind.Sword) + { + item.Name = "Sword"; + item.tag1 = "Sword"; + item.Kind = Weapon.WeaponKind.Sword; + + item.PrimaryStatKind = EntityStatKind.MeleeAttack; + item.PrimaryStatValue = 5; + return item; + } + + return null; + } + public virtual Equipment GetRandomHelmet() { var item = new Equipment(EquipmentKind.Helmet); @@ -278,7 +296,7 @@ private Jewellery AddRing(string asset, EntityStatKind sk, int minDropDungeonLev jew.SetPrimaryStat(sk, statValue); var name = "ring of "; jew.Name = name + sk; - jew.tag1 = "ring_" + sk; + jew.tag1 = "ring_of_" + sk; if (sk == EntityStatKind.ResistCold || sk == EntityStatKind.ResistFire || sk == EntityStatKind.ResistPoison) { @@ -288,5 +306,10 @@ private Jewellery AddRing(string asset, EntityStatKind sk, int minDropDungeonLev return jew; } + + protected virtual List GetPlainsAtLevel(int level, EquipmentKind ek, bool materialAware, ref bool fakeDecreaseOfLevel) + { + return lootCreators[ek].GetPlainsAtLevel(level, materialAware, true, ref fakeDecreaseOfLevel); + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/EquipmentTypeFactory.cs b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/EquipmentTypeFactory.cs new file mode 100644 index 00000000..ca537a46 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/EquipmentTypeFactory.cs @@ -0,0 +1,294 @@ +using Dungeons.Core; +using Roguelike.LootFactories; +using Roguelike.Tiles.Looting; +using Roguelike.Tiles; +using System.Collections.Generic; +using System; +using SimpleInjector; +using System.Linq; + +public abstract class EquipmentTypeFactory : AbstractLootFactory +{ + protected Dictionary> factory = new Dictionary>(); + + protected Dictionary prototypesPlain = new Dictionary(); + protected Dictionary prototypesUnique = new Dictionary(); + + public EquipmentTypeFactory(Container container) : base(container) + { + } + + protected void CreatePrototypes() + { + foreach (var tag in factory.Keys) + { + if (factory.ContainsKey(tag)) + { + var res = factory[tag](tag); + + var pv = GetFromEq(res); + if (res.Class == EquipmentClass.Plain) + prototypesPlain.Add(tag, pv); + if (res.Class == EquipmentClass.Unique) + prototypesUnique.Add(tag, pv); + } + else + ReportError("!factory.ContainsKey(tag): " + tag); + } + } + + protected PrototypeValue GetFromEq(Equipment res) + { + var pv = new PrototypeValue() + { + Level = res.LevelIndex, + IsMaterialAware = res.IsMaterialAware(), + Material = res.Material, + Tag = res.tag1, + Kind = res.EquipmentKind + }; + if (res is Weapon wpn) + pv.WeaponKind = wpn.Kind; + + return pv; + } + + protected void ReportError(string error) + { + container.GetInstance().LogError(error); + } + + public List GetUniqueItems(int level) + { + return prototypesUnique.Where(i => i.Value.Level <= level).Select(i => i.Key).ToList(); + } + + public List GetNotUniqueItems(int level) + { + return prototypesPlain.Where(i => i.Value.Level <= level).Select(i => i.Key).ToList(); + } + + public override Loot GetRandom(int level) + { + var lootProto = GetRandromFromPrototype(level); + if (lootProto != null) + return lootProto; + + //Equipment loot = GetRandomFromAll();-returned uniq + + return null; + } + + + protected void EnsureMaterialFromLootSource(Equipment eq, EquipmentMaterial mat) + { + if (!eq.IsMaterialAware()) + return; + { + eq.SetMaterial(mat); + } + } + + protected virtual Loot GetRandromFromPrototype(int level) + { + var eq = GetLootAtLevel(level); + if (eq == null) return null; + + if (eq.Item2 == EquipmentMaterial.Bronze && level > 7) + { + // if (LevelIndex > 7) + { + int k = 0; + k++; + } + } + return CreateItem(level, eq); + + } + + protected Loot CreateItem(int level, Tuple eqDesc) + { + if (eqDesc != null && eqDesc.Item1.Any()) + { + var eqCreator = factory[eqDesc.Item1]; + var eqDone = eqCreator(eqDesc.Item1); + if (eqDesc.Item2 != EquipmentMaterial.Unset) + EnsureMaterialFromLootSource(eqDone, eqDesc.Item2); + + if (eqDone is Weapon wpn && wpn.IsMagician && wpn.Class != EquipmentClass.Unique) + { + wpn.SetLevelIndex(level);//TODO + } + return eqDone; + } + + return null; + } + + public List GetPlainsAtLevel(int level, bool materialAware, bool alwaysTwo, ref bool fakeDecreaseOfLevel) + { + //var plains = prototypesPlain.Keys.ToList(); + + var plainsAtLevel = GetPlainsAtLevel(level, materialAware); + if (level > 1) + { + fakeDecreaseOfLevel = false; + while (!plainsAtLevel.Any() || alwaysTwo)// && && RandHelper.GetRandomDouble() < 0.5f) + { + level--; + plainsAtLevel.AddRange(GetPlainsAtLevel(level, materialAware)); + fakeDecreaseOfLevel = true; + if (plainsAtLevel.Count > 1) + break; + } + } + return plainsAtLevel; + } + + private List GetPlainsAtLevel(int level, bool materialAware) + { + var plainsAtLevel = prototypesPlain.Where(i => i.Value.Level == level).Select(i => i.Value).ToList(); + if (materialAware) + plainsAtLevel = plainsAtLevel.Where(i => i.IsMaterialAware).ToList(); + return plainsAtLevel; + } + + protected virtual Tuple GetLootAtLevel(int level) + { + EquipmentMaterial mat = EquipmentMaterial.Unset; + bool fakeDecreaseOfLevel = false; + var plainsAtLevel = GetPlainsAtLevel(level, false, alwaysTwo: false, ref fakeDecreaseOfLevel); + if (level > 1) + plainsAtLevel.AddRange(GetPlainsAtLevel(level - 1, false, alwaysTwo: false, ref fakeDecreaseOfLevel));//give a chance to get material aware + var eq = plainsAtLevel.GetRandomElem(); + var orgLevel = level; + //if (eq.Name == "Hammer") + //{ + // int k = 0; + // k++; + //} + //Debug.WriteLine("GetLootAtLevel "+ eq + " start"); + if (eq.IsMaterialAware) + { + GetMaterial(ref level, ref mat, fakeDecreaseOfLevel); + //Debug.WriteLine("GetMaterial " + mat + " level != orgLevel "+ (level != orgLevel)); + //if (level != orgLevel) + //{ + // var newEq = plains.Where(i => i.LevelIndex == level).Where(i => i.IsMaterialAware()).ToList().GetRandomElem(); + // if (newEq == null) + // { + // mat = EquipmentMaterial.Unset; + // level = orgLevel; + + // GetMaterial(ref level, ref mat, fakeDecreaseOfLevel); + // Debug.WriteLine("GetMaterial rewert!"); + // } + // else + // { + // eq = newEq; + // } + //} + } + + + // Debug.WriteLine("GetLootAtLevel " + eq + " end"); + return ReturnEqDescription(mat, eq); + } + + protected static void GetMaterial(ref int level, ref EquipmentMaterial mat, bool fakeDecreaseOfLevel) + { + var lvl = level; + if (fakeDecreaseOfLevel) + lvl++; + mat = GetMaterial(mat, lvl); + //TODO ? too complicated.. + //if (upgMaterial) + // level--; + } + protected static EquipmentMaterial GetMaterial(int lvl) + { + EquipmentMaterial mat = EquipmentMaterial.Bronze; + return GetMaterial(mat, lvl); + } + + + private static EquipmentMaterial GetMaterial(EquipmentMaterial mat, int lvl) + { + float nextMatThreshold = 0.4f; + if (lvl > MaterialProps.SteelDropLootSrcLevel) + { + mat = EquipmentMaterial.Iron; + if (RandHelper.GetRandomDouble() > nextMatThreshold)//make it more unpredictable + { + mat = EquipmentMaterial.Steel; + //upgMaterial = true; + } + } + else if (lvl > MaterialProps.IronDropLootSrcLevel) + { + if (RandHelper.GetRandomDouble() > nextMatThreshold) + { + mat = EquipmentMaterial.Iron; + //upgMaterial = true; + } + } + + return mat; + } + + protected Tuple GetRandomFromList(int level, EquipmentMaterial mat, List plains) + { + PrototypeValue eq = GetRandFromList(level, plains); + return ReturnEqDescription(mat, eq); + } + + private PrototypeValue GetRandFromList(int level, List plains) + { + var eqsAtLevel = plains.Where(i => i.Level == level).ToList(); + var eq = eqsAtLevel.GetRandomElem(); + return eq; + } + + protected Tuple ReturnEqDescription(EquipmentMaterial mat, PrototypeValue eq) + { + Tuple res = null; + if (eq != null) + { + if (eq.IsMaterialAware) + { + if (mat == EquipmentMaterial.Unset) + { + mat = EquipmentMaterial.Bronze; + } + } + res = new Tuple(eq.Tag, mat); + } + + return res; + } + + //public override Loot GetByName(string name) + //{ + // return GetByAsset(name); + //} + + public override Loot GetByAsset(string tagPart) + { + return GetByAsset(factory, tagPart); + } + + public override IEnumerable GetAll() + { + var loot = new List(); + foreach (var lc in factory) + { + var lootItem = GetByAsset(lc.Key); + loot.Add(lootItem); + } + + return loot; + } + + + +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/EquipmentTypeFactory.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/EquipmentTypeFactory.cs.meta new file mode 100644 index 00000000..f607784b --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/EquipmentTypeFactory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7411daf569df1c744be8d082cc6b1d90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/LootFactory.cs b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/LootFactory.cs index b197414a..de09b2e7 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/LootFactory.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/LootFactory.cs @@ -40,7 +40,7 @@ public override Loot GetByAsset(string tagPart) { var loot = fac.GetByAsset(tagPart); if (loot != null) - return ReturnLoot(loot); + return PrepareLoot(tagPart, ReturnLoot(loot)); } return null; @@ -51,6 +51,33 @@ Loot ReturnLoot(Loot loot) var eq = loot as Equipment; if (eq != null) eq.Identified += Eq_Identified; + + if (loot is StackedLoot sl) + { + if (sl is Gem || sl is Recipe) + { + sl.Count = 1; + if (sl is Recipe rec) + { + if (rec.Kind == RecipeKind.Pendant)//not used! + rec.Kind = RecipeKind.Toadstools2Potion; + } + } + else if (sl.Count == 1) + sl.Count = GetStackableDefaultCount(); + + if (sl is ProjectileFightItem pfi) + { + if (pfi.FightItemKind == FightItemKind.Stone) + sl.Count *= 2;//for arrows + } + + else if (sl is GenericLoot gl) + { + if (gl.tag1 == "BarleySack") + sl.Count = 1; + } + } return loot; } @@ -62,17 +89,17 @@ private void Eq_Identified(object sender, Loot e) }); } - public override Loot GetByName(string name) - { - foreach (var fac in factories) - { - var loot = fac.GetByName(name); - if (loot != null) - return ReturnLoot(loot); - } + //public override Loot GetByName(string name) + //{ + // foreach (var fac in factories) + // { + // var loot = fac.GetByName(name); + // if (loot != null) + // return ReturnLoot(loot); + // } - return null; - } + // return null; + //} public override Loot GetRandom(int level) { diff --git a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/MiscLootFactory.cs b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/MiscLootFactory.cs index 68186607..37753b01 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/MiscLootFactory.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/MiscLootFactory.cs @@ -27,6 +27,7 @@ protected override void Create() { var kind = PotionKind.Unset; var specialKind = SpecialPotionKind.Unset; + var specialPotionSize = SpecialPotionSize.Small; if (tag == "health_potion") kind = PotionKind.Health; @@ -37,13 +38,17 @@ protected override void Create() else if (tag == "antidote_potion") kind = PotionKind.Antidote; - else if (tag == "magic_potion") + else if (tag.Contains("magic_potion")) { kind = PotionKind.Special; specialKind = SpecialPotionKind.Magic; } - - else if (tag == "strength_potion") + else if (tag.Contains("virility_potion")) + { + kind = PotionKind.Special; + specialKind = SpecialPotionKind.Virility; + } + else if (tag.Contains("strength_potion")) { kind = PotionKind.Special; specialKind = SpecialPotionKind.Strength; @@ -51,14 +56,30 @@ protected override void Create() else Dungeons.DebugHelper.Assert(false); + if (kind == PotionKind.Special) + { + specialPotionSize = SpecialPotionSize.Small; + if (tag.Contains("big")) + specialPotionSize = SpecialPotionSize.Big; + //else if (tag.Contains("medium")) + // specialPotionSize = SpecialPotionSize.Medium; + } + Potion loot = null; if (specialKind != SpecialPotionKind.Unset) - loot = new SpecialPotion(specialKind, SpecialPotionSize.Small); + loot = new SpecialPotion(specialKind, specialPotionSize); else loot = new Potion(kind); return loot; }; - var names = new[] { "antidote_potion", "health_potion", "mana_potion", "magic_potion", "strength_potion" }; + var names = new[] { + "antidote_potion", + "health_potion", + "mana_potion", + "small_magic_potion", "medium_magic_potion", "big_magic_potion", + "small_strength_potion" , "medium_strength_potion" ,"big_strength_potion" , + "small_virility_potion" ,"medium_virility_potion","big_virility_potion" + }; foreach (var name in names) { factory[name] = createPotion; @@ -68,7 +89,9 @@ protected override void Create() factory["magic_dust"] = (string tag) => { - return new MagicDust(); + var md = new MagicDust(); + //md.Count = 100; + return md; }; factory["hooch"] = (string tag) => @@ -179,8 +202,8 @@ protected override void Create() kind = HunterTrophyKind.Claw; else if (tt.EndsWith("fang")) kind = HunterTrophyKind.Fang; - else if (tt.EndsWith("tusk")) - kind = HunterTrophyKind.Tusk; + //else if (tt.EndsWith("tusk")) + // kind = HunterTrophyKind.Tusk; EnchanterSize enchanterSize = EnchanterSize.Small; if (tt.StartsWith("big")) @@ -190,7 +213,7 @@ protected override void Create() factory[tt] = (string tag) => { - return new HunterTrophy(kind) { EnchanterSize = enchanterSize, tag1 = tag }; + return new HunterTrophy(kind) { EnchanterSize = enchanterSize }; }; } @@ -229,22 +252,70 @@ protected override void Create() } factory["cannon"] = (string tag)=> new Cannon(); + + //var sli = CreateSanderusLoot(); + //foreach (var sl in sli) + // factory[sl.tag1] = (string tag)=> sl; + } + + public static List CreateSanderusLoot() + { + var res = new List(); + var tags = new[] { "ArchangelGabrielFeathers", "PegansOil", "HoovesOfTheDonkey" , "JacobRungs" }; + foreach (var tag in tags) + { + string description = ""; + string name = ""; + if (tag == "ArchangelGabrielFeathers") + { + description = "Feathers from the wings of Archangel Gabriel lost during the Annunciation"; + name = "Archangel Gabriel's feathers"; + } + else if (tag == "PegansOil") + { + description = "Oil in which the pagans wanted to fry John the Baptist"; + name = "Saint Oil"; + } + else if (tag == "HoovesOfTheDonkey") + { + description = "Hooves of the donkey on which the Holy Family fled to Egypt"; + name = "Holy Family's Donkey hooves"; + } + else if (tag == "JacobRungs") + { + description = "Rungs from the ladder that patriarch Jacob dreamed of"; + name = "Patriarch Jacob's Rungs"; + } + var loot = new GenericLoot(name, description, tag); + loot.Name = name; + loot.Price = 200; + + res.Add(loot); + } + return res; } private static ProjectileFightItem CreateFightItem(FightItemKind fik) { - int max = 3; + int max = 3 + RandHelper.GetRandomInt(2); int add = 3; + int min = 1; var fi = new ProjectileFightItem(fik, null) { }; if (fik.IsBowLikeAmmunition()) - max = 6; + { + max = 20; + min = 10; + } if (fik == FightItemKind.HunterTrap) { max = 1; add = 2; } - fi.Count = RandHelper.GetRandomInt(max) + add; + var co = RandHelper.GetRandomInt(max) + add; + if (co < min) + co = min; + fi.Count = co; return fi; } @@ -261,6 +332,8 @@ private void InitRepices() var kinds = GetEnumValues(); foreach (var kind in kinds) { + if (kind == RecipeKind.Pendant) + continue; recipesPrototypes.Add(createRecipeFromKind(kind)); } @@ -281,18 +354,14 @@ private static List GetEnumValues() where T : IConvertible return EnumHelper.GetEnumValues(true); } - public override Loot GetByName(string name) - { - return GetByAsset(name); - } + //public override Loot GetByName(string name) + //{ + // return GetByAsset(name); + //} public override Loot GetByAsset(string tagPart) { - var tile = factory.FirstOrDefault(i => i.Key == tagPart); - if (tile.Key != null) - return tile.Value(tagPart); - - return null; + return GetByAsset(factory, tagPart); } public override Loot GetRandom(int level)//TODO ! level diff --git a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/ScrollsFactory.cs b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/ScrollsFactory.cs index 8a08995c..f91f6e79 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/ScrollsFactory.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/LootFactories/ScrollsFactory.cs @@ -16,7 +16,7 @@ public class ScrollsFactory : AbstractLootFactory public ScrollsFactory(Container container) : base(container) { } - + protected override void Create() { Func createScroll = (string tag) => @@ -31,12 +31,13 @@ protected override void Create() var scroll = tag == "swiatowit_scroll" ? new SwiatowitScroll() : new Scroll(); scroll.tag1 = tag; scroll.Kind = Scroll.DiscoverKindFromName(tag); - scroll.Count = Enumerable.Range(1, 3).ToList().GetRandomElem(); + scroll.Count = GetStackableDefaultCount(); return scroll; }; var names = new[] { "fire_ball_scroll" , "ice_ball_scroll", "poison_ball_scroll", "identify_scroll", "teleport_scroll", "portal_scroll", "transform_scroll", "mana_shield_scroll", - "skeleton_scroll", "dziewanna_scroll", "swarog_scroll", "swiatowit_scroll", "swap_positions_scroll", + "skeleton_scroll", "dziewanna_scroll", "swarog_scroll", "swiatowit_scroll", "jarowit_scroll", "wales_scroll", + "swap_positions_scroll", "cracked_stone_scroll", "perun_scroll", "frighten_scroll" }; foreach (var name in names) @@ -48,25 +49,22 @@ public override Loot GetRandom(int level) return GetRandom(factory); } - public override Loot GetByName(string name) - { - return GetByAsset(name); - } + //public override Loot GetByName(string name) + //{ + // return GetByAsset(name); + //} public override Loot GetByAsset(string tagPart) { - var tile = factory.FirstOrDefault(i => i.Key == tagPart); - if (tile.Key != null) - return tile.Value(tagPart); - - return null; + return GetByAsset(factory, tagPart); + } public Loot GetByKind(Spells.SpellKind kind) { var tile = factory.FirstOrDefault(i => Scroll.DiscoverKindFromName(i.Key) == kind); if (tile.Key != null) - return tile.Value(tile.Key); + return PrepareLoot(tile.Key, tile.Value(tile.Key)); return null; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/AbilityManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/AbilityManager.cs new file mode 100644 index 00000000..41ad3c51 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/AbilityManager.cs @@ -0,0 +1,377 @@ +using Dungeons; +using Dungeons.Tiles; +using Roguelike.Abilities; +using Roguelike.Abstract.HotBar; +using Roguelike.Attributes; +using Roguelike.Effects; +using Roguelike.Events; +using Roguelike.Managers; +using Roguelike.TileContainers; +using Roguelike.Tiles.LivingEntities; +using Roguelike.Tiles.Looting; +using System.Linq; + +namespace Roguelike.Core.Managers +{ + public enum AbilityStartKind { Unset, WhenActivated } + + public class AbilityManager + { + GameManager gm; + bool hookedToHero; + LivingEntity activeAbilityVictim; + public AbilityManager(GameManager gm) + { + this.gm = gm; + + } + + /// + /// Called when: user assign ab to a slot + /// + /// + /// + /// + /// + public bool ActivateAbility(AdvancedLivingEntity abilityUser, AbilityKind abilityKind, IHotbarItem oldHotbarItem) + { + if (abilityUser is Hero && !hookedToHero) + { + hookedToHero = true; + this.gm.Hero.CurrentEquipment.EquipmentChanged += CurrentEquipment_EquipmentChanged; + } + + var ab = abilityUser.Abilities.GetAbility(abilityKind) as ActiveAbility; + if (ab == null) + return false; + //var state = attacker.GetAbilityState(ab); + //if (abilityUser.ActivatedAbilityKind != abilityKind)//state == AbilityState.Unset || oldHotbarItem == null) + + DoActivateAbility(abilityUser, ab); + return true; + + + } + + private void CurrentEquipment_EquipmentChanged(object sender, LootContainers.EquipmentChangedArgs e) + { + if (this.gm.Hero.SelectedActiveAbility != null) + { + DoActivateAbility(this.gm.Hero, this.gm.Hero.SelectedActiveAbility as ActiveAbility); + } + } + + + private void DoActivateAbility(AdvancedLivingEntity attacker, ActiveAbility ab, bool fromTurnChanged = false) + { + + var abilityKind = ab.Kind; + + var state = attacker.GetAbilityState(ab); + + var canHighlight = attacker.CanHighlightAbility(abilityKind); + var newState = canHighlight ? AbilityState.Activated : AbilityState.Unusable; + + gm.AppendAction(new AbilityStateChangedEvent() + { + AbilityKind = abilityKind, + AbilityState = newState, + AbilityUser = attacker + }); + + if (newState == AbilityState.Activated) + { + //attacker.ActivatedAbilityKind = abilityKind; + string reason; + if (!attacker.CanUseAbility(ab.Kind, gm.CurrentNode, out reason)) + return; + } + + if (newState == AbilityState.Activated && ab.RunAtActivation) + { + if (state == AbilityState.Working || state == AbilityState.CoolingDown) + return; + UseActiveAbility(abilityKind, attacker); + } + } + + /// + /// + /// + /// + /// + /// + /// + public bool UseActiveAbility(AdvancedLivingEntity attacker, bool turnStart, LivingEntity targetLe = null) + { + bool done = false; + var ale = attacker; + var ab = ale.Abilities.ActiveItems + .Where(i => i.RunAtTurnStart == turnStart && ale.HasAbilityActivated(i)) + .SingleOrDefault(); + + if (ab != null) + { + string reason; + if (attacker.CanUseAbility(ab.Kind, gm.CurrentNode, out reason)) + { + UseActiveAbility(ale.SelectedActiveAbility.Kind, ale, targetLe); + done = true; + } + } + + return done; + } + + void UseActiveAbility(AbilityKind abilityKind, AdvancedLivingEntity abilityUser, LivingEntity victim = null) + { + var advEnt = abilityUser as AdvancedLivingEntity; + if (!advEnt.Abilities.IsActive(abilityKind)) + return; + + bool activeAbility = true; + UseAbility(victim, abilityUser, abilityKind, activeAbility); + } + + public void UseAbility(LivingEntity victim, LivingEntity abilityUser, AbilityKind abilityKind, bool activeAbility) + { + //abilityUser.Abili + Ability ab = abilityUser.GetActiveAbility(abilityKind); + if (ab is null) + ab = abilityUser.GetPassiveAbility(abilityKind); + + bool used = false; + if (ab is ActiveAbility aab) + used = UseActiveAbility(aab, abilityUser as AdvancedLivingEntity, victim, sendEvent: false); + else + { + if (abilityKind == AbilityKind.StrikeBack)//StrikeBack is currently only a passive one! (maybe somneday will be also active) + { + gm.ApplyPhysicalAttackPolicy(abilityUser, victim, (p) => + { + used = true; + abilityUser.AppendUsedAbilityAction(abilityKind); + if (gm.AttackPolicyDone != null) + gm.AttackPolicyDone(); + }, EntityStatKind.ChanceToStrikeBack); + } + } + + if (used) + { + if (activeAbility) + { + HandleActiveAbilityUsed(abilityUser, abilityKind); + } + } + } + + public void HandleActiveAbilityUsed(LivingEntity abilityUser, AbilityKind abilityKind) + { + var ab = abilityUser.GetActiveAbility(abilityKind); + abilityUser.HandleActiveAbilityUsed(abilityKind); + } + + + + public Hero Hero { get => gm.Hero; } + public AbilityStartKind AbilityStartKind { get; set; } = AbilityStartKind.WhenActivated; + + TileNeighborhood? GetTileNeighborhoodKindCompareToHero(LivingEntity target) + { + TileNeighborhood? neib = null; + if (target.Position.X > Hero.Position.X) + neib = TileNeighborhood.East; + else if (target.Position.X < Hero.Position.X) + neib = TileNeighborhood.West; + else if (target.Position.Y > Hero.Position.Y) + neib = TileNeighborhood.South; + else if (target.Position.Y < Hero.Position.Y) + neib = TileNeighborhood.North; + return neib; + } + + public bool UseActiveAbility(ActiveAbility ab, AdvancedLivingEntity ale, LivingEntity victim, bool sendEvent) + { + bool used = false; + var abilityKind = ab.Kind; + var endTurn = false; + if (ab.CoolDownCounter > 0) + return false; + + if (abilityKind != AbilityKind.StrikeBack)//StrikeBack is currently only a passive one! + { + var currentOne = ale.Abilities.ActiveItems + .Where(i => ale.GetAbilityState(i) == AbilityState.Working) + .SingleOrDefault(); + if (currentOne != null /*&& currentOne.Kind != ab.Kind*/) + { + if (currentOne.TurnsIntoLastingEffect) + { + var leff = ale.LastingEffectsSet.GetByAbilityKind(currentOne.Kind); + ale.RemoveLastingEffect(leff); + } + } + } + + if (abilityKind == AbilityKind.ZealAttack) + used = true; + + else if (abilityKind == AbilityKind.Stride) + { + int horizontal = 0, vertical = 0; + var neibKind = GetTileNeighborhoodKindCompareToHero(victim); + if (neibKind.HasValue) + { + InputManager.GetMoveData(neibKind.Value, out horizontal, out vertical); + var newPos = InputManager.GetNewPositionFromMove(victim.point, horizontal, vertical); + activeAbilityVictim = victim;// as Enemy; + activeAbilityVictim.MoveDueToAbilityVictim = true; + + var desc = ""; + var attack = ale.Stats.GetStat(EntityStatKind.Strength).SumValueAndPercentageFactor(ab.PrimaryStat, true); + var damage = victim.CalcMeleeDamage(attack, ref desc); + var inflicted = victim.InflictMeleeDamage(ale, false, ref damage, ref desc); + + gm.ApplyMovePolicy(victim, newPos.Point); + used = true; + } + } + else if (abilityKind == Abilities.AbilityKind.OpenWound) + { + if (victim != null) + { + //psk = EntityStatKind.BleedingExtraDamage; + //ask = EntityStatKind.BleedingDuration; + var duration = ab.AuxStat.Factor; + var damage = Calculated.FactorCalculator.AddFactor(3, ab.PrimaryStat.Factor);//TODO 3 + victim.StartBleeding(damage, null, (int)duration); + used = true; + } + else + { + //var leSrc = new AbilityLastingEffectSrc(ab, 0); + //abilityUser.LastingEffectsSet.AddPercentageLastingEffect(EffectType.OpenWound, leSrc, abilityUser); + //used = true; + } + } + else if (abilityKind == AbilityKind.Smoke) + { + AddSmoke(ale); + endTurn = true; + used = true; + + } + else + used = AddLastingEffectFromAbility(ab, ale); + + if (used) + { + ale.WorkingAbilityKind = abilityKind; + if (sendEvent) + HandleActiveAbilityUsed(ale, abilityKind); + } + + if (endTurn) + gm.Context.MoveToNextTurnOwner();//TODO call HandleHeroActionDone + return used; + } + + LastingEffect AddAbilityLastingEffectSrc(EffectType et, ActiveAbility ab, LivingEntity abilityUser, int abilityStatIndex = 0) + { + var src = new AbilityLastingEffectSrc(ab, abilityStatIndex); + if (et != EffectType.IronSkin) + { + if ( + ab.Kind == AbilityKind.ElementalVengeance || + ab.Kind == AbilityKind.Rage + ) + src.Duration = 3; + else + src.Duration = 1; + } + else + src.Duration = (int)ab.AuxStat.Factor; + return abilityUser.LastingEffectsSet.AddLastingEffect(et, src, abilityUser); + } + + public void MakeGameTick() + { + if (activeAbilityVictim != null) + { + if (activeAbilityVictim.Alive && activeAbilityVictim.State != EntityState.Idle) + return; + activeAbilityVictim.MoveDueToAbilityVictim = false;//AbilityKind.Stride + activeAbilityVictim = null; + } + } + + public bool AddLastingEffectFromAbility(ActiveAbility ab, LivingEntity abilityUser) + { + if (!ab.TurnsIntoLastingEffect) + return false; + + if (ab.CoolDownCounter > 0) + return false; + + bool used = false; + var abilityKind = ab.Kind; + + if (ab.Kind == AbilityKind.ElementalVengeance) + { + var attacks = new EffectType[] { EffectType.FireAttack, EffectType.PoisonAttack, EffectType.ColdAttack }; + int i = 0; + foreach (var et in attacks) + { + AddAbilityLastingEffectSrc(et, ab, abilityUser, i); + i++; + } + used = true; + } + else if (ab.Kind == AbilityKind.IronSkin) + { + AddAbilityLastingEffectSrc(EffectType.IronSkin, ab, abilityUser); + used = true; + } + else if (abilityKind == AbilityKind.Rage) + { + AddAbilityLastingEffectSrc(EffectType.Rage, ab, abilityUser); + used = true; + } + + return used; + } + + private void AddSmoke(LivingEntity abilityUser) + { + var smokeAb = Hero.GetActiveAbility(AbilityKind.Smoke); + var smokes = gm.CurrentNode.AddSmoke(gm.Hero, (int)smokeAb.PrimaryStat.Factor, (int)smokeAb.AuxStat.Factor); + gm.AppendAction((TilesAppendedEvent ac) => + { + ac.Tiles = smokes.Cast().ToList(); + }); + } + + public void HandleSmokeTiles() + { + var smokes = gm.CurrentNode.Layers.GetTypedLayerTiles(KnownLayer.Smoke); + ProjectileFightItem smokeEnded = null; + foreach (var smoke in smokes) + { + smoke.Durability--; + if (smoke.Durability <= 0) + { + gm.CurrentNode.Layers.GetLayer(KnownLayer.Smoke).Remove(smoke); + gm.AppendAction(new LootAction(smoke, null) { Kind = LootActionKind.Destroyed, Loot = smoke }); + smokeEnded = smoke; + } + } + if (smokeEnded != null && smokeEnded.ActiveAbilitySrc == AbilityKind.Smoke) + { + smokeEnded.Caller.StartAbilityCooling(AbilityKind.Smoke); + + } + } + + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/AbilityManager.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Managers/AbilityManager.cs.meta new file mode 100644 index 00000000..d43648f4 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/AbilityManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: feaa0515ce1d7bb4f9e2de4a68041dad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/AlliesManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/AlliesManager.cs index 38067d10..4950292a 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/AlliesManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/AlliesManager.cs @@ -8,20 +8,15 @@ using System.Linq; using System.Collections.Generic; + namespace Roguelike.Managers { - //if (ally is God god) - // { - // god.Point = Hero.Position;//TODO - // god.MakeTurn(this); - // return; - //} public class AlliesManager : EntitiesManager { EnemiesManager enemiesManager; - - public AllyBehaviour AllyBehaviour { get; set; } = AllyBehaviour.GoFreely; + public const string ZyndramHerd = "ZyndramSquad"; + public AllyBehaviour AllyBehaviour { get; set; } = AllyBehaviour.StayClose; public AlliesManager(GameContext context, EventsManager eventsManager, Container container, EnemiesManager enemiesManager, GameManager gm) : base(TurnOwner.Allies, context, eventsManager, container, gm) @@ -29,7 +24,8 @@ public AlliesManager(GameContext context, EventsManager eventsManager, Container context.TurnOwnerChanged += OnTurnOwnerChanged; context.ContextSwitched += Context_ContextSwitched; this.enemiesManager = enemiesManager; - + CheckBusyOnes = true; + EmitAllIdleWhenNooneBusy = true; } public override void MakeTurn() @@ -44,6 +40,18 @@ protected override void MakeSpecialActions() } + public override List GetActiveEntities() + { + var basicList = GetBasicActiveEntitiesList(); + //var res = basicList.Where(i => i is God || + //i.DistanceFrom(gameManager.Hero) < 15 || + //(i is INPC npc && npc.WalkKind == WalkKind.GoToHome) + //) + //.ToList(); + + return basicList; + } + public override bool RemoveDeadEntity(LivingEntity ent) { var res = RemoveEntity(ent); @@ -95,16 +103,7 @@ public void MakeHeroAllyMove(LivingEntity ally) } } - //if (ally.FixedWalkTarget != null) - //{ - // if (ally.PathToTarget != null && ally.PathToTarget.Count == 1) - // { - // ally.FixedWalkTarget = null; - // } - // if (!MakeMoveOnPath(ally, ally.FixedWalkTarget.point, true)) - // MakeRandomMove(ally); - // return; - //} + var allEnemies = enemiesManager.GetActiveEnemies(); var enemiesInvolved = enemiesManager.GetActiveEnemiesInvolved(); if (AllyBehaviour == AllyBehaviour.StayStill) @@ -125,7 +124,8 @@ public void MakeHeroAllyMove(LivingEntity ally) } } - private void DoMixedMode(AllyBehaviour allyBehaviour, LivingEntity ally, List allEnemies, List enemiesInvolved, int maxEntityDistanceToToChase) + private void DoMixedMode(AllyBehaviour allyBehaviour, LivingEntity ally, List allEnemies, + List enemiesInvolved, int maxEntityDistanceToToChase) { bool moveCloserToHero = false; LivingEntity maxDistanceFrom = allyBehaviour == AllyBehaviour.GoFreely ? ally: Hero; @@ -150,8 +150,8 @@ private void DoMixedMode(AllyBehaviour allyBehaviour, LivingEntity ally, List allEnemies, - List enemiesInvolved, + List allEnemies, + List enemiesInvolved, int maxDistance, LivingEntity maxDistanceFrom ) @@ -166,7 +166,9 @@ LivingEntity maxDistanceFrom if (ally.AllyModeTarget == null || ally.AllyModeTarget.DistanceFrom(maxDistanceFrom) > maxDistance) { ally.AllyModeTarget = enemiesInvolved - .Where(i => i.Revealed && i.DistanceFrom(maxDistanceFrom) <= maxDistance) + .Where(i => i.Revealed && i.DistanceFrom(maxDistanceFrom) <= maxDistance + && Node.FindPath(ally, i.point)!=null + ) .OrderBy(i => i.DistanceFrom(ally)) .ToList() .FirstOrDefault(); @@ -217,11 +219,9 @@ public void AddEntity(IAlly ent) AddEntity(ent as LivingEntity); } - //public God RemoveGod() - //{ - // var god = AllEntities.Where(i => i is God).FirstOrDefault(); - // AllEntities.Remove(god); - // return god as God; - //} + protected override bool IsBusy(LivingEntity i) + { + return base.IsBusy(i); + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/AnimalsManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/AnimalsManager.cs index 1d698a58..e74eec0e 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/AnimalsManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/AnimalsManager.cs @@ -1,34 +1,51 @@ -using Dungeons.Tiles; +using Dungeons.Core; +using Dungeons.Tiles; using Roguelike.Policies; using Roguelike.Tiles.LivingEntities; using SimpleInjector; using System.Collections.Generic; using System.Linq; + namespace Roguelike.Managers { public class AnimalsManager : EntitiesManager { - + public AnimalsManager(GameContext context, EventsManager eventsManager, Container container, GameManager gm) : base(TurnOwner.Animals, context, eventsManager, container, gm) { context.ContextSwitched += Context_ContextSwitched; BrutalPendingForAllIdleFalseMode = true; + EmitAllIdleWhenNooneBusy = true; + } + + public override List FindBusyOnes(BusyOnesCheckContext context) + { + var ents = AllEntities.Where(i => IsBusy(i)).ToList(); + ents = ents.Where(i => i.State != EntityState.Moving).ToList(); + return ents; } private void Context_ContextSwitched(object sender, ContextSwitch e) { var entities = Context.CurrentNode.GetTiles(); base.AllEntities.Clear(); - entities.ForEach(i=> base.AllEntities.Add(i)); + entities.ForEach(i => base.AllEntities.Add(i)); entitiesSet = true; } public override void MakeRandomMove(LivingEntity entity) { var anim = entity as Animal; - + + if (entity.CanMakeRandomMove()) + base.MakeRandomMove(entity); + } + + bool RunAwayIfNeeded(LivingEntity entity) + { + var anim = entity as Animal; if (anim.LastHitCoolDown > 0) { var empties = new List(); @@ -45,12 +62,33 @@ public override void MakeRandomMove(LivingEntity entity) if (target != null) { MakeMoveOnPath(anim, target.point, false, true); - return; + return true; } } } - if (entity.CanMakeRandomMove()) + return false; + } + + public override void MakeTurn() + { + base.MakeTurn(); + } + + public override void MakeTurn(LivingEntity entity) + { + if (RunAwayIfNeeded(entity)) + return; + var randMoveThresh = 0.5f; + if (IsZyndrams(entity) && entity.MovesCounter > ZyndramBackMovesCounter) + { + randMoveThresh = 0.75f; + } + //if (entity.State != EntityState.Idle)//maybe animation of walk is in progress? + // return; + if (entity.FixedWalkTarget != null) + FollowTarget(entity, entity.FixedWalkTarget as LivingEntity); + else if(RandHelper.GetRandomDouble() > randMoveThresh) base.MakeRandomMove(entity); } @@ -59,5 +97,20 @@ protected override bool ShalLReportTurnOwner(Policy policy) { return false; } + + + public override List GetActiveEntities() + { + var basicList = GetBasicActiveEntitiesList(); + var res = basicList + .Where(i => + i.DistanceFrom(gameManager.Hero) < 15 || + (IsZyndrams(i) && i.MovesCounter < ZyndramBackMovesCounter) + ) + .ToList(); + return res; + } + + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/EnemiesManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/EnemiesManager.cs index ba30f96e..e79b66a2 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/EnemiesManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/EnemiesManager.cs @@ -1,7 +1,8 @@ using Dungeons.Core; using Dungeons.Tiles; using Roguelike.Events; -using Roguelike.Tiles; +using Roguelike.Policies; +using Roguelike.State; using Roguelike.Tiles.LivingEntities; using Roguelike.Tiles.Looting; using SimpleInjector; @@ -28,12 +29,12 @@ public EnemiesManager(GameContext context, EventsManager eventsManager, Containe context.ContextSwitched += Context_ContextSwitched; } - public virtual List GetActiveEnemies() + public virtual List GetActiveEnemies() { - return this.AllEntities.Where(i => i.Revealed && i.Alive).Cast().ToList(); + return this.AllEntities.Where(i => i.Revealed && i.Alive).ToList(); } - public List GetActiveEnemiesInvolved() + public List GetActiveEnemiesInvolved() { return GetActiveEnemies().Where(i=>i.DistanceFrom(Hero) <= MaxDistanceForInvolvedOne).ToList(); } @@ -77,12 +78,17 @@ public override void MakeTurn(LivingEntity enemy) if (enemy.DistanceFrom(Hero) > MaxDistanceForInvolvedOne) return; - var enemyCasted = enemy as Enemy; - if (enemyCasted.HitRandomTarget) { - var other = GetActiveEnemiesInvolved().Where(i=> i!= enemy).OrderBy(i=>i.DistanceFrom(enemyCasted)).FirstOrDefault(); - AttackIfPossible(enemyCasted, other); - enemyCasted.HitRandomTarget = false; + var enemyCasted = enemy as Enemy; + if (enemyCasted != null && enemyCasted.HitRandomTarget) + { + var other = GetActiveEnemiesInvolved().Where(i => i != enemy).OrderBy(i => i.DistanceFrom(enemyCasted)).FirstOrDefault(); + var attacked = AttackIfPossible(enemyCasted, other); + enemyCasted.HitRandomTarget = false; + if (!attacked) + MakeRandomMove(enemy); + return; + } } if (enemy.HandleOrder(BattleOrder.Surround, Hero, Node)) @@ -127,7 +133,7 @@ public override void MakeTurn(LivingEntity enemy) { //if (CastEffectsForAllies(entity)) // return; - if (MakeEmergencyTeleport(enemyCasted)) + if (MakeEmergencyTeleport(enemy as Enemy)) return; if (AttackIfPossible(enemy, target)) @@ -156,7 +162,7 @@ public override void MakeTurn(LivingEntity enemy) if (!makeRandMove) enemy.ChaseCounter++; if (detailedLogs) - context.Logger.LogInfo("!ShallChaseTarget true, makeRandMove: "+ makeRandMove); + context.Logger.LogInfo("!ShallChaseTarget true, makeRandMove: "+ makeRandMove + " enemy: " + enemy + " target: "+ target); } else makeRandMove = true; @@ -246,10 +252,7 @@ public override void MakeRandomMove(LivingEntity entity) protected override bool MoveEntity(LivingEntity entity, Point newPos, List fullPath = null) { - if (entity.InitialPoint == LivingEntity.DefaultInitialPoint) - { - entity.InitialPoint = entity.point; - } + var moved = base.MoveEntity(entity, newPos, fullPath); return moved; @@ -267,9 +270,12 @@ protected override void OnPolicyAppliedAllIdle() bool AttackAlly(LivingEntity enemy, bool forced) { + if (AlliesManager.AllyBehaviour == AllyBehaviour.StayStill) + return false; var ally = AlliesManager.AllEntities.Where(i => i.DistanceFrom(enemy) < 2).FirstOrDefault(); if (ally != null) { + var rand = RandHelper.Random.NextDouble(); if ((forced && rand < 0.75) || rand < 0.3f)//TODO attack if there is no clear path to hero return AttackIfPossible(enemy, ally); @@ -279,6 +285,8 @@ bool AttackAlly(LivingEntity enemy, bool forced) private bool MakeEmergencyTeleport(Enemy enemy) { + if (enemy is null) + return false; if (enemy.PowerKind != EnemyPowerKind.Plain) { var enCasted = enemy; @@ -305,7 +313,12 @@ private bool MakeEmergencyTeleport(Enemy enemy) public void TeleportEnemy(Enemy enemy, Tile firstEmpty) { Level.SetTile(enemy, firstEmpty.point); - gameManager.AppendAction(new EnemyAction() { Info = enemy.Name + " has teleported", Enemy = enemy as Enemy, Kind = EnemyActionKind.Teleported }); + var cmd = new CommandUseInfo(EntityCommandKind.TeleportCloser); + + gameManager.AppendAction(new EnemyAction(cmd) + { + Info = enemy.Name + " has teleported", Enemy = enemy as Enemy, Kind = EnemyActionKind.SendComand + }); gameManager.SoundManager.PlaySound("teleport"); } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/EntitiesManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/EntitiesManager.cs index 295d83c1..f314d0e5 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/EntitiesManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/EntitiesManager.cs @@ -13,6 +13,7 @@ using System.Diagnostics; using System.Drawing; using System.Linq; +//using static UnityEngine.EventSystems.EventTrigger; namespace Roguelike.Managers { @@ -20,6 +21,7 @@ public enum BattleOrder { Unset, Surround } public class EntitiesManager { + protected AttackStrategy attackStrategy; public Hero Hero { get => context.Hero; } @@ -27,21 +29,17 @@ public class EntitiesManager public AbstractGameLevel Node { get => context.CurrentNode; } public GameContext Context { get => context; set => context = value; } - public List AllEntities + public List AllEntities { - get => entities; - private set => entities = value; + get => entities; + private set => entities = value; } public IEnumerable AllAllies { get => entities.Cast(); } - public bool PendingForAllIdle - { - get => pendingForAllIdle; - set - { - pendingForAllIdle = value; - //context.Logger.LogInfo("pendingForAllIdle set to: "+ pendingForAllIdle); - } - } + public enum State { Idle, Looping, WaitingForAllIdle }; + + public State CurrentState { get; set; } + + public bool CheckBusyOnes { get; set; } protected GameManager gameManager; @@ -49,7 +47,7 @@ public bool PendingForAllIdle protected GameContext context; protected Container container; TurnOwner turnOwner; - bool pendingForAllIdle = false; + public EntitiesManager(TurnOwner turnOwner, GameContext context, EventsManager eventsManager, Container container, GameManager gameManager) @@ -62,121 +60,191 @@ public EntitiesManager(TurnOwner turnOwner, GameContext context, EventsManager e Context.ContextSwitched += Context_ContextSwitched; this.eventsManager = eventsManager; - attackStrategy = new AttackStrategy(context, gameManager); + attackStrategy = new AttackStrategy(gameManager); attackStrategy.OnPolicyApplied = (Policy pol) => { OnPolicyApplied(pol); }; } public virtual List GetActiveEntities() { - return this.entities.Where(i => - i.Revealed && - i.Alive && - (i is God || i.DistanceFrom(gameManager.Hero) < 15)) + var basicList = GetBasicActiveEntitiesList(); + + var res = basicList.Where(i => i is God || + i.DistanceFrom(gameManager.Hero) < 15 || + (i is INPC npc && npc.WalkKind == WalkKind.GoToHome) + ) .ToList(); + + //var res1 = basicList.Where(i => i.State != EntityState.Idle) + //.ToList(); + //if (res1.Count > res.Count) + //{ + // context.Logger.LogError("ret res1!"); + // res = res1; + //} + return res; + } + + protected List GetBasicActiveEntitiesList() + { + return this.entities + .Where(i => i.Revealed && i.Alive) + .ToList(); } - private bool ReportAllDone(bool justEndedLoop) + protected virtual bool CheckState(BusyOnesCheckContext context) { if (Context.TurnOwner == turnOwner) { - var busyOnes = FindBusyOnes(); - if (!busyOnes.Any()) - { - OnPolicyAppliedAllIdle(); - return true; - } - else if (justEndedLoop) - { - PendingForAllIdle = true; - } + return DoCheckState(context); } return false; } - List ignoredForPendingForAllIdle = new List(); - protected bool BrutalPendingForAllIdleFalseMode = false; - public void ForcePendingForAllIdleFalse() + protected bool DoCheckState(BusyOnesCheckContext context) { - if (BrutalPendingForAllIdleFalseMode) + var busyOnes = FindBusyOnes(context); + if (!busyOnes.Any() && (CurrentState == State.WaitingForAllIdle || CurrentState == State.Idle)) { - var busyOnes = FindBusyOnes(); - if (busyOnes.Any()) - { - if (busyOnes.Count() == 1) - { - ignoredForPendingForAllIdle.Add(busyOnes.First()); - } - } + OnPolicyAppliedAllIdle(); + return true; + } + else if (context == BusyOnesCheckContext.EndOfTurn) + { + CurrentState = State.WaitingForAllIdle; } - PendingForAllIdle = false; + return false; } - public List FindBusyOnes() + protected bool BrutalPendingForAllIdleFalseMode = false; + public void ForcePendingForAllIdleFalse() { - var farOnes = entities.Where(i => i.DistanceFrom(gameManager.Hero) > 15 && i.State != EntityState.Sleeping).ToList(); - farOnes.ForEach(i => i.State = EntityState.Idle); + var busyOnes = FindBusyOnes(BusyOnesCheckContext.ForceIdle); + busyOnes.ForEach(i => i.State = EntityState.Idle); + CurrentState = State.Idle; + } - var ents = entities - .Where(i => i.State != EntityState.Idle && i.State != EntityState.Sleeping && !ignoredForPendingForAllIdle.Contains(i)) - .ToList(); + public enum BusyOnesCheckContext { Unset, StartOfTurn, EndOfTurn, PolicyApplied, ForceIdle } + public virtual List FindBusyOnes(BusyOnesCheckContext context) + { + //HACK - state change! + //var farOnes = entities.Where(i => i.DistanceFrom(gameManager.Hero) > 15 && i.State != EntityState.Sleeping).ToList(); + //farOnes.ForEach(i => i.State = EntityState.Idle); + var ents = entities.Where(i => IsBusy(i)).ToList(); return ents; } + protected virtual bool IsBusy(LivingEntity i) + { + if (!i.Alive) + return false; + return i.State != EntityState.Idle && + i.State != EntityState.Sleeping + //i.State != EntityState.DestPointTask && + //!ignoredForPendingForAllIdle.Contains(i) + ; + } + private void Context_ContextSwitched(object sender, ContextSwitch e) { //SetEntities(Context.CurrentNode.GetTiles().Where(i=> !(i is Hero)).ToList()); } + void LogInfo(string log) + { + context.Logger.LogInfo(log); + } + + void LogError(string log) + { + context.Logger.LogError(log); + } + + void WriteDetailedLog(string log) + { + if (detailedLogs) + LogInfo(this + " " + log); + } protected bool detailedLogs = false; + List _busyOnes = new List(); + protected bool EmitAllIdleWhenNooneBusy = false; + protected const int ZyndramBackMovesCounter = 40; + public static bool IsZyndrams(LivingEntity i) + { + return i.tag1.StartsWith("zyndram_"); + } public virtual void MakeTurn() { - //this.skipInTurn = skipInTurn; - if (pendingForAllIdle) + if (CurrentState == State.WaitingForAllIdle) { + var busyOnes = FindBusyOnes(BusyOnesCheckContext.StartOfTurn); if (detailedLogs) { - var busyOnes = FindBusyOnes(); - context.Logger.LogInfo(this + " MakeTurn pendingForAllIdle!, returning... busy:"+ busyOnes.FirstOrDefault()); + WriteDetailedLog(" MakeTurn waitingForAllIdle!, returning... busy:" + busyOnes.FirstOrDefault()); } - return; + + if (EmitAllIdleWhenNooneBusy && !busyOnes.Any()) + { + OnPolicyAppliedAllIdle(); + return; + } + + bool shallRet = true; + if (CheckBusyOnes) + { + CheckBusyOnes = false; + busyOnes = HackBusyOnes(busyOnes); + if (!busyOnes.Any()) + { + shallRet = false; + } + + } + + if (shallRet) + return; + CurrentState = State.Idle; } + CurrentState = State.Looping; RemoveDead(); var activeEntities = GetActiveEntities(); - if(detailedLogs) - context.Logger.LogInfo(this+" MakeTurn start, count: " + activeEntities.Count); + WriteDetailedLog("MakeTurn start, count: " + activeEntities.Count); if (!activeEntities.Any()) { - if(detailedLogs) - context.Logger.LogInfo("no one to move..."); + WriteDetailedLog("no one to move..."); OnPolicyAppliedAllIdle(); return; } - + foreach (var entity in activeEntities) { - //detailedLogs = false; - + try { var startPoint = entity.Position; - // detailedLogs = true; + if(detailedLogs) - context.Logger.LogInfo("turn of: " + entity + " started"); + WriteDetailedLog("turn of: " + entity + " started"); + + if (entity.State != EntityState.Idle && entity.State != EntityState.Sleeping) + { + if(!IsZyndrams(entity)) + LogError("entity.State != EntityState.Idle "+ entity + " " + entity.tag1); + continue; + } if (entity is AdvancedLivingEntity ade) ade.ApplyAbilities(); entity.ApplyLastingEffects(); entity.ReduceHealthDueToSurface(gameManager.CurrentNode);//TODO ReduceHealthDueToSurface shall be no matter if entity is active if (!entity.Alive) { - if(detailedLogs) - context.Logger.LogInfo("!entity.Alive"); + WriteDetailedLog(" !entity.Alive "+ entity.Name); continue; } @@ -188,11 +256,10 @@ public virtual void MakeTurn() string reason = ""; if (entity.IsMoveBlockedDueToLastingEffect(out reason)) { - if (detailedLogs) - context.Logger.LogInfo("!"+ reason); + WriteDetailedLog("!" + reason); continue; } - + entity.MovesCounter++; MakeTurn(entity); if (!context.Hero.Alive) @@ -206,7 +273,7 @@ public virtual void MakeTurn() // context.Logger.LogError("EnemiesManager endPoint == startPoint !!! entity: " + entity); //} if (detailedLogs) - context.Logger.LogInfo("turn of: " + entity + " ended"); + WriteDetailedLog("turn of: " + entity + " ended"); } catch (Exception ex) { @@ -219,23 +286,41 @@ public virtual void MakeTurn() RemoveDead(); - if (detailedLogs) - context.Logger.LogInfo(this+" MakeTurn ends"); + WriteDetailedLog("MakeTurn ends"); Dungeons.DebugHelper.Assert(Context.TurnOwner == turnOwner); + CurrentState = State.WaitingForAllIdle; + CheckState(BusyOnesCheckContext.EndOfTurn); + } - ReportAllDone(true); + private List HackBusyOnes(List busyOnes) + { + var busyOnesAgain = _busyOnes.Where(i => busyOnes.Contains(i)).ToList(); + if (busyOnesAgain.Any()) + { + var names = String.Join(";", busyOnesAgain.Select(i => i.Name)); + LogError("Forcing idle for entities: " + String.Join("; ", names + " "+ busyOnesAgain.First())); + foreach (var bo in busyOnesAgain) + { + bo.State = EntityState.Idle; + } + } + busyOnes = FindBusyOnes(BusyOnesCheckContext.ForceIdle); + _busyOnes = busyOnes; + return busyOnes; } protected virtual void MakeSpecialActions() { - + } public virtual void MakeTurn(LivingEntity entity) { + if (entity.IsMercenary && entity.MovesCounter > ZyndramBackMovesCounter && RandHelper.GetRandomDouble() > 0.5f) + return; MakeRandomMove(entity); } - + public virtual void MakeRandomMove(LivingEntity entity) { var emptyTypes = Node.GetExtraTypesConsideredEmpty(); @@ -243,7 +328,7 @@ public virtual void MakeRandomMove(LivingEntity entity) if (pt != null && pt.Item1.IsValid() && !Node.GetSurfaceKindsUnderPoint(pt.Item1).Contains(SurfaceKind.Lava)) { //if (detailedLogs) - // context.Logger.LogInfo("!MoveEntity " + pt.Item1); + // LogInfo("!MoveEntity " + pt.Item1); MoveEntity(entity, pt.Item1, null); } else if (detailedLogs) @@ -258,7 +343,7 @@ public virtual void MakeRandomMove(LivingEntity entity) neibs += ty; } - context.Logger.LogInfo("MakeRandomMove not done pt: " + pt + " empty types: " + types + ", neibs: "+ neibs); + WriteDetailedLog("MakeRandomMove not done pt: " + pt + " empty types: " + types + ", neibs: " + neibs); } } @@ -273,12 +358,19 @@ public void SetEntities(List list) { entities = list; entitiesSet = true; + entities.ForEach(i => EnsureInitPoint(i)); } public virtual void AddEntity(LivingEntity ent) { + if (entities.Contains(ent)) + { + LogError("entity already edded "+ent); + return; + } entities.Add(ent); entitiesSet = true; + EnsureInitPoint(ent); } public bool Contains(LivingEntity ent) @@ -288,10 +380,18 @@ public bool Contains(LivingEntity ent) protected virtual bool ShalLReportTurnOwner(Policy policy) { + if (policy is MovePolicy) + return false;//when hero had a hound in the camp this was logged + return true; } - protected virtual void OnPolicyApplied(Policy policy) + /// + /// Animation is done if all done end the turn + /// + /// + public //TODO for god + virtual void OnPolicyApplied(Policy policy) { if (!entitiesSet) context.Logger.LogError("!entitiesSet"); @@ -300,38 +400,47 @@ protected virtual void OnPolicyApplied(Policy policy) { if (ShalLReportTurnOwner(policy) && GameContext.ShalLReportTurnOwner(policy)) { - - context.Logger.LogError("OnPolicyApplied " + policy + " context.TurnOwner != turnOwner, context.TurnOwner: " + context.TurnOwner + " this: " + this + ", policy : "+policy); + context.Logger.LogError("OnPolicyApplied " + policy + " context.TurnOwner != turnOwner, context.TurnOwner: " + context.TurnOwner + + " this: " + this + ", policy : " + policy + " CurrentState: " + CurrentState); } //return;//that return probably causes a critical bug that hero is not moving at all } // return;//in ascii/UT mode this can happend - if (pendingForAllIdle) + if (CurrentState == State.WaitingForAllIdle) { - if(detailedLogs) - context.Logger.LogInfo("calling ReportAllDone... Context.TurnOwner: " + Context.TurnOwner); - ReportAllDone(false); + if (detailedLogs) + WriteDetailedLog("calling ReportAllDone... Context.TurnOwner: " + Context.TurnOwner); + CheckState(BusyOnesCheckContext.PolicyApplied); } } protected virtual void OnPolicyAppliedAllIdle() { - if(detailedLogs) - context.Logger.LogInfo(this+ " OnPolicyAppliedAllIdle Context.TurnOwner "+ Context.TurnOwner); + if (detailedLogs) + WriteDetailedLog(" OnPolicyAppliedAllIdle Context.TurnOwner " + Context.TurnOwner); if (Context.TurnOwner == turnOwner)//this check is mainly for ASCII/UT { if (detailedLogs) - Context.Logger.LogInfo(this+ " OnPolicyAppliedAllIdle calling MoveToNextTurnOwner"); - PendingForAllIdle = false; + WriteDetailedLog(" OnPolicyAppliedAllIdle calling MoveToNextTurnOwner"); + CurrentState = State.Idle; Context.MoveToNextTurnOwner(); } } protected virtual bool MoveEntity(LivingEntity entity, Point newPos, List fullPath = null) { + EnsureInitPoint(entity); return gameManager.ApplyMovePolicy(entity, newPos, fullPath, (e) => OnPolicyApplied(e)); } + private static void EnsureInitPoint(LivingEntity entity) + { + if (entity.InitialPoint == LivingEntity.DefaultInitialPoint) + { + entity.InitialPoint = entity.point; + } + } + public bool CanMoveEntity(LivingEntity entity, Point pt) { if (!Node.IsPointInBoundaries(pt)) @@ -364,7 +473,8 @@ public void RemoveDead() public bool RemoveAlly(IAlly ent) { - return RemoveEntity(ent as LivingEntity); + var rem = RemoveEntity(ent as LivingEntity); + return rem; } public virtual bool RemoveDeadEntity(LivingEntity ent) @@ -374,55 +484,70 @@ public virtual bool RemoveDeadEntity(LivingEntity ent) public bool RemoveEntity(LivingEntity ent) { - return entities.Remove(ent); + var res = entities.Remove(ent); + if (ent is IAlly ally) + { + ally.Active = false; + Context.EventsManager.AppendAction(new AllyAction() { InvolvedTile = ally, AllyActionKind = AllyActionKind.Released }); ; + } + return res; } protected bool MakeMoveOnPath(LivingEntity entity, Point target, bool forHeroAlly, bool alwaysAddNextPoint = false) { bool moved = false; - entity.PathToTarget = Node.FindPath(entity.point, target, forHeroAlly, true, false, entity); - if (entity.PathToTarget != null && entity.PathToTarget.Count > 1) + entity.PathToTarget = Node.FindPath(entity, target); + if (entity.PathToTarget != null) { - var fullPath = new List(); - var node = entity.PathToTarget[1]; - var pt = new Point(node.Y, node.X); - fullPath.Add(pt); - - if (entity.PathToTarget.Count > 2) + if (entity.PathToTarget.Count > 1) { - var canMoveFaster = entity.CalcShallMoveFaster(Node); - - if (canMoveFaster) + var fullPath = new List(); + var node = entity.PathToTarget[1]; + var pt = new Point(node.Y, node.X); + fullPath.Add(pt); + + if (entity.PathToTarget.Count > 2) { - node = entity.PathToTarget[2]; - var nextPoint = new Point(node.Y, node.X); - if (target != nextPoint || alwaysAddNextPoint)//what for ? + var canMoveFaster = entity.CalcShallMoveFaster(Node); + + if (canMoveFaster) { - fullPath.Add(nextPoint); - pt = nextPoint; + node = entity.PathToTarget[2]; + var nextPoint = new Point(node.Y, node.X); + if (target != nextPoint || alwaysAddNextPoint)//what for ? + { + fullPath.Add(nextPoint); + pt = nextPoint; + } } } - } - if (Node.GetTile(pt) is LivingEntity) - { - //gm.Assert(false, "Level.GetTile(pt) is LivingEntity "+ enemy + " "+pt); - return false; - } + var atPoint = Node.GetTile(pt); + if (atPoint is LivingEntity && atPoint != entity) + { + //gm.Assert(false, "Level.GetTile(pt) is LivingEntity "+ enemy + " "+pt); + return false; + } - MoveEntity(entity, pt, fullPath); - moved = true; + MoveEntity(entity, pt, fullPath); + moved = true; + } } - + else + { + int k = 0; + k++; + } + entity.LastMoveOnPathResult = moved; return moved; } protected const int MaxEntityDistanceToToChase = 6; public bool ShallChaseTarget ( - LivingEntity chaser, - LivingEntity target, - int maxEntityDistanceToToChase = MaxEntityDistanceToToChase, + LivingEntity chaser, + LivingEntity target, + int maxEntityDistanceToToChase = MaxEntityDistanceToToChase, LivingEntity maxDistanceFrom = null ) { @@ -431,13 +556,15 @@ public bool ShallChaseTarget return false; } - if(chaser.HasLastingEffect(Effects.EffectType.Frighten)) - return false; + if (chaser.HasLastingEffect(Effects.EffectType.Frighten)) + return false; var isSmoke = this.gameManager.CurrentNode.IsAtSmoke(chaser); if (isSmoke) return false; + var isAlly = chaser is IAlly; + if (chaser.WasEverHitBy(target)) { chaser.MoveKind = EntityMoveKind.FollowingHero; @@ -446,7 +573,7 @@ public bool ShallChaseTarget if (maxDistanceFrom == null) maxDistanceFrom = chaser; - if (chaser.ChaseCounter > 4) + if (chaser.ChaseCounter > 4 && !isAlly) { chaser.ChaseCounter = 0; return false; @@ -454,7 +581,9 @@ public bool ShallChaseTarget var dist = maxDistanceFrom.DistanceFrom(target); if (dist < maxEntityDistanceToToChase) + { return true; + } return false; } @@ -467,7 +596,29 @@ public List GetInRange(LivingEntity src, int rangeFrom, Enemy skip) .ToList(); } - + public void FollowTarget(LivingEntity follower, LivingEntity target) + { + if (target == null) + return; + + if (follower.DistanceFrom(target) > 2)//do not block him + { + var moved = MakeMoveOnPath(follower, target.point, false); + if (!moved) + { + var ce = gameManager.CurrentNode.GetClosestEmpty(target); + if (ce != null) + moved = MakeMoveOnPath(follower, ce.point, false); + } + } + } + + protected void FollowTarget(INPC npc) + { + var target = this.AllEntities.Where(i => i.Name == npc.FollowedTargetName).SingleOrDefault(); + FollowTarget(npc.LivingEntity, target); + } + } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/EventsManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/EventsManager.cs index 4223e00c..0ef9e567 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/EventsManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/EventsManager.cs @@ -30,6 +30,19 @@ public void Assert(bool check, string desc) } } + public const string TurnHint1 = "/"; + public const string TurnHint2 = "\\"; + + public static string CurrentTurnHint = TurnHint1; + + public static void GotoNextTurnHint() + { + if (CurrentTurnHint == TurnHint1) + CurrentTurnHint = TurnHint2; + else + CurrentTurnHint = TurnHint1; + } + public void AppendAction(GameEvent ac) { if (GameManager != null)//Main Menu? @@ -40,6 +53,8 @@ public void AppendAction(GameEvent ac) if(LastActions.Contains(ac)) return; + + ac.TurnHint = CurrentTurnHint; LastActions.Add(ac); if (EventAppended != null)//send it to listeners as logic of game depends on it { @@ -55,8 +70,11 @@ public void AppendAction(GameEvent ac) //Caused recursive call to AppendAction //Assert(false, ex.Message + "\r\n"+ ex.StackTrace); - if (!LastActions.Contains(ac)) - LastActions.Add(ex.Message + "\r\n" + ex.StackTrace); + + + //beneath code added debug string to the game window! + //if (!LastActions.Contains(ac)) + //LastActions.Add(ex.Message + "\r\n" + ex.StackTrace); } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/GameManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/GameManager.cs index f26bd673..3f00e616 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/GameManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/GameManager.cs @@ -3,17 +3,13 @@ using Dungeons.Fight; using Dungeons.Tiles; using Newtonsoft.Json; -using Newtonsoft.Json.Schema; using Roguelike.Abilities; using Roguelike.Abstract.Inventory; -using Roguelike.Abstract.Projectiles; -using Roguelike.Abstract.Spells; using Roguelike.Abstract.Tiles; using Roguelike.Attributes; using Roguelike.Calculated; using Roguelike.Core.Managers; using Roguelike.Managers.Policies; -using Roguelike.Effects; using Roguelike.Events; using Roguelike.Extensions; using Roguelike.Generators; @@ -32,17 +28,16 @@ using SimpleInjector; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.Linq; -using System.Security.Cryptography; using Roguelike.Spells; using Dungeons.Core.Policy; using Dungeons.Tiles.Abstract; +using InteractiveTile = Roguelike.Tiles.Interactive.InteractiveTile; namespace Roguelike.Managers { - public enum InteractionResult { None, Handled, ContextSwitched, Attacked, Blocked }; + public enum InteractionResult { None, Handled, ContextSwitched, Attacked, Blocked }; public struct MoveResult { public bool Possible { get; set; } @@ -94,7 +89,7 @@ public class GameManager { protected GameContext context; - LootGenerator lootGenerator; + protected LootGenerator lootGenerator; EventsManager eventsManager; EnemiesManager enemiesManager; AlliesManager alliesManager; @@ -123,12 +118,16 @@ public virtual void SetLoadedContext(AbstractGameLevel node, Hero hero) internal ProjectileFightItemPolicyManager ProjectileFightItemPolicyManager { get; set; } public EnemiesManager EnemiesManager { get => enemiesManager; set => enemiesManager = value; } + + public NpcManager NpcManager { get; set; } public EventsManager EventsManager { get => eventsManager; set => eventsManager = value; } public GameContext Context { get => context; set => context = value; } public AbstractGameLevel CurrentNode { get => context.CurrentNode; } public AlliesManager AlliesManager { get => alliesManager; set => alliesManager = value; } public LootGenerator LootGenerator { get => lootGenerator; set => lootGenerator = value; } + public List PossibleNpcDestMoveTargets = new List(); + public IPersister Persister { get => persister; @@ -169,6 +168,8 @@ public GameManager(Container container) EventsManager.GameManager = this; lastInst = EventsManager; + AbilityManager = new AbilityManager(this); + lootManager = container.GetInstance(); lootManager.GameManager = this; @@ -185,12 +186,21 @@ public GameManager(Container container) }; Context.TurnOwnerChanged += Context_TurnOwnerChanged; - + Enemy.LevelStatIncreaseCalculated = (float inc) => + { + if (GameState.CoreInfo.Demo)//demo has less emeies/worse balance + { + inc -= inc * 15f / 100f; + } + return inc; + }; enemiesManager = new EnemiesManager(Context, EventsManager, Container, null, this); AlliesManager = new AlliesManager(Context, EventsManager, Container, enemiesManager, this); enemiesManager.AlliesManager = AlliesManager; + NpcManager = new NpcManager(Context, EventsManager, Container, this); + animalsManager = new AnimalsManager(Context, EventsManager, Container, this); Persister = container.GetInstance(); @@ -217,7 +227,23 @@ private void Context_TurnOwnerChanged(object sender, TurnOwner e) } } - UseActiveAbilities(Hero, null, true); + var ab = Hero.SelectedActiveAbility as ActiveAbility; + if (ab != null) + { + if (ab.RunAtTurnStart) + AbilityManager.UseActiveAbility(Hero, true); + else + { + AbilityManager.ActivateAbility(Hero, ab.Kind, null); + } + } + + if (Hero.DestPointDesc.State == DestPointState.StayingAtTarget) + { + var increasedStateCounter = Hero.DestPointDesc.IncreaseStateCounter(); + if (!increasedStateCounter) + inputManager.HidingHeroInteractionOff(CurrentNode); + } } } @@ -305,6 +331,10 @@ public virtual void SetContext(AbstractGameLevel node, Hero hero, GameContextSwi if (!node.Inited) InitNode(node, gameState, kind); + if (kind == GameContextSwitchKind.NewGame || kind == GameContextSwitchKind.GameLoaded) + { + PossibleNpcDestMoveTargets = node.GetTiles().Where(i => i.IsNpcDestMoveTarget).ToList(); + } Context.SwitchTo(node, hero, gameState, kind, AlliesManager, after, stairsUsedByHero, portal); if (kind == GameContextSwitchKind.NewGame) @@ -406,38 +436,8 @@ protected virtual void OnActionAppended(GameEvent ev) { if (lea.Kind == LivingEntityActionKind.Died) { - if (context.CurrentNode.HasTile(lea.InvolvedEntity)) - { - var set = context.CurrentNode.SetTile(context.CurrentNode.GenerateEmptyTile(), lea.InvolvedEntity.point); - if (!set) - Logger.LogError("SetTile empty failed for " + lea.InvolvedEntity); - } - else - { - Logger.LogError("context.CurrentNode HasTile failed for " + lea.InvolvedEntity); - } - if (lea.InvolvedEntity is Enemy || lea.InvolvedEntity is Animal) - { - ILootSource ls = lea.InvolvedEntity; - if (lea.InvolvedEntity is Enemy enemy) - { - var exp = AdvancedLivingEntity.EnemyDamagingTotalExpAward[enemy.PowerKind] * enemy.Level / 10; - Hero.IncreaseExp(exp); - var allies = context.CurrentNode.GetActiveAllies(); - foreach (var al in allies) - al.IncreaseExp(exp); - } - context.CurrentNode.SetTile(new Tile(), lea.InvolvedEntity.point); - if (lea.InvolvedEntity.tag1 == "boar_butcher") - { - //GameState.History.LivingEntity.CountByTag1() - } - else - lootManager.TryAddForLootSource(ls); - } + HandleLeDeath(lea); } - - } else if (ev is GameStateAction gsa) { @@ -450,6 +450,40 @@ protected virtual void OnActionAppended(GameEvent ev) k++; } } + + private void HandleLeDeath(LivingEntityAction lea) + { + if (context.CurrentNode.HasTile(lea.InvolvedEntity)) + { + var set = context.CurrentNode.SetTile(context.CurrentNode.GenerateEmptyTile(), lea.InvolvedEntity.point); + if (!set) + Logger.LogError("SetTile empty failed for " + lea.InvolvedEntity); + } + else + { + Logger.LogError("context.CurrentNode HasTile failed for " + lea.InvolvedEntity); + } + if (lea.InvolvedEntity is Enemy || lea.InvolvedEntity is Animal) + { + ILootSource ls = lea.InvolvedEntity; + if (lea.InvolvedEntity is Enemy enemy) + { + var exp = AdvancedLivingEntity.EnemyDamagingTotalExpAward[enemy.PowerKind] * enemy.Level;//* 2 with that level would big 1 higher + Hero.IncreaseExp(exp); + var allies = context.CurrentNode.GetActiveAllies(); + foreach (var al in allies) + al.IncreaseExp(exp*.99f); + } + context.CurrentNode.SetTile(new Tile(), lea.InvolvedEntity.point); + if (lea.InvolvedEntity.tag1 == "boar_butcher") + { + //GameState.History.LivingEntity.CountByTag1() + } + else + lootManager.TryAddForLootSource(ls); + } + } + public bool AddLootReward(Loot item, Animal lootSource, bool animated) { if (CurrentNode.GetTile(lootSource.GetPoint()).IsEmpty) @@ -520,10 +554,11 @@ public void SkipHeroTurn() Context.MoveToNextTurnOwner(); } - public void RemoveDead() + public virtual void RemoveDead() { EnemiesManager.RemoveDead(); ReportHeroDeathIfNeeded(); + } private void ReportHeroDeathIfNeeded() @@ -567,8 +602,6 @@ public virtual Loot TryGetRandomLootByDiceRoll(LootSourceKind lsk, ILootSource l public virtual void OnHeroPolicyApplied(Policy policy) { - //SpellManager.OnHeroPolicyApplied(policy); - //MeleePolicyManager.OnHeroPolicyApplied(policy); if (policy.Kind == PolicyKind.Move || policy.Kind == PolicyKind.ProjectileCast || policy.Kind == PolicyKind.SpellCast) FinishHeroTurn(policy); @@ -626,12 +659,13 @@ public bool AppendTileByScrollUsage(T tile, Point pt) where T : Tile protected virtual bool HandlePortalAdded() { - if (Hero.ActiveSpellSource is Book) + if (Hero.SelectedSpellSource is Book) { return true; } - var portalScroll = Hero.Inventory.GetItems().Where(i => i.Kind == Spells.SpellKind.Portal).FirstOrDefault(); - return Hero.Inventory.Remove(portalScroll) != null; + //BUG was utylized twice! + //var portalScroll = Hero.Inventory.GetItems().Where(i => i.Kind == Spells.SpellKind.Portal).FirstOrDefault(); + return true;// Hero.Inventory.Remove(portalScroll) != null; } public bool AppendTile(Tile tile, Point point) @@ -690,12 +724,15 @@ public bool ReplaceTile(Tile replacer, Point point, bool animated, Tile position { var le = replacer as LivingEntity; if (le != null) - AppendAction((LivingEntityAction ac) => + { + AppendAction((LivingEntityAction ac) => { - ac.InvolvedEntity = le; + ac.InvolvedEntity = le; ac.Kind = LivingEntityActionKind.AppendedToLevel; ac.Info = le.Name + " spawned"; + ac.Silent = le.Herd == "ZyndramSquad"; }); + } } } return true; @@ -753,10 +790,6 @@ public virtual void Load ) { persistancyWorker.Load(heroName, this, quick, WorldLoader); - if (GameSettings.Serialization.RestoreHeroToSafePointAfterLoad) - { - - } this.Hero.Abilities.EnsureItems();//new game version might added... if (GameState.CoreInfo.PermanentDeath) { @@ -818,6 +851,8 @@ public bool CollectLoot(Loot lootTile, bool fromDistance, Policy policy = null) if (lootTile is Recipe) inv = Hero.Crafting.Recipes.Inventory; + else if(lootTile is MagicDust) + inv = Hero.Crafting.InvItems.Inventory; var collected = false; if (gold != null) @@ -882,33 +917,22 @@ public void PrintHeroStats(string context, bool onlyNonZero = true) //} } - //public void DoAlliesTurn(bool skipHero = false) - //{ - // this.AlliesManager.MakeEntitiesMove(skipHero ? Hero : null); - // //DoEnemiesTurn(); - //} - public virtual Equipment GenerateRandomEquipment(EquipmentKind kind) { return lootGenerator.GetRandomEquipment(kind, Hero.Level, Hero.GetLootAbility()); } - LivingEntity activeAbilityVictim; - public void MakeGameTick() + //Called every frame + public virtual void MakeGameTick() { - if (activeAbilityVictim != null) - { - if (activeAbilityVictim.Alive && activeAbilityVictim.State != EntityState.Idle) - return; - activeAbilityVictim.MoveDueToAbilityVictim = false; - activeAbilityVictim = null; - } + AbilityManager.MakeGameTick(); + if (context.PendingTurnOwnerApply) { EntitiesManager mgr = GetCurrentEntitiesManager(); if (mgr != null) { - mgr.MakeTurn(); + mgr.MakeTurn();//make turn and if all done call Context.MoveToNextTurnOwner(); } } } @@ -919,17 +943,18 @@ public EntitiesManager GetCurrentEntitiesManager() if (context.TurnOwner == TurnOwner.Allies) { mgr = AlliesManager; - //logger.LogInfo("MakeGameTick call to AlliesManager.MoveHeroAllies"); } else if (context.TurnOwner == TurnOwner.Enemies) { mgr = EnemiesManager; - //logger.LogInfo("MakeGameTick call to EnemiesManager.MakeEntitiesMove"); } else if (context.TurnOwner == TurnOwner.Animals) { mgr = animalsManager; - //logger.LogInfo("MakeGameTick call to EnemiesManager.MakeEntitiesMove"); + } + else if (context.TurnOwner == TurnOwner.Npcs) + { + mgr = NpcManager; } return mgr; @@ -958,8 +983,11 @@ public bool CanSell(Loot lootToSell, IInventoryOwner src, IInventoryOwner dest, if (!goldInvolved) return true; + + price = src.GetPrice(lootToSell) * count; + return dest.Gold >= price; } @@ -1000,6 +1028,9 @@ public Loot SellItem return null; } + if (!loot.IsSellable() && (dest is Merchant)) + return null; + if (destInv is CurrentEquipment ce && (destInv.Owner == srcInv.Owner || destInv.Owner is Ally) && addItemArg is CurrentEquipmentAddItemArg ceAddArgs) @@ -1020,9 +1051,14 @@ public Loot SellItem { logger.LogInfo("dest.Gold < loot.Price"); SoundManager.PlayBeepSound(); - AppendAction(new InventoryAction(destInv) { Kind = InventoryActionKind.ItemTooExpensive, Info = "ItemTooExpensive" }); + AppendAction(new InventoryAction(destInv) { Kind = InventoryActionKind.ItemTooExpensive, Info = "Not enough gold" }); return null; } + if (!destInv.CanAddLoot(loot) && destInv.Owner is Merchant merch) + { + RegenerateMerchantInv(merch, true); + } + if (!destInv.CanAddLoot(loot)) { logger.LogInfo("!dest.Inventory.CanAddLoot(loot)"); @@ -1070,26 +1106,33 @@ private void AddLootToMerchantInv(Merchant merch, List lootKinds) { foreach (var lootKind in lootKinds) { - int levelIndex = Hero.Level; - Loot loot = null; - loot = lootGenerator.GetRandomLoot(lootKind, levelIndex); - if (loot is Equipment) - continue;//generated lower - if (loot != null && !merch.Inventory.Items.Any(i => i.tag1 == loot.tag1)) + try { - loot.Revealed = true; - //TODO use Items to avoid sound - merch.Inventory.Items.Add(loot); - if (loot is FightItem fi) + int levelIndex = Hero.Level; + Loot loot = null; + loot = lootGenerator.GetRandomLoot(lootKind, levelIndex); + if (loot is Equipment) + continue;//generated lower + if (loot != null && !merch.Inventory.Items.Any(i => i.tag1 == loot.tag1)) { - if (fi.FightItemKind.IsBowLikeAmmunition()) + loot.Revealed = true; + //TODO use Items to avoid sound + merch.Inventory.Items.Add(loot); + if (loot is FightItem fi) { - fi.Count = RandHelper.GetRandomInt(50) + 25; + if (fi.FightItemKind.IsBowLikeAmmunition()) + { + fi.Count = RandHelper.GetRandomInt(50) + 25; + } + else + fi.Count = RandHelper.GetRandomInt(5) + 1; } - else - fi.Count = RandHelper.GetRandomInt(3) + 1; } } + catch (Exception ex) + { + Logger.LogError(ex); + } } } @@ -1097,7 +1140,35 @@ private void AddLootToMerchantInv(Merchant merch, List lootKinds) GenerateMerchantEq(merch, false); } - private void GenerateMerchantEq(Merchant merch, bool magic) + protected void AddSpecialBowLikeAmmo(Merchant merch, bool both) + { + bool arr = true; + bool bolt = true; + if (!both) + { + if (RandHelper.GetRandomDouble() > 0.5f) + arr = false; + else + bolt = false; + } + if (bolt) + { + var pfiksSpecialBolt = new[] + { + FightItemKind.PoisonBolt, FightItemKind.IceBolt, FightItemKind.FireBolt + }; + merch.Inventory.Items.Add(new ProjectileFightItem(pfiksSpecialBolt.GetRandomElem()) { Count = both ? 10 : 5 }); + } + if (arr) + { + var pfiksSpecialArrow = new[] + { + FightItemKind.PoisonArrow, FightItemKind.IceArrow, FightItemKind.FireArrow + }; + merch.Inventory.Items.Add(new ProjectileFightItem(pfiksSpecialArrow.GetRandomElem()) { Count = both ? 10 : 5 }); + } + } + protected virtual void GenerateMerchantEq(Merchant merch, bool magic) { var args = new AddItemArg(); args.notifyObservers = false;//prevent 100 sounds @@ -1105,89 +1176,141 @@ private void GenerateMerchantEq(Merchant merch, bool magic) eqKinds.Shuffle(); int levelIndex = Hero.Level; bool rangeGen = false; + Dictionary wpnKindCounter = new Dictionary(); + var kvs = EnumHelper.GetEnumValues(true); + foreach (var kv in kvs) + { + wpnKindCounter[kv] = 0; + } + foreach (var eqKind in eqKinds) { - if (eqKind == EquipmentKind.Trophy || eqKind == EquipmentKind.Unset || eqKind == EquipmentKind.God) - continue; + try + { + if (eqKind == EquipmentKind.Trophy || eqKind == EquipmentKind.Unset || eqKind == EquipmentKind.God) + continue; - var breakCount = 2; - if (eqKind == EquipmentKind.Weapon) - breakCount = 5; + var breakCount = 2; + if (eqKind == EquipmentKind.Weapon) + breakCount = 5; - int count = 0; - int attemptTries = 0; - while (count < breakCount) - { - var eq = lootGenerator.GetRandomEquipment(eqKind, levelIndex, merch.GetLootAbility()); - if (eq != null && !merch.Inventory.Items.Any(i => i.tag1 == eq.tag1)) + int count = 0; + int attemptTries = 0; + while (count < breakCount) { - if (eq.Class == EquipmentClass.Unique) - continue; - eq.Revealed = true; - if (magic) - eq.MakeMagic(); + var eq = lootGenerator.GetRandomEquipment(eqKind, levelIndex, merch.GetLootAbility()); + if (eq != null && !merch.Inventory.Items.Any(i => i.tag1 == eq.tag1)) + { + if (eq.Class == EquipmentClass.Unique) + continue; + eq.Revealed = true; + if (magic) + eq.MakeMagic(); + else + eq.MakeEnchantable(); + + merch.Inventory.Add(eq, args); + var wpn = eq as Weapon; + if (wpn != null) + { + if(wpn.IsBowLike) + rangeGen = true; + + wpnKindCounter[wpn.Kind]++; + } + count++; + } else - eq.MakeEnchantable(); + attemptTries++; - merch.Inventory.Add(eq, args); - var wpn = eq as Weapon; - if (wpn != null && wpn.IsBowLike) - rangeGen = true; - count++; + if (attemptTries == 5) + break; } - else - attemptTries++; + } + catch (Exception ex) + { + Logger.LogError(ex); + } + } - if (attemptTries == 5) - break; + if (merch.Name != "Basil") + { + foreach (var kv in wpnKindCounter) + { + if (kv.Value == 0) + { + var eq = lootGenerator.LootFactory.EquipmentFactory.GetWeapons(kv.Key, levelIndex).GetRandomElem(); + if (eq != null) + merch.Inventory.Add(eq); + } } + + AddSpecialBowLikeAmmo(merch, false); } if (!rangeGen) { - if (levelIndex <= 2) + try { - var bow = lootGenerator.GetLootByTileName("crude_crossbow"); - if (bow.Damage == 0) + if (levelIndex <= 2) { - int kk = 0; - kk++; + var bow = lootGenerator.GetLootByTileName("crude_crossbow"); + if (bow.Damage == 0) + { + int kk = 0; + kk++; + } + merch.Inventory.Add(bow); } - merch.Inventory.Add(bow); + else if (levelIndex <= 4) + merch.Inventory.Add(lootGenerator.GetLootByTileName("solid_bow"), args); + else if (levelIndex <= 6) + merch.Inventory.Add(lootGenerator.GetLootByTileName("composite_bow"), args); + } + catch (Exception ex) + { + Logger.LogError(ex); } - else if (levelIndex <= 4) - merch.Inventory.Add(lootGenerator.GetLootByTileName("solid_bow"), args); - else if (levelIndex <= 6) - merch.Inventory.Add(lootGenerator.GetLootByTileName("composite_bow"), args); } } - public void RegenerateMerchantInv(Merchant merch) + public void RegenerateMerchantInv(Merchant merch, bool rebind) { PopulateMerchantInv(merch, this.Hero.Level); + if(rebind) + AppendAction(new InventoryAction(merch.Inventory) { Info = "", Kind = InventoryActionKind.NeedsRebind }); + } - protected virtual void PopulateMerchantInv(Merchant merch, int heroLevel) + public virtual void PopulateMerchantInv(Merchant merch, int heroLevel) { + Logger.LogInfo("PopulateMerchantInv start"); + if (merch == null) + { + Logger.LogError("PopulateMerchantInv !merch"); + return; + } merch.Inventory.Items.Clear(); var lootKinds = Enum.GetValues(typeof(LootKind)).Cast() .Where(i => i != LootKind.Unset && i != LootKind.Other && i != LootKind.Seal && i != LootKind.SealPart && i != LootKind.Gold && i != LootKind.Book && i != LootKind.FightItem) .ToList(); + Logger.LogInfo("PopulateMerchantInv AddLootToMerchantInv..."); AddLootToMerchantInv(merch, lootKinds); - merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.Stone) { Count = 4 }); - if (RandHelper.GetRandomDouble() > 0.7) - merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.CannonBall) { Count = 4 }); - merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.ThrowingKnife) { Count = 4 }); - merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.ThrowingTorch) { Count = 4 }); + merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.Stone) { Count = 40 }); + //if (RandHelper.GetRandomDouble() > 0.7) + // merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.CannonBall) { Count = 8 }); + merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.ThrowingKnife) { Count = 40 }); + merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.ThrowingTorch) { Count = 20 }); if (RandHelper.GetRandomDouble() > 0.5) - merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.HunterTrap) { Count = 4 }); + merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.HunterTrap) { Count = 8 }); merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.PlainArrow) { Count = RandHelper.GetRandomInt(50) + 25 }); merch.Inventory.Items.Add(new ProjectileFightItem(FightItemKind.PlainBolt) { Count = RandHelper.GetRandomInt(50) + 25 }); + Logger.LogInfo("PopulateMerchantInv after ProjectileFightItem..."); //for (int i = 0; i < 4; i++) { var loot = new MagicDust(); @@ -1205,21 +1328,6 @@ protected virtual void PopulateMerchantInv(Merchant merch, int heroLevel) if (!merch.Inventory.Items.Any(i => i is Book)) merch.Inventory.Items.Add(lootGenerator.GetRandomLoot(LootKind.Book, 1)); - - - //int maxPotions = 4; - //for (int numOfLootPerKind = 0; numOfLootPerKind < maxPotions; numOfLootPerKind++) - //{ - // var hp = new Potion(); - // hp.SetKind(PotionKind.Health); - // hp.Revealed = true; - // merch.Inventory.Add(hp); - - // var mp = new Potion(); - // hp.SetKind(PotionKind.Mana); - // mp.Revealed = true; - // merch.Inventory.Add(mp); - //} } List lootSourcesWithDelayedEnemies = new List(); @@ -1285,8 +1393,17 @@ public T AddAlly() where T : class, IAlly return ally; } - public void AddAlly(IAlly ally) + public bool AddAlly(IAlly ally) { + if (ally.Kind == AllyKind.Enemy) + { + if (AlliesManager.AllAllies.Where(i => i.Kind == AllyKind.Enemy).Any()) + { + AppendAction(new GameInstructionAction() { Info = "Skeleton ally already used" }); ; + SoundManager.PlayBeepSound(); + return false; + } + } ally.Active = true; var le = ally as LivingEntity; le.Container = this.Container; @@ -1294,7 +1411,7 @@ public void AddAlly(IAlly ally) if (ally.TakeLevelFromCaster) { - float lvl = Hero.Level * 0.8f; + float lvl = Hero.Level;// * 0.8f; if (lvl < 1) lvl = 1; @@ -1302,16 +1419,16 @@ public void AddAlly(IAlly ally) } AlliesManager.AddEntity(le); - if (ally.Kind != AllyKind.Paladin) + if (!(ally is INPC))//Roslaw? { var empty = CurrentNode.GetClosestEmpty(Hero, true, false); ReplaceTile(le, empty); if (ally is Ally ally_) ally_.SetNextExpFromLevel(); le.PlayAllySpawnedSound(); - - AppendAction(new AllyAction() { Info = le.Name + " has been added", InvolvedTile = ally, AllyActionKind = AllyActionKind.Created }); } + AppendAction(new AllyAction() { Info = le.Name + " has been added", InvolvedTile = ally, AllyActionKind = AllyActionKind.Created }); + return true; } public bool CanUseSpellSource(LivingEntity caster, SpellSource scroll) @@ -1333,8 +1450,9 @@ public bool UtylizeSpellSource(LivingEntity caster, SpellSource spellSource) } if (spellSource is Scroll && caster is AdvancedLivingEntity advEnt) - return advEnt.Inventory.Remove(spellSource) != null; - + { + return advEnt.RemoveFromInv(spellSource) != null; + } if (spellSource is WeaponSpellSource) { spellSource.Count--; @@ -1389,7 +1507,7 @@ public virtual void HandeTileHit(Policy policy, LivingEntity attacker, IHitable hitBlocker = true; if (chest != null) { - info = attackerName+" hit a "; + info = attackerName + " hit a "; if (chest.ChestVisualKind == ChestVisualKind.Chest) info += "chest"; else if (chest.ChestVisualKind == ChestVisualKind.Grave) @@ -1398,7 +1516,12 @@ public virtual void HandeTileHit(Policy policy, LivingEntity attacker, IHitable else if (barrel != null) info = attackerName + " hit a barrel"; else - info = attackerName + " hit a wall"; + { + if(hitTile is Wall wall && wall.tag1.Contains("tree")) + info = attackerName + " hit a tree"; + else + info = attackerName + " hit a wall"; + } } if (hitBlocker) @@ -1412,9 +1535,9 @@ public virtual void HandeTileHit(Policy policy, LivingEntity attacker, IHitable return; } - var chestWasLooted = chest!=null ? chest.IsLooted : false; + var chestWasLooted = chest != null ? chest.IsLooted : false; this.LootManager.TryAddForLootSource(ls); - if(chest!=null && chestWasLooted) + if (chest != null && chestWasLooted) HandleChestHit(ls); //Logger.LogInfo("TimeTracker TryAddForLootSource: " + tr.TotalSeconds); } @@ -1452,6 +1575,10 @@ public virtual void HandeTileHit(Policy policy, LivingEntity attacker, IHitable candle.SetLooted(true); } } + else if (hitTile is InteractiveTile it && it.tag1.Contains("ladder")) + { + HandleLadder(it); + } else if (hitTile is CrackedStone cs) { info = attackerName + " hit a cracked stone"; @@ -1467,6 +1594,23 @@ public virtual void HandeTileHit(Policy policy, LivingEntity attacker, IHitable ReplaceTile(new Tile(), cs.point); } } + else if (hitTile is Bonfire bf) + { + info = attackerName + " hit a bonfire"; + AppendAction((InteractiveTileAction ac) => + { + ac.InteractiveKind = InteractiveActionKind.Hit; + ac.Info = info; + ac.InvolvedTile = bf; + ac.PlaySound = policyIsMelee;//TODO + }); + + } + + } + + protected virtual void HandleLadder(InteractiveTile it) + { } protected virtual bool GeneratesLoot(ILootSource ls) @@ -1523,20 +1667,16 @@ public bool UtylizeSpellSource(LivingEntity caster, SpellSource spellSource, Abs } else { - if (!(caster is God)) + if (!(caster is God) && !(caster.tag1.StartsWith("fallen_one"))) { - if (caster.Stats.Mana < spell.ManaCost) - { - ReportFailure("Not enough mana to cast a spell"); + if(!TryToBurnMana(caster, spell.ManaCost, false)) return false; - } - caster.ReduceMana(spell.ManaCost); } } spell.Utylized = true; - UtylizeSpellSource(caster, spellSource); - + var utylized = UtylizeSpellSource(caster, spellSource); + Logger.LogInfo("UtylizeSpellSource by caster: " + utylized); } catch (Exception) { @@ -1547,6 +1687,35 @@ public bool UtylizeSpellSource(LivingEntity caster, SpellSource spellSource, Abs return true; } + int godBeepCoolDown = 0; + public bool TryToBurnMana(LivingEntity caster, int manaCost, bool god) + { + if (caster.Stats.Mana < manaCost) + { + if (caster is Hero) + { + var me = "Not enough mana to cast"; + if (god) + me += " Slavic god's spell"; + else + me += " a spell"; + + + var sound = !god || godBeepCoolDown == 0; + if (god) + { + godBeepCoolDown++;// == 0 + //godBeepCoolDown = 10; + } + + ReportFailure(me, sound); + } + return false; + } + caster.ReduceMana(manaCost); + return true; + } + public void SaveGameOptions() { Persister.SaveOptions(Options.Instance); @@ -1618,7 +1787,7 @@ public AttackDescription CreateAttackDescription(LivingEntity caster, Projectile return null; } - return new AttackDescription(caster, caster.UseAttackVariation, AttackKind.PhysicalProjectile); + return new AttackDescription(caster, caster.UseAttackVariation, AttackKind.PhysicalProjectile, null, pfi); } //public void CallTryAddForLootSource(Dungeons.Tiles.Abstract.IHitable obstacle, Policy policy) @@ -1645,14 +1814,17 @@ public void FinishPolicyApplied(LivingEntity caster, Action AfterApply, { if (caster is Hero) OnHeroPolicyApplied(policy); + else if (caster is God) + AlliesManager.OnPolicyApplied(policy); if (AfterApply != null) AfterApply(policy); } - public void ReportFailure(string infoToDisplay) + public void ReportFailure(string infoToDisplay, bool playBeepSound = true) { - SoundManager.PlayBeepSound(); + if(playBeepSound) + SoundManager.PlayBeepSound(); if (infoToDisplay.Any()) { AppendAction(new GameEvent() { Info = infoToDisplay, Level = ActionLevel.Important }); @@ -1668,7 +1840,7 @@ public T GetStackedLootFromInv(Inventory inv) where T : StackedLoot public Action AttackPolicyInitializer; public Action AttackPolicyDone; - + public void ApplyHeroPhysicalAttackPolicy(IHitable target, bool allowPostAttackLogic) { Action afterApply = (p) => { }; @@ -1683,152 +1855,8 @@ public void ApplyPhysicalAttackPolicy(LivingEntity attacker, IHitable target, Ac MeleePolicyManager.ApplyPhysicalAttackPolicy(attacker, target, afterApply, null, esk); } - public bool UseActiveAbilities(LivingEntity attacker, LivingEntity targetLe, bool turnStart) - { - bool done = false; - var ale = attacker as AdvancedLivingEntity ; - foreach (var aa in ale.Abilities.ActiveItems) - { - if (aa.RunAtTurnStart == turnStart) - { - if (HasAbilityActivated(aa, ale)) - { - string reason; - if (attacker.CanUseAbility(aa.Kind, CurrentNode, out reason)) - { - UseActiveAbility(targetLe, attacker, ale.SelectedActiveAbility.Kind); - done = true; - } - } - } - } - - return done; - } - - protected virtual bool HasAbilityActivated(ActiveAbility ab, AdvancedLivingEntity ale) - { - return ale.SelectedActiveAbility != null && ale.SelectedActiveAbility.Kind == ab.Kind; - } - - public void UseActiveAbility(LivingEntity victim, LivingEntity abilityUser, Abilities.AbilityKind abilityKind) - { - var advEnt = abilityUser as AdvancedLivingEntity; - if (!advEnt.Abilities.IsActive(abilityKind)) - return; - - bool activeAbility = true; - UseAbility(victim, abilityUser, abilityKind, activeAbility); - } - - public void UseAbility(LivingEntity victim, LivingEntity abilityUser, Abilities.AbilityKind abilityKind, bool activeAbility) - { - var ab = abilityUser.GetActiveAbility(abilityKind); - - bool used = false; - if (abilityKind == Abilities.AbilityKind.StrikeBack)//StrikeBack is passive! - { - ApplyPhysicalAttackPolicy(abilityUser, victim, (p) => - { - used = true; - AppendUsedAbilityAction(abilityUser, abilityKind); - if (AttackPolicyDone != null) - AttackPolicyDone(); - }, EntityStatKind.ChanceToStrikeBack); - } - else if (abilityKind == AbilityKind.Stride) - { - int horizontal = 0, vertical = 0; - var neibKind = GetTileNeighborhoodKindCompareToHero(victim); - if (neibKind.HasValue) - { - InputManager.GetMoveData(neibKind.Value, out horizontal, out vertical); - var newPos = InputManager.GetNewPositionFromMove(victim.point, horizontal, vertical); - activeAbilityVictim = victim;// as Enemy; - activeAbilityVictim.MoveDueToAbilityVictim = true; - - var desc = ""; - var attack = abilityUser.Stats.GetStat(EntityStatKind.Strength).SumValueAndPercentageFactor(ab.PrimaryStat, true); - var damage = victim.CalcMeleeDamage(attack, ref desc); - var inflicted = victim.InflictDamage(abilityUser, false, ref damage, ref desc); - - ApplyMovePolicy(victim, newPos.Point); - used = true; - } - } - else if (abilityKind == Abilities.AbilityKind.OpenWound) - { - if (victim != null) - { - //psk = EntityStatKind.BleedingExtraDamage; - //ask = EntityStatKind.BleedingDuration; - var duration = ab.AuxStat.Factor; - var damage = Calculated.FactorCalculator.AddFactor(3, ab.PrimaryStat.Factor);//TODO 3 - victim.StartBleeding(damage, null, (int)duration); - used = true; - } - else - { - //var leSrc = new AbilityLastingEffectSrc(ab, 0); - //abilityUser.LastingEffectsSet.AddPercentageLastingEffect(EffectType.OpenWound, leSrc, abilityUser); - //used = true; - } - } - else if (abilityKind == Abilities.AbilityKind.ZealAttack) - { - used = true; - } - else - used = UseActiveAbility(ab, abilityUser, false); - - if (used) - { - if (activeAbility) - { - HandleActiveAbilityUsed(abilityUser, abilityKind); - } - } - } - - public bool UseActiveAbility(ActiveAbility ab, LivingEntity abilityUser, bool sendEvent) - { - bool used = false; - var abilityKind = ab.Kind; - var endTurn = false; - if (ab.CoolDownCounter > 0) - return false; - - if (abilityKind == AbilityKind.Smoke) - { - AddSmoke(abilityUser); - endTurn = true; - used = true; - - } - else - used = AddLastingEffectFromAbility(ab, abilityUser); - - if (used) - { - abilityUser.WorkingAbilityKind = abilityKind; - if(sendEvent) - HandleActiveAbilityUsed(abilityUser, abilityKind); - } - - if(endTurn) - Context.MoveToNextTurnOwner();//TODO call HandleHeroActionDone - return used; - } - - private void AddSmoke(LivingEntity abilityUser) - { - var smokeAb = Hero.GetActiveAbility(AbilityKind.Smoke); - var smokes = CurrentNode.AddSmoke(Hero, (int)smokeAb.PrimaryStat.Factor, (int)smokeAb.AuxStat.Factor); - AppendAction((TilesAppendedEvent ac) => - { - ac.Tiles = smokes.Cast().ToList(); - }); - } + public AbilityManager AbilityManager { get; private set; } + public void SpreadOil(Tile startingTile, int minRange = 1, int maxRange = 1) { @@ -1844,97 +1872,6 @@ public void SpreadOil(Tile startingTile, int minRange = 1, int maxRange = 1) SoundManager.PlaySound("oil_splash"); } } - - - LastingEffect AddAbilityLastingEffectSrc(EffectType et, ActiveAbility ab, LivingEntity abilityUser, int abilityStatIndex = 0) - { - var src = new AbilityLastingEffectSrc(ab, abilityStatIndex); - if (et != EffectType.IronSkin) - { - if( - ab.Kind == AbilityKind.ElementalVengeance || - ab.Kind == AbilityKind.Rage - ) - src.Duration = 3; - else - src.Duration = 1; - } - else - src.Duration = (int)ab.AuxStat.Factor; - return abilityUser.LastingEffectsSet.AddLastingEffect(et, src, abilityUser); - } - - public bool AddLastingEffectFromAbility(ActiveAbility ab, LivingEntity abilityUser) - { - if (!ab.TurnsIntoLastingEffect) - return false; - - if(ab.CoolDownCounter > 0) - return false; - - bool used = false; - var abilityKind = ab.Kind; - - if (ab.Kind == AbilityKind.ElementalVengeance) - { - var attacks = new EffectType[] { EffectType.FireAttack, EffectType.PoisonAttack, EffectType.ColdAttack }; - int i = 0; - foreach (var et in attacks) - { - AddAbilityLastingEffectSrc(et, ab, abilityUser, i); - i++; - } - used = true; - } - else if (ab.Kind == AbilityKind.IronSkin) - { - //var ab = abilityUser.GetActiveAbility(abilityKind); - //var defensePercadd = ab.PrimaryStat.Factor; - AddAbilityLastingEffectSrc(EffectType.IronSkin, ab, abilityUser); - used = true; - } - else if (abilityKind == Abilities.AbilityKind.Rage) - { - AddAbilityLastingEffectSrc(EffectType.Rage, ab, abilityUser); - used = true; - } - - return used; - } - - public void HandleActiveAbilityUsed(LivingEntity abilityUser, AbilityKind abilityKind) - { - var ab = abilityUser.GetActiveAbility(abilityKind); - abilityUser.HandleActiveAbilityUsed(abilityKind); - AppendUsedAbilityAction(abilityUser, abilityKind); - } - - public void AppendUsedAbilityAction(LivingEntity abilityUser, Abilities.AbilityKind abilityKind) - { - EventsManager.AppendAction(new LivingEntityAction(LivingEntityActionKind.UsedAbility) - { - Info = abilityUser.Name + " used ability " + abilityKind, - Level = ActionLevel.Important, - InvolvedEntity = abilityUser - }); - - if(abilityKind == AbilityKind.Smoke) - SoundManager.PlaySound("cloth"); - } - - TileNeighborhood? GetTileNeighborhoodKindCompareToHero(LivingEntity target) - { - TileNeighborhood? neib = null; - if (target.Position.X > Hero.Position.X) - neib = TileNeighborhood.East; - else if (target.Position.X < Hero.Position.X) - neib = TileNeighborhood.West; - else if (target.Position.Y > Hero.Position.Y) - neib = TileNeighborhood.South; - else if (target.Position.Y < Hero.Position.Y) - neib = TileNeighborhood.North; - return neib; - } public List GetRessurectTargets(Enemy enemyChemp) { @@ -1953,11 +1890,12 @@ public List GetRessurectTargets(Enemy enemyChemp) return ressurectTargets; } - public void SendCommand(Enemy enemyChemp, EnemyAction ea) + public void DoCommand(Enemy enemyChemp, CommandUseInfo command) { - if (ea.CommandKind == EntityCommandKind.RaiseMyFriends) + if (command.Kind == EntityCommandKind.Resurrect) { - foreach (var dead in GetRessurectTargets(enemyChemp)) + var rts = GetRessurectTargets(enemyChemp); + foreach (var dead in rts) { var healthProvider = new Enemy(this.Container); healthProvider.SetLevel(dead.Level); @@ -1990,14 +1928,15 @@ virtual public void PrepareDiscussionForShowing(INPC npc) public void ForceNextTurnOwner() { + Logger.LogError("ForceNextTurnOwner! TurnOwner: " + Context.TurnOwner); this.EnemiesManager.ForcePendingForAllIdleFalse(); this.AlliesManager.ForcePendingForAllIdleFalse(); this.AnimalsManager.ForcePendingForAllIdleFalse(); - Hero.State = Roguelike.Tiles.LivingEntities.EntityState.Idle;//with state Attacking no move was possible + Hero.State = EntityState.Idle;//with state Attacking no move was possible Context.MoveToNextTurnOwner(); } - public bool CanCallIdentify(AdvancedLivingEntity priceProvider, Tile shownFor) + public bool CanCallIdentify(LivingEntity priceProvider, Tile shownFor) { if (priceProvider is Hero) { @@ -2009,24 +1948,7 @@ public bool CanCallIdentify(AdvancedLivingEntity priceProvider, Tile shownFor) public virtual void OnBeforeAlliesTurn() { - var smokes = CurrentNode.Layers.GetTypedLayerTiles(KnownLayer.Smoke); - ProjectileFightItem smokeEnded = null; - foreach (var smoke in smokes) - { - smoke.Durability--; - if (smoke.Durability <= 0) - { - //CurrentNode.RemoveLoot(smoke.point); - CurrentNode.Layers.GetLayer(KnownLayer.Smoke).Remove(smoke); - AppendAction(new LootAction(smoke, null) { Kind = LootActionKind.Destroyed, Loot = smoke }); - smokeEnded = smoke; - } - } - if (smokeEnded !=null && smokeEnded.ActiveAbilitySrc == AbilityKind.Smoke) - { - smokeEnded.Caller.HandleActiveAbilityEffectDone(AbilityKind.Smoke, false); - - } + AbilityManager.HandleSmokeTiles(); var surfaces = CurrentNode.SurfaceSets.Sets; foreach (var surfaceSet in surfaces) @@ -2050,6 +1972,7 @@ public virtual void OnBeforeAlliesTurn() } } + public virtual void OnAfterAlliesTurn() { @@ -2142,5 +2065,131 @@ public void HandleChestHit(ILootSource lootSource) AppendAction(new InteractiveTileAction() { InteractiveKind = InteractiveActionKind.HitWhenLooted, InvolvedTile = chest }); } } + + + //bool IsUsing(LivingEntity le, DestPointDesc dpd) + //{ + // return dpd.MoveOnPathTarget == + // dpd.State == DestPointState.StayingAtTarget; + //} + + public bool IsUsingPrivy(LivingEntity le, DestPointDesc dpd) + { + return dpd.MoveOnPathTarget is Privy && + le.point == dpd.MoveOnPathTarget.point && + dpd.State == DestPointState.StayingAtTarget; + + } + + public virtual God createGod(SpellKind sk) + { + var god = new God(Container); + return god; + } + + public virtual List GetLivingEntitiesForGodSpell(bool allies, SpellKind sk) + { + var les = new List(); + if (sk == SpellKind.Wales && allies) + { + les.Add(Hero); + foreach (var ally in AlliesManager.AllAllies) + { + if (ally is God) + continue; + + les.Add(ally as LivingEntity); + } + } + return les; + } + + public IEnumerable GetEnemiesForGodAttack(int range)//SwiatowitScroll.MaxRange + { + return EnemiesManager.GetActiveEnemiesInvolved() + .Where(i => i.DistanceFrom(Hero) <= range) + .OrderBy(i => i.DistanceFrom(Hero)) + .Take(5); + } + + public bool IsEnemyToHero(Tile tile) + { + LivingEntity le = null; + if (tile is INPC npc) + le = npc.LivingEntity; + else + le = tile as LivingEntity; + + if(le is null) + return false; + return EnemiesManager.Contains(le); + } + + public virtual int GetStartWalkToInteractiveTurnsCount() + { + return 15; + } + + public void MakeFakeClones(LivingEntity fo, LivingEntity victomToSorround, bool sorroundHero) + { + for (int i = 0; i < 3; i++) + { + var le = new Enemy(Container); + le.tag1 = "cloned_" + fo.tag1; + var emp = this.CurrentNode.GetClosestEmpty(this.Hero); + le.SetRandomSpellSource(); + var lev = fo.Level / 3; + if (lev == 0) + lev = 1; + AppendEnemy(le, emp.point, lev); + le.SetFakeLevel(victomToSorround.Level); + + le.Name = fo.Name; + le.DisplayedName = fo.DisplayedName; + } + } + + static Dictionary MessageToVoice = new Dictionary() + { + { "Kill Him!", "kill_him"}, + { "I'll crush you!", "crush_you"}, + { "Join our amy of skeletons", "join_skeletons"}, + { "I'll burn you", "I_will_burn_you"}, + { "You'll be my slave for enhernity ", "my_slaves"} + }; + public string SayEnemyWelcomeVoice(Enemy enemyTile) + { + var message = MessageToVoice.Keys.ToList().GetRandomElem(); + var voice = MessageToVoice[message]; + if (enemyTile.tag1.StartsWith("fallen_one")) + { + message = "I'll crush you!"; + voice = "FallenOne_"+ voice; + } + SoundManager.PlayVoice(voice); + return message; + } + + public bool IsAnyManagerBusy(EntitiesManager.BusyOnesCheckContext context, ref string message) + { + message = ""; + bool busy = false; + var mgs = new EntitiesManager[] { AlliesManager, AnimalsManager, EnemiesManager, NpcManager }; + + foreach (var mg in mgs) + { + var bos = mg.FindBusyOnes(context); + + if (bos.Any()) + { + busy = true; + message += mg.GetType() + " has busy ones: " + bos.Count + ", 1st: " + bos.First() + "; "; + } + if(context == EntitiesManager.BusyOnesCheckContext.ForceIdle) + mg.CheckBusyOnes = true; + } + + return busy; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/InputManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/InputManager.cs index d0d84573..7d93cfd4 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/InputManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/InputManager.cs @@ -5,6 +5,7 @@ using Dungeons.Tiles.Abstract; using Roguelike.Abstract.Tiles; using Roguelike.Events; +using Roguelike.TileContainers; using Roguelike.Tiles; using Roguelike.Tiles.Interactive; using Roguelike.Tiles.LivingEntities; @@ -121,9 +122,10 @@ public virtual InteractionResult InteractHeroWith(Tile tile) bool tileIsDoor = tile is Tiles.Interactive.Door; bool tileIsDoorBySymbol = tile.Symbol == Constants.SymbolDoor; - if (tile is Enemy || tile is Dungeons.Tiles.Wall || tile is Animal) + if (tile is Enemy || tile is Wall || tile is Animal || + gm.IsEnemyToHero(tile)) { - if (tile is Dungeons.Tiles.Wall wall && wall != null && wall.Child != null) + if (tile is Wall wall && wall != null && wall.Child != null) { return HandleInteractionWithInteractive(wall.Child as Tiles.Interactive.InteractiveTile); } @@ -141,6 +143,13 @@ public virtual InteractionResult InteractHeroWith(Tile tile) } else if (tile is INPC npc) { + if (gm.IsUsingPrivy(npc.LivingEntity, npc.DestPointDesc)) + { + gm.SoundManager.PlayBeepSound(); + return InteractionResult.Blocked; + } + + gm.AppendAction((NPCAction ac) => { ac.NPCActionKind = NPCActionKind.Engaged; ac.InvolvedTile = npc; }); return InteractionResult.Blocked; } @@ -156,7 +165,7 @@ public virtual InteractionResult InteractHeroWith(Tile tile) return InteractionResult.Blocked; } - else if (tile is INPC) + else if (tile is INPC || (tile is LivingEntity le && le.Proffesion != EntityProffesionKind.Unset)) { return InteractionResult.Blocked; } @@ -176,7 +185,7 @@ public virtual InteractionResult InteractHeroWith(Tile tile) if (door.Opened) return InteractionResult.None; - if (door.KeyName.Any()) + if (gm.GameSettings.Mechanics.KeyIsRequiredToEnterBoosRoom && door.KeyName.Any()) { if (door.KeyPuzzle == Tiles.Looting.KeyPuzzle.LeverSet) { @@ -231,11 +240,11 @@ public virtual InteractionResult InteractHeroWith(Tile tile) return InteractionResult.None; } - protected virtual InteractionResult HandleInteractionWithInteractive(Tiles.Interactive.InteractiveTile tile) + protected virtual InteractionResult HandleInteractionWithInteractive(Tiles.Interactive.InteractiveTile it) { - if (tile is Stairs) + if (it is Stairs) { - var stairs = tile as Stairs; + var stairs = it as Stairs; var destLevelIndex = -1; if (stairs.StairsKind == StairsKind.LevelDown || stairs.StairsKind == StairsKind.LevelUp) @@ -253,18 +262,53 @@ protected virtual InteractionResult HandleInteractionWithInteractive(Tiles.Inter return gm.DungeonLevelStairsHandler(destLevelIndex, stairs); } } - else if (tile is Portal) + else if (it is Portal) + { + return gm.HandlePortalCollision(it as Portal); + } + else if (it.HidesInteractedEntity) { - return gm.HandlePortalCollision(tile as Portal); + if (!it.Busy) + { + var set = gm.NpcManager.HandleStayingAtTargetState(it, gm.Hero, gm.Hero.DestPointDesc); + if (set) + { + HidingHeroInteractionOn(it); + } + } + else + gm.SoundManager.PlayBeepSound(); + return InteractionResult.Handled; } else { - gm.ApplyHeroPhysicalAttackPolicy(tile, true); + gm.ApplyHeroPhysicalAttackPolicy(it, true); return InteractionResult.Attacked; } return InteractionResult.Blocked;//blok hero by default } + private void HidingHeroInteractionOn(Tiles.Interactive.InteractiveTile it) + { + gm.Hero.DestPointDesc.State = DestPointState.StayingAtTarget; + gm.Hero.DestPointDesc.MoveOnPathTarget = it; + gm.Hero.DestPointDesc.ReturnPoint = gm.Hero.point; + gm.AppendAction(new HeroAction() { InvolvedTile = it, Kind = HeroActionKind.EnteredInteractive }); + } + + public void HidingHeroInteractionOff(AbstractGameLevel gl) + { + var dpd = gm.Hero.DestPointDesc; + gm.NpcManager.HandleTravelBackState(gm.Hero, dpd, gl, false); + + var it = gm.Hero; + dpd.State = DestPointState.Unset; + dpd.MoveOnPathTarget = null; + + gm.AppendAction(new HeroAction() { InvolvedTile = it, Kind = HeroActionKind.LeftInteractive }); + + } + public InteractionResult HandleHeroShift(int horizontal, int vertical) { InteractionResult res = InteractionResult.None; @@ -307,10 +351,17 @@ public InteractionResult HandleHeroShift(int horizontal, int vertical) else { //logger.LogInfo(" Hero ac ="+ ac); + if (gm.IsUsingPrivy(gm.Hero, gm.Hero.DestPointDesc)) + { + HidingHeroInteractionOff(gm.CurrentNode); + } + gm.ApplyMovePolicy(gm.Hero, newPos.Point, null, (e) => { gm.OnHeroPolicyApplied(e); }); + + } } catch (Exception ex) diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/LastActions.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/LastActions.cs index f3955923..68248bcf 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/LastActions.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/LastActions.cs @@ -33,7 +33,8 @@ internal void Add(GameEvent ac) { if (ac.Info.Trim() == string.Empty) return; - + if (ac.Silent) + return; if (ac is LivingEntityAction leac) { if (leac.Kind == LivingEntityActionKind.Moved) diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/LootManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/LootManager.cs index 05b86dfa..eac1257f 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/LootManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/LootManager.cs @@ -107,7 +107,7 @@ public List TryAddForLootSource(ILootSource lootSource)//Barrel, Chest fro return lootItems; } - + bool bowAmmoGenerated = false; private List TryAddForNonEnemy(ILootSource lootSource) { //GameManager.Logger.LogInfo("TryAddForNonEnemy lootSource.Level: " + lootSource.Level); @@ -184,6 +184,8 @@ private List TryAddForNonEnemy(ILootSource lootSource) { loot.Source = lootSource; lootItems.Add(loot); + + } if (lootSource is Barrel) @@ -209,11 +211,15 @@ private List TryAddForNonEnemy(ILootSource lootSource) return lootItems; GameManager.AppendAction((InteractiveTileAction ac) => { ac.InvolvedTile = chest; ac.InteractiveKind = InteractiveActionKind.ChestOpened; }); GameManager.AddLootReward(loot, lootSource, true);//add loot at closest empty - Loot lootEx1 = null; - if (RandHelper.GetRandomDouble() > .2f) - lootEx1 = LootGenerator.GetRandomEquipment(GameManager.Hero.Level, GameManager.Hero.GetLootAbility()); - else - lootEx1 = GetExtraLoot(lootSource, false, loot.LootKind); + Loot lootEx1 = GetAmmo(loot); + + if (lootEx1 == null) + { + if (RandHelper.GetRandomDouble() > .33f) + lootEx1 = LootGenerator.GetRandomEquipment(GameManager.Hero.Level, GameManager.Hero.GetLootAbility()); + else + lootEx1 = GetExtraLoot(lootSource, false, loot.LootKind); + } if (lootEx1 != null) { if (lootItems.Where(i => i.Name == lootEx1.Name).Any()) @@ -238,6 +244,32 @@ private List TryAddForNonEnemy(ILootSource lootSource) return lootItems; } + + Loot GetAmmo(Loot loot) + { + if (loot is Weapon wpn && wpn.IsBowLike) + { + if (!bowAmmoGenerated) + { + bowAmmoGenerated = true; + if (wpn.Kind == Weapon.WeaponKind.Bow) + { + var ammo = new ProjectileFightItem(FightItemKind.PlainArrow); + ammo.Count = (int)RandHelper.GetRandomFloatInRange(5, 20); + return ammo; + } + else if (wpn.Kind == Weapon.WeaponKind.Crossbow) + { + var ammo = new ProjectileFightItem(FightItemKind.PlainBolt); + ammo.Count = (int)RandHelper.GetRandomFloatInRange(5, 20); + return ammo; + } + } + } + + return null; + } + public Loot EnsureVariety(ILootSource lootSource, LootSourceKind lsk, Loot loot) { if (loot is Recipe rec) @@ -284,8 +316,9 @@ Loot TryAddLootForDeadEnemy(Enemy enemy) } if (loot != null) + { GameManager.AddLootReward(loot, enemy, false); - + } if (enemy.DeathLoot != null) GameManager.AddLootReward(enemy.DeathLoot, enemy, false); diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/NpcManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/NpcManager.cs new file mode 100644 index 00000000..c4b93aba --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/NpcManager.cs @@ -0,0 +1,287 @@ +using Dungeons.Core; +using Roguelike.Abstract.Tiles; +using Roguelike.Events; +using Roguelike.Policies; +using Roguelike.TileContainers; +using Roguelike.Tiles.Interactive; +using Roguelike.Tiles.LivingEntities; +using SimpleInjector; +using System; +using System.Drawing; +using System.Linq; + +namespace Roguelike.Managers +{ + public class NpcManager : EntitiesManager + { + public NpcManager + ( + GameContext context, + EventsManager eventsManager, + Container container, + GameManager gm + ) : + base(TurnOwner.Npcs, context, eventsManager, container, gm) + { + context.TurnOwnerChanged += Context_TurnOwnerChanged; ; + context.ContextSwitched += Context_ContextSwitched; + EmitAllIdleWhenNooneBusy = true; + } + + private void Context_ContextSwitched(object sender, ContextSwitch e) + { + var entities = Context.CurrentNode.GetNpcs(); + base.SetEntities(entities.Cast().ToList()); + } + + private void Context_TurnOwnerChanged(object sender, TurnOwner e) + { + + } + + bool IsNpcAtTarget(INPC npc, Point target, bool precisely) + { + var dist = npc.LivingEntity.DistanceFrom(target); + if (dist == 0) + return true; + if (precisely) + return false; + if (dist <= 1 && npc.DestPointDesc.State != DestPointState.TravelingTo && npc.DestPointDesc.StateCounter > 10) + return true; + return false; + + } + + public bool HandleStayingAtTargetState(InteractiveTile target, LivingEntity le, DestPointDesc dpd) + { + if (target.HidesInteractedEntity) + { + //dpd.OriginalTargetPoint = target.point; + var set = gameManager.CurrentNode.SetTile(le, target.point); + Log("StayingAtTarget " + set); + return set; + } + return false; + } + + public InteractiveTile HandleTravelBackState(LivingEntity le, DestPointDesc dpd, AbstractGameLevel gl, bool setTile) + { + InteractiveTile target; + var itPoint = le.point; + bool set = false; + if (setTile) + { + var emp = gl.GetClosestEmpty(le); + set = gl.SetTile(le, emp.point); + + Log("HandleTravelBackState set le: " + set); + } + target = dpd.UnbusyTarget();//target is privy or fireplace + if (target != null) + { + set = gl.SetTile(target, target.point); + Log("HandleTravelBackState set target: " + set); + } + dpd.ActivityKind = DestPointActivityKind.Home; + return target; + } + + public void SetNpcDestPointState(INPC npc, DestPointState destPointState, InteractiveTile target = null) + { + var log = "SetNpcDestPointState " + destPointState; + Log(log); + npc.DestPointDesc.State = destPointState; + if (destPointState == DestPointState.StayingAtTarget) + { + HandleStayingAtTargetState(target, npc.LivingEntity, npc.DestPointDesc); + } + else if (destPointState == DestPointState.TravelingBack) + { + target = HandleTravelBackState(npc.LivingEntity, npc.DestPointDesc, gameManager.CurrentNode, true); + } + else if (destPointState == DestPointState.Unset) + { + npc.DestPointDesc.ActivityKind = DestPointActivityKind.Unset; + //npc.WalkKind = WalkKind.Unset; + } + + if (target != null) + { + if (destPointState == DestPointState.StayingAtTarget) + gameManager.AppendAction(new NpcAction(npc, npc.DestPointDesc, target) { Info = npc.Name + " interacted with a " + target.Name }); + else if (destPointState == DestPointState.TravelingBack) + gameManager.AppendAction(new NpcAction(npc, npc.DestPointDesc, target) { Info = npc.Name + " is traveling home from " + target.Name }); + else if (destPointState == DestPointState.Unset) + gameManager.AppendAction(new NpcAction(npc, npc.DestPointDesc, target) { Info = npc.Name + " arrived home from " + target.Name }); + } + + } + + + private static void Log(string log) + { + //Debug.WriteLine(log); + } + public void StartWalkToInteractive(INPC npc, InteractiveTile target, DestPointActivityKind ak) + { + if (npc.WalkKind != WalkKind.Unset) + return; + + var desc = new DestPointDesc() + { + MoveOnPathTarget = target, + ReturnPoint = npc.LivingEntity.point, + ActivityKind = ak + }; + + npc.DestPointDesc = desc; + var empts = gameManager.CurrentNode.GetEmptyNeighborhoodPoint(target); + if (empts != null) + { + desc.TargetPoint = empts.Item1; + npc.LivingEntity.PathToTarget = gameManager.CurrentNode.FindPath(npc.LivingEntity, desc.TargetPoint); + if (npc.LivingEntity.PathToTarget != null) + { + //npc.WalkKind = WalkKind.GoToInteractive; + SetNpcDestPointState(npc, DestPointState.TravelingTo, target); + } + } + } + + public override void MakeTurn(LivingEntity entity) + { + if (entity is INPC npc) + { + var increasedStateCounter = npc.DestPointDesc.IncreaseStateCounter(); + var log = "NpcManager MakeTurn: " + entity + ", npc.WalkKind: " + npc.WalkKind; + if (gameManager.AlliesManager.AllAllies.Contains(npc as IAlly)) + return; + + if (npc.PointedByHeroCounter > 0) + { + npc.PointedByHeroCounter--; + return; + } + + var moveOnPathTarget = npc.DestPointDesc.MoveOnPathTarget; + if (npc.WalkKind == WalkKind.GoToHome) + { + moveOnPathTarget = gameManager.CurrentNode.GetTile(npc.DestPointDesc.ReturnPoint); + } + log += ", Move Target: " + moveOnPathTarget; + Log(log); + if (npc.DestPointDesc.IsWalkToTargetInProcess) + { + HandleWalkToInteractive(npc, increasedStateCounter); + } + else if (npc.WalkKind == WalkKind.FollowingTarget) + { + FollowTarget(npc); + } + else + { + if (npc.WalkKind == WalkKind.Unset && + npc.DestPointDesc.State != DestPointState.StayingAtTarget && + npc.LivingEntity.DistanceFrom(npc.LivingEntity.InitialPoint) > 5 && + RandHelper.Random.NextDouble() > .5f) + { + MakeTravelingBack(npc); + } + if (npc.WalkKind == WalkKind.GoToHome) + { + HandleTravelBack(npc, npc.LivingEntity.InitialPoint); + } + else if (RandHelper.Random.NextDouble() > .75f) + { + MakeRandomMove(entity); + } + else if (npc.DestPointDesc.StateCounter > gameManager.GetStartWalkToInteractiveTurnsCount()) + { + if (RandHelper.GetRandomDouble() > 0.75) + { + var rand = gameManager.PossibleNpcDestMoveTargets.Where + ( + i => i.DistanceFrom(npc.LivingEntity.point) < 12 && !i.Busy + ) + .ToList() + .GetRandomElem(); + if (rand != null) + StartWalkToInteractive(npc, rand, rand.DestPointActivityKind); + } + } + } + } + } + + public void MakeTravelingBack(INPC npc) + { + SetNpcDestPointState(npc, DestPointState.TravelingBack, null); + npc.DestPointDesc.ReturnPoint = npc.LivingEntity.InitialPoint; + } + + + private void HandleWalkToInteractive(INPC npc, bool increasedStateCounter) + { + if (npc.DestPointDesc.State == DestPointState.TravelingTo) + { + if (IsNpcAtTarget(npc, npc.DestPointDesc.TargetPoint, false)) + { + if (npc.DestPointDesc.MoveOnPathTarget is InteractiveTile it) + { + if (it.InteractWith(npc.LivingEntity)) + { + //gameManager.AddHiddenNpc(npc, it); + SetNpcDestPointState(npc, DestPointState.StayingAtTarget, it); + } + } + } + else + { + if(RandHelper.GetRandomDouble() > 0.25)//do not hurry, let player chase him + MakeMoveOnPath(npc.LivingEntity, npc.DestPointDesc.TargetPoint, true); + } + + } + else if (npc.DestPointDesc.State == DestPointState.StayingAtTarget) + { + if (!increasedStateCounter) + { + SetNpcDestPointState(npc, DestPointState.TravelingBack, null); + } + } + else if (npc.DestPointDesc.State == DestPointState.TravelingBack) + { + HandleTravelBack(npc, npc.DestPointDesc.ReturnPoint); + } + } + + private void HandleTravelBack(INPC npc, Point backPoint) + { + if (RandHelper.GetRandomDouble() > 0.25)//do not hurry + { + MakeMoveOnPath(npc.LivingEntity, backPoint, true); + if (IsNpcAtTarget(npc, backPoint, true)) + { + SetNpcDestPointState(npc, DestPointState.Unset, null); + } + } + } + + //TODO + protected override bool ShalLReportTurnOwner(Policy policy) + { + return false; + } + + public override void MakeTurn() + { + //?? + //if ((gameManager.CurrentNode is GameLevel))//only world + //{ + // CheckState(true); + // return; + //} + base.MakeTurn(); + } + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/NpcManager.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Managers/NpcManager.cs.meta new file mode 100644 index 00000000..25698521 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/NpcManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d9152b23f5e6bd4f8fca606f2f71d23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/MeleePolicyManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/MeleePolicyManager.cs index 829173d6..d7ac4da1 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/MeleePolicyManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/MeleePolicyManager.cs @@ -1,5 +1,6 @@ using Dungeons.Tiles; using Dungeons.Tiles.Abstract; +using Roguelike.Abilities; //using NUnit.Framework.Interfaces; using Roguelike.Attributes; using Roguelike.Managers; @@ -80,7 +81,6 @@ private void OnPolicyApplied(LivingEntity attacker, IHitable target, Action FindBulkAttackTargets(Enemy lastTarget, EntityStatKind ent { HeroBulkAttackTargets = new List(); var hero = gm.Hero; - var bulkFromZealAttack = hero.SelectedActiveAbility != null && hero.SelectedActiveAbility.Kind == Abilities.AbilityKind.ZealAttack; + var bulkFromZealAttack = hero.SelectedActiveAbility != null && hero.SelectedActiveAbility.Kind == AbilityKind.ZealAttack; string reason; if (bulkFromZealAttack && !hero.CanUseAbility(AbilityKind.ZealAttack, gm.CurrentNode, out reason)) return HeroBulkAttackTargets; @@ -78,8 +78,8 @@ protected List FindBulkAttackTargets(Enemy lastTarget, EntityStatKind ent var ak = AbilityKind.BulkAttack; if (bulkFromZealAttack) ak = AbilityKind.ZealAttack; - if (ak == AbilityKind.BulkAttack) - gm.AppendUsedAbilityAction(hero, ak); + if (ak == AbilityKind.BulkAttack)//TODO all passive ab shall report it + hero.AppendUsedAbilityAction(ak); } } @@ -122,9 +122,9 @@ protected Enemy GetNextTarget() gm.RemoveDead(); var targets = HeroBulkAttackTargets.Where(i => i.Alive).ToList(); var rand = false; - var target = rand ? targets.GetRandomElem() : targets.First(); - if (target == null) - return null; + var target = rand ? targets.GetRandomElem() : targets.FirstOrDefault(); + if (target == null) + return null; HeroBulkAttackTargets.Remove(target); return target; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/ProjectileFightItemPolicyManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/ProjectileFightItemPolicyManager.cs index bf37156f..09c8280c 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/ProjectileFightItemPolicyManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/ProjectileFightItemPolicyManager.cs @@ -25,7 +25,7 @@ internal ProjectileFightItemPolicyManager(GameManager gm) : base(gm) public void Log(string log) { - //logger.LogInfo("gm: "+log); + Logger.LogInfo("gm: "+log); } public ILogger Logger { get => gm.Logger; } @@ -89,7 +89,7 @@ public bool ApplyAttackPolicy bool res = false; if (ab != null && ab.Kind == AbilityKind.Smoke) { - res = gm.UseActiveAbility(ab as ActiveAbility, caster, true); + res = gm.AbilityManager.UseActiveAbility(ab as ActiveAbility, caster as AdvancedLivingEntity, target as LivingEntity, sendEvent: true); } else { @@ -115,7 +115,7 @@ public bool ApplyAttackPolicy var diff = cb - ca; } if (abUsed) - gm.HandleActiveAbilityUsed(caster, ab.Kind); + gm.AbilityManager.HandleActiveAbilityUsed(caster, ab.Kind); Log("ApplyAttackPolicy done res: " + res); Log(""); @@ -148,9 +148,9 @@ Action AfterApply if (BeforeApply != null) BeforeApply(policy); - policy.TargetHit += (s, e) => + policy.TargetHit += (s, hitObject) => { - HandeTileHit(caster, target, policy); + HandeTileHit(caster, hitObject, policy); //gm.CallTryAddForLootSource(e, policy); }; diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/SpellPolicyManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/SpellPolicyManager.cs index 750f12c5..2f8b1248 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/SpellPolicyManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/Policies/SpellPolicyManager.cs @@ -17,6 +17,9 @@ using System.Linq; using Dungeons.Core.Policy; using Dungeons.Tiles.Abstract; +using Roguelike.Effects; +using System.Collections.Generic; +using Roguelike.Abstract.Tiles; namespace Roguelike.Managers.Policies { @@ -169,34 +172,17 @@ public Abstract.Spells.ISpell ApplyPassiveSpell gm.SoundManager.PlaySound("hey"); } - - else if (kind == SpellKind.Dziewanna) { - reportSpellDone = false; - int maxApples = 1; - if (RandHelper.GetRandomDouble() > 0.5) - maxApples += 1; - for (int appleIndex = 0; appleIndex < maxApples; appleIndex++) - { - var enemies = gm.CurrentNode.GetNeighborTiles(gm.Hero); - bool added = false; - foreach (var en in enemies) - { - var emptyOnes = gm.CurrentNode.GetEmptyNeighborhoodTiles(en, false); - if (emptyOnes.Any()) - { - AddApple(emptyOnes.First()); - added = true; - break; - } - } - if (!added) - { - var emp = gm.CurrentNode.GetClosestEmpty(gm.Hero); - AddApple(emp); - } - } + reportSpellDone = ApplyDziewannaSpell(); + } + else if (kind == SpellKind.Jarowit) + { + ApplyJarowitSpell(caster, spell); + } + else if (kind == SpellKind.Wales) + { + ApplyWalesSpell(kind); } else if (kind == SpellKind.CrackedStone) { @@ -215,7 +201,8 @@ public Abstract.Spells.ISpell ApplyPassiveSpell else if (spellSource is Book) suffix = "book"; - var info = gm.Hero.Name + " used " + spellSource.Kind.ToDescription() + " " + suffix; + var info = caster is God ? "God "+ caster.Name + " manifested its power" : + gm.Hero.Name + " used " + spellSource.Kind.ToDescription() + " " + suffix; gm.AppendAction((LivingEntityAction ac) => { @@ -241,6 +228,76 @@ public Abstract.Spells.ISpell ApplyPassiveSpell return default(T); } + private bool ApplyDziewannaSpell() + { + bool reportSpellDone = false; + int maxApples = 1; + if (RandHelper.GetRandomDouble() > 0.5) + maxApples += 1; + for (int appleIndex = 0; appleIndex < maxApples; appleIndex++) + { + var enemies = gm.CurrentNode.GetNeighborTiles(gm.Hero); + bool added = false; + foreach (var en in enemies) + { + var emptyOnes = gm.CurrentNode.GetEmptyNeighborhoodTiles(en, false); + if (emptyOnes.Any()) + { + AddApple(emptyOnes.First()); + added = true; + break; + } + } + if (!added) + { + var emp = gm.CurrentNode.GetClosestEmpty(gm.Hero); + AddApple(emp); + } + } + + return reportSpellDone; + } + + private void ApplyJarowitSpell(LivingEntity caster, Abstract.Spells.ISpell spell) + { + var iron = new IronSkinSpell(gm.Hero); + gm.Hero.LastingEffectsSet.AddLastingEffectFromSpell(Effects.EffectType.IronSkin, iron); + + foreach (var ally in gm.AlliesManager.AllAllies) + { + if (ally is God) + continue; + (ally as LivingEntity).LastingEffectsSet.AddLastingEffectFromSpell(Effects.EffectType.IronSkin, iron); + } + + var typedSpell = spell as JarowitSpell; + var ems = gm.EnemiesManager.GetActiveEnemiesInvolved().Where(i => i.DistanceFrom(caster) <= typedSpell.Range); + foreach (var en in ems) + { + var weak = new WeakenSpell(en); + en.LastingEffectsSet.AddLastingEffectFromSpell(Effects.EffectType.Weaken, weak); + } + } + + private void ApplyWalesSpell(SpellKind kind) + { + var les = gm.GetLivingEntitiesForGodSpell(true, kind); + var laEffs = new[] { EffectType.Poisoned, EffectType.Frozen, EffectType.Firing }; + foreach (var le in les) + { + le.ConsumePotion(new Potion(PotionKind.Health)); + le.ConsumePotion(new Potion(PotionKind.Mana)); + //le.ConsumePotion(new Potion(PotionKind.Antidote)); + + foreach (var lef in laEffs) + { + var eff = le.LastingEffectsSet.GetByType(lef); + if (eff != null) + le.RemoveLastingEffect(eff); + } + } + } + public event EventHandler ReportDelayedSpellDone; protected virtual void OnReportDelayedSpellDone(float delaySecs, LivingEntity caster) @@ -259,15 +316,14 @@ public OffensiveSpell ApplySpell(LivingEntity caster, SpellSource spellSource, T { if (os is SkeletonSpell skeletonSpell) { - gm.AddAlly(skeletonSpell.Ally); - res = ApplyAttackPolicyResult.OK; + if (gm.AddAlly(skeletonSpell.Ally)) + res = ApplyAttackPolicyResult.OK; + else + res = ApplyAttackPolicyResult.NotEnoughResources; } else if (spellSource is SwiatowitScroll) { - var ems = gm.EnemiesManager.GetActiveEnemiesInvolved() - .Where(i => i.DistanceFrom(gm.Hero) <= SwiatowitScroll.MaxRange) - .OrderBy(i => i.DistanceFrom(gm.Hero)) - .Take(5); + var ems = GetEnemiesForGodAttack(); if (ems.Any()) { HeroBulkAttackTargets = ems.Cast().ToList(); @@ -282,10 +338,25 @@ public OffensiveSpell ApplySpell(LivingEntity caster, SpellSource spellSource, T } else if (spell is PerunSpell ps) { - if (pointedTile is IDestroyable dest) + var ems = GetEnemiesForGodAttack(); + if (ems.Any()) { - res = ApplyAttackPolicy(caster, dest, spellSource); - callSpellDone = false; + IHitable target = ems.FirstOrDefault(); + var le = pointedTile as LivingEntity; + if (le != null && gm.EnemiesManager.Contains(le)) + { + target = le; + } + if (target != null) + { + res = ApplyAttackPolicy(caster, target, spellSource); + callSpellDone = false; + } + else + { + gm.AppendAction(new GameEvent() { Info = "No valid targets for a spell" }); + return null; + } } else { @@ -311,6 +382,12 @@ public OffensiveSpell ApplySpell(LivingEntity caster, SpellSource spellSource, T return null; } + public IEnumerable GetEnemiesForGodAttack() + { + return gm.GetEnemiesForGodAttack(SwiatowitScroll.MaxRange); + + } + public void OnSpellDone(LivingEntity caster) { if (caster is Hero) @@ -343,9 +420,32 @@ public ApplyAttackPolicyResult ApplyAttackPolicy if (!looped) applyingBulk = false; + if (spellSource == null) + { + gm.Logger.LogInfo("Error: ApplyAttackPolicy !spellSource, "+ caster); + return ApplyAttackPolicyResult.NotEnoughResources; + } AttackPolicy policy = null; gm.Logger.LogInfo("SpellManager.ApplyAttackPolicy caster: " + caster + " target: " + target + " spellSource: " + spellSource); - var spell = spellSource.CreateSpell(caster); + //var spell = spellSource.CreateSpell(caster); + + + Abstract.Spells.ISpell spell = null; + if (spellSource.IsProjectile) + { + var projectileCastPolicy = Container.GetInstance(); + policy = projectileCastPolicy; + spell = projectileCastPolicy.CreateSpell(caster, spellSource) as Abstract.Spells.ISpell; + } + else if (spellSource.Kind == SpellKind.Perun)//TODO + { + var staticSpellCastPolicy = Container.GetInstance(); + policy = staticSpellCastPolicy; + spell = staticSpellCastPolicy.CreateSpell(caster, spellSource) as Abstract.Spells.ISpell ; + } + else + spell = spellSource.CreateSpell(caster); + var projectileSpell = spell as IProjectileSpell; if (projectileSpell != null) @@ -364,26 +464,13 @@ public ApplyAttackPolicyResult ApplyAttackPolicy if (!looped && !gm.UtylizeSpellSource(caster, spellSource, spell)) return ApplyAttackPolicyResult.NotEnoughResources; - if (projectileSpell != null) - { - var projectileCastPolicy = Container.GetInstance(); - policy = projectileCastPolicy; - projectileCastPolicy.CreateSpell(caster, spellSource); - } - else if (spell is PerunSpell) - { - var staticSpellCastPolicy = Container.GetInstance(); - policy = staticSpellCastPolicy; - staticSpellCastPolicy.CreateSpell(caster, spellSource); - - } policy.AddTarget(target); policies.Add(policy); - policy.TargetHit += (s, e) => + policy.TargetHit += (s, hitObject) => { - HandeTileHit(caster, target, policy); + HandeTileHit(caster, hitObject, policy); }; if (BeforeApply != null) @@ -462,7 +549,7 @@ protected virtual bool ApplyBulkAttack(EntityStatKind esk, Action func) bool bulkOK = HeroBulkAttackTargets.Any(); if (bulkOK) { - while (HeroBulkAttackTargets.Any())//simplest from of attack + while (HeroBulkAttackTargets !=null && HeroBulkAttackTargets.Any())//simplest from of attack { var target = GetNextTarget(); func(target); diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Managers/QuestManager.cs b/Assets/Scripts/Common/Dlls/Roguelike/Managers/QuestManager.cs index 2359b9ba..73654480 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Managers/QuestManager.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Managers/QuestManager.cs @@ -2,7 +2,7 @@ { public class QuestManager { - public virtual bool EnsureQuestAssigned(string questKind) + public virtual bool EnsureQuestAssigned(string questKind, string questPrincipalName) { return false; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Policies/AttackPolicy.cs b/Assets/Scripts/Common/Dlls/Roguelike/Policies/AttackPolicy.cs index 2b75e28b..587c5089 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Policies/AttackPolicy.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Policies/AttackPolicy.cs @@ -1,4 +1,5 @@ -using Dungeons.Core.Policy; +using Dungeons.Core; +using Dungeons.Core.Policy; using Dungeons.Fight; using Dungeons.Tiles; using Dungeons.Tiles.Abstract; @@ -27,7 +28,7 @@ public void AddTarget(Dungeons.Tiles.Abstract.IHitable obstacle) Targets.Add(obstacle); } - public abstract void CreateSpell(LivingEntity caster, SpellSource spellSource); + public abstract ISpell CreateSpell(LivingEntity caster, SpellSource spellSource); public virtual void TryAttack(IPolicy policy, LivingEntity attacker, IHitable le) { @@ -57,10 +58,10 @@ protected virtual HitResult TryAttack(IPolicy policy, LivingEntity attacker, attacker.EventsManager.AppendAction(new LivingEntityAction(LivingEntityActionKind.Missed) { InvolvedEntity = attacker, - targetEntityPosition = target.Position, Info = attacker.Name + " missed " + target.Name, AttackKind = ak }); + attacker.Container.GetInstance().LogInfo(attacker + " missed " + target + " using "+ proj); return HitResult.Evaded; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Policies/MeleeAttackPolicy.cs b/Assets/Scripts/Common/Dlls/Roguelike/Policies/MeleeAttackPolicy.cs index 48a880ad..ff1e8ff0 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Policies/MeleeAttackPolicy.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Policies/MeleeAttackPolicy.cs @@ -51,7 +51,7 @@ public override void AttackNextTarget(LivingEntity attacker, IHitable victim) /// /// /// - public override void CreateSpell(LivingEntity caster, SpellSource spellSource) + public override ISpell CreateSpell(LivingEntity caster, SpellSource spellSource) { throw new System.NotImplementedException(); } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Policies/ProjectileCastPolicy.cs b/Assets/Scripts/Common/Dlls/Roguelike/Policies/ProjectileCastPolicy.cs index 69e1a7ba..009fa958 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Policies/ProjectileCastPolicy.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Policies/ProjectileCastPolicy.cs @@ -202,10 +202,12 @@ LivingEntity caster } SpellSource spellSource; - public override void CreateSpell(LivingEntity caster, SpellSource spellSource) + public override Dungeons.Tiles.Abstract.ISpell CreateSpell(LivingEntity caster, SpellSource spellSource) { this.spellSource = spellSource; - Projectile = spellSource.CreateSpell(caster) as IProjectileSpell; + var spell = spellSource.CreateSpell(caster) as IProjectileSpell; + Projectile = spell; + return spell; } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Policies/StaticSpellCastPolicy.cs b/Assets/Scripts/Common/Dlls/Roguelike/Policies/StaticSpellCastPolicy.cs index a2d5fbb2..7985b3bc 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Policies/StaticSpellCastPolicy.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Policies/StaticSpellCastPolicy.cs @@ -67,9 +67,10 @@ private bool ShallHitTarget(IHitable nextTarget) return true; } - public override void CreateSpell(LivingEntity caster, SpellSource spellSource) + public override Dungeons.Tiles.Abstract.ISpell CreateSpell(LivingEntity caster, SpellSource spellSource) { DamagingSpell = spellSource.CreateSpell(caster) as Abstract.Spells.IDamagingSpell; + return DamagingSpell; } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Quests/Quest.cs b/Assets/Scripts/Common/Dlls/Roguelike/Quests/Quest.cs index 85e13ef6..63bdcc9f 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Quests/Quest.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Quests/Quest.cs @@ -21,7 +21,12 @@ public Quest() } - public QuestStatus Status { get; set; } + public bool AllowChooseReward { get; set; } + public QuestStatus Status + { + get; + set; + } public string Tag { get; set; } public string Name { get; set; } public string QuestPrincipalName { get; set; }//typically merchant's name diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Serialization/JSONPersister.cs b/Assets/Scripts/Common/Dlls/Roguelike/Serialization/JSONPersister.cs index e84fd1fb..1713d9cc 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Serialization/JSONPersister.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Serialization/JSONPersister.cs @@ -10,8 +10,6 @@ using SimpleInjector; using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; using System.IO; using System.Linq; @@ -145,7 +143,7 @@ public string MakeJson(T entity) } - public T Load(string filePath, Container container) where T : class, IPersistable + public T Load(string filePath) where T : class, IPersistable { T entity = null; @@ -153,28 +151,9 @@ public T Load(string filePath, Container container) where T : class, IPersist { var json = File.ReadAllText(filePath); //this.container.GetInstance().LogInfo("Engine_JsonDeserializer..."); + entity = JsonToObject(json); + - if (!IsValidJson(json)) - { - this.Container.GetInstance().LogError("param json is not a valid json!"); - return null; - } - ITraceWriter traceWriter = null;// new MemoryTraceWriter(); - JsonSerializerSettings settings = new JsonSerializerSettings - { TraceWriter = traceWriter, TypeNameHandling = TypeNameHandling.All }; - //container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle(); - //settings.TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full; - settings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); - settings.ContractResolver = new SimpleInjectorContractResolver(container); - settings.ObjectCreationHandling = ObjectCreationHandling.Replace; - entity = JsonConvert.DeserializeObject(json, settings); - - //string outdata = traceWriter.ToString(); - //Console.WriteLine(outdata); - //if (siFromJson.Hero.CurrentEquipment.ContainsKey(EquipmentKind.Weapon)) - // Debug.Log("Engine_JsonDeserializer weapon = " + siFromJson.Hero.CurrentEquipment[EquipmentKind.Weapon]); - //else - // Debug.Log("Engine_JsonDeserializer no weapon "); } catch (Exception ex) { @@ -184,7 +163,33 @@ public T Load(string filePath, Container container) where T : class, IPersist return entity; } - + + public T JsonToObject(string json) where T : class, IPersistable + { + if (!IsValidJson(json)) + { + this.Container.GetInstance().LogError("param json is not a valid json!"); + return null; + } + ITraceWriter traceWriter = null;// new MemoryTraceWriter(); + JsonSerializerSettings settings = new JsonSerializerSettings + { TraceWriter = traceWriter, TypeNameHandling = TypeNameHandling.All }; + //container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle(); + //settings.TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Full; + settings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); + settings.ContractResolver = new SimpleInjectorContractResolver(Container); + settings.ObjectCreationHandling = ObjectCreationHandling.Replace; + return JsonConvert.DeserializeObject(json, settings); + + //string outdata = traceWriter.ToString(); + //Console.WriteLine(outdata); + //if (siFromJson.Hero.CurrentEquipment.ContainsKey(EquipmentKind.Weapon)) + // Debug.Log("Engine_JsonDeserializer weapon = " + siFromJson.Hero.CurrentEquipment[EquipmentKind.Weapon]); + //else + // Debug.Log("Engine_JsonDeserializer no weapon "); + } + + private static bool IsValidJson(string strInput) { strInput = strInput.Trim(); @@ -287,13 +292,13 @@ public void SaveHero(string json, string heroName, bool quick) public AlliesStore LoadAllies(string hero, bool quick) { var fileName = GetFullFilePath(FileKind.Allies, hero, quick); - return Load(fileName, Container); + return Load(fileName); } public Hero LoadHero(string heroName, bool quick) { var fileName = GetFullFilePath(FileKind.Hero, heroName, quick); - return Load(fileName, Container); + return Load(fileName); } public virtual void DeleteGame(string heroName, bool quick) @@ -318,7 +323,7 @@ public void SaveLevel(string heroName, GameLevel level, bool quick) public GameLevel LoadLevel(string heroName, int index, bool quick) { var filePath = GetFullFilePath(FileKind.GameLevel, heroName, quick, index.ToString()); - return Load(filePath, Container); + return Load(filePath); } public void SaveGameState(string heroName, GameState gameState, bool quick) @@ -331,7 +336,7 @@ public void SaveGameState(string heroName, GameState gameState, bool quick) public GameState LoadGameState(string heroName, bool quick) { var filePath = GetFullFilePath(FileKind.GameState, heroName, quick); - return Load(filePath, Container); + return Load(filePath); } public void SaveOptions(Options opt) @@ -347,7 +352,7 @@ public Options LoadOptions() { try { - return Load(filePath, Container); + return Load(filePath); } catch (Exception ex) { diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Serialization/PersistancyWorker.cs b/Assets/Scripts/Common/Dlls/Roguelike/Serialization/PersistancyWorker.cs index c7d3ac81..6b17edf9 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Serialization/PersistancyWorker.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Serialization/PersistancyWorker.cs @@ -90,6 +90,7 @@ public AbstractGameLevel Load(string heroName, GameManager gm, bool quick, Func< public static Hero LoadKeyGameElems(GameManager gm, string heroName, bool quick) { var hero = gm.Persister.LoadHero(heroName, quick); + hero.EnsureInvOwner(); var allies = gm.Persister.LoadAllies(heroName, quick); gm.AlliesManager.SetEntities(allies.Allies); diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Settings/RpgGameSettings.cs b/Assets/Scripts/Common/Dlls/Roguelike/Settings/RpgGameSettings.cs index af52e785..f04026fc 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Settings/RpgGameSettings.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Settings/RpgGameSettings.cs @@ -56,10 +56,15 @@ public float SoundVolume get; set; } = 1f; + + public bool VoiceActingOn { get; set; } = true; }; public class Serialization { + /// + /// If false Hero will be exactly at the save place (potentially dangerous) + /// public bool RestoreHeroToSafePointAfterLoad { get; set; } = true; public bool RestoreHeroToDungeon { get; set; } = false;//TODO this way loading predefinied levels did not worked in Unity public bool RegenerateLevelsOnLoad { get; set; } = true; @@ -74,6 +79,10 @@ public class Mechanics : SettingsBase public bool AllowEnchantOnDragDrop { get; set; } = true; public bool AutoCollectLootOnEntering { get; set; } + + public bool ShowEnemiesLevelOnWorldPitEntries { get; set; } = true; + + public bool KeyIsRequiredToEnterBoosRoom { get; set; } = true; } public class Input : SettingsBase @@ -97,6 +106,7 @@ public GameControllingMode GameControllingMode public class View : SettingsBase { + public bool DynamicTerrainLoad { get; set; } = true; public bool HintsOn { get; set; } = true; public bool ShowShortcuts { get; set; } = true; public bool ShowMiniMap { get; set; } = true; @@ -108,6 +118,12 @@ public bool UseTouchInterface get { return useTouchInterface; } set { useTouchInterface = value; } } + + public bool AutoCloseEntityDescriptor { get; set; } = true; + public bool ShowDungeonKeyHint { get; set; } = true; + + //Darkness embraces hero as he gets hurt + public bool DarknessEmbracesHero { get; set; } = true; } public class Colors @@ -130,6 +146,8 @@ public UIScheme() public class LookAndFeel : SettingsBase, IPersistable { public UIScheme SelectedScheme { get; set; } = new UIScheme(); + + public bool ShowRecentEvents { get; set; } = true; } public class Options : SettingsBase, IPersistable diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Spells/GodSpells.cs b/Assets/Scripts/Common/Dlls/Roguelike/Spells/GodSpells.cs index fabb79b2..8bebe988 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Spells/GodSpells.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Spells/GodSpells.cs @@ -43,8 +43,36 @@ public DziewannaSpell(LivingEntity caller) : base(caller, SpellKind.Dziewanna, E RequiresDestPoint = true; UnsetProp(AbilityProperty.Duration); } + } + + public class JarowitSpell : PassiveSpell + { + public JarowitSpell() : this(new LivingEntity()) { } + + public JarowitSpell(LivingEntity caller) : base(caller, SpellKind.Jarowit, EntityStatKind.Unset) + { + CoolingDownCounter = 10; + StatKind = EntityStatKind.Unset; + manaCost += 5; + RequiresDestPoint = false; + //UnsetProp(AbilityProperty.Duration); + } + } + + public class WalesSpell : PassiveSpell + { + public WalesSpell() : this(new LivingEntity()) { } - + public WalesSpell(LivingEntity caller) : base(caller, SpellKind.Wales, EntityStatKind.Unset) + { + CoolingDownCounter = 10; + StatKind = EntityStatKind.Unset; + manaCost -= 5; + if (manaCost < 0) + manaCost = 5; + RequiresDestPoint = false; + //UnsetProp(AbilityProperty.Duration); + } } public class SwarogSpell : PassiveSpell diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Spells/OffensiveSpells.cs b/Assets/Scripts/Common/Dlls/Roguelike/Spells/OffensiveSpells.cs index 13bde160..9f11f685 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Spells/OffensiveSpells.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Spells/OffensiveSpells.cs @@ -122,8 +122,9 @@ public class SkeletonSpell : OffensiveSpell public Ally Ally { get => enemy; set => enemy = value; } public Ally AllyNextLevel { get => enemyNextLevel; set => enemyNextLevel = value; } - public const int SkeletonSpellStrengthIncrease = 2; - public const int SkeletonSpellDefenseIncrease = 4; + public const int SkeletonSpellStrengthIncrease = 4; + public const int SkeletonSpellDefenseIncrease = 6; + public const int SkeletonSpellHealthIncrease = 4; public SkeletonSpell() : this(new LivingEntity(), Difficulty.Normal) { diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Spells/Spell.cs b/Assets/Scripts/Common/Dlls/Roguelike/Spells/Spell.cs index 4b99f9e7..ff37d56f 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Spells/Spell.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Spells/Spell.cs @@ -13,7 +13,9 @@ namespace Roguelike.Spells { public enum SpellKind { - Unset = 0, FireBall = 1, CrackedStone = 2, Skeleton = 3, Trap = 4, IceBall = 5, PoisonBall = 6, Transform = 7, + Unset = 0, FireBall = 1, CrackedStone = 2, Skeleton = 3, + //Trap = 4, + IceBall = 5, PoisonBall = 6, Transform = 7, Frighten = 8, Healing = 9, ManaShield = 10, Telekinesis = 11, StonedBall = 12, LightingBall = 13 //,MindControl , Mana = 14, BushTrap = 15, @@ -22,11 +24,13 @@ public enum SpellKind ResistAll = 25, Inaccuracy = 29, /*CallMerchant, CallGod,*/ Identify = 30, Portal = 33, Dziewanna = 40, - Swarog = 41, + Swarog = 41,//Let there be dark! Swiatowit = 42, FireStone = 50, SwapPosition = 53, - Perun + Perun = 55, + Jarowit = 56, + Wales = 58 } public class Spell : ISpell diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Spells/SpellConverter.cs b/Assets/Scripts/Common/Dlls/Roguelike/Spells/SpellConverter.cs index b6d8a2e4..463901fd 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Spells/SpellConverter.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Spells/SpellConverter.cs @@ -61,8 +61,8 @@ public static EffectType EffectTypeFromSpellKind(SpellKind sk) break; case SpellKind.Skeleton: break; - case SpellKind.Trap: - break; + //case SpellKind.Trap: + // break; case SpellKind.IceBall: return EffectType.Frozen; diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Spells/SpellStateSet.cs b/Assets/Scripts/Common/Dlls/Roguelike/Spells/SpellStateSet.cs index 97658358..a6c72acb 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Spells/SpellStateSet.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Spells/SpellStateSet.cs @@ -1,4 +1,5 @@ using Dungeons.Core; +using Roguelike.Abilities; using Roguelike.Spells; using System; using System.Collections.Generic; @@ -8,7 +9,9 @@ namespace Roguelike.Spells { public class SpellStateSet { - Dictionary spellStates = new Dictionary(); + private Dictionary spellStates = new Dictionary(); + + public Dictionary SpellStates { get => spellStates; set => spellStates = value; } public SpellStateSet() { @@ -23,6 +26,13 @@ public SpellStateSet() } } + public static SpellStateSet CreateEmpty() + { + var sss = new SpellStateSet(); + sss.spellStates.Clear(); + return sss; + } + private int GetMaxLevel(SpellKind sp) { int max = 10; @@ -37,8 +47,8 @@ private int GetMaxLevel(SpellKind sp) break; case SpellKind.Skeleton: break; - case SpellKind.Trap: - break; + //case SpellKind.Trap: + // break; case SpellKind.IceBall: break; case SpellKind.PoisonBall: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/State/GameState.cs b/Assets/Scripts/Common/Dlls/Roguelike/State/GameState.cs index febb3750..e68b5d98 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/State/GameState.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/State/GameState.cs @@ -54,6 +54,8 @@ public virtual HeroPath CreateHeroPath() return new HeroPath(); } + public Dictionary KeyValuePairs = new Dictionary(); + public CoreInfo CoreInfo { get; set; } = new CoreInfo(); public HeroPath HeroPath { get; set; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Strategy/AttackStrategy.cs b/Assets/Scripts/Common/Dlls/Roguelike/Strategy/AttackStrategy.cs index acc69070..c7f9d7fd 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Strategy/AttackStrategy.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Strategy/AttackStrategy.cs @@ -19,6 +19,10 @@ using System.Drawing; using System.Linq; using System.Runtime.InteropServices; +using Roguelike.Extensions; + +using static Roguelike.Tiles.LivingEntities.LivingEntity; + namespace Roguelike { @@ -39,17 +43,15 @@ public class TilesAtPathProvider : ITilesAtPathProvider public class AttackStrategy { - GameContext context; public GameManager GameManager { get; set; } public Action OnPolicyApplied; - public AbstractGameLevel Node { get => context.CurrentNode; } + public AbstractGameLevel Node { get => GameManager.CurrentNode; } public ITilesAtPathProvider TilesAtPathProvider { get; set; } - public AttackStrategy(GameContext context, GameManager gm) + public AttackStrategy(GameManager gm) { - this.context = context; this.GameManager = gm; - TilesAtPathProvider = context.Container.GetInstance(); + TilesAtPathProvider = gm.Container.GetInstance(); } public bool AttackIfPossible(LivingEntity attacker, LivingEntity target) @@ -62,8 +64,7 @@ public bool AttackIfPossible(LivingEntity attacker, LivingEntity target) var enemyCasted = attacker as Enemy; if (enemyCasted != null) { - if(enemyCasted.RessurectOrderCooldown > 0) - enemyCasted.RessurectOrderCooldown--; + enemyCasted.DescreseAdvEnemySkillUseCount(EntityCommandKind.Resurrect);// RessurectOrderCooldown--; bool resistOn = TurnOnResistAll(enemyCasted, target); if (resistOn) @@ -80,6 +81,13 @@ public bool AttackIfPossible(LivingEntity attacker, LivingEntity target) else attacker.LastAttackWasProjectile = false; } + else if (attacker is INPC npc) + { + if (TryUseProjectileAttack(attacker, target)) + return true; + else + attacker.LastAttackWasProjectile = false; + } var victim = GetPhysicalAttackVictim(attacker, target); if (victim != null) @@ -109,26 +117,34 @@ public bool AttackIfPossible(LivingEntity attacker, LivingEntity target) private bool TryUseSpecialSpell(LivingEntity attacker, LivingEntity target) { - if (attacker is Enemy en && attacker.Name.Contains("Skeleton") && en.PowerKind != EnemyPowerKind.Plain) + if (attacker is Enemy en) { - var ressurectTargets = GameManager.GetRessurectTargets(en); - if (ressurectTargets.Any() && en.RessurectOrderCooldown == 0 && en.RessurectOrdersCounter < 3) + var cmd = EntityCommandKind.Resurrect; + if (en.CanUseCommand(EntityCommandKind.Resurrect)) { - if (ressurectTargets.Count > 1 || en.Stats.HealthBelow(0.5f)) + var ressurectTargets = GameManager.GetRessurectTargets(en); + if (ressurectTargets.Any() && en.GetAdvEnemySkillCooldown(cmd) == 0 && en.GetAdvEnemySkillUseCount(cmd) < 3) { - en.SendCommand(EntityCommandKind.RaiseMyFriends); - return true; + if (ressurectTargets.Count > 1 || en.Stats.HealthBelow(0.5f)) + { + return SendCommand(en, EntityCommandKind.Resurrect, GameManager); + } } } + + if (en.CanUseCommand(EntityCommandKind.MakeFakeClones)) + { + return SendCommand(en, EntityCommandKind.MakeFakeClones, GameManager); + } } return false; } - bool IsClearPath(LivingEntity attacker, LivingEntity target) + public bool IsClearPath(LivingEntity attacker, LivingEntity target) { var isClearPath = false; //is target next to attacker - isClearPath = context.CurrentNode.GetNeighborTiles(attacker, true).Contains(target); + isClearPath = GameManager.CurrentNode.GetNeighborTiles(attacker, true).Contains(target); if (!isClearPath) { if (TilesAtPathProvider != null) @@ -144,10 +160,12 @@ bool IsClearPath(LivingEntity attacker, LivingEntity target) } else { - var pathToTarget = FindPathForEnemy(attacker, target, 1, true); + var forEnemyProjectile = true; + var pathToTarget = FindPathForEnemy(attacker, target, 1, forEnemyProjectile); if (pathToTarget != null) { - var obstacles = pathToTarget.Where(i => context.CurrentNode.GetTile(new System.Drawing.Point(i.Y, i.X)) is Dungeons.Tiles.Abstract.IObstacle).ToList(); + var obstacles = pathToTarget.Where(i => GameManager.CurrentNode.GetTile(new System.Drawing.Point(i.Y, i.X)) + is Dungeons.Tiles.Abstract.IObstacle).ToList(); if (!obstacles.Any()) { isClearPath = true; @@ -171,7 +189,7 @@ private bool UseMagicAttack(LivingEntity attacker, LivingEntity target) var useMagic = IsClearPath(attacker, target); if (useMagic) { - var applied = GameManager.SpellManager.ApplyAttackPolicy(attacker, target, attacker.ActiveManaPoweredSpellSource, null, (p) => { OnPolicyApplied(p); }); + var applied = GameManager.SpellManager.ApplyAttackPolicy(attacker, target, attacker.SelectedManaPoweredSpellSource, null, (p) => { OnPolicyApplied(p); }); return applied == ApplyAttackPolicyResult.OK; } @@ -337,8 +355,8 @@ private bool TryUseProjectileAttack(LivingEntity attacker, LivingEntity target) if (skip) return false; - var enemy = attacker as Enemy; - var fi = enemy.ActiveFightItem; + var enemy = attacker;// as Enemy; + var fi = enemy.SelectedFightItem; if (fi != null && fi.Count > 0) { var pfi = fi as ProjectileFightItem; @@ -368,32 +386,115 @@ private bool TryUseProjectileAttack(LivingEntity attacker, LivingEntity target) return false; } + bool TryUseMagicAttack(LivingEntity enemy, LivingEntity victim) { var en = enemy as Enemy; - if (enemy.ActiveScrollCoolDownCounter > 0) + if (enemy.SelectedScrollCoolDownCounter > 0) { bool decreaseCoolDown = RandHelper.Random.NextDouble() > .3f; if (en.PowerKind == EnemyPowerKind.Boss) decreaseCoolDown = RandHelper.Random.NextDouble() > .5f; if (decreaseCoolDown) - enemy.ActiveScrollCoolDownCounter--; + enemy.SelectedScrollCoolDownCounter--; } - if (enemy.ActiveManaPoweredSpellSource != null && enemy.ActiveScrollCoolDownCounter == 0) + + if (enemy.SelectedScrollCoolDownCounter == 0) { - if (UseMagicAttack(enemy, victim)) + SpellSource ss = enemy.SelectedManaPoweredSpellSource; + if (enemy.CanUseCommand(EntityCommandKind.SenseVictimWeakResist)) { - enemy.ActiveScrollCoolDownCounter = GetCoolDown(enemy as Enemy); - return true; + return SendCommand(en, EntityCommandKind.SenseVictimWeakResist, GameManager); } + + if (enemy.SelectedManaPoweredSpellSource != null && enemy.SelectedScrollCoolDownCounter == 0) + { + if (RandHelper.Random.NextDouble() > .65f) + { + if (UseMagicAttack(enemy, victim)) + { + enemy.SelectedScrollCoolDownCounter = GetCoolDown(enemy as Enemy); + return true; + } + } + } + } return false; } + static bool SetBestSpellSource(LivingEntity attacker, LivingEntity victim) + { + if (attacker.HasSpecialSkill(EntityCommandKind.SenseVictimWeakResist)) + { + var weakStat = SenseWeakStats(victim); + var scroll = new Scroll(weakStat.First().GetSpellKind()); + attacker.SelectedManaPoweredSpellSource = scroll; + attacker.SelectedScrollCoolDownCounter = 0; + return true; + } + + return false; + } + + /// + /// TODO Static as used also by ut + /// + /// + /// + /// + /// + public static bool SendCommand(LivingEntity attacker, EntityCommandKind commandKind, GameManager gm) + { + var res = false; + string specialInfo = ""; + if (commandKind == EntityCommandKind.Resurrect) + { + var en = attacker as Enemy; + en.SetAdvEnemySkillCooldown(EntityCommandKind.Resurrect, 10); + en.IncreaseAdvEnemySkillUseCount(EntityCommandKind.Resurrect); + gm.DoCommand(en, en.GetCommand(EntityCommandKind.Resurrect)); + res = true; + //soundToUse = "raise_my_friends"; + } + else if (commandKind == EntityCommandKind.MakeFakeClones) + { + gm.MakeFakeClones(attacker, gm.Hero, true); + res = true; + //soundToUse = "FallenOneSurround"; + } + else if (commandKind == EntityCommandKind.SenseVictimWeakResist) + { + res = SetBestSpellSource(attacker, gm.Hero); + specialInfo = attacker.Name + " has sensed your weak points"; + } + + var cmd = attacker.GetCommand(commandKind); + + var ea = new LivingEntityAction(); + ea.AttackerEntity = attacker; + ea.InvolvedEntity = gm.Hero; + ea.CommandKind = commandKind; + ea.Kind = LivingEntityActionKind.SendCommand; + ea.Info = specialInfo.Any() ? specialInfo : cmd.Info; + ea.Cmd = cmd; + + + gm.EventsManager.AppendAction(ea); + cmd.IncreaseUseCount(); + if (cmd.Sound.Any()) + { + var sm = gm.SoundManager; + if (sm != null) + sm.PlayVoice(cmd.Sound); + } + + return true; + } public List FindPathForEnemy(LivingEntity enemy, LivingEntity target, int startIndex = 0, bool forEnemyProjectile = false) { - var pathToTarget = context.CurrentNode.FindPath(enemy.point, target.point, false, true, forEnemyProjectile, enemy); + var pathToTarget = GameManager.CurrentNode.FindPath(enemy.point, target.point, false, forEnemyProjectile); if (pathToTarget != null && pathToTarget.Any()) { return pathToTarget.GetRange(startIndex, pathToTarget.Count - 1); @@ -458,6 +559,19 @@ private bool CanAttackTarget(List neibs, LivingEntity enemy) { return neibs.Any(i => i == enemy); } + + static List SenseWeakStats(LivingEntity victim) + { + var res = new List(); + var resists = new EntityStatKind [] { EntityStatKind.ResistCold, EntityStatKind.ResistPoison, EntityStatKind.ResistFire }; + + resists.ToList().ForEach(i=> victim.Stats.Ensure(i)); + + var list = victim.Stats.Stats.Where(i => resists.Contains(i.Key)).OrderBy(i => victim.GetCurrentValue(i.Key)).ToList(); + var lowestResist = list.First(); + res.Add(lowestResist.Key); + return res; + } } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/TileContainers/AbstractGameLevel.cs b/Assets/Scripts/Common/Dlls/Roguelike/TileContainers/AbstractGameLevel.cs index 42407092..9a94a1ff 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/TileContainers/AbstractGameLevel.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/TileContainers/AbstractGameLevel.cs @@ -1,4 +1,5 @@ -using Dungeons; +#define DEBUG_APP +using Dungeons; using Dungeons.Core; using Dungeons.Tiles; using Newtonsoft.Json; @@ -18,6 +19,8 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +#pragma warning disable 8632 + namespace Roguelike.TileContainers { @@ -109,6 +112,21 @@ public List GetActiveAllies() return GetTiles().Where(i => i is IAlly ally && ally.Active).Cast().ToList(); } + public List GetNpcs() + { + return GetTiles().Where(i => i is INPC).Cast().ToList(); + } + + public INPC GetNPCByName(string name) + { + return GetTiles().Where(i => i.Name == name).SingleOrDefault(); + } + + public INPC GetNPCByTag(string tag) + { + return GetTiles().Where(i => i.LivingEntity.tag1 == tag).SingleOrDefault(); + } + public override List GetTiles() { var res = base.GetTiles(); @@ -311,15 +329,42 @@ public List AddSmoke(LivingEntity abilityUser, int range, i return smokes; } + protected virtual bool ShallReportOverride() + { + return false; + } + Tile tileBeingSet = null; public override bool SetTile ( Tile tile, Point point, bool resetOldTile = true, bool revealReseted = true, bool autoSetTileDungeonIndex = true, bool reportError = true ) { +#if DEBUG_APP + var tileAt = GetTile(point); + if (tileAt != null && tileAt != tile && tileAt is LivingEntity leAt && tileBeingSet != tileAt) + { + if (ShallReportOverride()) + { + Logger.LogError("overriding " + leAt + " at " + point + " with " + tile); + return false; + } + } +#endif //Logger.LogInfo("Adding tile "+ tile + " at "+ point); - + //if (tile is Hero) + //{ + // int k = 0; + // k++; + // Debug.WriteLine("hero set at: "+ point); + //} + //if (tile is Privy) + //{ + // int k = 0; + // k++; + // Debug.WriteLine("Privy set at: " + point); + //} if (tile is IApproachableByHero) { var abh = tile as IApproachableByHero; @@ -327,7 +372,7 @@ public override bool SetTile ApproachableByHero.Add(abh); } - if (tile is ILootSource) + else if (tile is ILootSource) { var ls = tile as ILootSource; if (ls.Level <= 0) @@ -337,7 +382,7 @@ public override bool SetTile } } - if (tile is Hero) + else if (tile is Hero) { var tileAtPoint = GetTile(point); if (tileAtPoint == tile) @@ -346,14 +391,20 @@ public override bool SetTile { DebugHelper.Assert(false); } + + //if (tileAtPoint is Privy) + //{ + // Debug.WriteLine("Privy is at: " + point); + //} } else if (tile is Loot loot) { - if (Loot.ContainsKey(point)) + if (Loot.ContainsKey(point) && loot.Id != Loot[point].Id) { + var err = "loot already at point: " + Loot[point] + ", trying to add: " + tile + " point:" + point; if (Logger != null) - Logger.LogError("loot already at point: " + Loot[point] + ", trying to add: " + tile + " point:" + point); - DebugHelper.Assert(false); + Logger.LogError(err); + DebugHelper.Assert(false, err); return false; } //Logger.LogInfo("Adding Loot "+ tile + " at "+ point + " Loot.Count:"+ Loot.Count); @@ -368,22 +419,22 @@ public override bool SetTile else if (tile is Surface sur) { - var surfAtPoint = SurfaceSets.GetKind(sur.Kind); - if (surfAtPoint.Tiles.ContainsKey(point)) - { - //var alreadyAtPoint = surfAtPoint.Tiles[point]; - //if (alreadyAtPoint != sur.Kind && - // (!alreadyAtPoint.ToString().Contains("Water") || !sur.ToString().Contains("Water"))//Both water are fine - // ) - //{ - // if (Logger != null) - // { - // Logger.LogError("Surface already at point: " + Surfaces[point] + ", trying to add: " + tile + " point:" + point); - // DebugHelper.Assert(false); - // return false; - // } - //} - } + //var surfAtPoint = SurfaceSets.GetKind(sur.Kind); + //if (surfAtPoint.Tiles.ContainsKey(point)) + //{ + // //var alreadyAtPoint = surfAtPoint.Tiles[point]; + // //if (alreadyAtPoint != sur.Kind && + // // (!alreadyAtPoint.ToString().Contains("Water") || !sur.ToString().Contains("Water"))//Both water are fine + // // ) + // //{ + // // if (Logger != null) + // // { + // // Logger.LogError("Surface already at point: " + Surfaces[point] + ", trying to add: " + tile + " point:" + point); + // // DebugHelper.Assert(false); + // // return false; + // // } + // //} + //} //Logger.LogInfo("Adding Loot "+ tile + " at "+ point + " Loot.Count:"+ Loot.Count); tile.point = point; SurfaceSets.SetAt(point, sur); @@ -393,10 +444,18 @@ public override bool SetTile } Point? prevPos = tile?.point; + tileBeingSet = tile; var res = base.SetTile(tile, point, resetOldTile, revealReseted, autoSetTileDungeonIndex, reportError); - if (res && tile is LivingEntity && prevPos != null) + tileBeingSet = null; + if (tile is LivingEntity) + { + int k = 0; + var t1 = GetTile(point); + k++; + } + if (res && prevPos != null && tile is LivingEntity le) { - (tile as LivingEntity).PrevPoint = prevPos.Value; + le.PrevPoint = prevPos.Value; } return res; } @@ -450,7 +509,7 @@ public virtual List GeneratorNodes } } - public virtual void OnHeroPlaced(Hero hero) + public virtual void OnHeroPlaced(Hero hero, GameContextSwitchKind context) { } @@ -567,9 +626,9 @@ public Tiles.Loot GetLootTile(Point point) Point from, Point end, bool forHeroAlly, - bool canGoOverCrackedStone, + //bool canGoOverCrackedStone, bool forEnemyProjectile, - LivingEntity movingEntity, + LivingEntity? movingEntity, Dictionary excludedFromPathFind ) { @@ -585,7 +644,7 @@ Dictionary excludedFromPathFind var currPt = new Point(col, row); var tile = Tiles[row, col]; if(currPt == end) - tile = GetTile(currPt);//otherwise hero is not moving when clicking on loot in dungeon + continue;// tile = GetTile(currPt);//otherwise hero is not moving when clicking on loot in dungeon if (tile is Hero) { if (forHeroAlly) @@ -605,13 +664,7 @@ Dictionary excludedFromPathFind } else if (tile is Dungeons.Tiles.Abstract.IObstacle) { - if (forHeroAlly && tile is LivingEntity) - { - int k = 0; - k++; - } - else - value = 0;//0 + value = 0;//0 } else if (tile is Wall) value = 0;//0 @@ -671,8 +724,16 @@ public Tile ReplaceTile(Tile replacer, Point point) Dictionary excludedFromPathFind; - public List FindPath(Point from, Point endPoint, bool forHeroAlly, bool canGoOverCrackedStone, - bool forEnemyProjectile, LivingEntity movingEntity) + public List FindPath(LivingEntity entity, Point target)//, bool forHeroAlly) + { + bool forHeroAlly = entity is IAlly; + var trapAvoider = (forHeroAlly || entity is Hero) ? entity : null; + var forEnemyProjectile = false; + return FindPath(entity.point, target, forHeroAlly, forEnemyProjectile, trapAvoider); + } + + public List FindPath(Point from, Point endPoint, bool forHeroAlly, + bool forEnemyProjectile = false, LivingEntity movingEntity = null) { //Commons.TimeTracker tr = new Commons.TimeTracker(); if (excludedFromPathFind == null) @@ -684,7 +745,7 @@ public Tile ReplaceTile(Tile replacer, Point point) } } var startPoint = new Algorithms.Point(from.Y, from.X); - var findPathMatrix = InitMatrixBeforePathSearch(from, endPoint, forHeroAlly, canGoOverCrackedStone, forEnemyProjectile, + var findPathMatrix = InitMatrixBeforePathSearch(from, endPoint, forHeroAlly, forEnemyProjectile, movingEntity, excludedFromPathFind); var mPathFinder = new Algorithms.PathFinder(findPathMatrix); @@ -755,7 +816,9 @@ public void EnsureRevealed(int nodeIndex) public Tile GetHeroStartTile() { Tile heroStartTile; - var empOnes = GetEmptyTiles(nodeIndexMustMatch: false).Where(i => i.DungeonNodeIndex > Dungeons.TileContainers.DungeonNode.ChildIslandNodeIndex); + var empOnes = GetEmptyTiles(nodeIndexMustMatch: false) + .Where(i => i.DungeonNodeIndex > Dungeons.TileContainers.DungeonNode.ChildIslandNodeIndex) + .ToList(); var secret = Nodes.Where(i => i.Secret).FirstOrDefault(); if (secret != null) { @@ -773,27 +836,53 @@ public Tile GetHeroStartTile() public void ClearOldHeroPosition(GameContextSwitchKind context) { var heros = GetTiles(); - var heroInNode = heros.SingleOrDefault(); - - //if (heroInNode == null && context == GameContextSwitchKind.DungeonSwitched) - // Logger.LogError("SwitchTo heros.Count = " + heros.Count); + if (heros.Count > 1) + { + int k = 0; + k++; + } + //var heroInNode = heros.SingleOrDefault(); TODO!!! + foreach (var heroInNode in heros) + { + //if (heroInNode == null && context == GameContextSwitchKind.DungeonSwitched) + // Logger.LogError("SwitchTo heros.Count = " + heros.Count); - if (heroInNode != null) - SetEmptyTile(heroInNode.point);//Hero is going to be placed in the node, remove it from the old one (CurrentNode) + if (heroInNode != null) + SetEmptyTile(heroInNode.point);//Hero is going to be placed in the node, remove it from the old one (CurrentNode) + } } public bool PlaceHeroAtTile(GameContextSwitchKind context, Hero hero, Tile tile) { ClearOldHeroPosition(context); - if (SetTile(hero, tile.point, false)) + var pt = tile != null ? tile.point : hero.point; + if (tile is LivingEntity) + { + //ups + pt = GetClosestEmpty(tile).point; + } + var currentAt = GetTile(pt); + if (currentAt is Privy) { - hero.DungeonNodeIndex = tile.DungeonNodeIndex; - Logger.LogInfo("PlaceHeroAtTile ok" + tile.point+ " hero: " + GetTiles().FirstOrDefault() + " this: "+this); + var emp = GetClosestEmpties(pt).FirstOrDefault(); + if(emp!=null) + pt = emp.point; + } + if (hero.point == pt) + { + if(GetTile(pt) == hero) + return true; + } + if (SetTile(hero, pt, false)) + { + if(tile !=null) + hero.DungeonNodeIndex = tile.DungeonNodeIndex; + Logger.LogInfo("PlaceHeroAtTile ok" + pt + " hero: " + GetTiles().FirstOrDefault() + " this: "+this); return true; } else { - Logger.LogError("PlaceHeroAtTile failed at: "+tile.point); + Logger.LogError("PlaceHeroAtTile failed at: "+ pt + " tile:" + tile); DebugHelper.Assert(false); } return false; @@ -808,7 +897,7 @@ public HeroPlacementResult PlaceHeroNextToTile(GameContextSwitchKind context, He { if (baseTile != null) { - var emptyOne = GetClosestEmpty(baseTile); //GetEmptyNeighborhoodTiles(baseTile); + var emptyOne = GetClosestEmpty(baseTile); if (emptyOne != null) heroStartTile = emptyOne; else @@ -900,12 +989,12 @@ public override bool IsTileEmpty(Tile tile, EmptyCheckContext emptyCheckContext) return emp; } - public virtual Roguelike.Tiles.Interactive.InteractiveTile GetCamp() + public virtual Tile GetCampCentralPlace() { return null; } - public virtual Dungeons.Tiles.Tile GetEmptyNextToCamp() + public virtual Dungeons.Tiles.Tile GetEmptyNextToPortal() { return null; } @@ -941,7 +1030,8 @@ public void GenerateOilSpread(int oilSpreadsCount, List emp for (int attempt = 0; attempt < 10; attempt++) { var randTile = gl.GetRandomEmptyTile(emptyOnes); - if (gl.GetEmptyNeighborhoodTiles(randTile).Count > 1) + var neib = gl.GetEmptyNeighborhoodTiles(randTile); + if (neib!=null && neib.Count > 1) { gl.SpreadOil(randTile, emptyTilesToUse: emptyOnes, reveal: RevealOilOnGeneration()); break; @@ -958,5 +1048,19 @@ public void RevealAllNodes() } ); } + + public virtual KeyPuzzle GetCurrentNodeKeyPuzzle() + { + var door = this.GetTiles().Where(i => i.KeyPuzzle != KeyPuzzle.Unset).FirstOrDefault(); + if (door != null) + return door.KeyPuzzle; + + return KeyPuzzle.Unset; + } + + public virtual Roguelike.Tiles.Interactive.InteractiveTile GetCampPortal() + { + return null; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/TileContainers/GameLevel.cs b/Assets/Scripts/Common/Dlls/Roguelike/TileContainers/GameLevel.cs index 5dc20984..d33ea6d6 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/TileContainers/GameLevel.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/TileContainers/GameLevel.cs @@ -9,6 +9,7 @@ using Roguelike.Serialization; using Roguelike.Tiles.Interactive; using Roguelike.Tiles.LivingEntities; +using Roguelike.Tiles.Looting; using SimpleInjector; using System; using System.Collections.Generic; @@ -21,10 +22,14 @@ public class GameLevel : AbstractGameLevel, IPersistable { public Stairs StairsUp { get => stairsUp; set => stairsUp = value; } - public Stairs StairsDown { get => stairsDown; set => stairsDown = value; } + public Stairs StairsDown + { + get; + set; + } Stairs stairsUp = null; - Stairs stairsDown = null; + public event EventHandler NodeRevealed; public LeverSet BossRoomLeverSet { @@ -67,7 +72,7 @@ public override bool BossLeversSolved() return BossRoomLeverSet.IsOpened(); } - public override void OnHeroPlaced(Hero hero) + public override void OnHeroPlaced(Hero hero, GameContextSwitchKind context) { try { diff --git a/Assets/Scripts/Common/Dlls/Roguelike/TileParts/LootExtendedInfo.cs b/Assets/Scripts/Common/Dlls/Roguelike/TileParts/LootExtendedInfo.cs index 43426cad..1fe7ac8f 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/TileParts/LootExtendedInfo.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/TileParts/LootExtendedInfo.cs @@ -49,13 +49,5 @@ public override string ToString() } return res; } - - //public object Clone() - //{ - // var clone = this.MemberwiseClone() as LootExtendedInfo; - // clone.Stats = this.Stats.Clone() as EntityStats; - // return clone; - //} - } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Abstract/IAdvancedEntity.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Abstract/IAdvancedEntity.cs index afa875c8..38d87c7a 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Abstract/IAdvancedEntity.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Abstract/IAdvancedEntity.cs @@ -1,5 +1,6 @@ using Roguelike.Abilities; using Roguelike.Abstract.Inventory; +using Roguelike.LootContainers; using Roguelike.Spells; using Roguelike.Tiles.Looting; using System; @@ -16,6 +17,9 @@ public interface IAdvancedEntity : IInventoryOwner int AbilityPoints { get; set; } AbilitiesSet Abilities { get; } SpellStateSet Spells { get; } + bool IsMecenary { get; } + bool IsMercenary { get; } + bool IncreaseAbility(AbilityKind kind); bool IncreaseSpell(SpellKind sk); @@ -24,6 +28,8 @@ public interface IAdvancedEntity : IInventoryOwner PassiveAbility GetPassiveAbility(AbilityKind kind); ActiveAbility GetActiveAbility(AbilityKind kind); string GetExpInfo(); + bool InventoryAcceptsItem(Inventory inventory, Loot loot, AddItemArg addItemArg); + bool MoveEquipmentCurrent2Inv(IEquipment eq, CurrentEquipmentKind cek); event EventHandler StatsRecalculated; diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Abstract/IAlly.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Abstract/IAlly.cs index b4773299..1787bde6 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Abstract/IAlly.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Abstract/IAlly.cs @@ -14,6 +14,7 @@ public interface IAlly bool SetLevel(int level, Difficulty? diff = null); bool TakeLevelFromCaster { get; } string Name { get; } + bool PendingReturnToCamp { get; set; } event EventHandler LeveledUp; diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Barrel.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Barrel.cs index 0b386e10..728bb7ce 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Barrel.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Barrel.cs @@ -32,12 +32,18 @@ public BarrelKind BarrelKind { barrelKind = value; SetSound(); - if(barrelKind == BarrelKind.PileOfSkulls) + if (barrelKind == BarrelKind.PileOfSkulls) tag1 = "pile_skulls"; else if (barrelKind == BarrelKind.OilBarrel) tag1 = "barrel_oil"; else if (barrelKind == BarrelKind.Barrel) + { + if (tag1 == "barrel_special_pcim_pond") + { + return;//deserialization was braking the tag + } tag1 = "barrel1"; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Bonfire.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Bonfire.cs new file mode 100644 index 00000000..8ba9c7ea --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Bonfire.cs @@ -0,0 +1,29 @@ +using Roguelike.Tiles.LivingEntities; +using SimpleInjector; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Roguelike.Tiles.Interactive +{ + public class Bonfire : InteractiveTile + { + public Bonfire(Container cont) : base(cont, ',') + { +#if ASCII_BUILD + color = ConsoleColor.Blue; +#endif + DestPointActivityKind = DestPointActivityKind.Grill; + Kind = InteractiveTileKind.Bonfire; + tag1 = "bonfire"; + InteractSound = "punch"; + } + + internal override bool InteractWith(LivingEntity le) + { + SetBusy(le); + return true; + } + + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Bonfire.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Bonfire.cs.meta new file mode 100644 index 00000000..dbc6880e --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Bonfire.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1ffd6aeb142ab62448f75b81e3898db7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Chest.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Chest.cs index f9582093..b7cf98d5 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Chest.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Chest.cs @@ -70,6 +70,8 @@ public ChestKind ChestKind void SetTag1BasedOnKind() { + if (ChestVisualKind == ChestVisualKind.Grave) + return; tag1 = "chest_plain1"; if (ChestKind == ChestKind.Gold) tag1 = "chest_gold"; @@ -165,5 +167,10 @@ public LootSourceKind LootSourceKind public int Durability { get; set; } = 3; public bool Destroyed { get; set; } + + public override string ToString() + { + return base.ToString() + ", ChestKind: " + ChestKind + ", ChestVisualKind: " + ChestVisualKind; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/InteractiveTile.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/InteractiveTile.cs index ebc2e1fe..230fb08e 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/InteractiveTile.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/InteractiveTile.cs @@ -4,6 +4,7 @@ using Roguelike.Events; using Roguelike.Managers; using Roguelike.Tiles.Abstract; +using Roguelike.Tiles.LivingEntities; using SimpleInjector; using System; using System.Linq; @@ -13,7 +14,7 @@ namespace Roguelike.Tiles.Interactive public enum InteractiveTileKind { Unset, Stairs, Doors, Barrel, TreasureChest, - Trap, Lever, DeadBody, TorchSlot, Candle, CrackedStone + Trap, Lever, DeadBody, TorchSlot, Candle, CrackedStone, Bonfire, Privy, Ladder, Other } public interface IApproachableByHero @@ -50,6 +51,22 @@ public InteractiveTile(Container cont, char symbol) : base(symbol) this.container = cont; } + LivingEntity busyBy; + [JsonIgnore] + public bool Busy { get { return busyBy != null; } } + + [JsonIgnore] + public bool IsNpcDestMoveTarget { get { return DestPointActivityKind != DestPointActivityKind.Unset; } } + public DestPointActivityKind DestPointActivityKind { get; set; } + + public void SetBusy(LivingEntity le) + { + if (le != null) + this.busyBy = le; + else + this.busyBy = null; + } + public override void PlayHitSound(IProjectile proj) { PlaySound(proj.HitSound); @@ -103,10 +120,6 @@ public InteractiveTileKind Kind set { _kind = value; - if (_kind == InteractiveTileKind.TreasureChest) - { - - } } } public bool CanBeHitBySpell() @@ -116,14 +129,22 @@ public bool CanBeHitBySpell() public override string ToString() { - var res = base.ToString(); - res += ", " + Kind + " Lvl:" + Level; + var res = " L:" + Level + " " + base.ToString(); + res += " " + Kind; return res; } + internal virtual bool InteractWith(LivingEntity npc) + { + return false; + } + /// /// Was hero ever close to the tile? /// public bool ApproachedByHero { get; set; } + public bool HidesInteractedEntity { get; set; } + [JsonIgnore] + public LivingEntity BusyBy { get => busyBy; set => busyBy = value; } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Portal.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Portal.cs index b1b79dc9..12ee6478 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Portal.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Portal.cs @@ -53,6 +53,12 @@ public string[] GetFeatures(bool w) SpellKind ISpell.Kind => SpellKind.Portal; + public bool SendByGod + { + get ; + set ; + } + public SpellStatsDescription CreateSpellStatsDescription(bool currentLevel) { return new SpellStatsDescription(1, ManaCost, 10, ((ISpell)this).Kind, 0); diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Privy.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Privy.cs new file mode 100644 index 00000000..13c37fa8 --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Privy.cs @@ -0,0 +1,31 @@ +using Roguelike.Tiles.LivingEntities; +using SimpleInjector; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Roguelike.Tiles.Interactive +{ + public class Privy : InteractiveTile + { + public Privy(Container cont) : base(cont, ',') + { +#if ASCII_BUILD + color = ConsoleColor.Blue; +#endif + tag1 = "privy"; + Kind = InteractiveTileKind.Privy; + HidesInteractedEntity = true; + DestPointActivityKind = DestPointActivityKind.Privy; + } + + internal override bool InteractWith(LivingEntity le)//typically npc + { + if (Busy) + return false; + SetBusy(le); + return true; + } + + } +} diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Privy.cs.meta b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Privy.cs.meta new file mode 100644 index 00000000..10f1d79a --- /dev/null +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Privy.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1f789d6bb579fb43adafdd36f46dd7a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Stairs.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Stairs.cs index 1d338b6c..81cfc8f7 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Stairs.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Interactive/Stairs.cs @@ -1,6 +1,5 @@ #define ASCII_BUILD using Dungeons; -using Newtonsoft.Json; using SimpleInjector; using System; using System.Diagnostics; @@ -25,6 +24,8 @@ public string PitName } } + public int PitStartEnemiesLevel { get; set; } + public StairsKind StairsKind { get => kind; @@ -60,11 +61,11 @@ public StairsKind StairsKind } } } - //[JsonConstructor] + public Stairs(Container cont) : this(cont, StairsKind.LevelDown) { } - + public Stairs(Container cont, StairsKind kind) : base(cont, '>') { #if ASCII_BUILD @@ -96,7 +97,9 @@ public string GetPlaceName() public override string ToString() { - return base.ToString() + ", pit: "+PitName; + var res = "EL: "+ PitStartEnemiesLevel+ " "+ base.ToString() + ", pit: "+PitName; + + return res; } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/AdvancedLivingEntity.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/AdvancedLivingEntity.cs index bcdd85d9..1eda8658 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/AdvancedLivingEntity.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/AdvancedLivingEntity.cs @@ -25,11 +25,13 @@ namespace Roguelike.Tiles.LivingEntities { public enum AllyKind { Unset, Hound, Enemy, Merchant, Paladin } - public enum EntityProffesionKind { Unset, King, Prince, Knight, Priest, Mercenary, Merchant, Peasant, Bandit, Adventurer, Slave } + public enum EntityProffesionKind { Unset, King, Prince, Knight, Priest, Mercenary, Merchant, Peasant, Bandit, + Adventurer, Slave, TeutonicKnight, Smith , Woodcutter, Carpenter, Warrior + } public enum EntityGender { Unset, Male, Female } public enum RelationToHeroKind { Unset, Neutral, Friendly, Antagonistic, Hostile }; public enum EntityKind { Unset, Human, Animal, Undead, Daemon } - public enum AnimalKind { Unset, Hound, Pig, Hen, Rooster, Deer } + public enum AnimalKind { Unset, Hound, Pig, Hen, Rooster, Deer, Horse } public class RelationToHero { @@ -46,14 +48,14 @@ public Discussion Discussion get => discussion; set => discussion = value; } - public EntityProffesionKind Proffesion { get; set; } + public event EventHandler ExpChanged; - public event EventHandler UrgentTopicChanged; public event EventHandler StatsRecalculated; public event EventHandler LeveledUp; protected CurrentEquipment currentEquipment; protected Inventory inventory = null; - + //public BusyDestPointKind BusyDestPointKind { get; set; } + public virtual Inventory Inventory { get => inventory; @@ -63,7 +65,11 @@ public virtual Inventory Inventory inventory.Owner = this; } } - public ProjectileFightItem ActiveProjectileFightItem => ActiveFightItem as ProjectileFightItem; + public DestPointDesc DestPointDesc { get; + set; + } = new DestPointDesc(); + + public ProjectileFightItem SelectedProjectileFightItem => SelectedFightItem as ProjectileFightItem; //[JsonIgnoreAttribute] public CurrentEquipment CurrentEquipment @@ -116,6 +122,11 @@ public int LevelUpPoints } } + public bool HasAbilityActivated(ActiveAbility ab) + { + return SelectedActiveAbility != null && SelectedActiveAbility.Kind == ab.Kind; + } + public bool IncreaseSpell(SpellKind sk) { var state = this.Spells.GetState(sk); @@ -129,7 +140,7 @@ public bool IncreaseSpell(SpellKind sk) return false; } - public bool InventoryAcceptsItem(Inventory inv, Loot loot, AddItemArg addItemArg) + public virtual bool InventoryAcceptsItem(Inventory inv, Loot loot, AddItemArg addItemArg) { if (inv is CurrentEquipment) { @@ -209,7 +220,7 @@ public LootAbility GetLootAbility() return GetPassiveAbility(AbilityKind.LootingMastering) as LootAbility; } - public PassiveAbility GetPassiveAbility(AbilityKind kind) + public override PassiveAbility GetPassiveAbility(AbilityKind kind) { return Abilities.PassiveItems.Where(i => i.Kind == kind).SingleOrDefault(); } @@ -227,11 +238,7 @@ public int GetPrice(Loot loot) return price; } - //public new static AdvancedLivingEntity CreateDummy() - //{ - // return new AdvancedLivingEntity(new Point(0, 0), '\0'); - //} - public double CalcExpScale() + public double CalcExperienceScale() { var currExp = Experience - PrevLevelExperience; var scale = currExp / (NextLevelExperience - PrevLevelExperience); @@ -245,15 +252,13 @@ public bool IncreaseExp(double factor) bool thresholdReached = Experience >= NextLevelExperience; if (thresholdReached && canAdvanceInExp) { - PrevLevelExperience = NextLevelExperience; - NextLevelExperience = Calculated.FactorCalculator.AddFactor((int)NextLevelExperience, 110); - Level++; - if (Level == 2) - { - NextLevelExperience = NextLevelExperience * 1.3f; - } - LevelUpPoints += GenerationInfo.LevelUpPoints; - AbilityPoints += 3; + var calc = new NextLevelCalculator(this); + Level = calc.GetNextLevel(Level); + PrevLevelExperience = calc.PrevLevelExperience; + NextLevelExperience = calc.NextLevelExperience; + + LevelUpPoints += calc.LevelUpPoints; + AbilityPoints += GenerationInfo.AbilityPointLevelUpIncrease; //if (Level == 2 || Level == 3) // NextLevelExperience *= 1.5f; @@ -282,22 +287,29 @@ public void SetSpellCoolingDown(SpellKind kind) return; } - spell.CoolDownCounter = ActiveManaPoweredSpellSource.CreateSpell(this).CoolingDownCounter; + spell.CoolDownCounter = SelectedManaPoweredSpellSource.CreateSpell(this).CoolingDownCounter; } public override bool Consume(IConsumable consumable) { + StackedLoot stacked = null; if (inventory.Contains(consumable.Loot)) + stacked = consumable.Loot as StackedLoot; + + if (base.Consume(consumable)) { - var stacked = consumable.Loot as StackedLoot; - var rem = inventory.Remove(stacked); - if (rem == null) + if (stacked != null) { - Assert(false); - return false; + var rem = inventory.Remove(stacked); + if (rem == null) + { + Assert(false); + return false; + } } + return true; } - return base.Consume(consumable); + return false; } public void IncreaseStatByLevelUpPoint(EntityStatKind stat) @@ -425,7 +437,7 @@ virtual protected bool CanUseAnimalKindEq(IEquipment eq) return false; } - public virtual bool CanUseEquipment(IEquipment eq, bool autoPutoOn) + public override bool CanUseEquipment(IEquipment eq, bool autoPutoOn) { Func report = (string message) => { @@ -451,7 +463,7 @@ public virtual bool CanUseEquipment(IEquipment eq, bool autoPutoOn) foreach (var rs in eq.GetEffectiveRequiredStats()) { - if (rs.Value.Nominal > Stats.GetNominal(rs.Kind)) + if (!CanUseEquipment(eq, rs)) return report("Required statistic " + rs.Kind.ToDescription() + " not met."); } @@ -613,7 +625,7 @@ public override Weapon GetActiveWeapon() return currentEquipment[CurrentEquipmentKind.Weapon] as Weapon; } - public virtual SpellSource ActiveWeaponSpellSource + public virtual SpellSource SelectedWeaponSpellSource { get { @@ -623,15 +635,15 @@ public virtual SpellSource ActiveWeaponSpellSource } - public SpellSource ActiveSpellSource + public SpellSource SelectedSpellSource { get { - var spellSrc = ActiveManaPoweredSpellSource; + var spellSrc = SelectedManaPoweredSpellSource; if (spellSrc != null) return spellSrc; - var fi = ActiveFightItem; + var fi = SelectedFightItem; if (fi != null && GetStackedCountForHotBar(fi) > 0) { return null; @@ -736,10 +748,7 @@ private void AddAuxStat(PassiveAbility ab) // return Stats.GetCurrentValue(EntityStatKind.Strength) - StartStrength; //} - public bool CanUseEquipment(Equipment eq, EntityStat eqStat) - { - return Stats.GetNominal(eqStat.Kind) >= eq.GetReqStatValue(eqStat); - } + public override string ToString() { @@ -766,13 +775,6 @@ private void AccumulateEqFactors(bool positive) } } - public void SetHasUrgentTopic(bool ut) - { - this.HasUrgentTopic = ut; - if (UrgentTopicChanged != null) - UrgentTopicChanged(this, HasUrgentTopic); - } - public virtual void ApplyAbilities() { var toApply = Abilities.PassiveItems.Where(i => i.BeginTurnApply && i.Level > 0).ToList(); @@ -837,11 +839,11 @@ protected override LastingEffect EnsurePhysicalHitEffect(float inflicted, Living lastingEffectCalcInfo = victim.LastingEffectsSet.EnsureEffect(EffectType.Bleeding, inflicted / 3, this); //if (fi == null)//throwing knife will not cause stunning or tear apart { - if (CurrentWeaponCausesStunning() && CalculateIfStatChanceApplied(EntityStatKind.ChanceToCauseStunning)) + if (CurrentWeaponCausesStunning() && CalculateIfStatChanceApplied(EntityStatKind.ChanceToCauseStunning, victim)) lastingEffectCalcInfo = victim.LastingEffectsSet.EnsureEffect(EffectType.Stunned, 0, this); if (victim.Stats.Health < victim.Stats.GetNominal(EntityStatKind.Health) * 2 / 3) { - if (CalculateIfStatChanceApplied(EntityStatKind.ChanceToCauseTearApart)) + if (CalculateIfStatChanceApplied(EntityStatKind.ChanceToCauseTearApart, victim)) lastingEffectCalcInfo = victim.LastingEffectsSet.EnsureEffect(EffectType.TornApart, 0, this);//this is a death } //swords does not have any effect by default(beside unique ones), but have high hit % @@ -868,15 +870,17 @@ public virtual bool GetGoldWhenSellingTo(IInventoryOwner dest) /// protected override void OnDamageCaused(float inflicted, LivingEntity victim) { - double exp = 1f; + //double exp = 1f; if (victim is Enemy en) { var livePercentage = inflicted / en.GetTotalValue(EntityStatKind.Health) * 100; var award = EnemyDamagingTotalExpAward[en.PowerKind]; - exp = livePercentage * award / 100; + //exp = livePercentage * award / 100; } - var inc = (1 * victim.Level * exp); - this.IncreaseExp(inc); + //var inc = (1 * victim.Level * exp); + //this.IncreaseExp(inc); + base.OnDamageCaused(inflicted, victim); + } public string GetExpInfo() @@ -973,6 +977,9 @@ public override AbilityKind SelectedActiveAbilityKind public SpellStateSet Spells { get => spellStatesSet; set => spellStatesSet = value; } + public bool IsMecenary => base.IsMercenary; + + public FightItem GetFightItemKindAmmoForCurrentWeapon() { var wpn = this.GetActiveWeapon(); @@ -1063,5 +1070,29 @@ public override bool IsInProjectileReach(IProjectile fi, Point target) } return base.IsInProjectileReach(fi, target); } + + public virtual Loot RemoveFromInv(Loot item, RemoveItemArg arg = null) + { + return Inventory.Remove(item); + } + + public override bool CanHighlightAbility(AbilityKind kind) + { + var ab = GetActiveAbility(kind); + if (ab == null) + return false; + if (ab.CoolDownCounter > 0) + return false; + if (kind == AbilityKind.OpenWound) + { + var wpn = this.GetCurrentEquipment(EquipmentKind.Weapon) as Weapon; + if (wpn == null) + return false; + if (wpn.Kind != Weapon.WeaponKind.Sword && wpn.Kind != Weapon.WeaponKind.Dagger && wpn.Kind != Weapon.WeaponKind.Axe) + return false; + } + + return true; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/AlliedEnemy.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/AlliedEnemy.cs index 09300d25..a6074f29 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/AlliedEnemy.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/AlliedEnemy.cs @@ -19,12 +19,17 @@ public AlliedEnemy(Container cont) : base(cont) public override float GetStartStat(EntityStatKind esk) { var startStat = base.GetStartStat(esk); + + //same as skeleton if (esk == EntityStatKind.Strength) startStat += SkeletonSpell.SkeletonSpellStrengthIncrease; else if (esk == EntityStatKind.Defense) startStat += SkeletonSpell.SkeletonSpellDefenseIncrease; + else if (esk == EntityStatKind.Health) + startStat += SkeletonSpell.SkeletonSpellHealthIncrease; + return startStat; } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Ally.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Ally.cs index 6de27ac2..b56b4d4b 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Ally.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Ally.cs @@ -13,13 +13,18 @@ public enum AllyBehaviour { Unset, StayStill, StayClose, GoFreely } public abstract class Ally : AdvancedLivingEntity, IAlly { public bool IncreaseStatsDueToDifficulty = true;//too easy ? - public AllyBehaviour AllyBehaviour { get; set; } = AllyBehaviour.GoFreely; + public AllyBehaviour AllyBehaviour { get; set; } = AllyBehaviour.StayClose; public Ally(Container cont, char symbol = '!') : base(cont, new Point().Invalid(), symbol) { canAdvanceInExp = true; Inventory.InvBasketKind = InvBasketKind.Ally; CurrentEquipment.InvBasketKind = InvBasketKind.AllyEquipment; - Inventory.Capacity = 8; + SetInvCapacity(); + } + + public void SetInvCapacity() + { + Inventory.Capacity = 8 * 3; } protected override bool CanIncreaseStatsDueToDifficulty() @@ -39,7 +44,10 @@ public AllyKind Kind { kind = value; if (kind == AllyKind.Hound) + { AnimalKind = AnimalKind.Hound; + DestroySound = "dog_whining1"; + } } } override protected bool CanUseAnimalKindEq(IEquipment eq) @@ -77,6 +85,7 @@ private static bool IsBowLike(IEquipment eq) public Point Point { get => point; set => point = value; } public bool TakeLevelFromCaster { get; protected set; } + public bool PendingReturnToCamp { get; set ; } public override bool SetLevel(int level, Difficulty? diff = null) { diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Animal.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Animal.cs index 0ac9d210..ad202263 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Animal.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Animal.cs @@ -5,11 +5,34 @@ using Roguelike.TileContainers; using Roguelike.Tiles.Looting; using SimpleInjector; +using System.Collections.Generic; +using System.Linq; namespace Roguelike.Tiles.LivingEntities { - public class Animal : LivingEntity, ILootSource + public class Animal : LivingEntity, ILootSource { + static readonly Dictionary tag2Kind; + //= new Dictionary + //{ + // { "rooster", AnimalKind.Rooster}, + // { "hen", AnimalKind.Hen}, + // { "deer", AnimalKind.Deer}, + // { "pig", AnimalKind.Pig}, + // { "horse", AnimalKind.Horse}, + // { "hound", AnimalKind.Hound}, + + + //}; + + static readonly Dictionary kind2Tag; + + static Animal() + { + tag2Kind = EnumHelper.GetEnumValues(true).ToDictionary(kv => kv.ToString().ToLower(), kv => kv); + kind2Tag = tag2Kind.ToDictionary(kv => kv.Value, kv => kv.Key); + } + public int RandMoveCoolDown = 1; public bool PreventMove { get; set; } = false; AnimalKind kind; @@ -19,9 +42,8 @@ public Animal(Container cont, AnimalKind kind) : base(cont) { AnimalKind = kind; EntityKind = EntityKind.Animal; - - var he = Stats.GetStat(Attributes.EntityStatKind.Health); - he.Value.Nominal /= 3; + if(kind!= AnimalKind.Unset) + tag1 = kind2Tag[kind]; } public Animal(Container cont) : this(cont, AnimalKind.Unset) @@ -37,20 +59,32 @@ public AnimalKind AnimalKind set { kind = value; DisplayedName = kind.ToString(); - + if (kind == AnimalKind.Horse) + Stats.GetStat(Attributes.EntityStatKind.Health).Value.Nominal *= 4; + else if (kind == AnimalKind.Hen || kind == AnimalKind.Rooster) + { + Stats.GetStat(Attributes.EntityStatKind.Health).Value.Nominal /= 2; + Stats.GetStat(Attributes.EntityStatKind.Defense).Value.Nominal /= 2; + } } } + + public AnimalKind GetKindFromTag1() { if (tag1 == "rooster") return AnimalKind.Rooster; - if (tag1 == "hen") + else if (tag1 == "hen") return AnimalKind.Hen; - if (tag1 == "deer") + else if (tag1 == "deer") return AnimalKind.Deer; - if (tag1 == "pig") + else if (tag1 == "pig") return AnimalKind.Pig; + else if (tag1.Contains("horse"))//can be zyndram_horse + return AnimalKind.Horse; + else if (tag1.Contains("hound")) + return AnimalKind.Hound; return AnimalKind.Unset; } @@ -85,11 +119,11 @@ public override bool CalcShallMoveFaster(AbstractGameLevel node) { return LastHitCoolDown > 0; } - public HitResult OnMeleeHitBy(ILivingEntity attacker) - { - OnMeleeHitBy(attacker as LivingEntity); - return HitResult.Hit; - } + //public HitResult OnMeleeHitBy(ILivingEntity attacker) + //{ + // OnMeleeHitBy(attacker as LivingEntity); + // return HitResult.Hit; + //} public override float OnMeleeHitBy(LivingEntity attacker) @@ -114,14 +148,19 @@ private void OnDamaged() PlaySound("pig_scream"); LastHitCoolDown = 2; } - else if (AnimalKind == AnimalKind.Deer) { PlaySound("deer"); LastHitCoolDown = 3; } + else if (AnimalKind == AnimalKind.Horse) + { + PlaySound("horse_hit"); + LastHitCoolDown = 2; + } + // //else if (tag1 == "deer") - //this.Speed = 2; + //this.Speed = 2; } public override string ToString() diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Enemy.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Enemy.cs index c3552faa..5af09c50 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Enemy.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Enemy.cs @@ -17,8 +17,7 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; -//using UnityEditor.Experimental.GraphView; -//using static UnityEngine.EventSystems.EventTrigger; + namespace Roguelike.Tiles.LivingEntities { @@ -29,20 +28,18 @@ public enum IncreaseStatsKind { Level, PowerKind, Name, Difficulty, Ability } public class Enemy : LivingEntity { - [JsonIgnore] - public int RessurectOrdersCounter { get; set; } - - [JsonIgnore] - public int RessurectOrderCooldown { get; set; } - - + public const string ChempTagSuffix = "_ch"; public PrefferedFightStyle PrefferedFightStyle { get; set; }//= PrefferedFightStyle.Magic; public static char[] AllSymbols; public int NumberOfCastedEffectsForAllies = 0; public int NumberOfEmergencyTeleports = 0; - public EnemyPowerKind PowerKind { get; set; } = EnemyPowerKind.Plain; + public EnemyPowerKind PowerKind + { + get; + set; + } = EnemyPowerKind.Plain; public Dictionary StatsIncreased { get; set; } = new Dictionary(); public Loot DeathLoot { get; set; } @@ -62,8 +59,6 @@ public Enemy(char symbol, Container cont) : this(new Point().Invalid(), symbol, public Enemy(Point point, char symbol, Container cont) : base(point, symbol, cont) { - //MovesCountPerTurn = 2; - //IsWounded = true; this.Symbol = symbol; DestroySound = "death"; #if ASCII_BUILD @@ -111,20 +106,7 @@ public override bool RemoveFightItem(FightItem fi) return false; } - public void SendCommand(EntityCommandKind raiseMyFriends) - { - var ea = new EnemyAction(); - ea.Enemy = this; - ea.CommandKind = EntityCommandKind.RaiseMyFriends; - ea.Kind = EnemyActionKind.SendComand; - ea.Info = "Raise my friends!"; - Container.GetInstance().AppendAction(ea); - RessurectOrderCooldown = 10; - RessurectOrdersCounter++; - var sm = Container.GetInstance().SoundManager; - if (sm != null) - sm.PlayVoice("raise_my_friends"); - } + public override bool HasFightItem(FightItemKind fik) { @@ -148,33 +130,33 @@ public override float GetStartStat(EntityStatKind esk) return startStat; } - public FightItemKind ActiveFightItemKind { get; set; } + public FightItemKind SelectedFightItemKind { get; set; } public ProjectileFightItem SetActiveFightItem(FightItemKind kind) { - ActiveFightItemKind = kind; + SelectedFightItemKind = kind; if (!fightItems.ContainsKey(kind)) SetFightItem(new ProjectileFightItem(kind)); return fightItems[kind] as ProjectileFightItem; } - public override FightItem ActiveFightItem + public override FightItem SelectedFightItem { get { - if (ActiveFightItemKind == FightItemKind.Unset) + if (SelectedFightItemKind == FightItemKind.Unset) return null; - if(!fightItems.ContainsKey(ActiveFightItemKind)) + if(!fightItems.ContainsKey(SelectedFightItemKind)) return null; - return fightItems[ActiveFightItemKind]; + return fightItems[SelectedFightItemKind]; } set { var pfi = value as ProjectileFightItem; if (value == null) - ActiveFightItemKind = FightItemKind.Unset; + SelectedFightItemKind = FightItemKind.Unset; else - ActiveFightItemKind = pfi.FightItemKind; + SelectedFightItemKind = pfi.FightItemKind; SetFightItem(pfi); } @@ -201,8 +183,7 @@ protected bool WereStatsIncreased(IncreaseStatsKind kind) return false; } - bool specialNonplainCase; - + public virtual void SetNonPlain(bool boss, bool specialCase = false) { if (PowerKind != EnemyPowerKind.Plain) @@ -211,7 +192,7 @@ public virtual void SetNonPlain(bool boss, bool specialCase = false) //AssertFalse("SetNonPlain Kind != PowerKind.Plain " + this); return; } - specialNonplainCase = specialCase; + //specialNonplainCase = specialCase; NumberOfEmergencyTeleports = 2; PowerKind = boss ? EnemyPowerKind.Boss : EnemyPowerKind.Champion; if (PowerKind == EnemyPowerKind.Boss) @@ -315,18 +296,23 @@ protected override void InitActiveScroll() //ActiveScroll = new Scroll(SpellKind.IceBall); if (Name.ToLower() == "druid" || PowerKind == EnemyPowerKind.Boss) { - ActiveManaPoweredSpellSource = new Scroll(attackSpells.GetRandomElem()); - SetResistanceFromScroll(ActiveManaPoweredSpellSource); + SetRandomSpellSource(); + SetResistanceFromScroll(SelectedManaPoweredSpellSource); Stats.SetNominal(EntityStatKind.Mana, 10000);//TODO } else if (Name.ToLower() == "lava elemental" || Name.ToLower() == "stone golem") { - ActiveManaPoweredSpellSource = new Scroll(SpellKind.FireStone); + SelectedManaPoweredSpellSource = new Scroll(SpellKind.FireStone); Stats.SetNominal(EntityStatKind.Mana, 10000);//TODO } } + public void SetRandomSpellSource() + { + SelectedManaPoweredSpellSource = new Scroll(attackSpells.GetRandomElem()); + } + private void SetResistanceFromScroll(SpellSource activeScroll) { if (activeScroll == null) @@ -372,7 +358,7 @@ protected override void InitStatsFromName() { if (IsStrongerThanAve) { - IncreaseStats(1.5f, IncreaseStatsKind.Name); + IncreaseStats(1.3f, IncreaseStatsKind.Name); } } @@ -450,19 +436,20 @@ private static EntityKind EntityKindFromSymbol(char value) public void SetSpecialAttackStatFromSymbol() { - if (Symbol == EnemySymbols.SnakeSymbol || - Symbol == EnemySymbols.SpiderSymbol + if (Char.ToLower(Symbol) == EnemySymbols.SnakeSymbol || + Char.ToLower(Symbol) == EnemySymbols.SpiderSymbol //hornet has it done in : public override string Name ) { SetStat(EntityStatKind.PoisonAttack); + SetStat(EntityStatKind.ResistPoison, Stats.GetStat(EntityStatKind.ResistPoison).Value.Nominal*2); } } - private void SetStat(EntityStatKind esk) + private void SetStat(EntityStatKind esk, float nominal = 1.5f) { var att = Stats.GetStat(esk); - att.Value.Nominal = 1.5f; + att.Value.Nominal = nominal; } public static string NameFromSymbol(char symbol) @@ -477,9 +464,10 @@ public static string NameFromSymbol(char symbol) public static Enemy Spawn(char symbol, int level, Container cont, Difficulty? difficulty = null) { - var enemy = new Enemy(symbol, cont); + var enemy = cont.GetInstance();//new Enemy(symbol, cont); enemy.Container = cont; enemy.SetLevel(level, difficulty); + enemy.Symbol = symbol; enemy.tag1 = EnemySymbols.EnemiesToSymbols.Where(i => i.Value == symbol).Single().Key; enemy.Revealed = true; @@ -532,9 +520,7 @@ public override string Name AddImmunityFromName(); } } - - public bool HitRandomTarget { get; internal set; } - + public void SetPrefferedFightStyle(PrefferedFightStyle pfs) { PrefferedFightStyle = pfs; @@ -560,11 +546,11 @@ public override SpellSource GetAttackingScroll() { if (Name.ToLower().Contains("druid")) { - return ActiveManaPoweredSpellSource; + return SelectedManaPoweredSpellSource; } else if (Name.ToLower().Contains("lava el")) { - return ActiveManaPoweredSpellSource; + return SelectedManaPoweredSpellSource; } return base.GetAttackingScroll(); } @@ -580,7 +566,8 @@ public void InitFromTag(string tag, bool loadingGame) else if (tag.EndsWith("_king")) SetNonPlain(true); - SetNameFromTag1(); + if(tag1 != "boar_butcher") + SetNameFromTag1(); var symbol = Roguelike.EnemySymbols.GetSymbolFromName(tag); if (symbol != Roguelike.EnemySymbols.Unset) { @@ -634,5 +621,15 @@ public override bool HandleOrder(BattleOrder order, Hero hero, DungeonNode node) return base.HandleOrder(order, hero, node); } + + public override bool HasSpecialSkill(EntityCommandKind skill) + { + var res = base.HasSpecialSkill(skill); + if (skill == EntityCommandKind.Resurrect) + return PowerKind != EnemyPowerKind.Plain && res; + return res; + } + + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/God.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/God.cs index d92730cb..5fe9b62a 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/God.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/God.cs @@ -1,9 +1,7 @@ using Dungeons.Core; using Roguelike.Abstract.Tiles; -using Roguelike.Managers; using Roguelike.Tiles.Looting; using SimpleInjector; -using System; using System.Drawing; namespace Roguelike.Tiles.LivingEntities @@ -19,20 +17,17 @@ public class God : AdvancedLivingEntity, IAlly public Point Point { get => point; set => point = value; } public bool TakeLevelFromCaster { get; } + public bool PendingReturnToCamp { get; set; } public God(Container cont) : this(cont, new Point().Invalid(), '0') { - } public God(Container cont, Point point, char symbol) : base(cont, point, symbol) { Alive = false;//TODO, for turn to work - //HeroAlly = true; - //inventory = new InventoryGod(); - //inventory.InvType = InvType.God; - //inventory.PriceFactor = 1; - //SetMagicValue(); + Stats.SetNominal(Attributes.EntityStatKind.ChanceToPhysicalProjectileHit, 100); + Stats.SetNominal(Attributes.EntityStatKind.ChanceToCastSpell, 100); } public virtual Roguelike.Abstract.Spells.ISpell CreateSpell(out Scroll godScroll) @@ -40,7 +35,7 @@ public virtual Roguelike.Abstract.Spells.ISpell CreateSpell(out Scroll godScroll godScroll = null; return null; } - + public void SetNextLevelExp(double exp) { NextLevelExperience = exp; @@ -51,29 +46,6 @@ public virtual bool MakeTurn() return false; } - //public virtual int GetMagicFactor() - //{ - // return 1; - //} - - //public virtual int GetMagicAddition() - //{ - // return 0; - //} - - //public void SetMagicValue() - //{ - // var magicValue = BaseMagic.NominalValue; - // if (GameManager.Instance.Level != null) - // { - // magicValue *= (GameManager.Instance.Level.LevelIndex / 2 + GetMagicFactor()); - // magicValue += GetMagicAddition(); - // } - // Stats.SetNominal(EntityStatKind.Magic, magicValue); - // var ownerMagicAmount = Stats.GetCurrentValue(EntityStatKind.Magic); - // //int k = 0; - //} - //public virtual LootBase GetAwakeReward(bool primary) //{ // return null; @@ -146,31 +118,7 @@ public virtual bool MakeTurn() // } //} - //public Inventory Inventory - //{ - // get - // { - // return inventory; - // } - - // set - // { - // inventory = value; - // } - //} - - //public List AcceptedLoot - //{ - // get - // { - // return acceptedLoot; - // } - // set - // { - // acceptedLoot = value; - // } - //} //public List AwakingLoot //{ @@ -204,150 +152,56 @@ public virtual bool MakeTurn() //} //public string HiddenAwakingLoot = ""; - } - - //public class Swarog : God - //{ - // public Swarog():this(Point.Invalid) - // { - - // } - - // public Swarog(Point point) : base(point, '1') - // { - // PowerReleaseSpeach = "Darkness surround us!";//Let there be dark - // Name = "Swarog"; - // AwakingGift = "swarog_hammer"; - // AwakingLoot.Add("swarog_hammer_wooden_part1"); - // AwakingLoot.Add("swarog_hammer_wooden_part2"); - // } - - // //power: makes dark causing random monsters to hit each other - // public override string GetPrimaryStatDescription() - // { - // return "God of Sun, Fire and Smithing"; - // } - //} - - //public class Perun : God - //{ - // public static string HiddenGodAwakingLoot = "perun_sign_2"; - - // public Perun():this(Point.Invalid) - // { - - // } - - // public override int GetMagicFactor() - // { - // return 1; - // } - // public override int GetMagicAddition() - // { - // return 4; - // } - - // public Perun(Point point) : base(point, '2') - // { - // PowerReleaseSpeach = "Discover power of thunders"; - // Name = "Perun"; - // AwakingGift = "lighting_scroll"; - // AwakingLoot.Add("perun_sign_1"); - // AwakingLoot.Add("perun_sign_2"); - // HiddenAwakingLoot = HiddenGodAwakingLoot; - // // - // } - - // //power: hits random monsters with lightball spell - // public override string GetPrimaryStatDescription() - // { - // return "God of Lightning"; - // } - //} - - //public class Dziewanna : God - //{ - // public static string HiddenGodAwakingLoot = "dziewanna_horn"; - // public Dziewanna():this(Point.Invalid) - // { - - // } - - // public Dziewanna(Point point) : base(point, '3') - // { - // HiddenAwakingLoot = HiddenGodAwakingLoot; - // PowerReleaseSpeach = "Taste my juicy fruits!"; - // Name = "Dziewanna"; - // AwakingGift = "";//SpecialPotion - // AwakingLoot.Add("dziewanna_flower"); - // AwakingLoot.Add(HiddenAwakingLoot); - // } - - // public override LootBase GetAwakeReward(bool primary) - // { - // if(primary) - // return CommonRandHelper.GetRandomDouble() > .5f ? new SpecialPotion(SpecialPotionKind.Strength, true) : - // new SpecialPotion(SpecialPotionKind.Magic, true); - // return base.GetAwakeReward(primary); - // } - // //power: hits random monsters with poison arrow (or gives poisoned apple) - // public override string GetPrimaryStatDescription() - // { - // return "God of Nature"; - // } + //} - //} - //public class Jarowit : God - //{ - // public Jarowit() : this(Point.Invalid) - // { - // } - // public Jarowit(Point point) : base(point, '4') - // { - // PowerReleaseSpeach = "It's time of the Chosen One!"; - // Name = "Jarowit"; - // AwakingGift = "JarowitsShield"; - // AwakingLoot.Add("ruby_medium"); - // AwakingLoot.Add("emerald_medium"); - // AwakingLoot.Add("diamond_medium"); - // } + // } + // public Jarowit(Point point) : base(point, '4') + // { + // PowerReleaseSpeach = "It's time of the Chosen One!"; + // Name = "Jarowit"; + // AwakingGift = "JarowitsShield"; + // AwakingLoot.Add("ruby_medium"); + // AwakingLoot.Add("emerald_medium"); + // AwakingLoot.Add("diamond_medium"); + // } - // //power: hits random monsters with weaken spell, hero with iron skin - // public override string GetPrimaryStatDescription() - // { - // return "God of War"; - // } - //} + // //power: hits random monsters with weaken spell, hero with iron skin + // public override string GetPrimaryStatDescription() + // { + // return "God of War"; + // } + //} - //public class Swiatowit : God - //{ - // public static string SwiatowitHiddenAwakingLoot = "swiatowid_sword_part"; - // public Swiatowit() : this(Point.Invalid) - // { + //public class Swiatowit : God + //{ + // public static string SwiatowitHiddenAwakingLoot = "swiatowid_sword_part"; + // public Swiatowit() : this(Point.Invalid) + // { - // } - // public Swiatowit(Point point) : base(point, '5') - // { - // HiddenAwakingLoot = SwiatowitHiddenAwakingLoot; - // PowerReleaseSpeach = "Obey god's will!"; - // Name = "Swiatowit"; - // AwakingGift = "swiatowid_sword"; - // AwakingLoot.Add("swiatowid_horn"); - // AwakingLoot.Add(HiddenAwakingLoot); - // } + // } + // public Swiatowit(Point point) : base(point, '5') + // { + // HiddenAwakingLoot = SwiatowitHiddenAwakingLoot; + // PowerReleaseSpeach = "Obey god's will!"; + // Name = "Swiatowit"; + // AwakingGift = "swiatowid_sword"; + // AwakingLoot.Add("swiatowid_horn"); + // AwakingLoot.Add(HiddenAwakingLoot); + // } - // public override LootBase GetAwakeReward(bool primary) - // { - // if (primary) - // return new Gem(11); - // return new Gem(11); - // } + // public override LootBase GetAwakeReward(bool primary) + // { + // if (primary) + // return new Gem(11); + // return new Gem(11); + // } - // //power: hits random monsters with random spell, spell causes effect 50% - // public override string GetPrimaryStatDescription() - // { - // return "Highest God"; - // } + // //power: hits random monsters with random spell, spell causes effect 50% + // public override string GetPrimaryStatDescription() + // { + // return "Highest God"; + // } + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Hero.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Hero.cs index a686e40e..e47757a8 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Hero.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Hero.cs @@ -109,6 +109,13 @@ public override string ToString() public virtual void OnContextSwitched(Container container) { //this.Container = container; + + //TODO + EnsureInvOwner(); + } + + public void EnsureInvOwner() + { Inventory.Owner = this; Crafting.InvItems.Inventory.Owner = this; Crafting.Recipes.Inventory.Owner = this; @@ -269,5 +276,6 @@ public override bool Alive } } + public bool InStocks { get; set; } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/LivingEntity.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/LivingEntity.cs index f11748b9..a543660b 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/LivingEntity.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/LivingEntity.cs @@ -5,8 +5,6 @@ using Dungeons.Tiles.Abstract; using Newtonsoft.Json; using Roguelike.Abilities; -using Roguelike.Abstract.Projectiles; -//using Roguelike.Abstract.Spells; using Roguelike.Attributes; using Roguelike.Calculated; using Roguelike.Effects; @@ -17,6 +15,7 @@ using Roguelike.Managers; using Roguelike.Policies; using Roguelike.Spells; +using Roguelike.State; using Roguelike.TileContainers; using Roguelike.Tiles.Abstract; using Roguelike.Tiles.Looting; @@ -27,6 +26,31 @@ using System.Drawing; using System.Linq; +namespace Roguelike.State +{ + public class CommandUseInfo + { + public EntityCommandKind Kind { get; set; } + public int Cooldown { get; set; } + public int UseCounter { get; set; } + public string Sound { get; internal set; } + public string Info { get; internal set; } + public string MessageAboveHead { get; internal set; } + + public void IncreaseUseCount() + { + UseCounter++; + } + + public CommandUseInfo():this(EntityCommandKind.Unset) + { + } + public CommandUseInfo(EntityCommandKind cmd) + { + Kind = cmd; + } + } +} namespace Roguelike.Tiles.LivingEntities { public enum EntityState { Idle, Moving, Attacking, CastingProjectile, Sleeping } @@ -40,6 +64,14 @@ public enum DeathEffect { Unset, TearApart, BreakApart } public class LivingEntity : Tile, ILastingEffectOwner, IDestroyable, ILivingEntity { //some attributes describing LivingEntity + public EntityProffesionKind Proffesion + { + get; + set; + } + + public bool IsMercenary => Proffesion == EntityProffesionKind.Mercenary; + public bool HitRandomTarget { get; internal set; } public string Herd { get; set; } = ""; [JsonIgnore] public bool LastMeleeAttackWasOK { get; set; } @@ -59,6 +91,7 @@ public int Level } } + [JsonIgnore] public BattleOrder BattleOrder { get; set; } public bool LevelSet { get; set; } @@ -74,9 +107,14 @@ public int Level [JsonIgnore] public AbilityKind WorkingAbilityKind; + //[JsonIgnore] + //public AbilityKind ActivatedAbilityKind; + public event EventHandler Wounded; + + [JsonIgnore] public DeathEffect DeathEffect { get; set; } - public bool d_immortal = false; + public bool Immortal { get; set; } = false; protected const int StartStrength = 10; protected const int StartDefense = 6; public static readonly Dictionary StartStatValues = new Dictionary() { @@ -134,29 +172,32 @@ public bool Destroyed public Point InitialPoint = DefaultInitialPoint; EntityStats stats = new EntityStats(); Dictionary chanceToExperienceEffect = new Dictionary(); - public int ActiveScrollCoolDownCounter { get; set; } + public int SelectedScrollCoolDownCounter { get; set; } + + [JsonIgnore] + public bool EverSensedWeakPoint = false; Dictionary nonPhysicalDamageStats = new Dictionary(); public Tile FixedWalkTarget = null; public LivingEntity AllyModeTarget; public bool HasRelocateSkill { get; set; } //public static readonly EntityStats BaseStats; public string OriginMap { get; set; } - SpellSource activeManaPoweredSpellSource; + SpellSource selectedManaPoweredSpellSource; [JsonIgnore] public int TrappedCounter { get; set; } - public virtual SpellSource ActiveManaPoweredSpellSource + public virtual SpellSource SelectedManaPoweredSpellSource { get { - return activeManaPoweredSpellSource; + return selectedManaPoweredSpellSource; } - set { activeManaPoweredSpellSource = value; } + set { selectedManaPoweredSpellSource = value; } } public FightItem GetActivatedFightItem() { - var fightItem = ActiveFightItem; + var fightItem = SelectedFightItem; return fightItem; } @@ -176,13 +217,12 @@ public virtual Ability SelectedActiveAbility set; } - public virtual FightItem ActiveFightItem + public virtual FightItem SelectedFightItem { - get => activeFightItem; + get => selectedFightItem; set { - activeFightItem = value; - //RecentlyActivatedFightItem = value; + selectedFightItem = value; } } @@ -191,7 +231,9 @@ public Point Position get { return point; } } + [JsonIgnore] public bool CanAttack { get; set; } = true; + public EffectType DiedOfEffect; public EntityState state; @@ -317,7 +359,7 @@ protected virtual void IncreaseStats(float inc, IncreaseStatsKind kind) //kv.Value.Kind == EntityStatKind.Defense ) { - incToUse *= 1.5f; + incToUse *= 1.7f; } //enemies usually do not have that stat //if ( @@ -381,11 +423,9 @@ protected virtual void InitResistance() this.Stats.SetNominal(EntityStatKind.ResistLighting, rli); } - public float StatsIncreasePerLevel = .14f; - protected float GetIncrease(int level, float factor = 1) { - return 1 + (level * StatsIncreasePerLevel * factor); + return 1 + (level * GenerationInfo.EnemyStatsIncreasePerLevel * factor); } public bool WasEverHitBy(LivingEntity le) @@ -425,6 +465,7 @@ ILogger Logger get { return Container.GetInstance(); } } + [JsonIgnore] public virtual bool Alive { get { return alive; } @@ -483,7 +524,7 @@ public bool IsAlwaysHitting(AttackKind kind) return AlwaysHit.ContainsKey(kind) && AlwaysHit[kind]; } - internal bool CalculateIfStatChanceApplied(EntityStatKind esk, IHitable target = null, ProjectileFightItem pfi = null) + internal bool CalculateIfStatChanceApplied(EntityStatKind esk, IHitable target, ProjectileFightItem pfi = null) { if (!(target is LivingEntity)) return true; @@ -546,10 +587,13 @@ public virtual AttackDescription GetAttackValue(AttackKind attackKind) return new AttackDescription(this, true, attackKind); } - private void ReduceHealth(LivingEntity attacker, string sound, string damageDesc, string damageSource, ref float inflicted) + [JsonIgnore] + public AttackKind LastReceivedAttackKind { get; set; } + + private void ReduceHealth(LivingEntity attacker, string sound, string damageDesc, + string damageSource, ref float inflicted, AttackKind ak) { - if (d_immortal) - return; + LastReceivedAttackKind = ak; var manaShieldEffect = LastingEffectsSet.GetByType(EffectType.ManaShield); var manaReduce = inflicted; if (manaShieldEffect != null && this.Stats.Mana > 0) @@ -565,7 +609,13 @@ private void ReduceHealth(LivingEntity attacker, string sound, string damageDesc ReduceHealth(inflicted); attacker.OnDamageCaused(inflicted, this); - var ga = new LivingEntityAction(LivingEntityActionKind.GainedDamage) { InvolvedValue = inflicted, InvolvedEntity = this, Sound = sound }; + var ga = new LivingEntityAction(LivingEntityActionKind.GainedDamage) + { + InvolvedValue = inflicted, + InvolvedEntity = this, + AttackerEntity = attacker, + Sound = sound + }; var desc = damageDesc ?? Name + " received damage: " + inflicted.Formatted() + " " + damageSource; ga.Info = desc; @@ -595,7 +645,12 @@ public void HandleTransformOnAttack() } } - protected virtual void OnDamageCaused(float inflicted, LivingEntity victim) { } + [JsonIgnore] + public int InflictedHitsCount { get; set; } + protected virtual void OnDamageCaused(float inflicted, LivingEntity victim) + { + InflictedHitsCount++; + } internal bool EverCausedEffect(EffectType type) { @@ -728,12 +783,12 @@ public virtual float OnMeleeHitBy(LivingEntity attackerItf) } //this.LastingEffectsSet.AddPercentageLastingEffect(EffectType.Frozen, new Scroll(SpellKind.IceBall), attacker); - return InflictDamage(attacker, true, ref inflicted, ref desc); + return InflictMeleeDamage(attacker, true, ref inflicted, ref desc); } - public float InflictDamage(LivingEntity attacker, bool normalAttack, ref float inflicted, ref string desc) + public float InflictMeleeDamage(LivingEntity attacker, bool normalAttack, ref float inflicted, ref string desc) { - ReduceHealth(attacker, "punch", desc, "", ref inflicted); + ReduceHealth(attacker, "punch", desc, "", ref inflicted, AttackKind.Melee); if (normalAttack && inflicted > 0) StealStatIfApplicable(inflicted, attacker); @@ -760,7 +815,7 @@ public int GetFightItemKindHitCounter(FightItemKind kind) return fightItemHitsCounter.ContainsKey(kind) ? fightItemHitsCounter[kind] : 0; } - public Dictionary attackKindHitsCounter { get; set; } + public Dictionary AttackKindHitsCounter { get; set; } public HitResult OnHitBy(IDamagingSpell ds, IPolicy policy) { @@ -768,7 +823,7 @@ public HitResult OnHitBy(IDamagingSpell ds, IPolicy policy) return HitResult.Hit; } - public HitResult OnHitBy(Dungeons.Tiles.Abstract.IProjectile projectile, IPolicy policy) + public HitResult OnHitBy(IProjectile projectile, IPolicy policy) { if (projectile is Spell spell) @@ -826,6 +881,7 @@ HitResult ReturnHitResult(LivingEntity attacker, EntityStatKind esk, float damag lastingEffectsSet.EnsureEffect(et, npd, attacker, duration); PlaySound(snd);//TODO + LastReceivedAttackKind = AttackKind.PhysicalProjectile; return HitResult.Hit; } @@ -889,7 +945,19 @@ protected virtual HitResult OnHitBy(ProjectileFightItem pfi) inflicted += CalculateNonPhysicalDamage(kv.Key, kv.Value); } - ReduceHealth(attacker, sound, damageDesc, srcName, ref inflicted); + ReduceHealth(attacker, sound, damageDesc, srcName, ref inflicted, AttackKind.PhysicalProjectile); + + if (pfi.FightItemKind.IsBowLikeAmmunition()) + { + if (pfi.FightItemKind.IsCausingElementalVengeance()) + { + if (RandHelper.GetRandomDouble() > 0.25)//TODO ability + { + lastingEffectsSet.EnsureEffect(pfi.GetEffectType(), inflicted, attacker, 3); + } + } + } + return HitResult.Hit; } @@ -900,6 +968,8 @@ protected virtual float GetDamageAddition(ProjectileFightItem pfi) public LastingEffect StartBleeding(float damageEachTurn, LivingEntity attacker, int turnLasting) { + if (IsImmuned(EffectType.Bleeding)) + return null; return lastingEffectsSet.EnsureEffect(EffectType.Bleeding, damageEachTurn, attacker, turnLasting); } @@ -930,11 +1000,11 @@ protected virtual void OnHitBy(float amount, Spell spell, string damageDesc = nu // damageDesc = ""; //damageDesc += "_D " + spell.GetHashCode(); - //Debug - ReduceHealth(attacker, sound, damageDesc, srcName, ref amount); - if (!ImmuneOnEffects) - LastingEffectsSet.TryAddLastingEffectOnHit(amount, attacker, spell); + var inflicated = amount; + ReduceHealth(attacker, sound, damageDesc, srcName, ref inflicated, AttackKind.SpellElementalProjectile); + + LastingEffectsSet.TryAddLastingEffectOnHit(inflicated, attacker, spell); } protected virtual LastingEffect EnsurePhysicalHitEffect(float inflicted, LivingEntity victim) @@ -959,9 +1029,32 @@ public LastingEffect GetFirstLastingEffect(EffectType le) return LastingEffects.Where(i => i.Type == le).FirstOrDefault(); } + bool IsNegative(EffectType effect) + { + return effect == EffectType.Bleeding || + effect == EffectType.Poisoned || + effect == EffectType.Firing || + effect == EffectType.Frozen || + effect == EffectType.BushTrap || + effect == EffectType.TornApart || + effect == EffectType.Frighten || + effect == EffectType.Stunned || + effect == EffectType.Weaken || + effect == EffectType.Inaccuracy || + effect == EffectType.WebTrap; + } + public bool IsImmuned(EffectType effect) { - return immunedEffects.Contains(effect) || chanceToExperienceEffect[effect] == 0; + if (ImmuneOnEffects && IsNegative(effect)) + return true; + return immunedEffects.Contains(effect) || chanceToExperienceEffect[effect] == 0; + } + + public void RemoveImmunity(EffectType effect) + { + if (immunedEffects.Contains(effect)) + immunedEffects.Remove(effect); } public void AddImmunity(EffectType effect) @@ -998,16 +1091,25 @@ private static void StealStatIfApplicable(float damageAmount, LivingEntity attac } } - [JsonIgnore] + [JsonIgnore]//LastingEffectsSet is saved public List LastingEffects { get { return lastingEffectsSet.LastingEffects; } + } - public LastingEffectsSet LastingEffectsSet { get => lastingEffectsSet; } + public LastingEffectsSet LastingEffectsSet + { + get => lastingEffectsSet; + set + { + lastingEffectsSet = value; + lastingEffectsSet.LivingEntity = this; + } + } //TODO ! use IsInProjectileReach public float MaxMagicAttackDistance { get; internal set; } = GenerationInfo.MaxMagicAttackDistance; @@ -1017,15 +1119,21 @@ public virtual void RemoveLastingEffect(LastingEffect le) lastingEffectsSet.RemoveLastingEffect(le); } - public static float GetReducePercentage(float orgAmount, float discPerc) + public bool IsBadlyWounded() { - return orgAmount * discPerc / 100f; + var he = GetCurrentValue(EntityStatKind.Health) / GetTotalValue(EntityStatKind.Health); + return he <= .35f; } + //public static float GetReducePercentage(float orgAmount, float discPerc) + //{ + // return orgAmount * discPerc / 100f; + //} + public override string ToString() { - var str = base.ToString(); - str += " " + this.State + ", Alive:" + Alive + ", H:" + Stats.Health + ", Lvl:" + this.Level; + var str = " L:" + Level + " " + base.ToString(); + str += " "+ this.State + ", Alive:" + Alive + ", H:" + Stats.Health + ", Lvl:" + Level; return str; } @@ -1043,12 +1151,19 @@ protected void Assert(bool check, string desc = "") public bool DieIfShould(EffectType effect) { - if (Alive && IsHealthGone()) + if (Alive) { - Alive = false; - DiedOfEffect = effect; - //AppendDeadAction(); - return true; + if (IsHealthGone()) + { + if (Immortal) + { + return false; + } + Alive = false; + DiedOfEffect = effect; + //AppendDeadAction(); + return true; + } } return false; } @@ -1090,6 +1205,7 @@ public bool IsHealthGone() public virtual void ReduceHealth(float amount) { //Debug.WriteLine(Name+ " ReduceHealth: "+ amount); + Stats.GetStat(EntityStatKind.Health).Subtract(amount); DieIfShould(EffectType.Unset); } @@ -1099,7 +1215,6 @@ private float GetDefense() return GetCurrentValue(EntityStatKind.Defense); } - public virtual bool HasEnoughMana(float manaCost) { return GetCurrentValue(EntityStatKind.Mana) >= manaCost; @@ -1111,7 +1226,7 @@ public float GetCurrentValue(EntityStatKind kind) var cv = stat.Value.CurrentValue; if (stat.Unit == EntityStatUnit.Percentage && cv > 100) { - Logger.LogError("stat.Unit == EntityStatUnit.Percentage && cv > 100! " + this); + Logger.LogError(stat.Kind+ " stat.Unit == EntityStatUnit.Percentage && cv > 100! " + this); cv = 100; } @@ -1252,7 +1367,7 @@ public LastingEffect AddLastingEffectFromSpell(SpellKind spellKind, EffectType e public EffectiveFactor CalcEffectiveFactor(EntityStatKind kind, float nominalValuePercInc) { var statValue = Stats.GetStat(kind).Value.TotalValue; - var factor = statValue * nominalValuePercInc / 100f;// CalcEffectValue(nominalValuePercInc, statValue); + var factor = statValue * nominalValuePercInc / 100f; return new EffectiveFactor(factor); } @@ -1287,8 +1402,8 @@ public int GetSurfaceSkillLevel(SurfaceKind kind) public virtual void PlayAllySpawnedSound() { } Difficulty difficulty; - private FightItem activeFightItem; - + private FightItem selectedFightItem; + public static Func LevelStatIncreaseCalculated; public virtual bool SetLevel(int level, Difficulty? diff = null) { difficulty = Difficulty.Normal; @@ -1301,7 +1416,7 @@ public virtual bool SetLevel(int level, Difficulty? diff = null) { return false; } - this.Level = level; + SetLevel(level); InitStatsFromName(); var hard = diff == Difficulty.Hard; float inc = 1; @@ -1318,6 +1433,8 @@ public virtual bool SetLevel(int level, Difficulty? diff = null) if (level > 1) { inc = GetIncrease(hard ? level + 1 : level); + if(LevelStatIncreaseCalculated!=null) + inc = LevelStatIncreaseCalculated(inc); IncreaseStats(inc, IncreaseStatsKind.Level); } InitResistance(); @@ -1326,6 +1443,16 @@ public virtual bool SetLevel(int level, Difficulty? diff = null) return true; } + public void SetFakeLevel(int level) + { + SetLevel(level); + } + + private void SetLevel(int level) + { + this.Level = level; + } + protected virtual bool CanIncreaseStatsDueToDifficulty() { return true; @@ -1349,7 +1476,7 @@ public Point GetPoint() public virtual SpellSource GetAttackingScroll() { - return ActiveManaPoweredSpellSource; + return SelectedManaPoweredSpellSource; } public bool CanBeHitBySpell() @@ -1458,22 +1585,6 @@ public virtual bool CanUseAbility(AbilityKind kind, AbstractGameLevel node, out reason = "victim is too close"; return false; } - - //TODO - //if (node != null) - //{ - // var pathToTarget = node.FindPath(point, victim.Position, false, false, false, this); - // foreach (var nodePt in pathToTarget) - // { - // var pt = new Point(nodePt.Y, nodePt.X); - // var tile = node.GetTile(pt); - // if (tile != this && tile != victim && tile is Abstract.IObstacle) - // { - // reason = "victim is covered by " + tile.DisplayedName; - // return false; - // } - // } - //} } bool can = CanUseAbilityDueToItsState(ab, ref reason); return can; @@ -1481,9 +1592,10 @@ public virtual bool CanUseAbility(AbilityKind kind, AbstractGameLevel node, out protected bool CanUseAbilityDueToItsState(ActiveAbility ab, ref string reason) { - var can = GetAbilityState(ab) == AbilityState.Unset; + var state = GetAbilityState(ab); + var can = state == AbilityState.Activated; if (!can) - reason = "cooling it down"; + reason = "!CanUseAbilityDueToItsState "+ state; return can; } @@ -1514,19 +1626,7 @@ public virtual bool Consume(IConsumable consumable) if (consumable is Potion potion) { Dungeons.DebugHelper.Assert(consumable.ConsumptionSteps == 1); - if (potion.Kind == PotionKind.Antidote) - { - var le = GetFirstLastingEffect(EffectType.Poisoned); - if (le != null) - RemoveLastingEffect(le); - - consumed = true; - } - else - { - var factor = LastingEffectsSet.CalcLastingEffectInfo(EffectType.Unset, consumable); - consumed = DoConsume(consumable.StatKind, factor); - } + consumed = ConsumePotion(potion); } else { @@ -1547,8 +1647,8 @@ public virtual bool Consume(IConsumable consumable) EffectType et = EffectType.ConsumedRawFood; if (consumable.Roasted) et = EffectType.ConsumedRoastedFood; - LastingEffectsSet.AddPercentageLastingEffect(et, consumable, consumable.Loot); - consumed = true; + var le = LastingEffectsSet.AddPercentageLastingEffect(et, consumable, consumable.Loot); + consumed = le !=null; } } } @@ -1560,6 +1660,26 @@ public virtual bool Consume(IConsumable consumable) return consumed; } + public bool ConsumePotion(Potion potion) + { + bool consumed; + if (potion.Kind == PotionKind.Antidote) + { + var le = GetFirstLastingEffect(EffectType.Poisoned); + if (le != null) + RemoveLastingEffect(le); + + consumed = true; + } + else + { + var factor = LastingEffectsSet.CalcLastingEffectInfo(EffectType.Unset, potion); + consumed = DoConsume(potion.StatKind, factor); + } + + return consumed; + } + [JsonIgnore] public bool EverUsedFightItem { get; set; } @@ -1571,6 +1691,11 @@ public virtual ActiveAbility GetActiveAbility(AbilityKind kind) return null; } + public virtual PassiveAbility GetPassiveAbility(AbilityKind kind) + { + return null; + } + public virtual AbilityKind SelectedActiveAbilityKind { get { return AbilityKind.Unset; } @@ -1580,6 +1705,91 @@ public virtual AbilityKind SelectedActiveAbilityKind public int ChaseCounter { get; internal set; } public bool IsLooted { get; set; } + [JsonIgnore] + public bool LastMoveOnPathResult { get; internal set; } + public int MovesCounter { get; internal set; } + + Dictionary advEnemySkillUseCount = new Dictionary(); + public int GetAdvEnemySkillUseCount(EntityCommandKind skill) + { + EnsureCmd(skill); + return advEnemySkillUseCount.ContainsKey(skill) ? advEnemySkillUseCount[skill].UseCounter : 0; + } + + public int GetAdvEnemySkillCooldown(EntityCommandKind skill) + { + EnsureCmd(skill); + return advEnemySkillUseCount.ContainsKey(skill) ? advEnemySkillUseCount[skill].Cooldown : 0; + } + + CommandUseInfo EnsureCmd(EntityCommandKind skill) + { + if (!advEnemySkillUseCount.ContainsKey(skill)) + { + advEnemySkillUseCount[skill] = new CommandUseInfo(skill); + if (skill == EntityCommandKind.Resurrect) + { + advEnemySkillUseCount[skill].Sound = "raise_my_friends"; + advEnemySkillUseCount[skill].MessageAboveHead = "Raise my friends!"; + } + else if (skill == EntityCommandKind.SenseVictimWeakResist) + { + advEnemySkillUseCount[skill].Sound = "FallenOneSense"; + advEnemySkillUseCount[skill].MessageAboveHead = "Let me sense your weaknesses..."; + } + else if (skill == EntityCommandKind.MakeFakeClones) + { + advEnemySkillUseCount[skill].Sound = "FallenOneSurround"; + advEnemySkillUseCount[skill].MessageAboveHead = "Let me surround you..."; + } + + if (string.IsNullOrEmpty(advEnemySkillUseCount[skill].Info)) + advEnemySkillUseCount[skill].Info = Name + " used " + skill.ToDescription() + " skill"; + } + return advEnemySkillUseCount[skill]; + } + + public int SetAdvEnemySkillCooldown(EntityCommandKind skill, int val) + { + EnsureCmd(skill); + return advEnemySkillUseCount[skill].Cooldown = val; + } + + public void IncreaseAdvEnemySkillUseCount(EntityCommandKind skill) + { + EnsureCmd(skill); + GetCommand(skill).UseCounter = GetAdvEnemySkillUseCount(skill) + 1; + } + + public CommandUseInfo GetCommand(EntityCommandKind skill) + { + EnsureCmd(skill); + + return advEnemySkillUseCount[skill]; + } + + internal void DescreseAdvEnemySkillUseCount(EntityCommandKind cmd) + { + EnsureCmd(cmd); + int count = GetAdvEnemySkillUseCount(cmd); + if (count > 0) + { + advEnemySkillUseCount[cmd].UseCounter--; + } + } + public virtual bool HasSpecialSkill(EntityCommandKind skill) + { + var fo = tag1.StartsWith("fallen_one"); + if (skill == EntityCommandKind.Resurrect) + return Name.Contains("Skeleton"); + return fo; + } + + public bool HasSpecialSkillUnused(EntityCommandKind cmd) + { + return HasSpecialSkill(cmd) && GetAdvEnemySkillUseCount(cmd) == 0; + } + bool lastTimeWasLava = false; public void ReduceHealthDueToSurface(AbstractGameLevel CurrentNode) { @@ -1728,36 +1938,141 @@ public void PlayHitSound(IDamagingSpell spell) PlaySound(spell.HitSound); } + public virtual bool CanHighlightAbility(AbilityKind kind) + { + return false; + } + public AbilityState GetAbilityState(Ability ab) { if (ab.CoolDownCounter > 0) return AbilityState.CoolingDown; if (WorkingAbilityKind == ab.Kind || LastingEffectsSet.HasEffect(ab.Kind)) return AbilityState.Working; - + if (SelectedActiveAbilityKind == ab.Kind) + { + var canActivate = CanHighlightAbility(ab.Kind); + var newState = canActivate ? AbilityState.Activated : AbilityState.Unusable; + return newState; + } return AbilityState.Unset; } + public void AppendUsedAbilityAction(Abilities.AbilityKind abilityKind) + { + AppendAction(new AbilityStateChangedEvent() + { + Info = Name + " used ability " + abilityKind.ToDescription(), + Level = ActionLevel.Important, + AbilityUser = this, + AbilityKind = abilityKind, + AbilityState = AbilityState.Working + }); ; + + if (abilityKind == AbilityKind.Smoke) + PlaySound("cloth"); + } + public void HandleActiveAbilityUsed(AbilityKind abilityKind) { var ab = GetActiveAbility(abilityKind); + + AppendUsedAbilityAction(abilityKind); + if (ab.UsesCoolDownCounter) { - if (!LastingEffectsSet.HasEffect(abilityKind) && abilityKind != AbilityKind.Smoke) + //if (ab.TurnsIntoLastingEffect) + //{ + // if (!LastingEffectsSet.HasEffect(abilityKind) && abilityKind != AbilityKind.Smoke) + // { + // //WorkingAbilityKind = AbilityKind.Unset; + // HandleActiveAbilityEffectDone(abilityKind); + // } + //} + //else + if (abilityKind != AbilityKind.Smoke) { - //WorkingAbilityKind = AbilityKind.Unset; - HandleActiveAbilityEffectDone(abilityKind, false); + var lastsManyTurns = LastingEffectsSet.HasEffect(abilityKind); + + if (!lastsManyTurns) + { + StartAbilityCooling(abilityKind, ab); + } } - //if(!LastingEffectsSet.HasEffect(abilityKind)) - // HandleActiveAbilityEffectDone(abilityKind); } } - public void HandleActiveAbilityEffectDone(AbilityKind abilityKind, bool fromLastEff) + + public void StartAbilityCooling(AbilityKind abilityKind) { var ab = GetActiveAbility(abilityKind); + StartAbilityCooling(abilityKind, ab); + } + + public void StartAbilityCooling(AbilityKind abilityKind, ActiveAbility ab) + { + //start cool down ab.CoolDownCounter = ab.MaxCollDownCounter; WorkingAbilityKind = AbilityKind.Unset; + AppendAction(new AbilityStateChangedEvent() + { AbilityKind = abilityKind, AbilityState = AbilityState.CoolingDown, AbilityUser = this }); + } + + public float GetHealthRatio() + { + return GetCurrentValue(EntityStatKind.Health) / GetTotalValue(EntityStatKind.Health); + } + + public float GetManaRatio() + { + return GetCurrentValue(EntityStatKind.Mana) / GetTotalValue(EntityStatKind.Mana); + } + + public bool CanBeBlessed() + { + if (HasLastingEffect(EffectType.Poisoned)) + return true; + if (HasLastingEffect(EffectType.Frozen)) + return true; + if (HasLastingEffect(EffectType.Bleeding)) + return true; + + if (GetHealthRatio() < 1) + return true; + + if (GetManaRatio() < 0.5f) + return true; + + return false; + } + + public virtual bool CanUseEquipment(IEquipment eq, bool autoPutoOn) + { + return false; + + } + + public bool CanUseEquipment(IEquipment eq, EntityStat eqStat) + { + var ceiled = EntityStat.GetRoundedStat(Stats.GetNominal(eqStat.Kind));//skeleton had dex 24.9 on level 6! , was hown as 25 , but could not use eq + return ceiled >= eq.GetReqStatValue(eqStat); + } + + internal bool CanUseCommand(EntityCommandKind cmd) + { + if (!HasSpecialSkill(cmd)) + return false; + + var skill = GetCommand(cmd); + + if (cmd == EntityCommandKind.MakeFakeClones) + { + return skill.UseCounter == 0 && GetHealthRatio() < 0.5; + } + if (cmd == EntityCommandKind.SenseVictimWeakResist) + return skill.UseCounter == 0 && GetHealthRatio() < 0.75; + + return true; } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Merchant.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Merchant.cs index 7b2c263c..200ffc21 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Merchant.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/Merchant.cs @@ -1,27 +1,95 @@ using Dungeons.Core; using Newtonsoft.Json; +using Roguelike.Abilities; using Roguelike.Abstract.Tiles; using Roguelike.Attributes; using Roguelike.LootContainers; using Roguelike.Tiles.Abstract; using SimpleInjector; +using System; using System.Drawing; +using System.Dynamic; namespace Roguelike.Tiles.LivingEntities { - public class Merchant : NPC, IAlly, IMerchant + public class TileProfessionNameParts { - public AllyBehaviour AllyBehaviour { get; set; } + public const string NPC = "NPC_"; + public const string Merchant = "merchant_"; + public const string Mecenary = "paladin_"; + public const string TeutonicKnight = "teutonic_knight_"; + public const string Smith = "smith_"; + public const string Woodcutter = "woodcutter_"; + public const string WarriorJurands = "warrior_Jurands"; + public const string WarriorLeszys = "warrior_Leszys"; + public const string Warrior = "warrior_"; + + + public const string Ally = "ally_";//ally is hound or a skeleton + public const string Knight = "knight_"; + public static string GetTileTagPart(EntityProffesionKind kind) + { + string pref = ""; + string rest = ""; + switch (kind) + { + case EntityProffesionKind.Unset: + break; + case EntityProffesionKind.King: + break; + case EntityProffesionKind.Prince: + break; + case EntityProffesionKind.Knight: + break; + case EntityProffesionKind.Priest: + break; + case EntityProffesionKind.Mercenary: + pref = NPC; + rest = Mecenary; + break; + case EntityProffesionKind.Merchant: + break; + case EntityProffesionKind.Peasant: + break; + case EntityProffesionKind.Bandit: + break; + case EntityProffesionKind.Adventurer: + break; + case EntityProffesionKind.Slave: + break; + case EntityProffesionKind.TeutonicKnight: + break; + case EntityProffesionKind.Smith: + break; + case EntityProffesionKind.Woodcutter: + break; + case EntityProffesionKind.Carpenter: + break; + case EntityProffesionKind.Warrior: + break; + default: + break; + } + + return pref + rest; + } + } + public class Merchant : NPC, IAlly, IMerchant + { public const int HoundPrice = 100; public bool AllowBuyHound { get; set; } = false; - public Merchant(Container cont) : base(cont) { Proffesion = EntityProffesionKind.Merchant; Gold = 100000; - Inventory.Capacity = 64; + inventory = new Inventory(cont); + inventory.Capacity = 80; + RelationToHero.Kind = RelationToHeroKind.Neutral; + + Immortal = true;//Mainly for Sanderus + #if ASCII_BUILD color = ConsoleColor.Yellow; #endif @@ -30,38 +98,38 @@ public Merchant(Container cont) : base(cont) public override void SetNameFromTag1() { var name = GetNameFromTag1(); - name.Replace("ally_Merchant", ""); + name.Replace(TileProfessionNameParts.Merchant, ""); name = name.Trim(); Name = name; } + Inventory inventory = null; [JsonIgnore] public override Inventory Inventory { - get => base.Inventory; + get => inventory; set { - base.Inventory = value; + Inventory = value; Inventory.PriceFactor = 4; Inventory.InvBasketKind = InvBasketKind.Merchant; } } - public bool Active { get; set; } - public AllyKind Kind { get => AllyKind.Merchant; } - public Point Point { get => point; set => point = value; } - - public bool TakeLevelFromCaster { get; } - internal void OnContextSwitched(Container container) { Inventory.Container = container; Inventory.Owner = this; } - public void SetNextLevelExp(double exp) + LootAbility lootAb; + internal LootAbility GetLootAbility() { - NextLevelExperience = exp; + if (lootAb == null) + lootAb = new LootAbility(true); + + return lootAb; } + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/NPC.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/NPC.cs index 635d9ae6..863698b7 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/NPC.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/NPC.cs @@ -1,37 +1,178 @@ using Dungeons.Core; +using Roguelike.Abilities; +using Roguelike.Abstract.Inventory; +using Roguelike.Abstract.Tiles; using Roguelike.Discussions; +using Roguelike.LootContainers; +using Roguelike.Spells; +using Roguelike.Tiles.Abstract; using Roguelike.Tiles.Interactive; +using Roguelike.Tiles.Looting; using SimpleInjector; using System; +using System.Collections.Generic; using System.Drawing; - +#pragma warning disable 8632 namespace Roguelike.Tiles.LivingEntities { - public interface INPC + public interface INPC : IAdvancedEntity { string Name { get; } TrainedHound TrainedHound { get; set; } - LivingEntity LivingEntity { get;} + LivingEntity LivingEntity { get; } Discussion Discussion { get; set; } void SetHasUrgentTopic(bool ut); bool HasUrgentTopic { get; set; } event EventHandler UrgentTopicChanged; + + DestPointDesc DestPointDesc { get; set; } + + public WalkKind WalkKind { get; } + + public string FollowedTargetName { get; set; } + + public RelationToHero RelationToHero { get; set; } + public IGodStatue GodStatue { get; set; } + int PointedByHeroCounter { get; set; } + } + + public enum DestPointState { + Unset, TravelingTo, StayingAtTarget, TravelingBack } - public class NPC : AdvancedLivingEntity, INPC, IApproachableByHero + public enum WalkKind { Unset, GoToHome, GoToInteractive, FollowingTarget } + + public enum DestPointActivityKind { Unset, Home, Privy, Grill } + + public class DestPointDesc + { + public DestPointDesc() + { + } + + public bool IsWalkToTargetInProcess => State != DestPointState.Unset; + + private DestPointState state; + + public Dungeons.Tiles.Tile? MoveOnPathTarget { get; set; } + + public Point TargetPoint { get; set; }//can not enter interactive, so its next to interactive one + public Point ReturnPoint { get; set; } + public DestPointState State + { + get => state; + set + { + state = value; + StateCounter = 0; + } + } + + public int StayingDuration { get; set; } = 4; + public int StateCounter { get; private set; } = 0; + internal bool IncreaseStateCounter() + { + if (StateCounter < StayingDuration - 1 || State != DestPointState.StayingAtTarget) + { + StateCounter++; + return true; + } + return false; + } + + internal InteractiveTile? UnbusyTarget() + { + if (MoveOnPathTarget is InteractiveTile it) + { + it.SetBusy(null); + ActivityKind = DestPointActivityKind.Unset; + if(MoveOnPathTarget == it) + MoveOnPathTarget = null; + return it; + } + + return null; + } + + public DestPointActivityKind ActivityKind { get; + set; } + //public Point OriginalTargetPoint { get; internal set; } + } + + /// + /// + /// + public class NPC : + LivingEntity, + INPC, + IApproachableByHero, + IAlly { public TrainedHound TrainedHound { get; set; } + public bool TakeLevelFromCaster => false; + public int PointedByHeroCounter { get; set; } - public NPC(Container cont) : base(cont, new Point().Invalid(), '!') + public NPC(Container cont) : base(new Point().Invalid(), '!', cont) { + } public LivingEntity LivingEntity => this; - - public bool ApproachedByHero { get ; set ; } + + public bool ApproachedByHero { get; set; } public string ActivationSound { get; set; } + + public WalkKind WalkKind { + get { + if (DestPointDesc.State == DestPointState.TravelingBack) + return WalkKind.GoToHome; + else if (DestPointDesc.State == DestPointState.TravelingTo) + return WalkKind.GoToInteractive; + else if (!string.IsNullOrEmpty(FollowedTargetName)) + return WalkKind.FollowingTarget; + + return WalkKind.Unset; + } + } + public string FollowedTargetName { get ; set ; } + + public IGodStatue GodStatue { get ; set ; } + public AllyBehaviour AllyBehaviour { get ; set; } + public bool Active { get; set; } + + public AllyKind Kind { get; }//TODO remove and ret from method? + + public Point Point + { + get => point; set => point = value; + } + + public bool PendingReturnToCamp { get ; set; } + + + public Discussion Discussion { get ; set ; } + public bool HasUrgentTopic { get ; set ; } + public DestPointDesc DestPointDesc { get; set ; } = new DestPointDesc(); + public RelationToHero RelationToHero { get; set; } = new RelationToHero(); + public int AbilityPoints { get ; set ; } + + public AbilitiesSet Abilities { get; set; } = AbilitiesSet.CreateEmpty(); + + public SpellStateSet Spells { get; set; } = SpellStateSet.CreateEmpty(); + + public bool IsMecenary => LivingEntity.IsMercenary; + + public virtual Inventory Inventory { get; set; } + + public int Gold { get; set ; } + public double NextLevelExperience { get; private set; } public event EventHandler Activated; + public event EventHandler UrgentTopicChanged; +#pragma warning disable 67 + public event EventHandler StatsRecalculated; + public event EventHandler LeveledUp; +#pragma warning restore 67 public bool Activate() { @@ -49,5 +190,72 @@ public string GetPlaceName() { return ""; } + + public void SetNextLevelExp(double exp) + { + NextLevelExperience += exp; + } + + public void SetHasUrgentTopic(bool ut) + { + this.HasUrgentTopic = ut; + if (UrgentTopicChanged != null) + UrgentTopicChanged(this, HasUrgentTopic); + } + + Dictionary activeEquipment = new Dictionary(); + public Dictionary GetActiveEquipment() + { + return activeEquipment; + } + + public bool IncreaseAbility(AbilityKind kind) + { + return true; + } + + public bool IncreaseSpell(SpellKind sk) + { + return true; + } + + public string GetExpInfo() + { + return ""; + } + + public bool InventoryAcceptsItem(Inventory inventory, Loot loot, AddItemArg addItemArg) + { + return true; + } + + public bool MoveEquipmentCurrent2Inv(IEquipment eq, CurrentEquipmentKind cek) + { + return false; + } + + public int GetPrice(Loot loot) + { + return loot.Price*4; + } + + public bool GetGoldWhenSellingTo(IInventoryOwner dest) + { + return this != dest; + } + + public double Experience { get; set; } + //bool IAdvancedEntity.IsMercenary { get => LivingEntity.IsMercenary;} + + public bool IncreaseExp(double factor) + { + Experience += factor; + return true; + } + + public bool IsSellable(Loot loot) + { + return loot.IsSellable(); + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/TrainedHound.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/TrainedHound.cs index a18934ae..067f9f47 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/TrainedHound.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/LivingEntities/TrainedHound.cs @@ -1,4 +1,6 @@ -using SimpleInjector; +using Roguelike.Attributes; +using Roguelike.Spells; +using SimpleInjector; namespace Roguelike.Tiles.LivingEntities { @@ -19,6 +21,21 @@ public TrainedHound(Container cont) : base(cont) #endif } + public override float GetStartStat(EntityStatKind esk) + { + var startStat = base.GetStartStat(esk); + if (esk == EntityStatKind.Strength) + startStat += SkeletonSpell.SkeletonSpellStrengthIncrease; + + else if (esk == EntityStatKind.Defense) + startStat += SkeletonSpell.SkeletonSpellDefenseIncrease; + + else if (esk == EntityStatKind.Health) + startStat += SkeletonSpell.SkeletonSpellHealthIncrease; + + return startStat; + } + public void bark(bool strong) { PlaySound("ANIMAL_Dog_Bark_02_Mono"); diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Enchanter.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Enchanter.cs index 6356a446..838a3be3 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Enchanter.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Enchanter.cs @@ -5,7 +5,7 @@ namespace Roguelike.Tiles.Looting { - public enum EnchantSrc { Unset, Ruby, Emerald, Diamond, Amber, Fang, Tusk, Claw } + public enum EnchantSrc { Unset, Ruby, Emerald, Diamond, Amber, Fang, /*Tusk*/ Claw } public enum EnchanterSize { Small, Medium, Big } public abstract class Enchanter : StackedLoot @@ -15,6 +15,12 @@ public abstract class Enchanter : StackedLoot public static string Big = "big"; public bool Damaged { get; set; } + public Enchanter() + { + EnchanterSize = EnchanterSize.Small; + + } + EnchanterSize enchanterSize = EnchanterSize.Small; public EnchanterSize EnchanterSize { @@ -84,6 +90,13 @@ public virtual int GetStatIncrease(EquipmentKind ek, EntityStatKind esk = Entity } public abstract void SetProps(); + protected abstract string CalcTagFromProps(); + + protected void SetPropsCommon() + { + SetPrice(); + tag1 = CalcTagFromProps(); + } public abstract bool ApplyTo(Equipment eq, Func ekProvider, out string error); @@ -104,6 +117,7 @@ protected virtual void SetPrice() protected void SetName(string typeName) { Name = EnchanterSize + " " + typeName; + DisplayedName = Name; } public override string PrimaryStatDescription @@ -123,5 +137,12 @@ public override string PrimaryStatDescription { } } + + public override RecipeKind GetMatchingRecipe(Loot other) + { + if (other is Equipment) + return RecipeKind.EnchantEquipment; + return RecipeKind.Unset; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Equipment.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Equipment.cs index 7d1d53a9..6b55501c 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Equipment.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Equipment.cs @@ -1,14 +1,11 @@ using Dungeons.Core; -using Roguelike.Abstract; using Roguelike.Attributes; using Roguelike.Calculated; using Roguelike.Extensions; using Roguelike.TileParts; using Roguelike.Tiles.LivingEntities; -using Roguelike.Tiles.Looting; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; namespace Roguelike.Tiles.Looting @@ -40,6 +37,8 @@ public interface IEquipment string Tag1 { get; } bool IsBetter(IEquipment currentEq); + + float GetReqStatValue(EntityStat es); } public class Equipment : Loot, IEquipment @@ -51,7 +50,9 @@ public class Equipment : Loot, IEquipment EquipmentClass _class; public int RequiredLevel { get; set; } = 1; EntityStats requiredStats = new EntityStats(); - public bool IsIdentified { get; set; } = true; + public bool IsIdentified { + get; + set; } = true; public event EventHandler Identified; public bool Enchantable { get { return maxEnchants > 0; } } @@ -74,13 +75,25 @@ public Equipment() : this(EquipmentKind.Unset) LootKind = LootKind.Equipment; } + public override bool IsMatchingRecipe(RecipeKind kind) + { + if(Class == EquipmentClass.Unique) + return false; + if (kind == RecipeKind.OneEq || kind == RecipeKind.TwoEq || + kind == RecipeKind.EnchantEquipment || kind == RecipeKind.UnEnchantEquipment) + return true; + return false; + } + public void SetMaterial(EquipmentMaterial material) { if (material == EquipmentMaterial.Unset) return; + if (Class == EquipmentClass.Unique) + return; if (this.Material != EquipmentMaterial.Unset && - this.Material != EquipmentMaterial.Bronze) + this.Material != EquipmentMaterial.Bronze) { Dungeons.DebugHelper.Assert(false);//ups already set return; @@ -110,6 +123,7 @@ public void SetMaterial(EquipmentMaterial material) { if (Class != EquipmentClass.Unique) { + this.DisplayedName = ""; SetDisplayedName(); EnhanceStatsDueToMaterial(material); } @@ -118,17 +132,27 @@ public void SetMaterial(EquipmentMaterial material) public void SetDisplayedName() { - var dn = ""; - var name = Name; + if (string.IsNullOrEmpty(displayedName) || displayedName == "Unset") + this.DisplayedName = GetDefaultName(); + } + + protected override string GetDefaultName() + { + return GetNameWithMaterial(Name); + } + + private string GetNameWithMaterial(string name) + { + string defaultName = ""; if (Material != EquipmentMaterial.Unset) { - dn = this.Material.ToDescription() + " "; + defaultName = this.Material.ToDescription() + " "; name = name.ToLower(); } else name = name.ToUpperFirstLetter(); - this.DisplayedName = dn + name; + return defaultName + name; } public override string Name @@ -136,13 +160,102 @@ public override string Name get => base.Name; set { - base.Name = value; - //if (Class == EquipmentClass.Unique || (this is Weapon wpn && wpn.IsMagician)) - DisplayedName = Name.ToUpperFirstLetter(); + //if (Name.Contains("Armor") && !DisplayedName.Contains("Armor")) + //{ + // int k = 0; + // k++; + //} + //if (Name.Contains("Shield") && !DisplayedName.Contains("Shield")) + //{ + // int k = 0; + // k++; + //} + var val = value; + base.Name = val; + + + EnsureDisplayedName(); + + } + } + + + + protected override bool DisplayedNameNeedsToBeSet() + { + if (!name.Any()) + return false; + if (Class == EquipmentClass.Unique) + { + if (StringStartsWithMaterial(DisplayedName, EquipmentMaterial.Bronze) || + StringStartsWithMaterial(DisplayedName, EquipmentMaterial.Iron) || + StringStartsWithMaterial(DisplayedName, EquipmentMaterial.Steel)) + return true; + } + + if (name.Any()) + { + var dn = DisplayedName; + if (!Char.IsUpper(name[0]) && (!dn.Any() || !Char.IsUpper(dn[0]))) + return true; + } + var assetNoLevel = EnsureLevelNotInAssetName(tag1); + + if (assetNoLevel.Any(char.IsDigit))//wand1 + return false; + + return base.DisplayedNameNeedsToBeSet(); + } + + + private void EnsureDisplayedName() + { + if (DisplayedNameNeedsToBeSet()) + { + if (Class == EquipmentClass.Unique) + { + DisplayedName = Name.ToUpperFirstLetter(); + } + else if (Name.Any()) + { + DisplayedName = Name.ToUpperFirstLetter(); + } } } + bool StringStartsWithMaterial(string str) + { + return StringStartsWithMaterial(str, Material); + } + bool StringStartsWithMaterial(string str, EquipmentMaterial mat) + { + return str.StartsWith(mat.ToDescription()); + } + + + public override string DisplayedName + { + set + { + var val = value; + if (value.Any()) + { + if (IsMaterialAware() && Class != EquipmentClass.Unique && !StringStartsWithMaterial(val)) + { + val = GetNameWithMaterial(val); + } + } + base.DisplayedName = val; + } + } + + //public void SetNameFromAsset(string asset) + //{ + // DisplayedName = ""; + // Name = asset.Replace("_", " ").ToUpperFirstLetter(); + //} + protected virtual void EnhanceStatsDueToMaterial(EquipmentMaterial material) { throw new Exception("EnhanceMaterial!"); @@ -150,7 +263,7 @@ protected virtual void EnhanceStatsDueToMaterial(EquipmentMaterial material) public bool MakeEnchantable(int enchantSlotsToMake = 1) { - if (!IsIdentified) + if ( Class != EquipmentClass.Plain && !IsIdentified) return false; if (maxEnchants > 0) return false; //already done @@ -252,10 +365,7 @@ internal void RemoveMagicStat(EntityStatKind stat, int statValue) public EquipmentClass Class { - get - { - return _class; - } + get {return _class;} set { @@ -265,14 +375,15 @@ public EquipmentClass Class public virtual EquipmentKind EquipmentKind { - get - { - return kind; - } - + get{return kind;} set { kind = value; + if (kind == EquipmentKind.Armor && !name.Contains("Armor")) + { + int k = 0; + k++; + } } } @@ -329,7 +440,9 @@ public void SetPrimaryStat(EntityStatKind primaryStat, float value) { this.primaryStat = new EntityStat(primaryStat, 0); PrimaryStatValue = value; - this.Name += " of " + primaryStat.ToDescription(); + //if (string.IsNullOrEmpty(this.Name)) + // Name = EquipmentKind.ToString(); + //this.Name += " of " + primaryStat.ToDescription(); } public List> GetPossibleMagicStats() @@ -440,6 +553,33 @@ public virtual bool IsMaterialAware() } } + //TODO + //else if (this is Armor arm && + // (arm.EquipmentKind == EquipmentKind.Helmet || arm.EquipmentKind == EquipmentKind.Shield)) + //{ + // if (arm.EquipmentKind == EquipmentKind.Helmet) + // { + // if (arm.tag1 == "helm" || + // arm.tag1 == "full_helm" || + // arm.tag1 == "holly_helm" + // ) + // { + // return true; + // } + // } + // if (arm.EquipmentKind == EquipmentKind.Shield) + // { + // if (arm.tag1 == "enhanced_buckler" || + // arm.tag1 == "long_shield" || + // arm.tag1 == "war_shield" || + // arm.tag1 == "king's_buckler" + // ) + // { + // return true; + // } + // } + //} + return false; } @@ -555,9 +695,15 @@ void AddMagicStat(EntityStatKind stat, bool secLevel) AddMagicStat(stat, secLevel, value, false); } - public void MakeMagicSecLevel(EntityStatKind stat, int statValue) + public void PromoteToSecondMagicClass() { - AddMagicStat(stat, true, statValue, false); + if (!IsSecondMagicLevel) + { + var stats = GetPossibleMagicStats(); + var stat = RandHelper.GetRandomElem(stats); + AddMagicStat(stat.Key, true); + + } } EntityStats unidentifiedStats; @@ -612,26 +758,6 @@ public override void HandleGenerationDone() public bool priceAlrIncreased; - //public void IncreasePriceBasedOnExtInfo() - //{ - // if(!IsIdentified) - // return; - // //if (priceAlrIncreased) - // //{ - // // //DebugHelper.Assert(false, "priceAlrIncreased"); - // // return; - // //} - // basePrice = Price; - // foreach (var st in ExtendedInfo.Stats.GetStats()) - // { - // var prInc = GetPriceForFactor(st.Key, (int)st.Value.Factor); - // if (prInc > 0) - // Price += prInc; - // } - - // //priceAlrIncreased = true; - //} - public int GetPriceForFactor(EntityStatKind esk, int factor) { var price = 0; @@ -670,7 +796,7 @@ public void SetUnique(EntityStats lootStats, int lootLevel) { SetClass(EquipmentClass.Unique, lootLevel, lootStats); Material = EquipmentMaterial.Unset; - DisplayedName = Name.ToUpperFirstLetter(); + EnsureDisplayedName(); } public int MinDropDungeonLevel { get { return levelIndex; } } @@ -704,8 +830,9 @@ public virtual void SetLevelIndex(int li) public virtual void SetClass(EquipmentClass _class, int levelIndex, EntityStats lootStats = null, bool magicOfSecondLevel = false) { - SetLevelIndex(levelIndex); SetClass(_class); + SetLevelIndex(levelIndex); + if (lootStats == null) { if (_class == EquipmentClass.Magic) @@ -720,6 +847,7 @@ public virtual void SetClass(EquipmentClass _class, int levelIndex, EntityStats private void SetClass(EquipmentClass _class) { Class = _class; + IsIdentified = true; if (_class != EquipmentClass.Plain) IsIdentified = false; } @@ -853,5 +981,16 @@ public void SetPriceFromLevel() } public string Tag1 { get { return tag1; } } + + public override RecipeKind GetMatchingRecipe(Loot other) + { + if (other is Gem || other is HunterTrophy) + return RecipeKind.EnchantEquipment; + + if (other is Equipment) + return RecipeKind.TwoEq; + + return RecipeKind.Unset; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/FightItem.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/FightItem.cs index 3f718e7d..d5372c13 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/FightItem.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/FightItem.cs @@ -1,4 +1,5 @@ -using Dungeons.Tiles; +using Dungeons.Core; +using Dungeons.Tiles; using Newtonsoft.Json; using Roguelike.Abilities; using Roguelike.Abstract.Projectiles; @@ -30,6 +31,14 @@ public enum FightItemKind IronBolt = 56,//Iron Arrowhead SteelBolt = 57,//Steel Arrowhead + PoisonArrow = 60, + IceArrow = 61, + FireArrow = 62, + + PoisonBolt = 66, + IceBolt = 67, + FireBolt = 68, + PoisonCocktail = 70, WeightedNet = 75, @@ -93,7 +102,7 @@ public FightItem(FightItemKind kind) { this.FightItemKind = kind; this.LootKind = LootKind.FightItem; - + Count = 1; } //can not be used as torch amount is calculated see GetStackedCountForHotBar @@ -129,6 +138,11 @@ public bool IsBowLikeAmmo } public const int BaseCannonBallDamage = 60; + string GetDescForBowLike(string suffix) + { + return Name + suffix; + } + public virtual FightItemKind FightItemKind { get { return fightItemKind; } @@ -143,6 +157,8 @@ public virtual FightItemKind FightItemKind { PrimaryStatDescription = "Stone, can cause harm if thrown by a skilled man."; HitTargetSound = "punch"; + Price = 2; + Count = (int)RandHelper.GetRandomFloatInRange(8, 10); } else if (fightItemKind == FightItemKind.CannonBall) { @@ -172,28 +188,57 @@ public virtual FightItemKind FightItemKind } else if (fightItemKind == FightItemKind.PlainArrow) { - baseDamage += 1; + baseDamage += 2; Price *= 2; - PrimaryStatDescription = Name + ", basic ammo for a bow"; + PrimaryStatDescription = GetDescForBowLike(", basic ammo for a bow"); HitTargetSound = "arrow_hit_body"; } else if (fightItemKind == FightItemKind.IronArrow) { baseDamage += 5; Price *= 3; - PrimaryStatDescription = Name + ", ammo with an iron head for a bow"; + PrimaryStatDescription = GetDescForBowLike(", ammo with an iron head for a bow"); HitTargetSound = "arrow_hit_body"; } else if (fightItemKind == FightItemKind.SteelArrow) { - baseDamage += 9; + baseDamage += 10; Price *= 4; - PrimaryStatDescription = Name + ", ammo with a steel head for a bow"; + PrimaryStatDescription = GetDescForBowLike(", ammo with a steel head for a bow"); + HitTargetSound = "arrow_hit_body"; + } + + else if ( + fightItemKind == FightItemKind.PoisonArrow || + fightItemKind == FightItemKind.IceArrow || + fightItemKind == FightItemKind.FireArrow || + fightItemKind == FightItemKind.PoisonBolt || + fightItemKind == FightItemKind.IceBolt || + fightItemKind == FightItemKind.FireBolt + ) + { + baseDamage += 5; + Price *= 5; + var head = fightItemKind.ToDescription(); + var arrowLikeKind = fightItemKind.ToDescription(); + + var kind = head.Replace("Arrow", "").Replace("Bolt", ""); + if (kind == "Poison") + kind = "poisonous"; + else if (kind == "Ice") + kind = "freezing"; + else if (kind == "Fire") + kind = "flaming"; + + PrimaryStatDescription = GetDescForBowLike(", ammo with a " + + kind + + " head for a "+ (arrowLikeKind.Contains("Arrow") ? "bow" : "crossbow" )); HitTargetSound = "arrow_hit_body"; } + else if (fightItemKind == FightItemKind.PlainBolt) { - baseDamage += 2; + baseDamage += 3; Price *= 2; PrimaryStatDescription = Name + ", basic ammo for a crossbow"; HitTargetSound = "arrow_hit_body"; @@ -207,7 +252,7 @@ public virtual FightItemKind FightItemKind } else if (fightItemKind == FightItemKind.SteelBolt) { - baseDamage += 10; + baseDamage += 12; Price *= 4; PrimaryStatDescription = Name + ", ammo with a steel head for a crossbow"; HitTargetSound = "arrow_hit_body"; @@ -468,7 +513,7 @@ public override List GetLootStatInfo(LivingEntity caller) if (FightItemKind == FightItemKind.WeightedNet) { var ab = GetAbility(); - lsi.Desc = "Duration: "+ ab.PrimaryStat.Factor; + lsi.Desc = "Duration: "+ ((Duration -1) + ab.PrimaryStat.Factor); } res.Add(lsi); @@ -476,6 +521,16 @@ public override List GetLootStatInfo(LivingEntity caller) } return res; } + + public override bool IsMatchingRecipe(RecipeKind kind) + { + if (base.IsMatchingRecipe(kind)) + return true; + if ((kind == RecipeKind.Arrows ||kind == RecipeKind.Bolts) && FightItemKind == FightItemKind.Stone) + return true; + + return false; + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Food.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Food.cs index ad163712..ee7f5845 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Food.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Food.cs @@ -215,5 +215,12 @@ public override string GetId() { return base.GetId() + "_" + Kind + "_" + Roasted; } + + public override bool IsMatchingRecipe(RecipeKind kind) + { + if (kind == RecipeKind.NiesiolowskiSoup && this.Kind == FoodKind.Plum) + return true; + return false; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Gem.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Gem.cs index cf3e6fef..6c8a3fa4 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Gem.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Gem.cs @@ -14,12 +14,24 @@ public enum GemKind { Unset, Ruby, Emerald, Diamond, Amber } public class Gem : Enchanter { - public GemKind GemKind { get; set; } + + public GemKind GemKind + { + get => gemKind; + set + { + gemKind = value; + EnchantSrcFromGemKind(); + SetName(GemKind.ToDescription()); + } + } static Dictionary enhancmentPropsRuby = new Dictionary(); static Dictionary enhancmentPropsEmer = new Dictionary(); static Dictionary enhancmentPropsDiam = new Dictionary(); static Dictionary> enhancmentProps = new Dictionary>(); + private GemKind gemKind; + public int GameLevel { get; set; } = 1; public Gem() : this(GemKind.Unset) @@ -39,16 +51,19 @@ public Gem(GemKind kind = GemKind.Unset, int gameLevel = 0) Symbol = '*'; Name = "Gem"; GemKind = kind; - - EnchanterSize = EnchanterSize.Small; - EnchantSrcFromGemKind(); - + if (gameLevel >= 0) SetRandomKindAndLevelSize(gameLevel, kind == GemKind.Unset); else SetProps(); - - //PrimaryStatDescription = desc; + } + + public override bool IsMatchingRecipe(RecipeKind kind) + { + if (kind == RecipeKind.EnchantEquipment || kind == RecipeKind.UnEnchantEquipment || + kind == RecipeKind.ThreeGems || kind == RecipeKind.TransformGem) + return true; + return false; } public void EnchantSrcFromGemKind() @@ -129,13 +144,11 @@ public void SetRandomKindAndLevelSize(int gameLevel, bool setKind) public override void SetProps() { - SetPrice(); - tag1 = CalcTagFrom(); - SetName(GemKind.ToDescription()); + base.SetPropsCommon(); } - public string CalcTagFrom() + protected override string CalcTagFromProps() { return CalcTagFrom(GemKind, EnchanterSize); } @@ -145,8 +158,6 @@ public static string CalcTagFrom(GemKind kind, EnchanterSize size) return kind.ToString().ToLower() + "_" + size.ToString().ToLower(); } - - public override bool ApplyTo(Equipment eq, Func ekProvider, out string error) { error = ""; @@ -320,5 +331,6 @@ protected override void SetPrice() if (GemKind == GemKind.Amber) Price *= 2; } + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Hooch.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Hooch.cs index 553b183f..71ecd804 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Hooch.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Hooch.cs @@ -60,19 +60,12 @@ public PercentageFactor GetSecondPercentageStatIncrease() return new PercentageFactor(-20); } - //string[] extDesc; - //public override string[] GetExtraStatDescription() - //{ - // if (extDesc == null) - // { - // extDesc = new string[4]; - // //extDesc[0] = "Press O to drink it."; - // extDesc[0] = "Drink Effect:"; - // extDesc[1] = "Strength +" + StrengthPercentage + "%"; - // extDesc[2] = "ChanceToChit -" + ChanceToHitPercentage + "%"; - // extDesc[3] = "Tour Lasting: " + TourLasting; - // } - // return extDesc; - //} + public override bool IsMatchingRecipe(RecipeKind kind) + { + if (kind == RecipeKind.AntidotePotion || kind == RecipeKind.ExplosiveCocktail) + return true; + return false; + } + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/HunterTrophy.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/HunterTrophy.cs index bfd58812..366156b0 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/HunterTrophy.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/HunterTrophy.cs @@ -7,34 +7,55 @@ namespace Roguelike.Tiles.Looting { - public enum HunterTrophyKind { Unset, Fang, Tusk, Claw }//Fang-Tooth + public enum HunterTrophyKind { Unset, Fang, /*Tusk,*/ Claw }//Fang-Tooth public class HunterTrophy : Enchanter { - public HunterTrophyKind TinyTrophyKind { get; set; } + HunterTrophyKind tinyTrophyKind; + public HunterTrophyKind TinyTrophyKind + { + get => tinyTrophyKind; + set { + tinyTrophyKind = value; + SetProps(); + } + } - static Dictionary> enhancmentProps = new Dictionary>(); + static Dictionary> enhancmentProps = + new Dictionary>(); static Dictionary enhancmentPropsFang = new Dictionary(); - static Dictionary enhancmentPropsTusk = new Dictionary(); + //static Dictionary enhancmentPropsTusk = new Dictionary(); static Dictionary enhancmentPropsClaw = new Dictionary(); + public HunterTrophy():this(HunterTrophyKind.Claw) + { + + } + public HunterTrophy(HunterTrophyKind kind) + { + Price = 5; + Symbol = '&'; + LootKind = LootKind.HunterTrophy; + this.TinyTrophyKind = kind; + SetProps(); + } public static string[] TinyTrophiesTags = new[] { - Enchanter.Big+ "_claw", Enchanter.Big + "_fang", - Enchanter.Medium + "_claw",Enchanter.Medium+"_fang", - Enchanter.Small+"_claw", Enchanter.Small+"_fang" + Enchanter.Big+ "_claw", Enchanter.Big + "_fang",//, Enchanter.Big + "_tusk", + Enchanter.Medium + "_claw", Enchanter.Medium+"_fang",//, Enchanter.Medium+ "_tusk", + Enchanter.Small+"_claw", Enchanter.Small+"_fang",//, Enchanter.Small+"_tusk" }; static HunterTrophy() { PopulateProps(enhancmentPropsFang, EntityStatKind.Defense, EntityStatKind.ChanceToBulkAttack, EntityStatKind.ChanceToEvadeMeleeAttack); - PopulateProps(enhancmentPropsTusk, EntityStatKind.Health, EntityStatKind.ChanceToCauseBleeding, EntityStatKind.Strength); - PopulateProps(enhancmentPropsClaw, EntityStatKind.ChanceToMeleeHit, EntityStatKind.ChanceToStrikeBack, EntityStatKind.Dexterity); + PopulateProps(enhancmentPropsClaw, EntityStatKind.Health, EntityStatKind.ChanceToCauseBleeding, EntityStatKind.Strength); + //PopulateProps(enhancmentPropsTusk, EntityStatKind.ChanceToMeleeHit, EntityStatKind.ChanceToStrikeBack, EntityStatKind.Dexterity); enhancmentProps[HunterTrophyKind.Fang] = enhancmentPropsFang; - enhancmentProps[HunterTrophyKind.Tusk] = enhancmentPropsTusk; + //enhancmentProps[HunterTrophyKind.Tusk] = enhancmentPropsTusk; enhancmentProps[HunterTrophyKind.Claw] = enhancmentPropsClaw; } @@ -43,19 +64,12 @@ public override string GetId() return base.GetId() + TinyTrophyKind + " " + EnchanterSize; } - public HunterTrophy(HunterTrophyKind kind) - { - Price = 5; - Symbol = '&'; - LootKind = LootKind.HunterTrophy; - SetKind(kind); - } + private void SetKind(HunterTrophyKind kind) { - TinyTrophyKind = kind; + tinyTrophyKind = kind; SetName(kind.ToDescription()); - SetPrice(); switch (kind) { @@ -67,10 +81,10 @@ private void SetKind(HunterTrophyKind kind) EnchantSrc = EnchantSrc.Fang; PrimaryStatDescription = "Sharp, hard, ready to bite. "; break; - case HunterTrophyKind.Tusk: - EnchantSrc = EnchantSrc.Tusk; - PrimaryStatDescription = "Big, sharp, ready to tear somebody apart. "; - break; + //case HunterTrophyKind.Tusk: + // EnchantSrc = EnchantSrc.Tusk; + // PrimaryStatDescription = "Big, sharp, ready to tear somebody apart. "; + // break; case HunterTrophyKind.Claw: EnchantSrc = EnchantSrc.Claw; PrimaryStatDescription = "Sharp, hard, ready to claw. "; @@ -78,9 +92,6 @@ private void SetKind(HunterTrophyKind kind) default: break; } - - //if (PrimaryStatDescription.Any()) - // PrimaryStatDescription += Strings.DropOnEnchantable; } @@ -128,6 +139,15 @@ public override bool ApplyTo(Equipment eq, Func ekProvider, out s public override void SetProps() { SetKind(this.TinyTrophyKind);//refresh + base.SetPropsCommon(); + } + + protected override string CalcTagFromProps() + { + var tags = TinyTrophiesTags + .Where(i => i.Contains(TinyTrophyKind.ToString().ToLower()) && i.Contains(EnchanterSize.ToString().ToLower())) + .SingleOrDefault(); + return tags??""; } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Jewellery.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Jewellery.cs index 424a3ffa..7a33df8d 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Jewellery.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Jewellery.cs @@ -46,7 +46,7 @@ public bool IsPendant set { isPendant = value; } } - public Jewellery() : base(EquipmentKind.Ring) + public Jewellery() : base(EquipmentKind.Unset) { } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Key.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Key.cs index a198bf5b..34ae5736 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Key.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Key.cs @@ -32,6 +32,7 @@ public Key() Name = "Key"; tag1 = "key"; Revealed = true; + Kind = KeyKind.Room; } public string KeyName { get; set; } @@ -52,7 +53,7 @@ public KeyKind Kind private void SetDescFromKind() { - var desc = ""; + var desc = "Opens something"; if (kind == KeyKind.Room) desc = "Opens a door"; else if (kind == KeyKind.BossRoom) @@ -91,7 +92,14 @@ public void SetHandlePart() public bool Matches(KeyHalf other) { - return tag1 != other.tag1; + return other !=null && tag1 != other.tag1; + } + + public override RecipeKind GetMatchingRecipe(Loot other) + { + if (other is KeyHalf && Matches(other as KeyHalf)) + return RecipeKind.TwoEq; + return RecipeKind.Unset; } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Loot.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Loot.cs index 7deb6ec4..435bfeb0 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Loot.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Loot.cs @@ -11,7 +11,14 @@ using System.Linq; namespace Roguelike.Tiles -{ +{ + namespace Looting + { + public interface IGodStatue + { + } + } + public class Strings { public const string PartOfCraftingRecipe = "Part of the crafting recipe."; @@ -142,7 +149,7 @@ public virtual bool Positionable public bool StackedInInventory { - get { return this is Roguelike.Tiles.Looting.StackedLoot; } + get { return this is StackedLoot; } } public Guid Id @@ -187,7 +194,7 @@ public override bool Equals(object obj) if (!this.StackedInInventory) return this.GetHashCode() == other.GetHashCode(); - return (this as Looting.StackedLoot).GetId() == (other as Looting.StackedLoot).GetId(); + return (this as StackedLoot).GetId() == (other as StackedLoot).GetId(); } public virtual bool IsConsumable() @@ -307,7 +314,6 @@ protected static string PartOfCraftingRecipe get { return "Part of a crafting recipe."; } } - [JsonIgnore] public ILootSource source; [JsonIgnore] public ILootSource Source @@ -336,5 +342,19 @@ public virtual bool IsCollectable get { return true; } } + public virtual RecipeKind GetMatchingRecipe(Loot other) + { + return RecipeKind.Unset; + } + + public virtual RecipeKind[] GetMatchingRecipes(Loot[] otherLoot) + { + return new[] { RecipeKind.Unset }; + } + + public virtual bool IsMatchingRecipe(RecipeKind kind) + { + return false; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/MagicDust.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/MagicDust.cs index 4db464de..1c371b93 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/MagicDust.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/MagicDust.cs @@ -14,5 +14,10 @@ public MagicDust() Price = 5; PrimaryStatDescription = PartOfCraftingRecipe; } + + public override bool IsMatchingRecipe(RecipeKind kind) + { + return true; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Misc.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Misc.cs index 3d22ffb6..85b1effe 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Misc.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Misc.cs @@ -9,9 +9,19 @@ public Feather() Name = "Feather"; Count = 20; PrimaryStatDescription = "Part of the recipe"; - Price = 5; + Price = 2; tag1 = "feather"; } + + public override bool IsMatchingRecipe(RecipeKind kind) + { + if (base.IsMatchingRecipe(kind)) + return true; + if ((kind == RecipeKind.Arrows || kind == RecipeKind.Bolts)) + return true; + + return false; + } } public class Hazel : StackedLoot @@ -20,12 +30,26 @@ public Hazel() { Name = "Hazel"; PrimaryStatDescription = "Part of the recipe"; - Price = 5; - Count = (int)RandHelper.GetRandomFloatInRange(30,40); + Price = 2; + Count = (int)RandHelper.GetRandomFloatInRange(15,40); tag1 = "hazel"; + + } + + public override bool IsMatchingRecipe(RecipeKind kind) + { + if (base.IsMatchingRecipe(kind)) + return true; + if ((kind == RecipeKind.Arrows || kind == RecipeKind.Bolts)) + return true; + + return false; } } + + + public enum GobletKind { Silver, Gold } public class Goblet : StackedLoot @@ -87,5 +111,7 @@ public override string GetId() { return Kind + "_" + base.GetId(); } + + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Mushroom.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Mushroom.cs index b0301d7e..63820d1c 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Mushroom.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Mushroom.cs @@ -76,38 +76,10 @@ protected virtual void SetDefaultTagFromKind() tag1 = "mash_Boletus"; } - //public override Loot CreateCrafted(Loot other) - //{ - // if (other is Potion && (other as Potion).Kind == PotionKind.Health) - // { - // return new SpecialPotion(SpecialPotionKind.Strength, SpecialPotionSize.Small); - // } - // else if (other is Potion && (other as Potion).Kind == PotionKind.Mana) - // { - // return new SpecialPotion(SpecialPotionKind.Magic, SpecialPotionSize.Small); - // } + - // return null; - //} - - //public override bool IsCraftableWith(LootBase other) - //{ - // if (other is Potion) - // { - // return true; - // } - - // return false; - //} void SetPrimaryStatDesc() { - //string desc = "Turns " + Extensions.FirstCharToUpper(SrcPotion.ToString()) + " Potion into"; - //if (DestPotion == SpecialPotionKind.Magic) - // desc += " a Magic "; - //else - // desc += " a Strength "; - - //desc += "Potion."; string desc = PartOfCraftingRecipe; desc += GetConsumeDesc(" Consumable"); PrimaryStatDescription = desc; @@ -118,13 +90,21 @@ public override string ToString() return base.ToString() + " " + MushroomKind; } - //public Loot Loot => this; - - //public EntityStatKind EnhancedStat => EntityStatKind.Health; - public override string[] GetExtraStatDescription() { return extraStatDescription; } + + public override bool IsMatchingRecipe(RecipeKind kind) + { + if (base.IsMatchingRecipe(kind)) + return true; + if((kind == RecipeKind.Toadstools2Potion || kind == RecipeKind.TransformPotion) && + (MushroomKind == MushroomKind.BlueToadstool || MushroomKind == MushroomKind.RedToadstool)) + return true; + if (kind == RecipeKind.CraftSpecialPotion && MushroomKind == MushroomKind.Boletus) + return true; + return false; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Plant.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Plant.cs index 23998bc7..de61b1d3 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Plant.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Plant.cs @@ -13,7 +13,15 @@ public enum PlantKind public class Plant : Consumable { - public PlantKind Kind { get; set; } + PlantKind kind; + public PlantKind Kind + { + get => kind; + set { + kind = value; + SetKindMembers(kind); + } + } public Plant() : this(PlantKind.Unset) { @@ -35,6 +43,11 @@ public override bool IsConsumable() public void SetKind(PlantKind kind) { Kind = kind; + SetKindMembers(kind); + } + + private void SetKindMembers(PlantKind kind) + { if (Kind == PlantKind.Sorrel) Duration = 5; else @@ -68,31 +81,13 @@ public override string GetId() return base.GetId() + "_" + Kind; } - //public override float GetStatIncrease(LivingEntity caller) - //{ - // return 10;// ConsumableHelper.GetStatIncrease(caller, this, 10); - //} - - //public override string PrimaryStatDescription => primaryStatDesc; - - //public EntityStatKind StatKind - //{ - // get - // { - // switch (Kind) - // { - // case PlantKind.Unset: - // break; - // case PlantKind.Thistle: - // break; - // case PlantKind.Sorrel: - // return EntityStatKind.Health; - // default: - // break; - // } - - // return EntityStatKind.Unset; - // } - //} + public override bool IsMatchingRecipe(RecipeKind kind) + { + if (kind == RecipeKind.AntidotePotion && this.Kind == PlantKind.Thistle) + return true; + if (kind == RecipeKind.NiesiolowskiSoup && this.Kind == PlantKind.Sorrel) + return true; + return false; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Potion.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Potion.cs index a0cda122..ba4ec7c3 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Potion.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Potion.cs @@ -12,7 +12,7 @@ public class Potion : Consumable public PotionKind Kind { get { return kind; } - set + set { kind = value; @@ -37,13 +37,13 @@ public PotionKind Kind PrimaryStatDescription = "Removes poison effect"; StatKind = EntityStatKind.Unset; } - + } } public Potion() : this(PotionKind.Unset) { - + } public Potion(PotionKind kind) @@ -65,7 +65,7 @@ public override PercentageFactor GetPercentageStatIncrease() public void SetKind(PotionKind kind) { this.Kind = kind; - + } public override string GetId() @@ -77,5 +77,17 @@ public override string ToString() { return base.ToString() + " PotionKind: " + Kind; } + + public override bool IsMatchingRecipe(RecipeKind kind) + { + if (base.IsMatchingRecipe(kind)) + return true; + if (kind == RecipeKind.TransformPotion && + (Kind == PotionKind.Mana || Kind == PotionKind.Health)) + return true; + if (kind == RecipeKind.CraftSpecialPotion && (Kind == PotionKind.Mana || Kind == PotionKind.Health)) + return true; + return false; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/ProjectileFightItem.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/ProjectileFightItem.cs index e4a93676..55b78099 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/ProjectileFightItem.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/ProjectileFightItem.cs @@ -124,6 +124,11 @@ public bool IsBetter(IEquipment currentEq) return this.Price > currentEq.Price; } + public float GetReqStatValue(EntityStat es) + { + return es.Value.TotalValue; + } + //public bool Countable { get; set; } = true; } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Recipe.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Recipe.cs index 2f4dbb16..58b6ef34 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Recipe.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Recipe.cs @@ -4,7 +4,8 @@ namespace Roguelike.Tiles.Looting { public enum RecipeKind { - Unset, Custom, ThreeGems, OneEq, TransformPotion, TwoEq, TransformGem, Toadstools2Potion, ExplosiveCocktail, Pendant, + Unset, Custom, ThreeGems, OneEq, TransformPotion, TwoEq, TransformGem, Toadstools2Potion, ExplosiveCocktail, + Pendant,//Not used EnchantEquipment, CraftSpecialPotion, RechargeMagicalWeapon, AntidotePotion, UnEnchantEquipment, Arrows, Bolts, NiesiolowskiSoup } @@ -95,7 +96,7 @@ void SetPrimaryStatDescription() desc = "Detaches enchant items from the equipment"; break; case RecipeKind.Arrows: - desc = "Creates arrows";//The tip's kind depends on used ingradient + desc = "Creates arrows";//The tip's kind depends on used ingredient break; case RecipeKind.Bolts: desc = "Creates bolts"; @@ -163,60 +164,71 @@ public RecipeKind Kind break; case RecipeKind.TwoEq: tag1 += "two_eq"; - MagicDustRequired = 2; + MagicDustRequired = 1; Name = "Two Equipments"; Price = 30; MinDropDungeonLevel = 2; break; case RecipeKind.TransformPotion: tag1 += "transform_potion"; - Name = ""; + Name = "Transform Potion"; break; case RecipeKind.TransformGem: tag1 += "transform_gem"; - //Name = ""; + Name = "Transform Gem"; break; case RecipeKind.Toadstools2Potion: tag1 += "toad_potions"; - DisplayedName = "Potion from Toadstools Recipe"; - //Name = "Potion from Toadstools"; + Name = "Potion from Toadstools"; break; case RecipeKind.ExplosiveCocktail: tag1 += "expl_cocktail"; - //Name = ""; + Name = "Explosive Cocktail"; break; case RecipeKind.Pendant: tag1 += "pendant"; break; case RecipeKind.EnchantEquipment: tag1 += "enchant"; - MagicDustRequired = 0;//drop in inv is free + Name = "Enchant Equipment"; + //MagicDustRequired = 0;//drop in inv is free break; case RecipeKind.CraftSpecialPotion: tag1 += "special_potion"; + Name = "Craft Special Potion"; break; case RecipeKind.RechargeMagicalWeapon: tag1 += "recharge_magical_weapon"; + Name = "Recharge Magical Weapon"; break; case RecipeKind.AntidotePotion: tag1 += "antidote_potion"; + Name = "Antidote Potion"; break; case RecipeKind.UnEnchantEquipment: tag1 += "unenchant"; + Name = "Unenchant Equipment"; break; case RecipeKind.Arrows: tag1 += "arrows"; + break; case RecipeKind.Bolts: tag1 += "bolts"; break; case RecipeKind.NiesiolowskiSoup: tag1 += "niesiolowski_soup"; + Name = "Niesiolowski Soup"; break; default: break; } - + + DisplayedName = Name; + if (!DisplayedName.EndsWith("Recipe")) + { + DisplayedName += " Recipe"; + } SetPrimaryStatDescription(); } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/SpecialPotion.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/SpecialPotion.cs index 575b2ac3..73a5ee59 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/SpecialPotion.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/SpecialPotion.cs @@ -6,8 +6,10 @@ namespace Roguelike.Tiles.Looting { - public enum SpecialPotionKind { Unset, Strength, Magic } - public enum SpecialPotionSize { Small, Medium, Big } + public enum SpecialPotionKind { Unset, Strength, Magic, Virility } + public enum SpecialPotionSize { Small, + //Medium, + Big } [Serializable] public class SpecialPotion : Potion @@ -16,6 +18,18 @@ public class SpecialPotion : Potion SpecialPotionSize size = SpecialPotionSize.Small; bool used = false; + string TagFromKind() + { + if (SpecialPotionKind == SpecialPotionKind.Magic) + return "magic_potion"; + else if (SpecialPotionKind == SpecialPotionKind.Strength) + return "strength_potion"; + else if (SpecialPotionKind == SpecialPotionKind.Virility) + return "virility_potion"; + + return ""; + } + public SpecialPotion() : this(SpecialPotionKind.Unset, SpecialPotionSize.Small) { @@ -27,11 +41,27 @@ public SpecialPotion(SpecialPotionKind kind, SpecialPotionSize size) : base(Poti SpecialPotionKind = kind; Price = 50; this.size = size; - if (size == SpecialPotionSize.Medium) - Price = 100; - else if (size == SpecialPotionSize.Big) + //if (size == SpecialPotionSize.Medium) + // Price = 100; + if (size == SpecialPotionSize.Big) Price = 200; Symbol = '`'; + SetTagFromSize(); + } + + public override string GetId() + { + return base.GetId() + "_" + SpecialPotionKind.ToString() + "_" + Size.ToString(); + } + + private void SetTagFromSize() + { + if (size == SpecialPotionSize.Big) + tag1 = "big_" + TagFromKind(); + //else if (size == SpecialPotionSize.Medium) + // tag1 = "medium_" + TagFromKind(); + else + tag1 = "small_" + TagFromKind(); } public SpecialPotionKind SpecialPotionKind @@ -46,16 +76,22 @@ public SpecialPotionKind SpecialPotionKind specialPotionKind = value; if (value == SpecialPotionKind.Strength) { - tag1 = "strength_potion";//HACK + SetTagFromSize(); Name = "Strength Potion"; StatKind = EntityStatKind.Strength; } else if (value == SpecialPotionKind.Magic) { - tag1 = "magic_potion"; + SetTagFromSize(); Name = "Magic Potion"; StatKind = EntityStatKind.Magic; } + else if (value == SpecialPotionKind.Virility) + { + SetTagFromSize(); + Name = "Virility Potion"; + StatKind = EntityStatKind.Virility; + } //tag1 += "_"+size.ToString();maybe UI can scale ? Name = size.ToString().ToUpperFirstLetter() + " " + Name; @@ -81,9 +117,9 @@ public bool Apply(AdvancedLivingEntity ent) public int GetEnhValue() { var value = 1; - if (size == SpecialPotionSize.Medium) - value = 3; - else if (size == SpecialPotionSize.Big) + //if (size == SpecialPotionSize.Medium) + // value = 3; + if (size == SpecialPotionSize.Big) value = 5; return value; @@ -99,7 +135,15 @@ public EntityStatKind GetDestStat() return SpecialPotionKind == SpecialPotionKind.Strength ? EntityStatKind.Strength : EntityStatKind.Magic; } - public SpecialPotionSize Size { get => size; set => size = value; } + public SpecialPotionSize Size + { + get => size; + set + { + size = value; + SetTagFromSize(); + } + } public override string[] GetExtraStatDescription() { diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/SpellSource.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/SpellSource.cs index 26c1bf4f..7a05c797 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/SpellSource.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/SpellSource.cs @@ -71,7 +71,7 @@ private void SetDesc() switch (kind) { case SpellKind.FireBall: - desc = "Inflicts fire damage, can be decreased by a related resist"; + desc = "Inflicts fire damage";//, can be decreased by a related resist TargetRequired = true; break; case SpellKind.CrackedStone: @@ -81,16 +81,16 @@ private void SetDesc() case SpellKind.Skeleton: desc = "Creates a skeleton which fights as hero ally"; break; - case SpellKind.Trap: - desc = "Inflicts physical damage blocks victim for a few turns"; - TargetRequired = true; - break; + //case SpellKind.Trap: + // desc = "Inflicts physical damage blocks victim for a few turns"; + // TargetRequired = true; + // break; case SpellKind.IceBall: - desc = "Inflicts cold damage, can be decreased by a related resist"; + desc = "Inflicts cold damage"; TargetRequired = true; break; case SpellKind.PoisonBall: - desc = "Inflicts poison damage, can be decreased by a related resist"; + desc = "Inflicts poison damage"; TargetRequired = true; break; case SpellKind.Transform: @@ -117,11 +117,11 @@ private void SetDesc() TargetRequired = true; break; case SpellKind.LightingBall: - desc = "Inflicts lighting damage, can be decreased by a related resist"; + desc = "Inflicts lighting damage"; TargetRequired = true; break; case SpellKind.Mana: - desc = "Restores some mana by sacrificing some health"; + desc = "Restores some mana by sacrificing some Health"; break; case SpellKind.BushTrap: desc = ""; @@ -150,18 +150,26 @@ private void SetDesc() case SpellKind.Dziewanna: desc = "Creates a poisonous apple(s), irresisteble for many entities"; break; + case SpellKind.Jarowit: + desc = "Decreases enemies Defense statistics, increases the caster and allies Defense statistics"; + break; case SpellKind.Swarog: - desc = "Turns off light for enemies turn making enemies confused and hitting random targets"; + desc = "Turns off light for enemies turn making them confused and hitting random targets. (For best results use in crowded places)"; break; case SpellKind.Inaccuracy: - desc = "Reduces the Chance to Hit statistic of the victim"; + desc = "Reduces the 'Chance to Hit' statistic of the victim"; TargetRequired = true; break; case SpellKind.Swiatowit: - desc = "Hit nearby enemies with a random magic spell"; + desc = "Hits nearby enemies with a random magic spell"; break; case SpellKind.Perun: - desc = "Hit pointed enemy with a devastating Axe of Perun"; + desc = "Hits pointed enemy with a devastating Axe of Perun"; + TargetRequired = true; + break; + + case SpellKind.Wales: + desc = "Blesses the caster and allies increasing their Health and Mana"; break; //case SpellKind.CallMerchant: // desc = "Teleports a merchant near to the hero"; @@ -269,6 +277,12 @@ public virtual ISpell CreateSpell(LivingEntity caller) case SpellKind.Dziewanna: spell = new DziewannaSpell(caller); break; + case SpellKind.Jarowit: + spell = new JarowitSpell(caller); + break; + case SpellKind.Wales: + spell = new WalesSpell(caller); + break; case SpellKind.Perun: spell = new PerunSpell(caller); break; @@ -292,6 +306,8 @@ public virtual ISpell CreateSpell(LivingEntity caller) break; } + if (GodKind) + spell.SendByGod = true; //if (spell is IProjectileSpell proj) // proj.Range += spell.CurrentLevel - 1; return spell; @@ -312,6 +328,21 @@ public bool IsOffensive return dummyForIsOffensive is OffensiveSpell; } } + + ISpell dummyForIsProjectile; + [JsonIgnore] + public bool IsProjectile + { + get + { + + if (dummyForIsProjectile == null) + dummyForIsProjectile = CreateSpell(new LivingEntity()); + + return dummyForIsProjectile is IProjectileSpell; + } + } + public override string[] GetExtraStatDescription() { Dungeons.DebugHelper.Assert(false, "call the one with (LivingEntity caller)"); @@ -354,5 +385,16 @@ public virtual SpellStatsDescription GetExtraStatDescription(LivingEntity caller public bool IsManaPowered { get { return this is Book || this is Scroll; } } - } + + public bool RequiresEmptyCellOnCast + { + get { + return Kind == SpellKind.Teleport || Kind == SpellKind.BushTrap + || Kind == SpellKind.CrackedStone || Kind == SpellKind.Portal + + ; + + } + } + } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/StackedLoot.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/StackedLoot.cs index 621be0e8..5596fe86 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/StackedLoot.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/StackedLoot.cs @@ -29,5 +29,10 @@ public StackedLoot Clone(int count) dest.Count = count; return dest; } + + public override string ToString() + { + return base.ToString() + " Count: "+Count; + } } } diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Weapon.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Weapon.cs index c7cdbff9..98156617 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Weapon.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/Weapon.cs @@ -63,7 +63,16 @@ public bool IsMagician Kind == Weapon.WeaponKind.Staff; } } - + + public override bool IsMatchingRecipe(RecipeKind kind) + { + if (base.IsMatchingRecipe(kind)) + return true; + if (kind == RecipeKind.RechargeMagicalWeapon && IsMagician) + return true; + return false; + } + public void SetInitChargesCount(int mult) { if (SpellSource == null) @@ -118,14 +127,17 @@ public override void SetLevelIndex(int li) else SetRequiredStat(li); - if (baseDamages.ContainsKey(Kind)) + if (Class != EquipmentClass.Unique) { - CalcDamageFromLevel(baseDamages[Kind]); - } - else - { - if(!this.Name.Contains("Hound")) - System.Diagnostics.Debug.WriteLine("!baseDamages.ContainsKey(Kind) " + this); + if (baseDamages.ContainsKey(Kind)) + { + CalcDamageFromLevel(baseDamages[Kind]); + } + else + { + if (!this.Name.Contains("Hound")) + System.Diagnostics.Debug.WriteLine("!baseDamages.ContainsKey(Kind) " + this); + } } } @@ -201,14 +213,15 @@ public WeaponKind Kind SpecialFeature = new EntityStat(EntityStatKind.ChanceToMeleeHit, chanceForEffect); break; case WeaponKind.Bashing: + SpecialFeature = new EntityStat(EntityStatKind.ChanceToCauseStunning, chanceForEffect); SpecialFeatureAux = new EntityStat(EntityStatKind.ChanceToMeleeHit, -chanceForEffect); break; case WeaponKind.Axe: - //Symbol = AxeSymbol; + SpecialFeature = new EntityStat(EntityStatKind.ChanceToCauseTearApart, chanceForEffect); - //Name = "Axe"; + break; case WeaponKind.Scepter: case WeaponKind.Staff: @@ -259,7 +272,7 @@ float GetDamageIncreasePerc() float fact = .1f; if (Kind == WeaponKind.Bashing) - return max+ fact*80; + return max+ fact*40; if (Kind == WeaponKind.Axe) return max + fact * 20; if (Kind == WeaponKind.Sword) @@ -338,8 +351,16 @@ public override string ToString() return base.ToString() + " " + Damage; } - public static readonly FightItemKind[] BowAmmoKinds = new[] { FightItemKind.PlainArrow, FightItemKind.IronArrow, FightItemKind.SteelArrow }; - public static readonly FightItemKind[] CrossBowAmmoKinds = new[] { FightItemKind.PlainBolt, FightItemKind.IronBolt, FightItemKind.SteelBolt }; + public static readonly FightItemKind[] BowAmmoKinds = new[] + { + FightItemKind.PlainArrow, FightItemKind.IronArrow, FightItemKind.SteelArrow , + FightItemKind.PoisonArrow, FightItemKind.IceArrow, FightItemKind.FireArrow + }; + public static readonly FightItemKind[] CrossBowAmmoKinds = new[] + { + FightItemKind.PlainBolt, FightItemKind.IronBolt, FightItemKind.SteelBolt , + FightItemKind.PoisonBolt, FightItemKind.IceBolt, FightItemKind.FireBolt , + }; public static readonly FightItemKind[] AllBowLikeAmmoKinds = null; //see ctor public static bool IsBowAmmoKind(FightItemKind kind) diff --git a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/WeaponSpellSource.cs b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/WeaponSpellSource.cs index 0896a93a..8be3a955 100644 --- a/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/WeaponSpellSource.cs +++ b/Assets/Scripts/Common/Dlls/Roguelike/Tiles/Looting/WeaponSpellSource.cs @@ -25,7 +25,7 @@ public WeaponSpellSource(Weapon weapon, SpellKind kind, int chargesCount = 15) : InitChargesCount = chargesCount; } - public void Restore() + public void RestoreCharges() { RestoresCount++; RestoredChargesCount = initChargesCount - 2 * RestoresCount; diff --git a/Godot_test/ClientScripts/GameEventHandler.cs b/Godot_test/ClientScripts/GameEventHandler.cs index ab7adce1..1868c3ca 100644 --- a/Godot_test/ClientScripts/GameEventHandler.cs +++ b/Godot_test/ClientScripts/GameEventHandler.cs @@ -72,9 +72,10 @@ public void ActionsManager_ActionAppended(object sender, GameEvent ev) } else if (lea.InvolvedEntity is Roguelike.Tiles.LivingEntities.Hero) { - var targetTile = Game.dungeon.GetTile(lea.targetEntityPosition); - var enGodot = Game.gameLevel.GetEntity(targetTile); - enGodot.getDamaged((float)lea.InvolvedValue, true); + //TODO + //var targetTile = Game.dungeon.GetTile(lea.targetEntityPosition); + //var enGodot = Game.gameLevel.GetEntity(targetTile); + //enGodot.getDamaged((float)lea.InvolvedValue, true); } } else if (lea.Kind == LivingEntityActionKind.AppendedToLevel) diff --git a/POCO/DungeonsConsoleRunner.Core/Program.cs b/POCO/DungeonsConsoleRunner.Core/Program.cs index 193dcc66..5d0c9b2f 100644 --- a/POCO/DungeonsConsoleRunner.Core/Program.cs +++ b/POCO/DungeonsConsoleRunner.Core/Program.cs @@ -13,7 +13,6 @@ static void Main(string[] args) var container = new ContainerConfigurator().Container; container.Register(); container.Register(); - //container.Register(); container.Register(); container.Register(); //container.Verify(); diff --git a/POCO/RoguelikeConsoleRunner.Core/GameController.cs b/POCO/RoguelikeConsoleRunner.Core/GameController.cs index a7bcfc2d..3693d2f8 100644 --- a/POCO/RoguelikeConsoleRunner.Core/GameController.cs +++ b/POCO/RoguelikeConsoleRunner.Core/GameController.cs @@ -1,14 +1,17 @@ using Dungeons; using Dungeons.ASCIIDisplay; +using Dungeons.Tiles; using Roguelike; using Roguelike.Abstract; using Roguelike.Abstract.Managers; using Roguelike.Events; using Roguelike.Managers; +using Roguelike.Tiles; using Roguelike.Tiles.Interactive; using Roguelike.Tiles.LivingEntities; using SimpleInjector; using System; +using System.Drawing; using System.Linq; namespace RoguelikeConsoleRunner @@ -66,11 +69,45 @@ private void Context_ContextSwitched(object sender, ContextSwitch context) Redraw(); } + //char SymbolToDrawLootSource(Dungeons.Tiles.Tile tile) + //{ + // if (tile is ILootSource ls) + // { + // return (char)ls.Level; + // } + // return tile.Symbol; + //} + protected override Dungeons.ASCIIDisplay.Screen CreateScreen() { screen = new Dungeons.ASCIIDisplay.Screen(DrawingEngine); screen.OriginX = 2; screen.OriginY = 2; + //screen.PrintInfo.SymbolToDraw = (Dungeons.Tiles.Tile tile) => { + // return SymbolToDrawLootSource(tile); + //}; + + //TODO + //screen.PrintInfo.CustomDrawer = (Dungeons.Tiles.Tile tile, IDrawingEngine drawingEngine) => { + + // var color = ConsoleColor.White; + + // if (tile is LivingEntity ls) + // { + // color = tile.Color; + // drawingEngine.ForegroundColor = color; + // drawingEngine.Write(ls.Level); + // return; + // } + // if (tile.Symbol == 'e') + // { + // int k = 0; + // k++; + // } + // drawingEngine.ForegroundColor = color; + // drawingEngine.Write(tile.Symbol); + + //}; return screen; } diff --git a/POCO/RoguelikeUnitTests.Core/AbilitiesPropsTests.cs b/POCO/RoguelikeUnitTests.Core/AbilitiesPropsTests.cs index 3946aad7..2827529e 100644 --- a/POCO/RoguelikeUnitTests.Core/AbilitiesPropsTests.cs +++ b/POCO/RoguelikeUnitTests.Core/AbilitiesPropsTests.cs @@ -99,25 +99,25 @@ public void PiercingArrowPropsTest(AbilityKind kind) game.GameManager.Hero.SelectedActiveAbility = ab; var nextLevelStats = ab.GetEntityStats(currentLevel: false); - Assert.AreEqual(nextLevelStats[0].Factor, 5);//EntityStatKind.ChanceForPiercing + Assert.AreEqual(nextLevelStats[0].Factor, 10);//EntityStatKind.ChanceForPiercing Assert.AreEqual(nextLevelStats[1].Factor, 2);//EntityStatKind.NumberOfPiercedVictims var fv = EntityStat.GetFormattedValue(nextLevelStats[0], true); - Assert.AreEqual(fv, "+5%"); + Assert.AreEqual(fv, "+10%"); ab.IncreaseLevel(game.Hero); Assert.AreEqual(ab.PrimaryStat.Kind, EntityStatKind.ChanceForPiercing); - Assert.AreEqual(ab.PrimaryStat.Factor, 5); + Assert.AreEqual(ab.PrimaryStat.Factor, 10); Assert.AreEqual(ab.AuxStat.Kind, EntityStatKind.NumberOfPiercedVictims); var numberOfPiercedVictims = 2; Assert.AreEqual(ab.AuxStat.Factor, numberOfPiercedVictims); numberOfPiercedVictims++; ab.IncreaseLevel(game.Hero); - Assert.AreEqual(ab.PrimaryStat.Factor, 10); + Assert.AreEqual(ab.PrimaryStat.Factor, 20); Assert.AreEqual(ab.AuxStat.Factor, numberOfPiercedVictims); - Assert.AreEqual(game.GameManager.Hero.GetEffectChance(EntityStatKind.ChanceToPhysicalProjectileHit, null), 85); + Assert.AreEqual(game.GameManager.Hero.GetEffectChance(EntityStatKind.ChanceToPhysicalProjectileHit, null), 95); } private static void FindBigFacs(Roguelike.RoguelikeGame game, bool act) @@ -182,7 +182,7 @@ public void PerfectHitPropsTest() Assert.AreEqual(ab.AuxStat.Factor, 0); ab.IncreaseLevel(game.Hero); - Assert.AreEqual(ab.PrimaryStat.Factor, 10); + Assert.AreEqual(ab.PrimaryStat.Factor, 15); Assert.AreEqual(ab.AuxStat.Factor, 5); } diff --git a/POCO/RoguelikeUnitTests.Core/AbilitiesTests.cs b/POCO/RoguelikeUnitTests.Core/AbilitiesTests.cs index 44818f46..9507b634 100644 --- a/POCO/RoguelikeUnitTests.Core/AbilitiesTests.cs +++ b/POCO/RoguelikeUnitTests.Core/AbilitiesTests.cs @@ -67,18 +67,24 @@ public class AbilitiesTests : TestBase [TestCase(AbilityKind.FireBallMastering)] [TestCase(AbilityKind.IceBallMastering)] [TestCase(AbilityKind.PoisonBallMastering)] + [Repeat(1)] public void MagicProjectileEnemyHitTest(AbilityKind ak) { var game = CreateGame(); + game.Hero.AlwaysHit[AttackKind.SpellElementalProjectile] = true; var ab = game.GameManager.Hero.GetPassiveAbility(ak); var enemy = PlainEnemies.First(); + + //Immune! enemy.ImmuneOnEffects = true; - var enemyBeginHealth = enemy.Stats.Health; + var enemyLastHealth = enemy.Stats.Health; UseSpellSource(game.Hero, enemy, true, AbilityKind2SpellKind[ak]); - Assert.AreEqual(enemy.LastingEffects.Count, 0); - Assert.Less(enemy.Stats.Health, enemyBeginHealth); - var diff1 = enemyBeginHealth - enemy.Stats.Health; - enemyBeginHealth = enemy.Stats.Health; + Assert.False(enemy.HasLastingEffect(Roguelike.Effects.EffectType.Firing)); + Assert.False(enemy.HasLastingEffect(Roguelike.Effects.EffectType.Frozen)); + Assert.False(enemy.HasLastingEffect(Roguelike.Effects.EffectType.Poisoned)); + Assert.Less(enemy.Stats.Health, enemyLastHealth); + var diff1 = enemyLastHealth - enemy.Stats.Health; + enemyLastHealth = enemy.Stats.Health; for(int i=0;i< ab.MaxLevel; i++) ab.IncreaseLevel(game.Hero); @@ -86,8 +92,8 @@ public void MagicProjectileEnemyHitTest(AbilityKind ak) GotoNextHeroTurn(); UseSpellSource(game.Hero, enemy, true, AbilityKind2SpellKind[ak]); - Assert.Less(enemy.Stats.Health, enemyBeginHealth); - var diff2 = enemyBeginHealth - enemy.Stats.Health; + Assert.Less(enemy.Stats.Health, enemyLastHealth); + var diff2 = enemyLastHealth - enemy.Stats.Health; Assert.Greater(diff2, diff1); } @@ -139,7 +145,7 @@ public void TestBulkAttackReal() var empOnes = game.GameManager.CurrentNode.GetEmptyNeighborhoodTiles(game.GameManager.Hero, false); Assert.Greater(empOnes.Count, 2); - var enemies = AllEnemies.Where(i => i.PowerKind == EnemyPowerKind.Champion).ToList(); + var enemies = AllEnemies;//.Where(i => i.PowerKind == EnemyPowerKind.Champion).ToList(); var enFirst = enemies[0]; var enSec = enemies[1]; var enThird = enemies[2]; @@ -178,7 +184,7 @@ public void TestBulkAttackReal() public void TestStrikeBack() { var game = CreateGame(); - game.Hero.d_immortal = true; + game.Hero.Immortal = true; var en = PlainEnemies.First(); PlaceCloseToHero(en); float en1Health = en.Stats.Health; @@ -277,12 +283,12 @@ public void TestExplosiveMasteringBurning() var explosiveCocktail = new ProjectileFightItem(FightItemKind.ExplosiveCocktail, hero); explosiveCocktail.Count = 10; hero.Inventory.Add(explosiveCocktail); - hero.ActiveFightItem = explosiveCocktail; + hero.SelectedFightItem = explosiveCocktail; for (int i = 0; i < 10; i++) { //champion.OnHitBy(explosiveCocktail); - UseFightItem(hero, champion, hero.ActiveProjectileFightItem); + UseFightItem(hero, champion, hero.SelectedProjectileFightItem); if (champion.HasLastingEffect(Roguelike.Effects.EffectType.Firing)) break; GotoNextHeroTurn(); @@ -293,7 +299,6 @@ public void TestExplosiveMasteringBurning() [TestCase(FightItemKind.ExplosiveCocktail)] [TestCase(FightItemKind.Stone)] - [TestCase(FightItemKind.WeightedNet)] [TestCase(FightItemKind.ThrowingKnife)] [TestCase(FightItemKind.ThrowingTorch)] //[Repeat(5)] @@ -322,7 +327,7 @@ public void TestBasicFightItem(FightItemKind kind) int repeat = 5; for (int i = 0; i < repeat; i++) { - Assert.True(UseFightItem(hero, enemy, hero.ActiveProjectileFightItem)); + Assert.True(UseFightItem(hero, enemy, hero.SelectedProjectileFightItem)); GotoNextHeroTurn(); } @@ -340,7 +345,7 @@ public void TestBasicFightItem(FightItemKind kind) //Assert.Less(damage2/damage1, 1.6f); for (int i = 0; i < repeat; i++) { - Assert.True(UseFightItem(hero, enemy, hero.ActiveProjectileFightItem)); + Assert.True(UseFightItem(hero, enemy, hero.SelectedProjectileFightItem)); GotoNextHeroTurn(); } var chempAfter2HitHealth = enemy.Stats.Health; @@ -367,6 +372,51 @@ public void TestBasicFightItem(FightItemKind kind) } } + [TestCase] + //[Repeat(5)] + public void TestWeightedNet() + { + FightItemKind kind = FightItemKind.WeightedNet; + var game = CreateGame(true, 100); + + //take one which is active to make sure will have it's turn + RevealAllEnemies(); + var enemy = GetEnemyToBeBeaten(); + var enemyBeginHealth = enemy.Stats.Health; + + var hero = game.GameManager.Hero; + hero.AlwaysHit[AttackKind.PhysicalProjectile] = true;//TODO + var fi = ActivateFightItem(kind, hero, 20); + + var damage1 = fi.Damage; + Assert.AreEqual(damage1, 2);//small one + + AssertWebTrapUsage(enemy, enemyBeginHealth, hero, 2); + MaximizeAbility(hero.GetActiveAbility(AbilityKind.WeightedNet), hero); + AssertWebTrapUsage(enemy, enemyBeginHealth, hero, 6); + } + + private void AssertWebTrapUsage(Enemy enemy, float enemyBeginHealth, Hero hero, int expTurns) + { + Assert.True(UseFightItem(hero, enemy, hero.SelectedProjectileFightItem)); + GotoNextHeroTurn(); + int counter = 0; + while (true) + { + var le = enemy.GetFirstLastingEffect(Roguelike.Effects.EffectType.WebTrap); + if (le == null) + break; + + counter++; + Assert.AreEqual(le.Description, "Web Trap"); + Assert.NotNull(le); + Assert.AreEqual(enemyBeginHealth, enemy.Stats.Health); + GotoNextHeroTurn(); + } + + Assert.AreEqual(counter, expTurns); + } + [TestCase(FightItemKind.ThrowingKnife)] public void TestBasicFightItemFactor(FightItemKind kind) { @@ -492,7 +542,7 @@ private void TestRestoreFactorChange(bool forMana) Assert.AreEqual(ab.PrimaryStat.Unit, EntityStatUnit.Percentage); AssertNextValue(i, ab, abVal, null); var factor = GetFactor(ab, true); - Assert.Less(factor, 10); + Assert.Less(factor, 10, ab.Name); abVal = factor; } if (forMana) @@ -588,7 +638,8 @@ public void TestChanceToRepeatMeleeAttack() } [Test] - public void TestScepterMastering_ChanceToCauseElementalAilment()//ChanceToCauseElementalAilment() + [Repeat(1)] + public void TestScepterMastering_ChanceToCauseElementalAilment() { var game = CreateGame(); float originalStatValue = 0; @@ -610,9 +661,12 @@ public void TestScepterMastering_ChanceToCauseElementalAilment()//ChanceToCauseE var en = PlainEnemies[i]; var spell = weapon.SpellSource.CreateSpell(game.Hero); PlaceCloseToHero(en); + bool firing = false; for (int at = 0; at < 10; at++) { + if (weapon.SpellSource is WeaponSpellSource wss && wss.Count <= 0) + wss.RestoreCharges(); Assert.AreEqual(game.GameManager.SpellManager.ApplyAttackPolicy(game.Hero, en, weapon.SpellSource), ApplyAttackPolicyResult.OK); GotoNextHeroTurn(); if (en.HasLastingEffect(Roguelike.Effects.EffectType.Firing)) @@ -631,10 +685,14 @@ public void TestScepterMastering_Range() { var game = CreateGame(true, 1); float originalStatValue = 0; + game.Hero.AlwaysHit[AttackKind.SpellElementalProjectile] = true; + game.Hero.AlwaysHit[AttackKind.WeaponElementalProjectile] = true; var destExtraStat = SetWeapon(AbilityKind.SceptersMastering, game.Hero, out originalStatValue); var weapon = game.Hero.GetActiveWeapon(); Assert.AreEqual(weapon.SpellSource.Kind, SpellKind.FireBall); - Assert.Greater(PlainEnemies.Count, 0); + if (PlainEnemies.Count == 0) + ChampionEnemies.First().PowerKind = EnemyPowerKind.Plain;//TODO + //Assert.Greater(PlainEnemies.Count, 0); var en = PlainEnemies[0]; en.d_canMove = false; PlaceCloseToHero(en, 4); @@ -673,7 +731,7 @@ public void TestScepterMastering_Range() [Test] [Repeat(1)] - public void TestWandMastering()//ChanceToElementalProjectileBulkAttack + public void TestWandMastering() { var gi = new Roguelike.Generators.GenerationInfo(); gi.MakeEmpty(); @@ -683,7 +741,7 @@ public void TestWandMastering()//ChanceToElementalProjectileBulkAttack float originalStatValue = 0; var destExtraStat = SetWeapon(AbilityKind.WandsMastering, game.Hero, out originalStatValue); var weapon = game.Hero.GetActiveWeapon(); - game.GameManager.Hero.d_immortal = true; + game.GameManager.Hero.Immortal = true; //Assert.Greater(empOnes.Count, 1); var enemies = AllEnemies; @@ -707,10 +765,13 @@ public void TestWandMastering()//ChanceToElementalProjectileBulkAttack chanceForBulk = game.Hero.Stats.GetCurrentValue(EntityStatKind.ChanceToElementalProjectileBulkAttack); Assert.Greater(chanceForBulk, 40); + //this test check if the second enemy is hit, not the 1st one. Thus 1st can be hit always. + game.Hero.AlwaysHit[AttackKind.WeaponElementalProjectile] = true; + for (int i = 0; i < 20; i++) { weapon.SpellSource.Count = 20; - //hit only 1st enemy + //hit only 1st enemy, due to spell second shall be hit var spell = weapon.SpellSource.CreateSpell(game.Hero); Assert.AreEqual(game.GameManager.SpellManager.ApplyAttackPolicy(game.Hero, en1, weapon.SpellSource), ApplyAttackPolicyResult.OK); GotoNextHeroTurn(); @@ -729,15 +790,17 @@ public void TestWandMastering()//ChanceToElementalProjectileBulkAttack } - [TestCase(EntityStatKind.ChanceToRepeatElementalProjectileAttack, AbilityKind.StaffsMastering)] [TestCase(EntityStatKind.StaffExtraElementalProjectileDamage, AbilityKind.StaffsMastering)] [TestCase(EntityStatKind.ScepterExtraElementalProjectileDamage, AbilityKind.SceptersMastering)] [TestCase(EntityStatKind.WandExtraElementalProjectileDamage, AbilityKind.WandsMastering)] + [Repeat(1)] public void TestMagicProjectileMasteringStats(EntityStatKind esk, AbilityKind ak) { var game = CreateGame(); float originalStatValue = 0; + game.Hero.AlwaysHit[AttackKind.SpellElementalProjectile] = true; + game.Hero.AlwaysHit[AttackKind.WeaponElementalProjectile] = true; SetWeapon(ak, game.Hero, out originalStatValue); var weapon = game.Hero.GetActiveWeapon(); @@ -805,8 +868,15 @@ private Roguelike.Abstract.Spells.ISpell AttackEnemy(Roguelike.RoguelikeGame gam return spell; } + Enemy GetEnemyToBeBeaten() + { + var enemy = PlainNormalEnemies.First(); + PrepareEnemyToBeBeaten(enemy); + return enemy; + } private float PrepareEnemyToBeBeaten(Enemy en) { + Assert.True(en.Revealed && en.Alive); PlaceCloseToHero(en); en.Stats.SetNominal(EntityStatKind.Health, 300); var enHealthBase = en.Stats.Health; @@ -853,7 +923,7 @@ private float TestWeaponKindMastering(AbilityKind kind) if(!wpn.IsBowLike) en.OnMeleeHitBy(hero); else - UseFightItem(hero, en, hero.ActiveFightItem as ProjectileFightItem); + UseFightItem(hero, en, hero.SelectedFightItem as ProjectileFightItem); var health1 = en.Stats.Health; return health - health1; }; @@ -871,7 +941,7 @@ private float TestWeaponKindMastering(AbilityKind kind) } pfi.Caller = hero; hero.Inventory.Add(pfi); - hero.ActiveFightItem = pfi; + hero.SelectedFightItem = pfi; } var damage = hitEnemy(); @@ -992,7 +1062,7 @@ public void TestTorchMastering()//ChanceToCauseFiring { var game = CreateGame(genNumOfEnemies: 100); var hero = game.GameManager.Hero; - hero.d_immortal = true; + hero.Immortal = true; var fi = ActivateFightItem(FightItemKind.ThrowingTorch, hero); @@ -1016,7 +1086,7 @@ private int CountLE(Hero hero, Enemy enemy) int firingCounterBefore = 0; for (int i = 0; i < 20; i++) { - Assert.True(UseFightItem(hero, enemy, hero.ActiveProjectileFightItem)); + Assert.True(UseFightItem(hero, enemy, hero.SelectedProjectileFightItem)); if (enemy.HasLastingEffect(Roguelike.Effects.EffectType.Firing)) { firingCounterBefore++; diff --git a/POCO/RoguelikeUnitTests.Core/BossFightTests.cs b/POCO/RoguelikeUnitTests.Core/BossFightTests.cs index 2d4c3a6b..b51924bb 100644 --- a/POCO/RoguelikeUnitTests.Core/BossFightTests.cs +++ b/POCO/RoguelikeUnitTests.Core/BossFightTests.cs @@ -1,5 +1,6 @@ using NUnit.Framework; using Roguelike.Events; +using System.Diagnostics; using System.Linq; namespace RoguelikeUnitTests @@ -8,6 +9,7 @@ namespace RoguelikeUnitTests class BossFightTests : TestBase { [Test] + [Repeat(1)] public void NonPlainEnemyUsesEffects() { var game = CreateGame(genNumOfEnemies: 1, numberOfRooms: 1); @@ -18,7 +20,9 @@ public void NonPlainEnemyUsesEffects() { if (e is LivingEntityAction lea) { - if (lea.Info.Contains("Ball")) + Debug.WriteLine("lea.Info: "+ lea.Info); + if (lea.Info.Contains("Ball") || + lea.Info.Contains("Casting Projectile")) spellAttackDone = true; } }; @@ -26,12 +30,12 @@ public void NonPlainEnemyUsesEffects() hero.Stats.SetNominal(Roguelike.Attributes.EntityStatKind.Health, 255); var enemy = AllEnemies.First(); enemy.SetNonPlain(true); - Assert.NotNull(enemy.ActiveManaPoweredSpellSource); + Assert.NotNull(enemy.SelectedManaPoweredSpellSource); var closeHero = game.Level.GetClosestEmpty(hero); game.Level.SetTile(enemy, closeHero.point); var enemyMana = enemy.Stats.Mana; - for (int i = 0; i < 10; i++) + for (int i = 0; i < 20; i++) { game.GameManager.EnemiesManager.AttackIfPossible(enemy, hero);//TODO if (enemy.Stats.Mana < enemyMana) diff --git a/POCO/RoguelikeUnitTests.Core/ControllTests.cs b/POCO/RoguelikeUnitTests.Core/ControllTests.cs index 291762e9..0ec2a585 100644 --- a/POCO/RoguelikeUnitTests.Core/ControllTests.cs +++ b/POCO/RoguelikeUnitTests.Core/ControllTests.cs @@ -1,5 +1,6 @@ using NUnit.Framework; using Roguelike; +using Roguelike.Generators; using Roguelike.Tiles.LivingEntities; using static Dungeons.TileContainers.DungeonNode; @@ -8,6 +9,66 @@ namespace RoguelikeUnitTests [TestFixture] class ControllTests : TestBase { + [Test] + public void TestAllyCanFindPathToEnemy() + { + var gi = new GenerationInfo(); + gi.MakeEmpty(); + + var game = CreateGame(gi: gi); + + Enemy en = AddEnemy(); + + var gm = game.GameManager; + var hero = gm.Hero; + var ally = AddAlly(hero, true); + Assert.AreEqual(GameManager.CurrentNode.GetTiles().Count, 1); + + PlaceClose(ally, en, 4); + Assert.Less(ally.DistanceFrom(en), 6); + + var path1 = GameManager.CurrentNode.FindPath(ally, en.point); + var enHealth = en.Stats.Health; + Assert.NotNull(path1); + for (int i = 0; i < 10; i++) + { + GotoNextHeroTurn(); + + } + Assert.Less(en.Stats.Health, enHealth); + Assert.True(en.EverHitBy.Contains(ally)); + Assert.True(ally.EverHitBy.Contains(en)); + } + + + [Test] + public void TestEnemyCanFindPathToHero() + { + var gi = new GenerationInfo(); + gi.MakeEmpty(); + + var game = CreateGame(gi: gi); + + var en = AddEnemy(); + + var gm = game.GameManager; + var hero = gm.Hero; + PlaceClose(hero, en, 4); + Assert.Less(en.DistanceFrom(en), 6); + + var path1 = GameManager.CurrentNode.FindPath(en, hero.point); + var heroHealth = hero.Stats.Health; + Assert.NotNull(path1); + for (int i = 0; i < 10; i++) + { + GotoNextHeroTurn(); + + } + Assert.Less(hero.Stats.Health, heroHealth); + Assert.True(hero.EverHitBy.Contains(en)); + Assert.False(en.EverHitBy.Contains(hero)); + } + [Test] public void TestEntityManagers() { @@ -30,6 +91,8 @@ public void TestTurnOwner() game.GameManager.MakeGameTick(); Assert.AreEqual(game.GameManager.Context.TurnOwner, TurnOwner.Animals); game.GameManager.MakeGameTick(); + Assert.AreEqual(game.GameManager.Context.TurnOwner, TurnOwner.Npcs); + game.GameManager.MakeGameTick(); Assert.AreEqual(game.GameManager.Context.TurnOwner, TurnOwner.Hero); } @@ -113,6 +176,8 @@ private void MoveToHeroTurn(RoguelikeGame game) Assert.AreEqual(game.GameManager.Context.TurnOwner, TurnOwner.Animals); game.GameManager.Context.MoveToNextTurnOwner(); + Assert.AreEqual(game.GameManager.Context.TurnOwner, TurnOwner.Npcs); + game.GameManager.Context.MoveToNextTurnOwner(); Assert.AreEqual(game.GameManager.Context.TurnOwner, TurnOwner.Hero); Assert.AreEqual(Game.GameManager.Context.GetActionsCount(), 0); //Assert.AreEqual(game.GameManager.Context.TurnOwner, TurnOwner.Hero); diff --git a/POCO/RoguelikeUnitTests.Core/DiscussionTests.cs b/POCO/RoguelikeUnitTests.Core/DiscussionTests.cs index 8ece6ff3..bd7c23ec 100644 --- a/POCO/RoguelikeUnitTests.Core/DiscussionTests.cs +++ b/POCO/RoguelikeUnitTests.Core/DiscussionTests.cs @@ -29,10 +29,11 @@ public void TestDiscussWithLech() var discussion = Factory.Create(Container, "Lech"); assertDisc(discussion); - var json = discussion.ToJson(); - discussion.MainItem = null; - discussion.FromJson(json); - assertDisc(discussion); + //TODO + //var json = discussion.ToJson(); + //discussion.MainItem = null; + //discussion.FromJson(json); + //assertDisc(discussion); } } } diff --git a/POCO/RoguelikeUnitTests.Core/DungeonGenerationTests.cs b/POCO/RoguelikeUnitTests.Core/DungeonGenerationTests.cs index 0697421e..2f51fefc 100644 --- a/POCO/RoguelikeUnitTests.Core/DungeonGenerationTests.cs +++ b/POCO/RoguelikeUnitTests.Core/DungeonGenerationTests.cs @@ -409,6 +409,7 @@ public void SurfaceHurtTestForHero(SurfaceKind surfaceKind, bool isBurning) } [TestCase(SurfaceKind.Oil)] + [Repeat(1)] public void OilSurfaceBurnsOut(SurfaceKind surfaceKind) { var game = CreateGame(); diff --git a/POCO/RoguelikeUnitTests.Core/DungeonInteractionTests.cs b/POCO/RoguelikeUnitTests.Core/DungeonInteractionTests.cs index a40ac731..d13c1bc1 100644 --- a/POCO/RoguelikeUnitTests.Core/DungeonInteractionTests.cs +++ b/POCO/RoguelikeUnitTests.Core/DungeonInteractionTests.cs @@ -46,18 +46,11 @@ public void ConsumeFood() CollectLoot(game, loot); Assert.True(game.Hero.Inventory.Contains(loot)); - game.Hero.Consume(loot); - Assert.True(!game.Hero.Inventory.Contains(loot)); + //game.Hero.ReduceHealth(1); + Assert.False(game.Hero.Consume(loot)); + Assert.True(game.Hero.Inventory.Contains(loot));//hero not hurt } - //[Test] - //public void LootCollect() - //{ - // var game = CreateGame(); - // Loot loot = new Loot(); - // CollectLoot(game, loot); - - //} [Test] public void DestroyBarrel() diff --git a/POCO/RoguelikeUnitTests.Core/EnemiesTests.cs b/POCO/RoguelikeUnitTests.Core/EnemiesTests.cs index 2e35a91d..b309a985 100644 --- a/POCO/RoguelikeUnitTests.Core/EnemiesTests.cs +++ b/POCO/RoguelikeUnitTests.Core/EnemiesTests.cs @@ -6,6 +6,8 @@ using Roguelike.Events; using Roguelike.Generators; using Roguelike.Managers; +using Roguelike.State; +using Roguelike.Strategy; using Roguelike.Tiles; using Roguelike.Tiles.Interactive; using Roguelike.Tiles.LivingEntities; @@ -20,22 +22,22 @@ class EnemiesTests : TestBase { [Test] - //[Repeat(10)] + [Repeat(1)] public void TestCommandRaiseMyFriends() { var game = CreateGame(genNumOfEnemies:0); - game.Hero.d_immortal = true; + game.Hero.Immortal = true; - var enemyPlain = SpawnEnemy(); - Assert.AreEqual(enemyPlain.Symbol, 's'); + var enemyPlain = SpawnEnemy(EnemySymbols.SkeletonSymbol); + Assert.AreEqual(enemyPlain.Symbol, EnemySymbols.SkeletonSymbol); PlaceCloseToHero(enemyPlain); - var enemyChemp = SpawnEnemy(); - enemyChemp.Symbol = EnemySymbols.SkeletonSymbol; + var enemyChemp = SpawnEnemy(EnemySymbols.SkeletonSymbol); + Assert.AreEqual(enemyChemp.Symbol, EnemySymbols.SkeletonSymbol); enemyChemp.SetNonPlain(false); PlaceCloseToHero(enemyChemp); - List de = game.Level.GetDeadEnemies(); + var de = game.Level.GetDeadEnemies(); Assert.AreEqual(de.Count, 0); Assert.AreEqual(game.Level.GetTiles().Count, 2); var ens = game.GameManager.EnemiesManager.GetEnemies(); @@ -60,10 +62,11 @@ public void TestCommandRaiseMyFriends() de = game.Level.GetDeadEnemies(); Assert.AreEqual(de.Count, 1); - var ea = new EnemyAction(); - ea.CommandKind = EntityCommandKind.RaiseMyFriends; - ea.Kind = EnemyActionKind.SendComand; - game.GameManager.SendCommand(enemyChemp, ea); + + //var cmd = new CommandUseInfo() { Kind = EntityCommandKind.Resurrect }; + //var ea = new EnemyAction(cmd); + //ea.Kind = EnemyActionKind.SendComand; + AttackStrategy.SendCommand(enemyChemp, EntityCommandKind.Resurrect, GameManager); de = game.Level.GetDeadEnemies(); Assert.AreEqual(de.Count, 0); @@ -225,13 +228,30 @@ public void TestIsImmuned() { var game = CreateGame(); var hero = game.Level.GetTiles().SingleOrDefault(); - var enemy = SpawnEnemy(); - - enemy.Symbol = 's'; + var enemy = SpawnEnemy('s'); Assert.AreEqual(enemy.Name, "Skeleton"); Assert.True(enemy.IsImmuned(Roguelike.Effects.EffectType.Bleeding)); } + [Test] + public void TestResists() + { + var game = CreateGame(); + var hero = game.Level.GetTiles().SingleOrDefault(); + var enemy = SpawnEnemy(); + enemy.Name = ""; + + var symbols = new [] { EnemySymbols.SpiderSymbol, EnemySymbols.SnakeSymbol }; + foreach (var sy in symbols) + { + enemy.Symbol = sy; + + var rc = enemy.Stats.GetStat(EntityStatKind.ResistCold).Value; + var rp = enemy.Stats.GetStat(EntityStatKind.ResistPoison).Value; + Assert.Less(rc.CurrentValue, rp.CurrentValue); + } + } + [Test] [Repeat(1)] public void TestSpeedInWater() @@ -259,7 +279,7 @@ public void TestSpeedInWater() Assert.AreEqual(enemies.Count, 1); var enemy = enemies.First(); enemy.SetSurfaceSkillLevel(SurfaceKind.ShallowWater, 1); - enemy.ActiveFightItem = null;//make sure it will move + enemy.SelectedFightItem = null;//make sure it will move //put enemy close to hero var closeTile = game.Level.GetEmptyTiles().Where(i => i.DistanceFrom(game.Hero) == 3).FirstOrDefault(); @@ -285,7 +305,7 @@ public void TestSpeedInWater() GotoNextHeroTurn(); var enTurnsCountAfter = game.GameManager.Context.TurnCounts[TurnOwner.Enemies]; Assert.Greater(enTurnsCountAfter, enTurnsCount); - Assert.IsNull(enemy.ActiveFightItem); + Assert.IsNull(enemy.SelectedFightItem); //check pos, enemy shall make 2 steps var enemyDistFromHeroNew = enemy.DistanceFrom(game.Hero); diff --git a/POCO/RoguelikeUnitTests.Core/EquipmentTests.cs b/POCO/RoguelikeUnitTests.Core/EquipmentTests.cs index 36c2146e..6686f7a4 100644 --- a/POCO/RoguelikeUnitTests.Core/EquipmentTests.cs +++ b/POCO/RoguelikeUnitTests.Core/EquipmentTests.cs @@ -163,14 +163,23 @@ public void EquipmentMagicProps() var game = CreateGame(); var hero = game.Hero; + var esk = EntityStatKind.MeleeAttack; Equipment wpn = game.GameManager.GenerateRandomEquipment(EquipmentKind.Weapon); var att = wpn.PrimaryStatValue; + Assert.AreEqual(wpn.PrimaryStatKind, esk); var price = wpn.Price; Assert.Greater(price, 0); - wpn.MakeMagic(EntityStatKind.MeleeAttack, 4); + var ms = wpn.GetMagicStats(); + Assert.AreEqual(ms.Count, 0);//not magic item + + wpn.MakeMagic(esk, 4); + ms = wpn.GetMagicStats(); + Assert.AreEqual(ms.Count, 0);//not identied wpn.Identify(); - Assert.AreEqual(att + 4, wpn.GetStats().GetTotalValue(EntityStatKind.MeleeAttack)); + ms = wpn.GetMagicStats(); + Assert.AreEqual(ms.Count, 1); + Assert.AreEqual(att + 4, wpn.GetStats().GetTotalValue(esk)); Assert.Greater(wpn.Price, price); } diff --git a/POCO/RoguelikeUnitTests.Core/FightItemTests.cs b/POCO/RoguelikeUnitTests.Core/FightItemTests.cs index c60a8465..e4c13541 100644 --- a/POCO/RoguelikeUnitTests.Core/FightItemTests.cs +++ b/POCO/RoguelikeUnitTests.Core/FightItemTests.cs @@ -2,14 +2,17 @@ using Dungeons.Tiles; using NUnit.Framework; using Roguelike.Abilities; +using Roguelike.Abstract; using Roguelike.Attributes; using Roguelike.Calculated; using Roguelike.Events; using Roguelike.LootFactories; using Roguelike.Tiles; +using Roguelike.Tiles.Abstract; using Roguelike.Tiles.LivingEntities; using Roguelike.Tiles.Looting; using RoguelikeUnitTests.Helpers; +using System.Collections.Generic; using System.Linq; namespace RoguelikeUnitTests @@ -48,15 +51,15 @@ public void ArrowPower() ActivateFightItem(FightItemKind.PlainArrow, hero); var wpn = GenerateEquipment("Bow"); Assert.True(SetHeroEquipment(wpn)); - var expectedBowAttackValue = Props.FightItemBaseDamage + 1 + Props.BowBaseDamage; + var expectedBowAttackValue = Props.FightItemBaseDamage + 2 + Props.BowBaseDamage; AssertAttackValue(hero, Roguelike.Attributes.AttackKind.PhysicalProjectile, expectedBowAttackValue); ActivateFightItem(FightItemKind.IronArrow, hero); - var expectedBowAttackValueIron = expectedBowAttackValue + 4; + var expectedBowAttackValueIron = expectedBowAttackValue + 3; AssertAttackValue(hero, Roguelike.Attributes.AttackKind.PhysicalProjectile, expectedBowAttackValueIron); ActivateFightItem(FightItemKind.SteelArrow, hero); - var expectedBowAttackValueSteel = expectedBowAttackValueIron + 4; + var expectedBowAttackValueSteel = expectedBowAttackValueIron + 5; AssertAttackValue(hero, Roguelike.Attributes.AttackKind.PhysicalProjectile, expectedBowAttackValueSteel); } @@ -100,7 +103,7 @@ public void WeaponPower() ActivateFightItem(FightItemKind.PlainArrow, hero); var wpn = GenerateEquipment("Bow"); Assert.True(SetHeroEquipment(wpn)); - var expectedBowAttackValue = Props.FightItemBaseDamage + 1 + Props.BowBaseDamage; + var expectedBowAttackValue = Props.FightItemBaseDamage + 2 + Props.BowBaseDamage; AssertAttackValue(hero, Roguelike.Attributes.AttackKind.PhysicalProjectile, expectedBowAttackValue); Assert.Greater(expectedBowAttackValue, expectedThrowingKnifeAttackValue); @@ -108,7 +111,7 @@ public void WeaponPower() ActivateFightItem(FightItemKind.PlainBolt, hero); wpn = GenerateEquipment("Crossbow"); Assert.True(SetHeroEquipment(wpn)); - var expectedCrossbowAttackValue = Props.FightItemBaseDamage + 2 + Props.CrossbowBaseDamage; + var expectedCrossbowAttackValue = Props.FightItemBaseDamage + 3 + Props.CrossbowBaseDamage; AssertAttackValue(hero, Roguelike.Attributes.AttackKind.PhysicalProjectile, expectedCrossbowAttackValue); Assert.Greater(expectedCrossbowAttackValue, expectedBowAttackValue); } @@ -122,6 +125,8 @@ private static AttackDescription AssertAttackValue(Roguelike.Tiles.LivingEntitie return ad; } + + private Enemy PrepareEnemy(bool addImmunities = true, float health = 300) { var hero = game.Hero; @@ -139,9 +144,11 @@ private Enemy PrepareEnemy(bool addImmunities = true, float health = 300) [TestCase(FightItemKind.PlainArrow, "Bow")] [TestCase(FightItemKind.IronArrow, "Bow")] [TestCase(FightItemKind.SteelArrow, "Bow")] + [TestCase(FightItemKind.PoisonArrow, "Bow")] [TestCase(FightItemKind.PlainBolt, "Crossbow")] [TestCase(FightItemKind.IronBolt, "Crossbow")] [TestCase(FightItemKind.SteelBolt, "Crossbow")] + [TestCase(FightItemKind.IceBolt, "Crossbow")] //[Repeat(1)] public void ArrowFightItemTest(FightItemKind fik, string weapon) { @@ -180,6 +187,50 @@ public void ArrowFightItemTest(FightItemKind fik, string weapon) Assert.Greater(diffBow, diffKnife); } + [TestCase(FightItemKind.PoisonArrow, "Bow")] + [TestCase(FightItemKind.FireArrow, "Bow")] + [TestCase(FightItemKind.IceBolt, "Crossbow")] + //[Repeat(1)] + public void ArrowLikeSpecialItemTest(FightItemKind fik, string weapon) + { + var game = CreateGame(); + var hero = game.Hero; + hero.UseAttackVariation = false;//other tests do it + hero.AlwaysHit[AttackKind.PhysicalProjectile] = true;//TODO + + var fi = ActivateFightItem(fik, hero, 10); + + var enemy = ActivePlainEnemies.First(); + PrepareToBeBeaten(enemy); + var enemyHealth = enemy.Stats.Health; + + var bow = GenerateEquipment(weapon); + Assert.True(SetHeroEquipment(bow)); + + var tile = game.GameManager.CurrentNode.GetClosestEmpty(hero); + game.GameManager.CurrentNode.SetTile(enemy, tile.point); + + bool works = false; + for (int i = 0; i < 10; i++) + { + Assert.True(UseFightItem(hero, enemy, fi)); + Assert.Greater(enemyHealth, enemy.Stats.Health); + var diffBow = enemyHealth - enemy.Stats.Health; + enemyHealth = enemy.Stats.Health; + GotoNextHeroTurn(); + + if (enemy.HasLastingEffect(Roguelike.Effects.EffectType.Poisoned) || + enemy.HasLastingEffect(Roguelike.Effects.EffectType.Frozen) || + enemy.HasLastingEffect(Roguelike.Effects.EffectType.Firing)) + { + works = true; + break; + } + } + + Assert.True(works); + } + [Test] public void StoneFightItemTest() { @@ -222,10 +273,10 @@ public void TestExplosiveOnHeroWithEnemyLevel(FightItemKind fightItemKind) var game = CreateGame(); var en = PlainEnemies.First(); en.AlwaysHit[AttackKind.PhysicalProjectile] = true; - en.ActiveFightItem = en.SetActiveFightItem(fightItemKind); - Assert.LessOrEqual(en.ActiveFightItem.Count, 4); - if (en.ActiveFightItem.Count < 2) - en.ActiveFightItem.Count = 2; + en.SelectedFightItem = en.SetActiveFightItem(fightItemKind); + Assert.LessOrEqual(en.SelectedFightItem.Count, 4); + if (en.SelectedFightItem.Count < 2) + en.SelectedFightItem.Count = 2; Assert.AreEqual(game.GameManager.Hero.GetFightItemKindHitCounter(fightItemKind), 0); var hero = game.GameManager.Hero; @@ -276,24 +327,7 @@ public void TestExplosiveOnHeroWithEnemyLevel(FightItemKind fightItemKind) // Assert.Greater(enemyHealth, enemy.Stats.Health); //} - [Test] - public void CannonFightItemTest() - { - var game = CreateGame(); - var hero = game.Hero; - hero.AlwaysHit[AttackKind.PhysicalProjectile] = true; - - var cannonBall = ActivateFightItem(FightItemKind.CannonBall, hero); - var fiCount = hero.Inventory.GetStackedCount(cannonBall); - var enemy = PrepareEnemyForCannonHit(); - float enemyHealth = enemy.Stats.Health; - - Assert.True(game.GameManager.HeroTurn); - Assert.True(UseFightItem(hero, enemy, cannonBall)); - Assert.AreEqual(hero.Inventory.GetStackedCount(cannonBall), fiCount - 1); - - Assert.Greater(enemyHealth, enemy.Stats.Health); - } + private void WaitForAbilityCoolDown(Ability ab, bool exactlyZero = true) { @@ -317,77 +351,6 @@ private void WaitForAbilityCoolDown(Ability ab, bool exactlyZero = true) GotoNextHeroTurn(); } - [Test] - [Repeat(5)] - public void CannonChanceToHitTest() - { - var game = CreateGame(); - var hero = game.Hero; - hero.Stats.SetNominal(EntityStatKind.Health, 500); - - var cannonBall = ActivateFightItem(FightItemKind.CannonBall, hero); - cannonBall.Count = 50; - var fiCount = hero.Inventory.GetStackedCount(cannonBall); - var enemy = PrepareEnemyForCannonHit(); - float enemyHealth = enemy.Stats.Health; - - var ab = hero.GetActiveAbility(Roguelike.Abilities.AbilityKind.Cannon); - Assert.AreEqual(ab.AuxStat.Kind, EntityStatKind.CannonExtraChanceToHit); - - var chanceProj = hero.Stats.GetNominal(EntityStatKind.ChanceToPhysicalProjectileHit); - hero.Stats.SetNominal(EntityStatKind.ChanceToPhysicalProjectileHit, 0); - var facOrg = ab.AuxStat.Factor; - ab.AuxStat.Factor = 0; - - Assert.True(game.GameManager.HeroTurn); - - int hitCount = HitEnemyWithCannon(cannonBall, ref fiCount, enemy, ref enemyHealth); - Assert.AreEqual(enemyHealth, enemy.Stats.Health); - - //restore stat - hero.Stats.SetNominal(EntityStatKind.ChanceToPhysicalProjectileHit, chanceProj); - - hitCount = HitEnemyWithCannon(cannonBall, ref fiCount, enemy, ref enemyHealth); - - Assert.Greater(hitCount, 3); - Assert.Less(hitCount, 10); - - MaximizeAbility(ab, game.Hero); - hitCount = HitEnemyWithCannon(cannonBall, ref fiCount, enemy, ref enemyHealth); - - Assert.Greater(hitCount, 6); - } - - private int HitEnemyWithCannon(ProjectileFightItem cannonBall, ref int fiCount, Enemy enemy, ref float enemyHealth) - { - var hitCount = 0; - for (int i = 0; i < 10; i++) - { - fiCount = UseCannon(cannonBall, fiCount, enemy); - if (enemyHealth > enemy.Stats.Health) - hitCount++; - - enemyHealth = enemy.Stats.Health; - } - - return hitCount; - } - - private int UseCannon(ProjectileFightItem cannonBall, int fiCount, Enemy enemy) - { - var hero = game.Hero; - var ab = hero.GetActiveAbility(Roguelike.Abilities.AbilityKind.Cannon); - Assert.True(UseFightItem(hero, enemy, cannonBall)); - var co = hero.Inventory.GetStackedCount(cannonBall); - Assert.AreEqual(co, fiCount - 1); - fiCount = co; - - GotoNextHeroTurn(); - WaitForAbilityCoolDown(ab); - PlaceEnemyForCannon(hero, enemy); - return fiCount; - } - private Enemy PrepareEnemyForCannonHit() { var hero = game.Hero; diff --git a/POCO/RoguelikeUnitTests.Core/FightMagicTests.cs b/POCO/RoguelikeUnitTests.Core/FightMagicTests.cs index 280d0a9c..b5b3ae1c 100644 --- a/POCO/RoguelikeUnitTests.Core/FightMagicTests.cs +++ b/POCO/RoguelikeUnitTests.Core/FightMagicTests.cs @@ -190,7 +190,7 @@ public void ScrollPowerVSMeleeTest(bool scroll, int heroLevel) var hp = dcMelee.HealthPercentage; var damageMelee = enemyHealth - enemy.Stats.Health; Assert.Greater(damageMelee, 15 * mult); - AssertHealthDiffPercentageNotBigger(dcMelee, dcSpell, 140); + AssertHealthDiffPercentageNotBigger(dcMelee, dcSpell, 152); //Assert.Less(Math.Abs(damageMelee - damageScroll), 30);//TODO % } @@ -226,7 +226,7 @@ public void EnemyAttackTest() var enemy = AllEnemies.Cast().First(); enemy.SetPrefferedFightStyle(PrefferedFightStyle.Magic);//use spells - enemy.ActiveManaPoweredSpellSource = new Scroll(SpellKind.FireBall); + enemy.SelectedManaPoweredSpellSource = new Scroll(SpellKind.FireBall); enemy.AlwaysHit[AttackKind.SpellElementalProjectile] = true; Assert.True(game.GameManager.HeroTurn); @@ -304,25 +304,38 @@ public void PerunScrollTest() [TestCase(SpellKind.Perun)] [TestCase(SpellKind.Swiatowit)] - public void PerunScrollPowerIncTest(SpellKind sk) + [Repeat(1)] + public void GodScrollPowerIncTest(SpellKind sk) { - var game = CreateGame();// genNumOfEnemies: 1); + var game = CreateGame(); var hero = game.Hero; Container.GetInstance().LogLevel = LogLevel.Info; - Enemy enemy = PlainEnemies.First(); + Enemy enemy = PlainEnemies.First(); PrepareToBeBeaten(enemy); PrepareToBeBeaten(hero); - + Assert.Less(enemy.Stats.GetStat(EntityStatKind.ResistPoison).Value.CurrentValue, 10); + Assert.Less(enemy.Stats.GetStat(EntityStatKind.ResistFire).Value.CurrentValue, 10); + Assert.Less(enemy.Stats.GetStat(EntityStatKind.ResistCold).Value.CurrentValue, 10); + var dc = new OuaDDamageComparer(enemy, this); var scroll = PrepareScroll(hero, sk, enemy, scrollCount:12); + hero.AlwaysHit[AttackKind.SpellElementalProjectile] = true;//that test checks the power not accurency + //hero.Stats.GetStat(EntityStatKind.ChanceToCastSpell).Value.Nominal = 100;//that test checks the power not accurency - game.GameManager.SpellManager.ApplySpell(hero, scroll, enemy); + var spell = game.GameManager.SpellManager.ApplySpell(hero, scroll, enemy); dc.RegisterHealth(); + if (dc.HealthDifference == 0) + { + GotoNextHeroTurn(); + int k = 0; + k++; + } Assert.Greater(dc.HealthDifference, 0); int biggerCounter = 0; float damageMin = dc.HealthDifference; float damageMax = dc.HealthDifference; + var lastDamage = spell.Damage; for (int level = 0; level < 10; level++) { hero.Level = level + 2;//2, 3... @@ -331,12 +344,22 @@ public void PerunScrollPowerIncTest(SpellKind sk) GotoNextHeroTurn(); hero.Consume(new Potion() { Kind = PotionKind.Mana }); GotoNextHeroTurn(); - var spell = game.GameManager.SpellManager.ApplySpell(hero, scroll, enemy); + //use god + spell = game.GameManager.SpellManager.ApplySpell(hero, scroll, enemy); + if (spell == null) + { + int k = 0; + k++; + } Assert.NotNull(spell); + Assert.Greater(spell.Damage, lastDamage); Assert.Greater(spell.CurrentLevel, 1); dc.RegisterHealth(); - Assert.Greater(dc.HealthDifference, 0); - //Assert.Greater(dc.HealthDifference, hd); + if(dc.HealthDifference == 0) + { + int k = 0; + k++; + } if (dc.HealthDifference > hd) biggerCounter++; else { @@ -348,13 +371,15 @@ public void PerunScrollPowerIncTest(SpellKind sk) hd = dc.HealthDifference; if (hd > damageMax) damageMax = hd; + + lastDamage = spell.Damage; } var expBiggerCounter = 8; if (sk == SpellKind.Swiatowit) - expBiggerCounter = 6; + expBiggerCounter = 5; Assert.Greater(biggerCounter, expBiggerCounter); - Assert.Greater(damageMax / damageMin, 4); + Assert.Greater(damageMax / damageMin, 4f); } [Test] @@ -372,9 +397,14 @@ public void SwiatowitScrollTest() var scroll = PrepareScroll(hero, SpellKind.Swiatowit, enemy); var enHealth = enemy.Stats.Health; - game.GameManager.SpellManager.ApplySpell(hero, scroll, enemy); - Assert.True(!game.GameManager.HeroTurn); - Assert.Less(enemy.Stats.Health, enHealth); + + for (int i = 0; i < 2; i++) + { + Assert.NotNull(game.GameManager.SpellManager.ApplySpell(hero, scroll, enemy)); + Assert.True(!game.GameManager.HeroTurn); + GotoNextHeroTurn(); + } + Assert.Less(enemy.Stats.Health, enHealth, enemy.ToString()); Assert.Greater(sndPlayed.Length, 0); } @@ -448,8 +478,34 @@ public void ManaScrollTest() } + [Test] [Repeat(1)] + public void OnlyOneSkeletonTest() + { + var game = CreateGame(); + var hero = game.Hero; + + var scroll = PrepareScroll(hero, SpellKind.Skeleton); + scroll.Count = 5; + var gm = game.GameManager; + var alliesCount = gm.CurrentNode.GetTiles().Count; + Assert.AreEqual(alliesCount, 0); + var spell = gm.SpellManager.ApplySpell(hero, scroll) as SkeletonSpell; + Assert.NotNull(spell); + Assert.NotNull(spell.Ally); + + Assert.AreEqual(gm.CurrentNode.GetTiles().Count, alliesCount + 1); + Assert.True(gm.AlliesManager.AllEntities.Contains((spell.Ally))); + + spell = gm.SpellManager.ApplySpell(hero, scroll) as SkeletonSpell; + var allyC = gm.CurrentNode.GetTiles().Count; + Assert.Null(spell); + Assert.AreEqual(allyC, alliesCount + 1); + } + + [Test] + [Repeat(1)] public void SkeletonScrollTest() { var game = CreateGame(); @@ -542,6 +598,7 @@ public void SwapScrollTest() } [Test] + [Repeat(1)] public void CrackedStoneScrollTest() { var gi = new GenerationInfo(); @@ -561,6 +618,8 @@ public void CrackedStoneScrollTest() var enemy = AllEnemies.First(); enemy.Name += " in test"; Assert.True(game.Level.SetTile(enemy, enemyPh)); + enemy.SelectedFightItem = null; + enemy.SelectedManaPoweredSpellSource = null; PassiveSpell spell; var scroll = PrepareScroll(hero, SpellKind.CrackedStone); @@ -575,6 +634,11 @@ public void CrackedStoneScrollTest() var enPos = enemy.point; GotoNextHeroTurn(); Assert.True(game.GameManager.EnemiesManager.ShallChaseTarget(enemy, game.Hero)); + if (enemy.point == enPos) + { + int k = 0; + k++; + } Assert.AreNotEqual(enemy.point, enPos); Assert.AreEqual(enemy.point.X, enPos.X);//shall try walk around the stone } diff --git a/POCO/RoguelikeUnitTests.Core/FightMeleeTests.cs b/POCO/RoguelikeUnitTests.Core/FightMeleeTests.cs index 8341b530..d7a24ff3 100644 --- a/POCO/RoguelikeUnitTests.Core/FightMeleeTests.cs +++ b/POCO/RoguelikeUnitTests.Core/FightMeleeTests.cs @@ -13,6 +13,8 @@ using System.Linq; using Roguelike.Tiles.Abstract; using Roguelike.Calculated; +using Dungeons.Core; +using System.Diagnostics; namespace RoguelikeUnitTests { @@ -41,7 +43,7 @@ public void NonPlainEnemyUsesEffects() var closeToHero = game.Level.GetClosestEmpty(hero, incDiagonals: false); game.Level.SetTile(enemy, closeToHero.point); - enemy.ActiveManaPoweredSpellSource = null;//this causes attack + enemy.SelectedManaPoweredSpellSource = null;//this causes attack //hit enemy to force him to use effect enemy.OnMeleeHitBy(hero); @@ -87,28 +89,63 @@ public void StunnedEffectFromWeapon() { var game = CreateGame();// genNumOfEnemies:1); var hero = game.Hero; - hero.d_immortal = true; + hero.Immortal = true; hero.Stats.SetNominal(EntityStatKind.ChanceToMeleeHit, 100); var wpn = GenerateEquipment("hammer"); - wpn.MakeMagic(EntityStatKind.ChanceToCauseStunning, 100); - wpn.Identify(); + //wpn.MakeMagic(EntityStatKind.ChanceToCauseStunning, 10); + //wpn.Identify(); SetHeroEquipment(wpn); + //hero.Stats.GetStat(EntityStatKind.ChanceToCauseStunning).Value.Nominal = 40; var ccs = hero.GetCurrentValue(EntityStatKind.ChanceToCauseStunning); - Assert.AreEqual(ccs, 100); - - var enemy = ActivePlainEnemies.First(); - PlaceCloseToHero(enemy); - enemy.OnMeleeHitBy(hero); - Assert.True(enemy.LastingEffects.Any()); - Assert.AreEqual(enemy.LastingEffects[0].Type, EffectType.Stunned); - Assert.AreEqual(enemy.LastingEffects[0].Description, "Stunned"); - Assert.Less(enemy.LastingEffects[0].PendingTurns, 5); - for (int i = 0; i < 5; i++) + Assert.AreEqual(ccs, 10); + //Assert.AreEqual(AllEnemies.Count, 1); + int effCounter = 0; + Enemy enemy = ActivePlainEnemies.Where(i => !i.IsImmuned(EffectType.Stunned)).First();//AllEnemies.First();//; + enemy.Immortal = true; + PrepareToBeBeaten(enemy); + + for (int ei = 0; ei < 50; ei++) { - GotoNextHeroTurn(); + PlaceCloseToHero(enemy); + enemy.OnMeleeHitBy(hero); + + if (enemy.HasLastingEffect(EffectType.Stunned)) + { + effCounter++; + var le = enemy.LastingEffectsSet.GetByType(EffectType.Stunned); + Assert.AreEqual(le.Description, "Stunned (Pending Turns: 3)"); + Assert.Less(le.PendingTurns, 5); + Debug.WriteLine("bl "+ enemy+" has le stune: "+le); + + for (int i = 0; i < 5; i++) + { + var pt = le.PendingTurns; + GotoNextHeroTurn(); + if (le.PendingTurns == pt && pt != 0) + { + int k = 0; + k++; + } + Assert.Less(le.PendingTurns, pt); + Debug.WriteLine(" il " + enemy + " has le stune: " + le); + if (!enemy.HasLastingEffect(EffectType.Stunned)) + break; + } + Debug.WriteLine("al " + enemy + " has le stune: " + le); + } + //GotoNextHeroTurn(); + if (enemy.HasLastingEffect(EffectType.Stunned)) + { + //enemy.ApplyLastingEffects(); + int k = 0; + k++; + } + + Assert.False(enemy.HasLastingEffect(EffectType.Stunned)); + //game.Level.SetEmptyTile(enemy.point);//make room for next one } - GotoNextHeroTurn(); - Assert.False(enemy.LastingEffects.Any()); + Assert.Greater(effCounter, 0); + Assert.Less(effCounter, 10); } [Test] @@ -152,7 +189,7 @@ public void DamageFromEnemiesVaries(AttackKind attackKind) var en = enemy as Enemy; var fi = en.AddFightItem(FightItemKind.ThrowingKnife); fi.Count = 15; - en.ActiveFightItem = fi as ProjectileFightItem; + en.SelectedFightItem = fi as ProjectileFightItem; DoDamage(enemy, game.Hero, (LivingEntity attacker, LivingEntity victim) => CallDoDamagePhysicalProjectile(attacker, victim)); } //else if (attackKind == AttackKind.WeaponElementalProjectile) @@ -161,7 +198,7 @@ public void DamageFromEnemiesVaries(AttackKind attackKind) //} else if (attackKind == AttackKind.SpellElementalProjectile) { - enemy.ActiveManaPoweredSpellSource = new Scroll(SpellKind.FireBall); + enemy.SelectedManaPoweredSpellSource = new Scroll(SpellKind.FireBall); enemy.Stats.SetNominal(EntityStatKind.Mana, 1000); DoDamage(enemy, game.Hero, (LivingEntity attacker, LivingEntity victim) => CallDoDamageSpellElementalProjectile(attacker, victim)); } @@ -214,7 +251,7 @@ private void CallDoDamageSpellElementalProjectile(LivingEntity attacker, LivingE } else { - game.GameManager.SpellManager.ApplyAttackPolicy(attacker, victim, attacker.ActiveManaPoweredSpellSource, null, (p) => {}); + game.GameManager.SpellManager.ApplyAttackPolicy(attacker, victim, attacker.SelectedManaPoweredSpellSource, null, (p) => {}); } } @@ -269,7 +306,7 @@ private void CallDoDamagePhysicalProjectile(LivingEntity attacker, LivingEntity else { var en = attacker as Enemy; - var pfi = en.ActiveFightItem as ProjectileFightItem; + var pfi = en.SelectedFightItem as ProjectileFightItem; if (attacker.DistanceFrom(victim) <= 1) return; diff --git a/POCO/RoguelikeUnitTests.Core/FoodConsumtionTests.cs b/POCO/RoguelikeUnitTests.Core/FoodConsumtionTests.cs index 1b51e3ef..35a27cb0 100644 --- a/POCO/RoguelikeUnitTests.Core/FoodConsumtionTests.cs +++ b/POCO/RoguelikeUnitTests.Core/FoodConsumtionTests.cs @@ -33,6 +33,17 @@ public void TestConsume(bool roasted) var hero = game.Hero; var expectedHealthRestore = roasted ? hero.Stats.Health / 2 : hero.Stats.Health / 4; + var food = Helper.AddTile(); + if (roasted) + food.MakeRoasted(); + AddItemToInv(food); + Assert.AreEqual(hero.Inventory.GetStackedCount(food), 1); + + var consumed = hero.Consume(food); + Assert.False(consumed);//not hurt, workaround for eating all meat at one btn press + Assert.AreEqual(hero.Inventory.GetStackedCount(food), 1); + Assert.Null(hero.LastingEffects.SingleOrDefault()); + var enemy = game.GameManager.CurrentNode.SpawnEnemy(1); //Assert.Greater(ActiveEnemies.Count, 0);//enemies on level can be poisonous, failing this test, so let's use skeleton var heroHealth = hero.Stats.Health; @@ -42,15 +53,10 @@ public void TestConsume(bool roasted) Assert.Greater(heroHealth, hero.Stats.Health); heroHealth = hero.Stats.Health; var heroHurtHealth = heroHealth; - - var food = Helper.AddTile(); - if (roasted) - food.MakeRoasted(); - AddItemToInv(food); - + var turnOwner = game.GameManager.Context.TurnOwner; Assert.AreEqual(turnOwner, Roguelike.TurnOwner.Hero); - hero.Consume(food); + Assert.True(hero.Consume(food)); Assert.Greater(hero.Stats.Health, heroHealth); var le = hero.LastingEffects.Single(); Assert.NotNull(le); diff --git a/POCO/RoguelikeUnitTests.Core/InventoryTests.cs b/POCO/RoguelikeUnitTests.Core/InventoryTests.cs index 37f57e12..bc2d1d49 100644 --- a/POCO/RoguelikeUnitTests.Core/InventoryTests.cs +++ b/POCO/RoguelikeUnitTests.Core/InventoryTests.cs @@ -173,57 +173,76 @@ public void PriceTests() Assert.Greater(priceInMerchInv, priceInHeroInv); } - [Test] - public void CountOfStackedRecipe() + [TestCase("craft_one_eq", "craft_three_gems", true)] + [TestCase("big_claw", "big_fang", false)] + public void CountOfStacked(string firstName, string secName, bool recipe) { var game = CreateGame(); var hero = game.Hero; Assert.AreEqual(hero.Inventory.ItemsCount, 0); - var loot1 = game.GameManager.LootGenerator.GetLootByAsset("craft_one_eq"); - PutEqOnLevelAndCollectIt(loot1); - Assert.AreEqual(hero.Crafting.Recipes.Inventory.ItemsCount, 1); - - var loot2 = game.GameManager.LootGenerator.GetLootByAsset("craft_one_eq"); - PutEqOnLevelAndCollectIt(loot2); - Assert.AreEqual(hero.Crafting.Recipes.Inventory.ItemsCount, 1); + var loot1_0 = game.GameManager.LootGenerator.GetLootByAsset(firstName) as StackedLoot; + PutEqOnLevelAndCollectIt(loot1_0); + var l1_0Count = loot1_0.Count; + var invLootIsCollectedTo = recipe ? hero.Crafting.Recipes.Inventory : hero.Inventory; - Assert.AreEqual(hero.Crafting.Recipes.Inventory.GetStackedCount(loot2 as StackedLoot), 2); + var loot1_1 = game.GameManager.LootGenerator.GetLootByAsset(firstName) as StackedLoot; + PutLootOnLevel(loot1_1); + var l1_1Count = loot1_1.Count; + CollectLoot(loot1_1); + GotoNextHeroTurn(); + Assert.AreEqual(invLootIsCollectedTo.ItemsCount, 1); + Assert.AreEqual(invLootIsCollectedTo.GetStackedCount(loot1_1.Name), l1_0Count + l1_1Count); - var loot3 = game.GameManager.LootGenerator.GetLootByAsset("craft_three_gems"); + var loot3 = game.GameManager.LootGenerator.GetLootByAsset(secName) as StackedLoot; PutEqOnLevelAndCollectIt(loot3); - Assert.AreEqual(hero.Crafting.Recipes.Inventory.ItemsCount, 2); - Assert.AreEqual(hero.Crafting.Recipes.Inventory.GetStackedCount(loot2 as StackedLoot), 2); - Assert.AreEqual(hero.Crafting.Recipes.Inventory.GetStackedCount(loot3 as StackedLoot), 1); + Assert.AreEqual(invLootIsCollectedTo.ItemsCount, 2); + + Assert.AreEqual(invLootIsCollectedTo.GetStackedCount(loot3), loot3.Count); + } + + [TestCase(FightItemKind.PlainArrow)] + [TestCase(FightItemKind.PlainBolt)] + [TestCase(FightItemKind.IronBolt)] + public void CountOfStackedBowLikeAmmo(FightItemKind fik) + { + var game = CreateGame(); + var fi = GameManager.LootGenerator.GetLootByAsset(fik.ToString()) as FightItem; + Assert.NotNull(fi); + Assert.GreaterOrEqual(fi.Count, 10); + Assert.LessOrEqual(fi.Count, 25); + } [Test] - public void CountOfStackedTrophies() + public void CountOfStackedPotions() { var game = CreateGame(); var hero = game.Hero; Assert.AreEqual(hero.Inventory.ItemsCount, 0); - var loot1 = game.GameManager.LootGenerator.GetLootByAsset("big_claw") as StackedLoot; + var loot1 = game.GameManager.LootGenerator.GetLootByAsset("small_strength_potion") as StackedLoot; Assert.NotNull(loot1); + var id1 = loot1.GetId(); + var l1Count = loot1.Count; PutEqOnLevelAndCollectIt(loot1); Assert.AreEqual(hero.Inventory.ItemsCount, 1); - - var loot2 = game.GameManager.LootGenerator.GetLootByAsset("big_claw"); - PutEqOnLevelAndCollectIt(loot2); - Assert.AreEqual(hero.Inventory.ItemsCount, 1); - Assert.AreEqual(hero.Inventory.GetStackedCount(loot2 as StackedLoot), 2); - - var loot3 = game.GameManager.LootGenerator.GetLootByAsset("big_fang"); - PutEqOnLevelAndCollectIt(loot3); + Assert.AreEqual(hero.Inventory.GetStackedCount(loot1 as StackedLoot), l1Count); + + var loot2 = game.GameManager.LootGenerator.GetLootByAsset("small_virility_potion") as StackedLoot; + //PutEqOnLevelAndCollectIt(loot2); + PutLootOnLevel(loot2); + var id2 = loot2.GetId(); + var l2Count = loot2.Count; + CollectLoot(loot2); + GotoNextHeroTurn(); Assert.AreEqual(hero.Inventory.ItemsCount, 2); - Assert.AreEqual(hero.Inventory.GetStackedCount(loot2 as StackedLoot), 2); - Assert.AreEqual(hero.Inventory.GetStackedCount(loot3 as StackedLoot), 1); + Assert.AreEqual(hero.Inventory.GetStackedCount(loot2 as StackedLoot), l2Count); } - [Test] + [Test] public void CountOfStackedGems() { var game = CreateGame(); @@ -231,23 +250,26 @@ public void CountOfStackedGems() Assert.AreEqual(hero.Inventory.ItemsCount, 0); - var lootBase = game.GameManager.LootGenerator.GetLootByAsset("diamond_big"); - Assert.NotNull(lootBase); - var loot1 = lootBase as StackedLoot; + var loot1 = game.GameManager.LootGenerator.GetLootByAsset("diamond_big")as StackedLoot; Assert.NotNull(loot1); + var l1Count = loot1.Count; PutEqOnLevelAndCollectIt(loot1); Assert.AreEqual(hero.Inventory.ItemsCount, 1); - var loot2 = game.GameManager.LootGenerator.GetLootByAsset("diamond_big"); - PutEqOnLevelAndCollectIt(loot2); + var loot2 = game.GameManager.LootGenerator.GetLootByAsset("diamond_big") as StackedLoot; + //PutEqOnLevelAndCollectIt(loot2); + PutLootOnLevel(loot2); + var l2Count = loot2.Count; + CollectLoot(loot2); + GotoNextHeroTurn(); Assert.AreEqual(hero.Inventory.ItemsCount, 1); - Assert.AreEqual(hero.Inventory.GetStackedCount(loot2 as StackedLoot), 2); + Assert.GreaterOrEqual(hero.Inventory.GetStackedCount(loot2 as StackedLoot), 1); - var loot3 = game.GameManager.LootGenerator.GetLootByAsset("emerald_medium"); + var loot3 = game.GameManager.LootGenerator.GetLootByAsset("emerald_medium") as StackedLoot; PutEqOnLevelAndCollectIt(loot3); Assert.AreEqual(hero.Inventory.ItemsCount, 2); - Assert.AreEqual(hero.Inventory.GetStackedCount(loot2 as StackedLoot), 2); - Assert.AreEqual(hero.Inventory.GetStackedCount(loot3 as StackedLoot), 1); + Assert.AreEqual(hero.Inventory.GetStackedCount(loot1.Name), l1Count+ l2Count); + Assert.AreEqual(hero.Inventory.GetStackedCount(loot3), loot3.Count); } [Test] diff --git a/POCO/RoguelikeUnitTests.Core/LastingEffectTests.cs b/POCO/RoguelikeUnitTests.Core/LastingEffectTests.cs index fc57175a..47c0e0a5 100644 --- a/POCO/RoguelikeUnitTests.Core/LastingEffectTests.cs +++ b/POCO/RoguelikeUnitTests.Core/LastingEffectTests.cs @@ -1,4 +1,5 @@ using NUnit.Framework; +using Roguelike; using Roguelike.Attributes; using Roguelike.Effects; using Roguelike.Events; @@ -81,11 +82,13 @@ void CheckActionDesc(EffectType et, EntityStatKind esk, char sign, LivingEntity { expected += "Spell was casted on " + ownerName + " "; } + else + expected = ownerName + " "; expected += le1.Description; - var ac = le1.CreateAction(le1); + var ac = le1.CreateAction(le1, false, target); //Assert.True(ac.Info.StartsWith(expected)); Assert.AreEqual(ac.Info, expected); } @@ -250,6 +253,7 @@ public void TestWeaken() } [Test] + [Repeat(1)] public void TestBleeding() { var game = CreateGame(true, 1); @@ -270,7 +274,10 @@ public void TestBleeding() }; var enemy = AllEnemies.First(); + enemy.RemoveImmunity(EffectType.Bleeding); + Assert.False(enemy.IsImmuned(EffectType.Bleeding)); Assert.True(enemy.Revealed); + PlaceCloseToHero(enemy); var enemyHealthStat = enemy.Stats.GetStat(EntityStatKind.Health); enemyHealthStat.Value.Nominal = 150; @@ -313,8 +320,9 @@ public void TestBleedingProlong() } } }; - - var enemy = ActivePlainEnemies.First(); + game.Hero.AlwaysHit[AttackKind.Melee] = true; + + var enemy = base.AddEnemy(EnemySymbols.BatSymbol); PlaceCloseToHero(enemy); var healthStat = enemy.Stats.GetStat(EntityStatKind.Health); healthStat.Value.Nominal = 150; @@ -323,7 +331,7 @@ public void TestBleedingProlong() SetHeroEquipment(wpn); enemy.OnMeleeHitBy(game.Hero); - var le1 = enemy.LastingEffects.Where(i => i.Type == EffectType.Bleeding).SingleOrDefault(); + var le1 = enemy.LastingEffectsSet.GetByType(EffectType.Bleeding); Assert.NotNull(le1); LastingEffect castedAgain = null; @@ -333,7 +341,7 @@ public void TestBleedingProlong() var enemyHealth = enemy.Stats.Health; for (int i = 0; i < turns * 2; i++)//there will be prolong { - castedAgain = enemy.LastingEffects.Where(le => le.Type == EffectType.Bleeding).SingleOrDefault(); + castedAgain = enemy.LastingEffectsSet.GetByType(EffectType.Bleeding); Assert.AreEqual(castedAgain, le1); game.GameManager.SkipHeroTurn(); diff --git a/POCO/RoguelikeUnitTests.Core/LocalizationTests.cs b/POCO/RoguelikeUnitTests.Core/LocalizationTests.cs index b557a233..ebbba05d 100644 --- a/POCO/RoguelikeUnitTests.Core/LocalizationTests.cs +++ b/POCO/RoguelikeUnitTests.Core/LocalizationTests.cs @@ -19,7 +19,7 @@ public void TestHints() var hintHistory = new HintHistory(); var hint = hintHistory.Get(Roguelike.Help.HintKind.LootCollectShortcut); - Assert.AreEqual(hint.Info, "Press 'G' to collect a loot under your position."); + Assert.True(hint.Info.StartsWith("Press 'G' to collect a loot under your position.")); //hintHistory.SetKeyCode(Roguelike.Help.HintKind.LootHightlightShortcut, (int)KeyCode.LeftControl, (Roguelike.Help.HintKind hk) => { return KeyCode.LeftControl.ToDescription(); }); //hint = hintHistory.Get(Roguelike.Help.HintKind.LootHightlightShortcut); diff --git a/POCO/RoguelikeUnitTests.Core/LootingTests.cs b/POCO/RoguelikeUnitTests.Core/LootingTests.cs index e4c05cc1..069a0374 100644 --- a/POCO/RoguelikeUnitTests.Core/LootingTests.cs +++ b/POCO/RoguelikeUnitTests.Core/LootingTests.cs @@ -1,4 +1,5 @@ -using Dungeons.Tiles.Abstract; + +using Dungeons.Tiles.Abstract; using NUnit.Framework; using Roguelike.Attributes; using Roguelike.Extensions; @@ -39,8 +40,27 @@ public void TestRecipiesRandomize() public void Names() { //var env = CreateTestEnv(); + var key = new Key(); + Assert.True(key.PrimaryStatDescription.Any()); var rec = new Recipe(RecipeKind.Toadstools2Potion); Assert.AreEqual(rec.DisplayedName, "Potion from Toadstools Recipe"); + { + var trop1 = new HunterTrophy(); + Assert.AreNotEqual(trop1.TinyTrophyKind, HunterTrophyKind.Unset); + Assert.True(trop1.tag1.Any()); + } + { + var trop1 = new HunterTrophy(HunterTrophyKind.Fang) { }; + Assert.AreEqual(trop1.tag1, "small_fang"); + var pr1 = trop1.Price; + trop1.EnchanterSize = EnchanterSize.Medium; + Assert.AreEqual(trop1.tag1, "medium_fang"); + Assert.Greater(trop1.Price, pr1); + + trop1.TinyTrophyKind = HunterTrophyKind.Claw; + Assert.AreEqual(trop1.tag1, "medium_claw"); + } + } [Test] @@ -246,6 +266,7 @@ public void KilledEnemyForGold() } [Test] + [Repeat(1)] public void KilledEnemyForTinyTrophy() { var env = CreateTestEnv(); @@ -299,7 +320,7 @@ public void KilledEnemyForScrolls() } min *= mult; max *= mult; - Assert.Greater(numberOfNextTypeOfScrolls, min, nextScrollType.Key.ToDescription()); + Assert.GreaterOrEqual(numberOfNextTypeOfScrolls, min, nextScrollType.Key.ToDescription()); Assert.Less(numberOfNextTypeOfScrolls, max, nextScrollType.Key.ToDescription()); } } @@ -391,7 +412,7 @@ public void KilledEnemyAtSamePlace() k++; } Assert.GreaterOrEqual(lootItems.Count, 1); - Assert.True(lootItems[0].DistanceFrom(loot) < 2); + Assert.Less(lootItems[0].DistanceFrom(loot), 2); } [Test] @@ -457,31 +478,38 @@ public void Barrels2Enemies() public void BarrelsSimpleTest(AttackKind ak) { var env = CreateTestEnv(); - var numberOfInteractiveTiles = 10; + var numberOfInteractiveTiles = 20; var lootInfo = new LootInfo(game, null); var barrels = env.AddTiles(() => new Barrel(Container), numberOfInteractiveTiles, (InteractiveTile barrel) => { }); var hero = game.Hero; + hero.Immortal = true; ProjectileFightItem fi = null; if (ak == AttackKind.PhysicalProjectile) { - fi = ActivateFightItem(FightItemKind.ThrowingKnife, hero, 10); + fi = ActivateFightItem(FightItemKind.ThrowingKnife, hero, numberOfInteractiveTiles); } - + var outcome = new List(); foreach (var barrel in barrels) { - HitHitable(ak, hero, fi, barrel); + HitHitable(ak, hero, fi, barrel, true); Assert.True(barrel.Destroyed); Assert.AreEqual(game.GameManager.RecentlyHit, barrel); var tileAt = game.Level.GetTile(barrel.point); Assert.True(tileAt != barrel); - + outcome.Add(tileAt); } var newLootItems = lootInfo.GetDiff(); + if(newLootItems.Count == 0) + { + int k = 0; + k++; + + } Assert.Greater(newLootItems.Count, 0); } @@ -510,7 +538,7 @@ var chests foreach (var chest in chests) { Assert.False(chest.IsLooted); - HitHitable(ak, hero, fi, chest); + HitHitable(ak, hero, fi, chest, true); Assert.True(chest.IsLooted); Assert.AreEqual(game.GameManager.RecentlyHit, chest); var tileAt = game.Level.GetTile(chest.point); @@ -522,7 +550,7 @@ var chests Assert.Greater(newLootItems.Count, 0); } - private void HitHitable(AttackKind ak, Hero hero, ProjectileFightItem fi, IDestroyable target) + private void HitHitable(AttackKind ak, Hero hero, ProjectileFightItem fi, IDestroyable target, bool forceHit) { game.GameManager.RecentlyHit = null; PlaceCloseToHero(target); @@ -530,13 +558,27 @@ private void HitHitable(AttackKind ak, Hero hero, ProjectileFightItem fi, IDestr if (ak == AttackKind.Melee) game.GameManager.InteractHeroWith(target as Dungeons.Tiles.Tile); else if (ak == AttackKind.PhysicalProjectile) - Assert.True(UseFightItem(hero, target, fi)); + { + var ufi = UseFightItem(hero, target, fi); + Assert.True(ufi); + } else if (ak == AttackKind.WeaponElementalProjectile) HeroUseWeaponElementalProjectile(target); else if (ak == AttackKind.SpellElementalProjectile) { - hero.Stats.SetNominal(EntityStatKind.Mana, 100); - Assert.True(UseFireBallSpellSource(hero, target, true, SpellKind.FireBall)); + hero.Stats.GetStat(EntityStatKind.Mana).Value.Subtracted = 0; + var hit = UseFireBallSpellSource(hero, target, true, SpellKind.FireBall); + if (!hit && forceHit) + { + for (int i = 0; i < 10; i++) + { + hero.Stats.GetStat(EntityStatKind.Mana).Value.Subtracted = 0; + hit = UseFireBallSpellSource(hero, target, true, SpellKind.FireBall); + if (hit) + break; + } + } + Assert.True(hit); } } @@ -605,7 +647,7 @@ public void PlainChests() Assert.Less(potions.Count, 43); var mushes = lootInfo.Get(); - Assert.Greater(mushes.Count, 1); + Assert.GreaterOrEqual(mushes.Count, 1); Assert.Less(mushes.Count, 20); var eqs = lootInfo.Get(); @@ -615,7 +657,7 @@ public void PlainChests() //scrolls var scrolls = lootInfo.Get(); Assert.Greater(scrolls.Count, 10); - float maxScrolls = 40; + float maxScrolls = 42; Assert.Less(scrolls.Count, maxScrolls); var typesGrouped = scrolls.GroupBy(f => f.Kind).ToList(); Assert.Greater(typesGrouped.Count, 5); @@ -760,7 +802,7 @@ public void KilledEnemyGivesPotionFromTimeToTime() Assert.Greater(lootItems.Count, 10); var potions = li.Get(); Assert.GreaterOrEqual(potions.Count, np.Count()); - Assert.Less(potions.Count, 17); + Assert.Less(potions.Count, 18); } } diff --git a/POCO/RoguelikeUnitTests.Core/LootingTestsHelper.cs b/POCO/RoguelikeUnitTests.Core/LootingTestsHelper.cs index 20af49f5..64ff84b6 100644 --- a/POCO/RoguelikeUnitTests.Core/LootingTestsHelper.cs +++ b/POCO/RoguelikeUnitTests.Core/LootingTestsHelper.cs @@ -52,6 +52,11 @@ public List AssertLootFromEnemies(LootKind[] expectedKinds, bool lootKindM var en = loot.Source as Enemy; Assert.True(en.PowerKind != EnemyPowerKind.Plain); } + if (string.IsNullOrEmpty(loot.tag1)) + { + int k = 0; + k++; + } Assert.True(!string.IsNullOrEmpty(loot.tag1)); } diff --git a/POCO/RoguelikeUnitTests.Core/SerializationTests.cs b/POCO/RoguelikeUnitTests.Core/SerializationTests.cs index 2eefb345..b454c691 100644 --- a/POCO/RoguelikeUnitTests.Core/SerializationTests.cs +++ b/POCO/RoguelikeUnitTests.Core/SerializationTests.cs @@ -1,6 +1,5 @@ using NUnit.Framework; using Roguelike; -using Roguelike.Core.Serialization; using Roguelike.History; using Roguelike.Serialization; using Roguelike.TileContainers; @@ -9,7 +8,6 @@ using Roguelike.Tiles.Looting; using System; using System.Linq; -using System.IO; namespace RoguelikeUnitTests { @@ -58,7 +56,7 @@ public void PermaDeathTest() [Test] [Repeat(1)] - public void NewGameTest() + public void SerializeNewGameTest() { string heroName; GameLevel gameLevel = null; @@ -77,7 +75,7 @@ public void NewGameTest() heroName = hero.Name; //move hero to rand position. - var pt = gameNode.GetFirstEmptyPoint(); + var pt = gameNode.GetFirstEmptyPoint(); Assert.AreNotEqual(hero.point, pt); gameNode.SetTile(hero, pt.Value); @@ -257,19 +255,5 @@ public void ManyGamesTest() } } - [Test] - public void GodotSavedGamesList() - { - var game = CreateGame(true); - var hero = game.Hero; - hero.Name = "SaveHeroStatsTest"; - - SaveLoad(); - - var savedGamesList = SavedGames.GetSavedGamesList(); - Assert.Greater(savedGamesList.Count, 0); - Assert.IsTrue(savedGamesList.Any(s => s == System.IO.Path.GetTempPath() + "Roguelike" + "\\" + "SaveHeroStatsTest")); - } - } } diff --git a/POCO/RoguelikeUnitTests.Core/SpellsTests.cs b/POCO/RoguelikeUnitTests.Core/SpellsTests.cs index 73a1c871..cdd5e978 100644 --- a/POCO/RoguelikeUnitTests.Core/SpellsTests.cs +++ b/POCO/RoguelikeUnitTests.Core/SpellsTests.cs @@ -159,6 +159,19 @@ public void SpellPowerTestEnemy() } } + //[Test] + //public void TargetRequirementTests() + //{ + // var game = CreateGame(); + // var spellKinds = EnumHelper.GetEnumValues(true); + // foreach (var spellKind in spellKinds) + // { + // var scroll = new Scroll(spellKind); + // var spell = scroll.CreateSpell(game.Hero); + + // //Assert.AreEqual(scroll.TargetRequired, spell is OffensiveSpell);//TODO + // } + //} [Test] @@ -289,7 +302,7 @@ public void CrackedStoneTest(AttackKind ak, string expSound) else if (ak == AttackKind.PhysicalProjectile) { var fi = ActivateFightItem(FightItemKind.ThrowingKnife, hero, 20); - Assert.True(UseFightItem(hero, crackedStone, hero.ActiveProjectileFightItem)); + Assert.True(UseFightItem(hero, crackedStone, hero.SelectedProjectileFightItem)); } else if (ak == AttackKind.SpellElementalProjectile) { diff --git a/POCO/RoguelikeUnitTests.Core/TestBase.cs b/POCO/RoguelikeUnitTests.Core/TestBase.cs index 3f33b97b..f513f5e6 100644 --- a/POCO/RoguelikeUnitTests.Core/TestBase.cs +++ b/POCO/RoguelikeUnitTests.Core/TestBase.cs @@ -24,6 +24,9 @@ using System.Drawing; using System.Linq; using Dungeons.Tiles.Abstract; +using System.Globalization; +using Roguelike.Abstract; +using System.Xml.Linq; namespace RoguelikeUnitTests { @@ -42,13 +45,16 @@ public class TestBase : ITestBase public RoguelikeGame Game { get => game; protected set => game = value; } public Container Container { get; set; } public BaseHelper Helper { get => helper; set => helper = value; } - GameManager GameManager { get => game.GameManager; } + public GameManager GameManager { get => game.GameManager; } protected BaseHelper helper; [SetUp] public void Init() { + CultureInfo.CurrentCulture = new CultureInfo("en-GB");//en-GB + OnInit(); + Log("--"); Log("Test SetUp: " + NUnit.Framework.TestContext.CurrentContext.Test.Name); Log("--"); @@ -56,14 +62,14 @@ public void Init() Assert.Greater(gi.NumberOfRooms, 1); Assert.Greater(gi.ForcedNumberOfEnemiesInRoom, 2); - OnInit(); + } [TearDown] public void Cleanup() { Log("--"); - Log("Test Cleanup: " + NUnit.Framework.TestContext.CurrentContext.Test.Name); + Log("Test Cleanup: " + TestContext.CurrentContext.Test.Name); Log("--"); Log("-"); } @@ -78,14 +84,42 @@ protected void IncreaseSpell(Roguelike.Tiles.LivingEntities.Hero hero, SpellKind protected void Log(string v) { - Debug.WriteLine(v); + Container.GetInstance().LogInfo(v); + //Debug.WriteLine(v); } - protected Enemy SpawnEnemy() + protected Ally AddAlly(Hero hero, bool skeleton, bool second = false) { + var am = GameManager.AlliesManager; + //Assert.AreEqual(am.AllEntities.Count, second ? 1 : 0); + + if (skeleton) + { + var scroll = new Scroll(SpellKind.Skeleton); + hero.Inventory.Add(scroll); + Assert.NotNull(GameManager.SpellManager.ApplySpell(hero, scroll)); + } + else + { + var hound = Container.GetInstance(); + GameManager.AddAlly(hound); + } + + //Assert.AreEqual(am.AllEntities.Count, second ? 2 : 1); + var ally = am.AllAllies.Last() as Ally; + Assert.AreEqual(ally.Name, skeleton ? "Skeleton" : "Hound"); + Assert.Less(ally.DistanceFrom(hero), 5); + return ally as Ally; + } + + protected Enemy SpawnEnemy(char symbol = 'b') + { + Enemy en = null; if (game != null) - return game.GameManager.CurrentNode.SpawnEnemy(1); - return Container.GetInstance(); + en = game.GameManager.CurrentNode.SpawnEnemy(1); + en = Container.GetInstance(); + en.Symbol = symbol; + return en; } protected void HeroUseWeaponElementalProjectile( IDestroyable victim) @@ -102,7 +136,7 @@ protected virtual void OnInit() Tile.IncludeDebugDetailsInToString = true; Container = new Roguelike.ContainerConfigurator().Container; Container.Register(); - + Container.GetInstance().LogLevel = LogLevel.Info; } protected T GenerateRandomEqOnLevelAndCollectIt() where T : Equipment, new() @@ -112,7 +146,7 @@ protected virtual void OnInit() return eq; } - private void CollectLoot(Loot loot) + protected void CollectLoot(Loot loot) { var set = Game.GameManager.CurrentNode.SetTile(game.Hero, loot.point); game.GameManager.CollectLootOnHeroPosition(); @@ -150,13 +184,13 @@ public void PutEqOnLevelAndCollectIt(Loot eq) protected List ActivePlainEnemies { - get { return game.GameManager.EnemiesManager.GetActiveEnemies().Where(i => i.PowerKind == EnemyPowerKind.Plain).ToList(); } + get { return ActiveEnemies.Where(i => i.PowerKind == EnemyPowerKind.Plain).ToList(); } } List ActiveEnemies { - get { return game.GameManager.EnemiesManager.GetActiveEnemies().ToList(); } + get { return game.GameManager.EnemiesManager.GetActiveEnemies().Where(i=> i is Enemy).Cast().ToList(); } } protected List AllEnemies @@ -213,7 +247,7 @@ public virtual RoguelikeGame CreateGame int? genNumOfEnemies = null, int numberOfRooms = 5, GenerationInfo gi = null, - LogLevel logLevel = LogLevel.Unset + LogLevel logLevel = LogLevel.Info ) { Container.GetInstance().LogLevel = logLevel; @@ -263,7 +297,7 @@ public virtual RoguelikeGame CreateGame genNumOfEnemiesToUse = genNumOfEnemiesToUse * numberOfRooms; } } - + game.GameManager.GameState.CoreInfo.Difficulty = GenerationInfo.Difficulty; var level = game.GenerateLevel(0, gi); var enemies = level.GetTiles(); @@ -366,7 +400,10 @@ public void GotoNextHeroTurn(GameManager gm) var ni = ActiveEnemies.Where(e => e.State != EntityState.Idle).ToList(); //game.GameManager.Logger.LogInfo("make enemies move " + game.GameManager.Context.PendingTurnOwnerApply); game.MakeGameTick();//make enemies move + Assert.AreEqual(gm.Context.TurnOwner, Roguelike.TurnOwner.Animals); game.MakeGameTick();//make animals move + Assert.AreEqual(gm.Context.TurnOwner, Roguelike.TurnOwner.Npcs); + game.MakeGameTick(); if (!game.GameManager.HeroTurn) { var tac1 = game.GameManager.Context.TurnActionsCount; @@ -407,22 +444,28 @@ protected void PlaceCloseToHero(IDestroyable destr, int? minDistance = null) PlaceCloseToHero(GameManager, destr, minDistance); } - public static void PlaceCloseToHero(GameManager gm, IDestroyable destr, int? minDistance = null) + public void PlaceCloseToHero(GameManager gm, IDestroyable destr, int? minDistance = null) { var hero = gm.Hero; + PlaceClose(hero, destr, minDistance); + } + + public void PlaceClose(LivingEntity le, IDestroyable destr, int? minDistance) + { + var gm = GameManager; Tile empty; if (minDistance == null) - empty = gm.CurrentNode.GetClosestEmpty(hero); + empty = gm.CurrentNode.GetClosestEmpty(le); else { empty = gm.CurrentNode.GetEmptyTiles() - .Where(i => i.DistanceFrom(hero) >= minDistance.Value) - .OrderBy(i=>i.DistanceFrom(hero)) + .Where(i => i.DistanceFrom(le) >= minDistance.Value) + .OrderBy(i => i.DistanceFrom(le)) .FirstOrDefault(); } gm.CurrentNode.SetTile(destr as Tile, empty.point); - if (destr is LivingEntity le && !gm.EnemiesManager.AllEntities.Contains(le)) - gm.EnemiesManager.AddEntity(le); + if (destr is Enemy le1 && !gm.EnemiesManager.AllEntities.Contains(le1)) + gm.EnemiesManager.AddEntity(le1); } protected Tuple SetCloseToLivingEntity(Dungeons.Tiles.Tile tile, LivingEntity le) @@ -464,7 +507,7 @@ protected bool UseSpellSource(Hero caster, IHitable victim, SpellSource spellSou caster.Inventory.Add(spellSource); if (caster is Hero) { - game.Hero.ActiveManaPoweredSpellSource = spellSource; + game.Hero.SelectedManaPoweredSpellSource = spellSource; return game.GameManager.SpellManager.ApplyAttackPolicy(caster, victim, spellSource) == ApplyAttackPolicyResult.OK; } return false; @@ -507,7 +550,12 @@ protected ProjectileFightItem ActivateFightItem(FightItemKind fik, Hero hero, in fi.Count = fiCount; fi.AlwaysHit = true; hero.Inventory.Add(fi); - hero.ActiveFightItem = fi; + + //var ak = FightItem.GetAbilityKind(fi); + //if(ak!= AbilityKind.Unset) + // hero.SelectedActiveAbility = hero.GetActiveAbility(ak); + //else + hero.SelectedFightItem = fi; return fi; } @@ -619,13 +667,13 @@ public void MakeEnemyThrowProjectileAtHero int pfiCount = 3 ) { - en.ActiveFightItem = en.SetActiveFightItem(fightItemKind); + en.SelectedFightItem = en.SetActiveFightItem(fightItemKind); en.Name = "bandit555"; en.d_canMove = false;//make sure will not move, but fight at distance en.AlwaysHit[AttackKind.PhysicalProjectile] = true; PlaceCloseToHero(gm, en, 2); - if (addFightItems && en.ActiveFightItem.Count != pfiCount) - en.ActiveFightItem.Count = pfiCount; + if (addFightItems && en.SelectedFightItem.Count != pfiCount) + en.SelectedFightItem.Count = pfiCount; Assert.AreEqual(gm.Hero.GetFightItemKindHitCounter(fightItemKind), 0); @@ -644,7 +692,7 @@ public void MakeEnemyThrowProjectileAtHero if (breakAfterOne) { Assert.AreEqual(fic, 1); - Assert.Greater(en.ActiveFightItem.Count, 0); + Assert.Greater(en.SelectedFightItem.Count, 0); } Assert.Greater(fic, 0); Assert.Less(fic, 4); @@ -658,5 +706,13 @@ public void MaximizeAbility(Ability ab, AdvancedLivingEntity le) } } + protected Enemy AddEnemy(char symbol = 'b') + { + var en = SpawnEnemy(symbol); + en.Revealed = true; + GameManager.CurrentNode.SetTileAtRandomPosition(en); + GameManager.EnemiesManager.AddEntity(en); + return en; + } } } diff --git a/POCO/RoguelikeUnitTests.Core/TestBaseTyped.cs b/POCO/RoguelikeUnitTests.Core/TestBaseTyped.cs index 0154373e..bdd31a6a 100644 --- a/POCO/RoguelikeUnitTests.Core/TestBaseTyped.cs +++ b/POCO/RoguelikeUnitTests.Core/TestBaseTyped.cs @@ -1,4 +1,5 @@ -using RoguelikeUnitTests.Helpers; +using Dungeons.Core; +using RoguelikeUnitTests.Helpers; namespace RoguelikeUnitTests { @@ -12,8 +13,8 @@ protected override void OnInit() public T CreateTestEnv(bool autoLoadLevel = true, int numEnemies = 10, int numRooms = 10) { - //var numEn = numEnemies / numRooms; - var game = CreateGame(autoLoadLevel, numEnemies, numRooms); + var logLevel = Container.GetInstance().LogLevel; + var game = CreateGame(autoLoadLevel, numEnemies, numRooms, logLevel : logLevel); helper = new T(); helper.Test = this; diff --git a/POCO/RoguelikeUnitTests.Core/Utils/DamageComparer.cs b/POCO/RoguelikeUnitTests.Core/Utils/DamageComparer.cs index 0f9ce220..f2787cbf 100644 --- a/POCO/RoguelikeUnitTests.Core/Utils/DamageComparer.cs +++ b/POCO/RoguelikeUnitTests.Core/Utils/DamageComparer.cs @@ -21,6 +21,13 @@ public DamageComparer(LivingEntity le) RegisterHealth(); } + public float RegisterHealthPercentage(Action ac) + { + ac(); + RegisterHealth(); + return HealthPercentage; + } + public void RegisterHealth() { if (!h1.HasValue)