/// <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> /// Creates a new instance of the GameEffect class using the serialized data /// </summary> /// <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> /// <returns>GameEffect created using the provided serialized data</returns> public static GameEffect Create(string[] line, ref int position, WildmenGame context) { GameEffect e = null; EffectTypes et = (EffectTypes)int.Parse(line[position++]); switch (et) { case EffectTypes.Scripted: e = new GameEffect(); break; case EffectTypes.UnitDeath: e = new UnitDeathEffect(); break; case EffectTypes.ScrollText: e = new ScrollTextEffect(); break; case EffectTypes.Animation: e = new AnimationEffect(); break; case EffectTypes.Spawner: e = new SpawnerEffect(); break; default: throw new Exception("Unknown effect"); } e.game = context; e.Deserialize(MessageType.GameEffectCreate, line, ref position, context); return e; }