/// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            var requestor        = this.GetRequestor(context.CreatureFinder);
            var inThingContainer = this.AtLocation.DecodeContainer(context.Map, context.ContainerManager, out byte index, requestor);

            // Adjust index if this a map location.
            var existingThing = (this.AtLocation.Type == LocationType.Map && (inThingContainer is ITile fromTile)) ? fromTile.FindItemWithTypeId(this.TypeId) : inThingContainer?.FindThingAtIndex(index);

            if (inThingContainer == null || !(existingThing is IItem existingItem))
            {
                return;
            }

            // At this point, we have an item to remove, let's proceed.
            (bool removeSuccessful, IThing remainder) = inThingContainer.RemoveContent(context.ItemFactory, ref existingThing, index, amount: existingItem.Amount);

            if (!removeSuccessful)
            {
                // Failing to remove the item from the original cylinder stops the entire operation.
                return;
            }

            if (inThingContainer is ITile atTile)
            {
                this.SendNotification(
                    context,
                    new TileUpdatedNotification(
                        () => context.Map.PlayersThatCanSee(atTile.Location),
                        atTile.Location,
                        context.MapDescriptor.DescribeTile));
            }

            // Evaluate if the new item triggers a separation.
            // context.EventRulesApi.EvaluateRules(this, EventRuleType.Separation, new SeparationEventRuleArguments(atCylinder.Location, item, this.GetRequestor(context.CreatureFinder)));
        }
Beispiel #2
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            if (this.Creature is IPlayer player)
            {
                this.SendNotification(context, new TextMessageNotification(() => player.YieldSingleItem(), MessageType.EventAdvance, "You are dead."));

                this.SendNotification(context, new GenericNotification(() => player.YieldSingleItem(), new PlayerCancelWalkPacket(player.Direction), new PlayerDeathPacket()));
            }

            // Remove the creature...
            if (context.Map.GetTileAt(this.Creature.Location) is ITile creatureTile)
            {
                // Add the corpse.
                var corpseCreationArguments = new ItemCreationArguments()
                {
                    TypeId = this.Creature.CorpseTypeId,
                };

                if (context.ItemFactory.Create(corpseCreationArguments) is IThing corpseCreated && this.AddContentToContainerOrFallback(context, creatureTile, ref corpseCreated))
                {
                    context.GameApi.CreateItemAtLocation(creatureTile.Location, context.PredefinedItemSet.FindPoolForBloodType(this.Creature.BloodType));
                }

                this.RemoveCreature(context, this.Creature);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            var requestor        = this.GetRequestor(context.CreatureFinder);
            var inThingContainer = this.Item.ParentContainer;

            if (inThingContainer == null || !(this.Item is IThing existingThing))
            {
                return;
            }

            // At this point, we have an item to remove, let's proceed.
            (bool removeSuccessful, IThing remainder) = inThingContainer.RemoveContent(context.ItemFactory, ref existingThing, amount: this.Item.Amount);

            if (!removeSuccessful)
            {
                // Failing to remove the item from the original cylinder stops the entire operation.
                return;
            }

            if (inThingContainer is ITile atTile)
            {
                this.SendNotification(
                    context,
                    new TileUpdatedNotification(
                        () => context.Map.PlayersThatCanSee(atTile.Location),
                        atTile.Location,
                        context.MapDescriptor.DescribeTile));
            }

            // Evaluate if the new item triggers a separation.
            // context.EventRulesApi.EvaluateRules(this, EventRuleType.Separation, new SeparationEventRuleArguments(atCylinder.Location, item, this.GetRequestor(context.CreatureFinder)));
        }
Beispiel #4
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            bool successfulRemoval = this.RemoveCreature(context, this.Creature);

            if (!successfulRemoval)
            {
                // handles check for isPlayer.
                // this.NotifyOfFailure();
                return;
            }
        }
Beispiel #5
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            bool successfulPlacement = this.PlaceCreature(context, this.AtTile, this.Creature);

            if (!successfulPlacement)
            {
                context.Logger.Warning($"Failed to place creature {this.Creature.Name} at {this.AtTile.Location}");

                return;
            }
        }
