/// <summary> /// Loads resource entries from provided file into provided dictionary /// </summary> /// <param id="dict">Dictionary the entries will be loaded into</param> /// <param id="xmlFile">File the entries are loaded from</param> public static void LoadResources(SortedDictionary<string, ResourceDb> dict, XDocument xmlFile) { var ressXml = xmlFile.Descendants("Resource"); foreach (var resXml in ressXml) { ResourceDb resEntry = new ResourceDb(); foreach (XNode en in resXml.Nodes()) { XElement e = en as XElement; if (e == null) continue; switch (e.Name.LocalName.ToLower()) { case "id": resEntry.Id = e.Value; break; case "name": resEntry.Name = e.Value; break; case "texture": if (!UI.Instance.HaveTexture(e.Value)) { throw new Exception("Entry: " + resEntry.Id + " - texture " + e.Value + " not found"); } resEntry.Texture = UI.Instance.GetTexture(e.Value); break; case "startwith": resEntry.StartWith = int.Parse(e.Value); break; case "amount": resEntry.InitialAmount = int.Parse(e.Value); break; case "occurchance": resEntry.OccurChance = float.Parse(e.Value, CultureInfo.InvariantCulture); break; case "cooldown": resEntry.Cooldown = int.Parse(e.Value); break; } } dict.Add(resEntry.Id, resEntry); } }
/// <summary> /// Initializes a resource using tile as position /// </summary> /// <param id="entry">Entry defining the type of this game object</param> /// <param id="game">Game this game object is in</param> /// <param id="player">Owner of this game object</param> /// <param id="mapTiles">Tile where this game object is placed</param> public override void Initialize(EntryDb entry, WildmenGame game, Player player, Tile mapTile) { AssignUID(game); this.entry = (ResourceDb)entry; this.Amount = this.entry.InitialAmount; this.game = game; this.Owner = player; player.Assign(this); this.NearestTile = mapTile; this.MapPosition = mapTile.MapPosition; this.RecalculatePosition = true; }
/// <summary> /// Carries out the order /// </summary> private void UpdateDoOrder() { GameEffect gEff = null; Building bldg = null; Tile tile = null; BuildingDb bldgEntry = null; switch (Order) { case GameObjectOrder.Idle: #region Logger.Log(string.Format("Unit #{0}:{1} finished move order", this.ObjectUID, this.Entry.Id), "Server update"); if (!game.IsServer) { State = GameObjectState.Idle; } else { // Find position along traveling vector available for movement bool closeFound = false; if (NearestTile == null) { // Nearest Tile not yet updated return; } // If standing too close to other unit, move aside (if possible) foreach (var closeUnit in NearestTile.Units) { // Ignore self if (closeUnit == this) continue; // Ignore moving units if (closeUnit.State == GameObjectState.MovingToOrder) continue; // Check minimal distance if ((closeUnit.MapPosition - this.MapPosition).Length() < MIN_IDLE_DISTANCE) { Vector2 vector; // If the map positions aren't same, move in the same amount you are standing towards the other unit if (this.MapPosition != closeUnit.MapPosition) { vector = this.MapPosition - closeUnit.MapPosition; vector.Normalize(); OrderPosition = this.MapPosition + vector * DODGE_DISTANCE; } // If the position is same, come up with some vector else { // Try to move in same amount you are standing from the origin position of the map vector = this.MapPosition; vector.Normalize(); if (NearestTile.Units.Any(q => (q.MapPosition - OrderPosition).Length() >= MOVEMENT_RANGE)) { OrderPosition = this.MapPosition + vector * DODGE_DISTANCE; } // If the place is already occupied, try inverted vector and orthogonal vectors else { Vector2 ort = new Vector2(-vector.Y, vector.X); Queue<Vector2> attemptVector = new Queue<Vector2>(); attemptVector.Enqueue(ort); attemptVector.Enqueue(-vector); attemptVector.Enqueue(-ort); attemptVector.Enqueue(vector); Vector2 projectedPosition = default(Vector2); while (attemptVector.Count > 0) { projectedPosition = this.MapPosition + attemptVector.Dequeue() * DODGE_DISTANCE; if (NearestTile.Units.Any(q => (q.MapPosition - projectedPosition).Length() >= MOVEMENT_RANGE)) break; } // If everything else fails, use last failed OrderPosition = projectedPosition; } } State = GameObjectState.MovingToOrder; closeFound = true; game.SendData(Network.MakeServerMessage(MessageType.GameObjUpdate, this)); break; } } // If there is no other unit, become idle if (!closeFound) { State = GameObjectState.Idle; } else { OrderTarget = null; OrderRange = MOVEMENT_RANGE; } } #endregion break; case GameObjectOrder.Attack: #region if (OrderTarget == null) return; if (OrderTarget == this) { CancelOrders(true); return; } // Animation effect TextureTileSet2D tex = new TextureTileSet2D(UI.Instance.GetTexture("combat"), 24, 1); Vector2 p = (Position + OrderTarget.Position) / 2; gEff = new AnimationEffect(tex, p + AlignmentOffset + Size / 2 - tex.FrameSize / 2, 24, 10, true); gEff.Initialize(game, NearestTile); this.game.Effects.Add(gEff); if (game.IsServer) { // Apply modifiers float amount = entry.AttackAmount; if (entry.Modifiers != null) for (int i = 0; i < entry.Modifiers.Length; i++) if (entry.Modifiers[i].Entry == OrderTarget.Entry) amount *= entry.Modifiers[i].Mod; Logger.Log(string.Format("Unit #{0}:{1} attacking (-{2}) #{3}:{4} (before {5}/?)", this.ObjectUID, entry.Id, (int)amount, OrderTarget.ObjectUID, OrderTarget.Entry.Id, OrderTarget.Health), "Server update"); // Apply damage OrderTarget.ReceiveDamage((int)amount); // Check whether target is alive if (OrderTarget.Health == 0) { CancelOrders(true); return; } } #endregion break; case GameObjectOrder.Construct: #region if (game.IsServer) { if (OrderTarget != null) { Debug.Assert(OrderTarget.GetEntityType == GameEntityType.Building); Logger.Log(string.Format("Unit #{0}:{1} constructing (+{2}) effect #{3}:{4} (before {5}/100)", this.ObjectUID, entry.Id, entry.ConstructAmount, OrderTarget.ObjectUID, OrderTarget.Entry.Id, ((Building)OrderTarget).Construction), "Server update"); // Construct part of the building ((Building)OrderTarget).ConstructionProgress(entry.ConstructAmount); } else { Logger.Log(string.Format("Unit #{0}:{1} starting construction of {2}", this.ObjectUID, entry.Id, OrderEntry.Id), "Server update"); tile = game.Map.GetTileFlat(OrderPosition); // Check whether building can be constructed at given position if (!Building.CanPlace(game.Map, tile) || Owner.Fow[tile.X, tile.Y] == 0) { Logger.Log(string.Format("Unit #{0}:{1} construction of {2} aborted - unable to place", this.ObjectUID, entry.Id, OrderEntry.Id), "Server update"); CancelOrders(true); return; } bldgEntry = (BuildingDb)OrderEntry; // Check whether building can be built more than once if (bldgEntry.OnlyOneAllowed && Owner.Buildings.Any(q => q.Entry == bldgEntry)) { ScrollUpMessage("Only one such building allowed at a time", 100, true); CancelOrders(true); return; } if (bldgEntry.UnlockedBy != null) { bldg = Owner.Buildings.FirstOrDefault(q => q.Entry == bldgEntry.UnlockedBy); if (bldg == null || !bldg.Constructed) { ScrollUpMessage("Building unavailable", 100, true); CancelOrders(true); return; } } // Check whether player canCast enough resources if (bldgEntry.Cost != null) { foreach (var costEntry in bldgEntry.Cost) { if (Owner.ResourceStockpiles[costEntry.Entry] < costEntry.Amount) { ScrollUpMessage("Not enough resources", 100, true); CancelOrders(true); return; } } } // Place new building bldg = new Building(); bldg.Initialize(bldgEntry, game, Owner, tile); bldg.InitializeGraphics(); game.SendData(Network.MakeServerMessage(MessageType.GameObjCreate, bldg)); // Deduct resources from the player if (bldgEntry.Cost != null) foreach (var costEntry in bldgEntry.Cost) Owner.ResourceStockpiles[costEntry.Entry] -= costEntry.Amount; // Set target for this unit (so it will automatically construct the building) OrderTarget = bldg; OrderEntry = null; game.SendData(Network.MakeServerMessage(MessageType.PlayerUpdate, Owner)); game.SendData(Network.MakeServerMessage(MessageType.GameObjUpdate, this)); } } #endregion break; case GameObjectOrder.Spell: #region EffectDb effect = (EffectDb)OrderEntry; if (game.IsServer) { // Check whether spell can be cast if (effect.UnlockedBy != null) { bldg = Owner.Buildings.FirstOrDefault(q => q.Entry == effect.UnlockedBy); if (bldg == null || !bldg.Constructed) { CancelOrders(true); return; } } Logger.Log(string.Format("Unit #{0}:{1} casting {2}", this.ObjectUID, entry.Id, OrderEntry.Id), "Server update"); switch (effect.Spell.Target) { case SpellEntry.TargetType.Tile: tile = game.Map.GetTileFlat(OrderPosition); gEff = new GameEffect(effect, this, tile); gEff.Initialize(game, tile); this.game.Effects.Add(gEff); break; case SpellEntry.TargetType.GameEntity: Debug.Assert(OrderTarget != null); gEff = new GameEffect(effect, this, OrderTarget); gEff.Initialize(game, OrderTarget.NearestTile); this.game.Effects.Add(gEff); break; default: throw new Exception("Undefined spell type"); } game.SendData(Network.MakeServerMessage(MessageType.GameEffectCreate, gEff)); } if (!effect.AutoRepeat) { State = GameObjectState.Idle; } #endregion break; case GameObjectOrder.Gather: #region if (OrderTarget.GetEntityType == GameEntityType.Building) { Debug.Assert(carryType != null); if (carryAmount > 0) { ScrollUpMessage(string.Format("Returned {0} of {1}", carryAmount, carryType.Name), 50); } if (game.IsServer) { // First zoneFound only transfers carried resources and sets order delay, second time around unit moves back to resource if (carryAmount > 0) { Debug.Assert(((BuildingDb)OrderTarget.Entry).ResourceCenter); Logger.Log(string.Format("Unit #{0}:{1} returned +{2} of {3} (before {4}) to #{5}:{6}", this.ObjectUID, this.Entry.Id, carryAmount, carryType.Id, Owner.ResourceStockpiles[carryType], OrderTarget.ObjectUID, OrderTarget.Entry.Id), "Server update"); // Give resources to player Owner.ResourceStockpiles[carryType] += carryAmount; carryAmount = 0; OrderTimeout = GATHER_RETURN_DELAY; game.SendData(Network.MakeServerMessage(MessageType.GameObjUpdate, this)); game.SendData(Network.MakeServerMessage(MessageType.PlayerUpdate, Owner)); } else { if (lastGatheredResource.Amount == 0) { CancelOrders(true); return; } // Head towards resource Debug.Assert(lastGatheredResource != null); OrderTarget = lastGatheredResource; OrderRange = entry.GatherRange; OrderTimeout = entry.GatherSpeed; game.SendData(Network.MakeServerMessage(MessageType.GameObjUpdate, this)); } } } else if (OrderTarget.GetEntityType == GameEntityType.Resource) { Debug.Assert(carryAmount == 0 || carryType == (ResourceDb)OrderTarget.Entry); Resource res = (Resource)OrderTarget; if (res.Cooldown > 0) break; if (game.IsServer) { if (carryAmount == 0) { carryType = (ResourceDb)res.Entry; } if (res.Amount == 0) { CancelOrders(true); return; } int take = res.Take(entry.GatherAmount); Logger.Log(string.Format("Unit #{0}:{1} gathered +{2} of {3} (before {4}) from #{5}:{6}", this.ObjectUID, this.Entry.Id, take, carryType.Id, carryAmount, OrderTarget.ObjectUID, OrderTarget.Entry.Id), "Server update"); carryAmount += take; game.SendData(Network.MakeServerMessage(MessageType.GameObjUpdate, res)); ScrollUpMessage(string.Format("Gathered {0} of {1}", take, OrderTarget.Entry.Name), 30, true); } } else { CancelOrders(true); } #endregion break; case GameObjectOrder.Train: #region if (game.IsServer) { bldg = (Building)OrderTarget; // If the building is already training, wait if (bldg.Spawner != null && bldg.Spawner.Active) break; bldgEntry = (BuildingDb)bldg.Entry; bool noEntry = true; BuildingDb.TrainEntry trainEntry = default(BuildingDb.TrainEntry); if (bldgEntry.Trains != null) { foreach (var iterTrainEntry in bldgEntry.Trains) { if (iterTrainEntry.TrainFrom == this.entry) { noEntry = false; trainEntry = iterTrainEntry; break; } } } if (noEntry) { ScrollUpMessage("Unable to train this unit in this building", 100, true); CancelOrders(true); return; } // Check whether player canCast enough resources if (trainEntry.Cost != null) { foreach (var costEntry in trainEntry.Cost) { if (Owner.ResourceStockpiles[costEntry.Entry] < costEntry.Amount) { ScrollUpMessage("Not enough resources", 100, true); CancelOrders(true); return; } } } Logger.Log(string.Format("Unit #{0}:{1} start training in #{2}:{3} towards {4}", this.ObjectUID, this.Entry.Id, OrderTarget.ObjectUID, OrderTarget.Entry.Id, bldgEntry.Id), "Server update"); // Initialize spawning of the new unit bldg.StartUnitSpawner(trainEntry.TrainTo, trainEntry.Speed); // Deduct resources from the player if (trainEntry.Cost != null) foreach (var costEntry in trainEntry.Cost) Owner.ResourceStockpiles[costEntry.Entry] -= costEntry.Amount; game.SendData(Network.MakeServerMessage(MessageType.PlayerUpdate, Owner)); // Info newText gEff = new ScrollTextEffect("Training", Position, 50, new Vector2(0, -1)); gEff.Initialize(game, bldg.NearestTile); this.game.Effects.Add(gEff); game.SendData(Network.MakeServerMessage(MessageType.GameEffectCreate, gEff)); // Kill the unit Kill(true); return; } #endregion break; } }
/// <summary> /// Method that deserializes the data from the line starting at given position in given context depending on the MessageType /// </summary> /// <param id="mt">MessageType defining the extend of deserialization which is to be performed</param> /// <param id="line">String array containing the serialized data</param> /// <param id="position">Current position in the array</param> /// <param id="context">Context of the serialized data, game where this INetworkSerializable object is in</param> public override void Deserialize(MessageType mt, string[] line, ref int position, WildmenGame context) { base.Deserialize(mt, line, ref position, context); long lastGatheredResId; string carryTypeId; switch (mt) { case MessageType.GameTransfer: case MessageType.GameObjCreate: // Entry entry = Db.Instance.Units[line[position++]].Clone(); entry.Deserialize(mt, line, ref position, context); // Last gathered resource lastGatheredResId = long.Parse(line[position++]); if (lastGatheredResId == -1) { lastGatheredResource = null; } else { lastGatheredResource = (Resource)game.GetGameObjectById(lastGatheredResId); } // Carrying resource entry carryTypeId = line[position++]; if (carryTypeId == Db.NOENTRY) { carryType = null; } else { carryType = Db.Instance.Resources[carryTypeId]; } // Carrying resource amount carryAmount = int.Parse(line[position++]); break; case MessageType.GameObjUpdate: // Entry lastGatheredResId = long.Parse(line[position++]); // Last gathered resource if (lastGatheredResId == -1) { lastGatheredResource = null; } else { lastGatheredResource = (Resource)game.GetGameObjectById(lastGatheredResId); } // Carrying resource entry carryTypeId = line[position++]; if (carryTypeId == Db.NOENTRY) { carryType = null; } else { carryType = Db.Instance.Resources[carryTypeId]; } // Carrying resource amount carryAmount = int.Parse(line[position++]); break; case MessageType.GameObjEntryUpdate: entry = Db.Instance.Units[line[position++]].Clone(); entry.Deserialize(mt, line, ref position, context); break; case MessageType.GameObjKill: break; default: throw new Exception("Unit deserialization error"); } }
/// <summary> /// Method that deserializes the data from the line starting at given position in given context depending on the MessageType /// </summary> /// <param id="mt">MessageType defining the extend of deserialization which is to be performed</param> /// <param id="line">String array containing the serialized data</param> /// <param id="position">Current position in the array</param> /// <param id="context">Context of the serialized data, game where this INetworkSerializable object is in</param> public override void Deserialize(MessageType mt, string[] line, ref int position, WildmenGame context) { if ((GameEntityType)(int.Parse(line[position++])) != GetEntityType) throw new Exception(); switch (mt) { case MessageType.GameTransfer: case MessageType.GameObjCreate: case MessageType.GameObjUpdate: long oid = long.Parse(line[position++]); if (mt == MessageType.GameTransfer || mt == MessageType.GameObjCreate) { ObjectUID = oid; } else { if (ObjectUID != oid) { throw new Exception(); } } if (mt == MessageType.GameTransfer || mt == MessageType.GameObjCreate) { int pId = int.Parse(line[position++]); if (pId == -1) Owner = context.NaturePlayer; else Owner = context.Players.First(p => p.Id == pId); Owner.Assign(this); } MapPosition = new Vector2(float.Parse(line[position++], CultureInfo.InvariantCulture), float.Parse(line[position++], CultureInfo.InvariantCulture)); if (NearestTile != null) { NearestTile.Unassign(this); } NearestTile = context.Map.Tiles[int.Parse(line[position++]), int.Parse(line[position++])]; NearestTile.Assign(this); entry = Db.Instance.Resources[line[position++]]; Amount = int.Parse(line[position++]); Cooldown = int.Parse(line[position++]); break; case MessageType.GameObjKill: break; case MessageType.GameObjEntryUpdate: entry = Db.Instance.Resources[line[position++]]; break; default: throw new Exception("Resource deserialization error"); } }
/// <summary> /// Sets the DbEntry for this resource /// </summary> /// <param id="newEntry">New DbEntry for this resource</param> public void SetDbEntry(IScriptDbEntry newEntry) { ResourceDb newEntryCast = newEntry as ResourceDb; if (newEntryCast == null) return; entry = newEntryCast; game.SendData(Network.MakeServerMessage(MessageType.GameObjEntryUpdate, this)); }