Beispiel #6
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            if (this.Creature is IPlayer player)
            {
                this.SendNotification(context, new TextMessageNotification(() => player.YieldSingleItem(), MessageType.EventAdvance, "You are dead."));

                this.SendNotification(context, new GenericNotification(() => player.YieldSingleItem(), new PlayerCancelWalkPacket(player.Direction), new PlayerDeathPacket()));
            }

            // Give out the experience if this is a monster
            if (this.Creature is IMonster monster && monster.ExperienceToYield > 0 && this.Creature is ICombatant monsterCombatant)
            {
                ulong totalDamageDealt = (ulong)monsterCombatant.DamageTakenInSession.Sum(t => t.Damage);

                foreach (var(combatantId, damage) in monsterCombatant.DamageTakenInSession)
                {
                    if (damage == 0)
                    {
                        continue;
                    }

                    var expPercentage = Convert.ToDecimal(damage) / totalDamageDealt;
                    var expToGive     = (long)Math.Round(monster.ExperienceToYield * expPercentage, 0, MidpointRounding.ToEven);

                    if (expToGive == 0)
                    {
                        continue;
                    }

                    if (context.CreatureFinder.FindCreatureById(combatantId) is ICombatant combatantGainingExp)
                    {
                        combatantGainingExp.AddExperience(expToGive);
                    }
                }
            }

            // Remove the creature...
            if (context.Map.GetTileAt(this.Creature.Location) is ITile creatureTile)
            {
                // Add the corpse.
                var corpseCreationArguments = new ItemCreationArguments()
                {
                    TypeId = this.Creature.CorpseTypeId,
                };

                if (context.ItemFactory.Create(corpseCreationArguments) is IThing corpseCreated && this.AddContentToContainerOrFallback(context, creatureTile, ref corpseCreated))
                {
                    context.GameApi.CreateItemAtLocation(creatureTile.Location, context.PredefinedItemSet.FindPoolForBloodType(this.Creature.BloodType));
                }

                this.RemoveCreature(context, this.Creature);
            }
        }
Beispiel #7
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            if (!this.Player.IsDead && this.Player.HasCondition(ConditionType.InFight))
            {
                this.SendNotification(
                    context,
                    new TextMessageNotification(
                        () => this.Player.YieldSingleItem(),
                        MessageType.StatusSmall,
                        "You may not logout during or immediately after a fight."));

                return;
            }

            if (!context.Map.GetTileAt(this.Player.Location, out ITile tile))
            {
                return;
            }

            // TODO: more validations missing
            using var uow = context.ApplicationContext.CreateNewUnitOfWork();

            if (!(uow.Characters.FindByName(this.Player.Name) is CharacterEntity characterEntity))
            {
                throw new InvalidOperationException($"Unable to find entity for player {this.Player.Name}.");
            }

            // At this point, we're allowed to log this player out, so go for it.
            var playerLocation = this.Player.Location;
            var saveAtLocation = this.Player.IsDead ? characterEntity.StartingLocation : playerLocation;
            var removedFromMap = context.GameApi.RemoveCreatureFromGame(this.Player);

            if (removedFromMap || this.Player.IsDead)
            {
                if (!this.Player.IsDead)
                {
                    this.SendNotification(context, new GenericNotification(() => context.Map.FindPlayersThatCanSee(playerLocation), new MagicEffectPacket(playerLocation, AnimatedEffect.Puff)));
                }

                if (this.Player.Client.Connection != null && !this.Player.Client.Connection.IsOrphaned)
                {
                    this.Player.Client.Connection.Close();
                }

                context.CreatureManager.UnregisterCreature(this.Player);

                characterEntity.LastLocation = saveAtLocation;

                uow.Complete();
            }
        }
Beispiel #8
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            const byte FallbackIndex = 0xFF;

            var inThingContainer = this.FromLocation.DecodeContainer(context.Map, context.ContainerManager, out byte index, this.FromCreature);

            // Adjust index if this a map location.
            var existingThing = (this.FromLocation.Type == LocationType.Map && (inThingContainer is ITile fromTile)) ? fromTile.FindItemWithTypeId(this.FromTypeId) : inThingContainer?.FindThingAtIndex(index);

            if (existingThing == null || !(existingThing is IItem existingItem))
            {
                // Silent fail.
                return;
            }

            IThing thingCreated = context.ItemFactory.Create(ItemCreationArguments.WithTypeId(this.ToTypeId));

            if (thingCreated == null)
            {
                return;
            }

            // At this point, we have an item to change, and we were able to generate the new one, let's proceed.
            (bool replaceSuccessful, IThing replaceRemainder) = inThingContainer.ReplaceContent(context.ItemFactory, existingThing, thingCreated, index, existingItem.Amount);

            if (!replaceSuccessful || replaceRemainder != null)
            {
                this.AddContentToContainerOrFallback(context, inThingContainer, ref replaceRemainder, FallbackIndex, includeTileAsFallback: true, this.GetRequestor(context.CreatureFinder));
            }

            if (replaceSuccessful)
            {
                if (inThingContainer is ITile atTile)
                {
                    this.SendNotification(
                        context,
                        new TileUpdatedNotification(
                            () => context.Map.PlayersThatCanSee(atTile.Location),
                            atTile.Location,
                            context.MapDescriptor.DescribeTile));

                    // Evaluate if the new item triggers a collision.
                    // context.EventRulesApi.EvaluateRules(this, EventRuleType.Collision, new CollisionEventRuleArguments(fromCylinder.Location, existingThing, this.GetRequestor(context.CreatureFinder)));
                }
            }
        }
Beispiel #9
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            var inThingContainer = this.Item.ParentContainer;

            if (!(this.Item is IThing existingThing) || !this.Item.HasExpiration)
            {
                // Silent fail.
                return;
            }

            var creationArguments = new ItemCreationArguments()
            {
                TypeId = this.Item.ExpirationTarget
            };

            if (this.Item.IsLiquidPool)
            {
                creationArguments.Attributes = new[]
Beispiel #10
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            context.Scheduler.CancelAllFor(this.Creature.Id, this.TypeToCancel);

            if (this.Creature is IPlayer player)
            {
                if (this.TypeToCancel == typeof(IOperation) && player is ICombatant playerAsCombatant)
                {
                    playerAsCombatant.SetAttackTarget(null);
                }

                this.SendNotification(
                    context,
                    new GenericNotification(
                        () => player.YieldSingleItem(),
                        new PlayerCancelAttackPacket(),
                        new PlayerCancelWalkPacket(this.Creature.Direction.GetClientSafeDirection())));
            }
        }
Beispiel #11
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            if (!this.Player.IsDead && this.Player.HasCondition(ConditionType.InFight))
            {
                this.SendNotification(
                    context,
                    new TextMessageNotification(
                        () => this.Player.YieldSingleItem(),
                        MessageType.StatusSmall,
                        "You may not logout during or immediately after a fight."));

                return;
            }

            if (!context.Map.GetTileAt(this.Player.Location, out ITile tile))
            {
                return;
            }

            // TODO: more validations missing

            // At this point, we're allowed to log this player out, so go for it.
            var playerLocation = this.Player.Location;
            var removedFromMap = this.RemoveCreature(context, this.Player);

            if (removedFromMap || this.Player.IsDead)
            {
                if (this.Player.Client.Connection != null)
                {
                    this.Player.Client.Connection.Close();
                }

                if (!this.Player.IsDead)
                {
                    this.SendNotification(context, new GenericNotification(() => context.Map.PlayersThatCanSee(playerLocation), new MagicEffectPacket(playerLocation, AnimatedEffect.Puff)));
                }

                context.CreatureManager.UnregisterCreature(this.Player);
            }
        }
Beispiel #12
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            var rng = new Random();

            using var uow = context.ApplicationContext.CreateNewUnitOfWork();

            for (int i = 0; i < this.Spawn.Count; i++)
            {
                var r = this.Spawn.Radius / 4;

                var monsterType = uow.MonsterTypes.GetById(this.Spawn.MonsterRaceId.ToString());

                if (!(monsterType is MonsterTypeEntity monsterTypeEntity))
                {
                    context.Logger.Warning($"Unable to place monster. Could not find a monster with the id {this.Spawn.MonsterRaceId} in the repository. ({nameof(SpawnMonstersOperation)})");

                    return;
                }

                var newMonster = context.CreatureFactory.Create(
                    new CreatureCreationArguments()
                {
                    Type     = CreatureType.Monster,
                    Metadata = monsterTypeEntity,
                }) as IMonster;

                var randomLoc = this.Spawn.Location + new Location {
                    X = (int)Math.Round(r * Math.Cos(rng.Next(360))), Y = (int)Math.Round(r * Math.Sin(rng.Next(360))), Z = 0
                };

                // Need to actually pathfind to avoid placing a monster in unreachable places.
                var(_, foundLocation, _) = context.PathFinder.FindBetween(this.Spawn.Location, randomLoc, newMonster, (i + 1) * 10);

                // TODO: some property of newMonster here to figure out what actually blocks path finding.
                if (context.Map.GetTileAt(foundLocation, out ITile targetTile) && !targetTile.IsPathBlocking())
                {
                    context.Scheduler.ScheduleEvent(new PlaceCreatureOperation(requestorId: 0, targetTile, newMonster));
                }
            }
        }
Beispiel #13
0
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            // This will eventually come from the character, or fall back.
            var targetLoginLocation = MapConstants.ThaisTempleMark;

            var creationArguments = new PlayerCreationArguments()
            {
                Client   = this.Client,
                Type     = CreatureType.Player,
                Metadata = this.PlayerMetadata,
            };

            if (!(context.CreatureFactory.CreateCreature(creationArguments) is IPlayer player))
            {
                context.Logger.Warning($"Unable to create player instance for {this.PlayerMetadata.Name}, aborting log in.");

                return;
            }

            if (!context.Map.GetTileAt(targetLoginLocation, out ITile targetTile) || !this.PlaceCreature(context, targetTile, player))
            {
                // Unable to place the player in the map.
                context.Scheduler.ScheduleEvent(
                    new GenericNotification(
                        () => player.YieldSingleItem(),
                        new GameServerDisconnectPacket("Your character could not be placed on the map.\nPlease try again, or contact an administrator if the issue persists.")));

                return;
            }

            var(descriptionMetadata, descriptionBytes) = context.MapDescriptor.DescribeAt(player, player.Location);

            // TODO: In addition, we need to send the player's inventory, the first time login message + outfit window here if applicable.
            // And any VIP records here.
            var notification = new GenericNotification(
                () => player.YieldSingleItem(),
                new PlayerLoginPacket(player.Id, player),
                new MapDescriptionPacket(player.Location, descriptionBytes),
                new MagicEffectPacket(player.Location, AnimatedEffect.BubbleBlue),
                new PlayerStatsPacket(player),
                new PlayerSkillsPacket(player),
                new WorldLightPacket(this.CurrentWorldLightLevel, this.CurrentWorldLightColor),
                new CreatureLightPacket(player),
                new TextMessagePacket(MessageType.StatusDefault, "This is a test message"),
                new PlayerConditionsPacket(player));

            if (descriptionMetadata.TryGetValue(IMapDescriptor.CreatureIdsToLearnMetadataKeyName, out object creatureIdsToLearnBoxed) &&
                descriptionMetadata.TryGetValue(IMapDescriptor.CreatureIdsToForgetMetadataKeyName, out object creatureIdsToForgetBoxed) &&
                creatureIdsToLearnBoxed is IEnumerable <uint> creatureIdsToLearn && creatureIdsToForgetBoxed is IEnumerable <uint> creatureIdsToForget)
            {
                notification.Sent += (client) =>
                {
                    foreach (var creatureId in creatureIdsToLearn)
                    {
                        client.AddKnownCreature(creatureId);
                    }

                    foreach (var creatureId in creatureIdsToForget)
                    {
                        client.RemoveKnownCreature(creatureId);
                    }
                };
            }

            notification.Send(new NotificationContext(context.Logger, context.MapDescriptor, context.CreatureFinder));
        }
Beispiel #14
0
        ///// <summary>
        ///// Gets the type of exhaustion that this operation produces.
        ///// </summary>
        // public override ExhaustionType ExhaustionType => ExhaustionType.None;

        /// <summary>
        /// Attempts to place a creature on the map.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        /// <param name="targetTile">The tile to place the creature at.</param>
        /// <param name="creature">The creature to place.</param>
        /// <returns>True if the creature is successfully added to the map, false otherwise.</returns>
        protected bool PlaceCreature(IElevatedOperationContext context, ITile targetTile, ICreature creature)
        {
            targetTile.ThrowIfNull(nameof(targetTile));
            creature.ThrowIfNull(nameof(creature));

            var(addSuccessful, _) = targetTile.AddContent(context.ItemFactory, creature);

            if (addSuccessful)
            {
                context.CreatureManager.RegisterCreature(creature);

                if (creature is ICombatant combatant)
                {
                    combatant.StatChanged += context.GameApi.CreatureStatChanged;

                    combatant.Death += context.CombatApi.CombatantDeath;
                    combatant.AttackTargetChanged += context.CombatApi.CombatantAttackTargetChanged;
                    combatant.FollowTargetChanged += context.CombatApi.CreatureFollowTargetChanged;
                    combatant.LocationChanged     += context.GameApi.AfterThingLocationChanged;

                    // As a creature that can sense.
                    combatant.CreatureSeen += context.CombatApi.CreatureHasSeenCreature;
                    combatant.CreatureLost += context.CombatApi.CreatureHasLostCreature;

                    // As a skilled creature
                    combatant.SkillChanged += context.GameApi.SkilledCreatureSkillChanged;

                    // Find now-spectators of this creature to start tracking that guy.
                    foreach (var spectator in context.Map.CreaturesThatCanSee(creature.Location))
                    {
                        if (spectator is ICombatant combatantSpectator)
                        {
                            combatant.StartSensingCreature(combatantSpectator);
                            combatantSpectator.StartSensingCreature(combatant);
                        }
                    }
                }

                /*
                 * if (creature is IPlayer player)
                 * {
                 *  player.Inventory.SlotChanged += context.GameApi.OnPlayerInventoryChanged;
                 * }
                 */

                context.Logger.Debug($"Placed {creature.Name} at {targetTile.Location}.");

                var placedAtStackPos = targetTile.GetStackOrderOfThing(creature);

                this.SendNotification(
                    context,
                    new CreatureMovedNotification(
                        () =>
                {
                    if (creature is IPlayer player)
                    {
                        return(context.Map.PlayersThatCanSee(creature.Location).Except(player.YieldSingleItem()));
                    }

                    return(context.Map.PlayersThatCanSee(creature.Location));
                },
                        creature.Id,
Beispiel #15
0
 /// <summary>
 /// Executes the operation's logic.
 /// </summary>
 /// <param name="context">The execution context for this operation.</param>
 protected abstract void Execute(IElevatedOperationContext context);
        /// <summary>
        /// Executes the operation's logic.
        /// </summary>
        /// <param name="context">A reference to the operation context.</param>
        protected override void Execute(IElevatedOperationContext context)
        {
            const byte FallbackIndex = 0xFF;

            var inThingContainer = this.FromLocation.DecodeContainer(context.Map, context.ContainerManager, out byte index, this.FromCreature);

            // Adjust index if this a map location.
            var existingThing = (this.FromLocation.Type == LocationType.Map && (inThingContainer is ITile fromTile)) ? fromTile.FindItemWithTypeId(this.FromTypeId) : inThingContainer?.FindThingAtIndex(index);

            if (existingThing == null || !(existingThing is IItem existingItem))
            {
                // Silent fail.
                return;
            }

            var creationArguments = new ItemCreationArguments()
            {
                TypeId = this.ToTypeId
            };

            IThing thingCreated = context.ItemFactory.Create(creationArguments);

            if (thingCreated == null)
            {
                return;
            }

            // At this point, we have an item to change, and we were able to generate the new one, let's proceed.
            (bool replaceSuccessful, IThing replaceRemainder) = inThingContainer.ReplaceContent(context.ItemFactory, existingThing, thingCreated, index, existingItem.Amount);

            if (!replaceSuccessful || replaceRemainder != null)
            {
                this.AddContentToContainerOrFallback(context, inThingContainer, ref replaceRemainder, FallbackIndex, includeTileAsFallback: true, this.GetRequestor(context.CreatureFinder));
            }

            if (replaceSuccessful)
            {
                if (inThingContainer is ITile atTile)
                {
                    this.SendNotification(
                        context,
                        new TileUpdatedNotification(
                            () => context.Map.PlayersThatCanSee(atTile.Location),
                            atTile.Location,
                            context.MapDescriptor.DescribeTile));

                    // Evaluate if the new item triggers a collision.
                    // context.EventRulesApi.EvaluateRules(this, EventRuleType.Collision, new CollisionEventRuleArguments(fromCylinder.Location, existingThing, this.GetRequestor(context.CreatureFinder)));
                }

                if (thingCreated is IItem itemCreated)
                {
                    // Start decay for items that need it.
                    if (itemCreated.HasExpiration)
                    {
                        // TODO: the item location will change and this will break.
                        var expirationOp = itemCreated.ExpirationTarget == 0 ?
                                           new DeleteItemOperation(requestorId: 0, thingCreated.TypeId, thingCreated.Location)
                            :
                                           new ExpireItemOperation(requestorId: 0, itemCreated) as IOperation;

                        context.Scheduler.ScheduleEvent(expirationOp, itemCreated.ExpirationTimeLeft);
                    }
                }
            }
        }