예제 #1
0
    public void SetConversation(byte arg1, int npcId, string script, int delay, byte arg5, Align align)
    {
        if (npcId == 0)
        {
            IFieldActor <Player> player = Field.State.Players.Values.FirstOrDefault();
            if (player is null)
            {
                return;
            }

            Field.BroadcastPacket(CinematicPacket.BalloonTalk(player.ObjectId, false, script, delay * 1000, 0));
            return;
        }

        if (arg1 == 1) // Use npc object id?
        {
            Npc npc = Field.State.Npcs.Values.FirstOrDefault(x => x.SpawnPointId == npcId);
            if (npc is null)
            {
                return;
            }

            Field.BroadcastPacket(CinematicPacket.BalloonTalk(npc.ObjectId, false, script, delay * 1000, 0));
            return;
        }

        Field.BroadcastPacket(CinematicPacket.Conversation(npcId, npcId.ToString(), script, delay * 1000, align));
    }
예제 #2
0
    public static void HandleEffect(GameSession session, SkillCast skillCast, int attackIndex)
    {
        Player player = session.Player;
        IFieldActor <Player> fieldPlayer = player.FieldPlayer;

        skillCast.EffectCoords = GetEffectCoords(skillCast, fieldPlayer.Coord, player.MapId, fieldPlayer.LookDirection, attackIndex);

        session.FieldManager.AddRegionSkillEffect(skillCast);

        Task removeEffectTask = RemoveEffects(session, skillCast);

        // TODO: Vibrate objects around skill
        if (skillCast.Interval <= 0)
        {
            HandleRegionSkill(session, skillCast);
            return;
        }

        // Task to loop trough all entities in range to do damage/heal
        Task.Run(async() =>
        {
            while (!removeEffectTask.IsCompleted)
            {
                HandleRegionSkill(session, skillCast);

                // TODO: Find the correct delay for the skill
                await Task.Delay(skillCast.Interval);
            }
        });
    }
예제 #3
0
    public static PacketWriter RemoveNpc(IFieldActor <NpcMetadata> npc)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.FieldRemoveNPC);

        pWriter.WriteInt(npc.ObjectId);
        return(pWriter);
    }
예제 #4
0
    public void DestroyMonster(int[] rangeId, bool arg2)
    {
        foreach (int spawnPointId in rangeId)
        {
            MapEventNpcSpawnPoint spawnPoint = MapEntityStorage.GetMapEventNpcSpawnPoint(Field.MapId, spawnPointId);
            if (spawnPoint is null)
            {
                continue;
            }

            foreach (string npcId in spawnPoint.NpcIds)
            {
                if (!int.TryParse(npcId, out int id))
                {
                    continue;
                }

                IFieldActor <NpcMetadata> fieldNpc = Field.State.Npcs.Values.FirstOrDefault(x => x.Value.Id == id);
                if (fieldNpc is null)
                {
                    continue;
                }

                Field.RemoveNpc(fieldNpc);
            }
        }
    }
예제 #5
0
    public void MoveNpc(int spawnTriggerId, string patrolDataName)
    {
        (PatrolData, List <WayPoint>)patrolData = MapEntityStorage.GetPatrolData(Field.MapId, patrolDataName);

        MapEventNpcSpawnPoint spawnPoint = MapEntityStorage.GetMapEventNpcSpawnPoint(Field.MapId, spawnTriggerId);

        if (spawnPoint is null)
        {
            return;
        }

        foreach (string npcId in spawnPoint.NpcIds)
        {
            if (!int.TryParse(npcId, out int id))
            {
                continue;
            }

            IFieldActor <NpcMetadata> fieldNpc = Field.State.Npcs.Values.FirstOrDefault(x => x.Value.Id == id);
            if (fieldNpc is null)
            {
                continue;
            }

            // Just setting the coord as the last waypoint for now, replace with moveTo later
            // fieldNpc.MoveTo(patrolData.Item2.Last().Position);
            fieldNpc.Coord = patrolData.Item2.Last().Position.ToFloat();
        }
    }
예제 #6
0
 private DamageHandler(IFieldActor source, IFieldActor target, double damage, HitType hitType)
 {
     Source  = source;
     Target  = target;
     Damage  = damage;
     HitType = hitType;
 }
예제 #7
0
    public static PacketWriter ControlNpc(IFieldActor <NpcMetadata> mob)
    {
        PacketWriter npcBuffer = new();

        npcBuffer.WriteInt(mob.ObjectId);
        npcBuffer.WriteByte();
        npcBuffer.Write(mob.Coord.ToShort());
        npcBuffer.WriteShort(mob.LookDirection);
        npcBuffer.Write(mob.Velocity.ToShort()); // Target Position's Displacement
        npcBuffer.WriteShort(100);               // Unknown
        if (mob.Value.IsBoss())
        {
            npcBuffer.WriteInt(); // Player target id, only used for bosses. Agro?
        }

        npcBuffer.WriteByte(1);  // Flag ?
        npcBuffer.WriteShort(mob.Animation);
        npcBuffer.WriteShort(1); // counter (increments every packet)
        // There can be more to this packet, probably dependent on Flag.

        PacketWriter pWriter = PacketWriter.Of(SendOp.NpcControl);

        pWriter.WriteShort(1); // Segments
        pWriter.WriteShort((short)npcBuffer.Length);
        pWriter.WriteBytes(npcBuffer.ToArray());
        return(pWriter);
    }
예제 #8
0
    public static PacketWriter AddNpc(IFieldActor <NpcMetadata> npc)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.FieldAddNPC);

        pWriter.WriteInt(npc.ObjectId);
        pWriter.WriteInt(npc.Value.Id);
        pWriter.Write(npc.Coord);
        pWriter.Write(npc.Rotation);
        if (npc.Value.IsBoss())
        {
            pWriter.WriteString(npc.Value.NpcMetadataModel.Model);
        }
        // If NPC is not valid, the packet seems to stop here

        pWriter.DefaultStatsMob(npc);

        pWriter.WriteByte();
        short count = 0;

        pWriter.WriteShort(count);
        for (int i = 0; i < count; i++)
        {
            pWriter.WriteInt();
            pWriter.WriteInt();
            pWriter.WriteInt();
            pWriter.WriteInt();
            pWriter.WriteInt();
            pWriter.WriteInt(); // usually 90000814 (skill id)??
            pWriter.WriteShort();
            pWriter.WriteInt();
            pWriter.WriteByte();
            pWriter.WriteLong();
        }

        pWriter.WriteLong();
        pWriter.WriteByte();
        pWriter.WriteInt(npc.Value.Level);
        if (npc.Value.IsBoss())
        {
            pWriter.WriteInt();
            pWriter.WriteUnicodeString();

            int skillCount = 0;
            pWriter.WriteInt(skillCount);
            for (int i = 0; i < skillCount; i++)
            {
                pWriter.WriteInt();   // skill id
                pWriter.WriteShort(); // skill level
            }
        }

        pWriter.WriteInt();
        pWriter.WriteByte();

        return(pWriter);
    }
예제 #9
0
    public static DamageHandler CalculateDamage(SkillCast skill, IFieldActor <Player> source, IFieldActor target)
    {
        if (source.Value.GmFlags.Contains("oneshot"))
        {
            return(new(source, target, target.Stats[StatAttribute.Hp].Total, HitType.Critical));
        }

        // get luck coefficient from class. new stat recommended, can be refactored away like isCrit was
        return(CalculateDamage(skill, source, target, 1));
    }
예제 #10
0
    public static PacketWriter Save(IFieldActor <Player> fieldPlayer, HashSet <int> newSkillIds = null)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.Job);

        pWriter.WriteInt(fieldPlayer.ObjectId);
        pWriter.Write(JobMode.Save);
        pWriter.WriteJobInfo(fieldPlayer.Value, newSkillIds);

        return(pWriter);
    }
예제 #11
0
 public static void DefaultStatsMob(this PacketWriter pWriter, IFieldActor mob)
 {
     pWriter.Write(StatsMode.SendStats);
     pWriter.WriteLong(mob.Stats[StatId.Hp].Bonus);
     pWriter.WriteInt(100); // Move speed (?)
     pWriter.WriteLong(mob.Stats[StatId.Hp].Base);
     pWriter.WriteInt(100); // Move speed (?)
     pWriter.WriteLong(mob.Stats[StatId.Hp].Total);
     pWriter.WriteInt(100); // Move speed (?)
 }
예제 #12
0
    public static PacketWriter Close(IFieldActor <Player> fieldPlayer)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.Job);

        pWriter.WriteInt(fieldPlayer.ObjectId);
        pWriter.Write(JobMode.Close);
        pWriter.WriteJobInfo(fieldPlayer.Value);

        return(pWriter);
    }
예제 #13
0
    public static PacketWriter UpdateFieldStats(IFieldActor <Player> player)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.STAT);

        pWriter.WriteInt(player.ObjectId);
        pWriter.WriteByte(); // Unknown (0x00/0x01)
        pWriter.Write(StatsMode.SendStats);
        pWriter.WriteFieldStats(player.Stats);

        return(pWriter);
    }
예제 #14
0
    public static PacketWriter UpdateMobStats(IFieldActor mob)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.STAT);

        pWriter.WriteInt(mob.ObjectId);
        pWriter.WriteByte();
        pWriter.WriteByte(1);
        pWriter.Write(StatsMode.UpdateMobStats);
        pWriter.WriteStat(mob.Stats, StatId.Hp);

        return(pWriter);
    }
예제 #15
0
    private static void HandleCast(GameSession session, PacketReader packet)
    {
        long   skillSN     = packet.ReadLong();
        int    serverTick  = packet.ReadInt();
        int    skillId     = packet.ReadInt();
        short  skillLevel  = packet.ReadShort();
        byte   attackPoint = packet.ReadByte();
        CoordF position    = packet.Read <CoordF>();
        CoordF direction   = packet.Read <CoordF>();
        CoordF rotation    = packet.Read <CoordF>();

        packet.ReadFloat();
        int clientTick = packet.ReadInt();

        packet.ReadBool();
        packet.ReadLong();
        bool flag = packet.ReadBool();

        if (flag)
        {
            packet.ReadInt();
            string unkString = packet.ReadUnicodeString();
        }

        IFieldActor <Player> fieldPlayer = session.Player.FieldPlayer;
        SkillCast            skillCast   = new(skillId, skillLevel, skillSN, serverTick, fieldPlayer.ObjectId, clientTick, attackPoint)
        {
            Position      = position,
            Direction     = direction,
            Rotation      = rotation,
            LookDirection = fieldPlayer.LookDirection
        };

        /* HOW TO HANDLE ADDITIONAL EFFECTS BY MAYGI:
         *  when handling an additional effect:
         + loop through all SkillMotion items in the skill
         ++ loop through all SkillAttacks in the attacks in the SkillMotion
         +++ grab a list of ConditionSkills from each SkillAttack
         +++ check for a cube magic path id on each SkillAttack and handle it if it exists
         ++++ if a cube magic path exists, you can grab a ConditionSkill reference from any index of the list, it doesn't matter in this case
         ++++ handle magic path move processing for square based abilities
         +++ loop through all ConditionSkills
         ++++ loop through all SkillData on each ConditionSkill and check the SkillInfo on each Condition Skill ID
         +++++ check if each SkillInfo has an additional skill
         ++++++ if an additional skill exists, check the proc and requirements (proc is a chance, requirement is a buff ID), and determine whether or not to proc additional effects for said proc)
         + also handle Splash Skills which trigger region effects
         */

        // TODO: Check BeginCondition
        fieldPlayer.Cast(skillCast);
    }
예제 #16
0
    public override void Execute(GameCommandTrigger trigger)
    {
        Player player = trigger.Session.Player;
        IFieldActor <Player> fieldPlayer  = player.FieldPlayer;
        FieldManager         fieldManager = trigger.Session.FieldManager;

        bool mapIsHome = player.MapId == (int)Map.PrivateResidence;

        if (!mapIsHome)
        {
            return;
        }

        Home home = GameServer.HomeManager.GetHomeById(player.VisitingHomeId);

        if (home.AccountId != player.AccountId)
        {
            return;
        }

        IFieldObject <GuideObject> ballObject = fieldManager.State.Guide.Values.FirstOrDefault(x => x.Value.IsBall);

        if (ballObject is not null)
        {
            fieldManager.RemoveGuide(ballObject);
            fieldManager.BroadcastPacket(HomeActionPacket.RemoveBall(ballObject));
            return;
        }

        int size = trigger.Get <int>("size");

        size = Math.Min(30 + size * 30, 330);
        if (size < 0)
        {
            size = 60;
        }

        GuideObject ball = new(0, player.CharacterId)
        {
            IsBall = true
        };
        IFieldObject <GuideObject> fieldObject = fieldManager.RequestFieldObject(ball);

        fieldObject.Coord    = CoordF.From(fieldPlayer.Coord.X, fieldPlayer.Coord.Y, fieldPlayer.Coord.Z + Block.BLOCK_SIZE * 2);
        fieldObject.Rotation = CoordF.From(0, 0, size);

        fieldManager.AddGuide(fieldObject);

        fieldManager.BroadcastPacket(HomeActionPacket.AddBall(fieldObject));
    }
}
예제 #17
0
    public static PacketWriter Pickup(IFieldActor <Player> fieldPlayer, int weaponId, CoordB coords)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.ResponseCube);

        pWriter.Write(ResponseCubePacketMode.Pickup);
        pWriter.WriteZero(1);
        pWriter.WriteInt(fieldPlayer.ObjectId);
        pWriter.Write(coords);
        pWriter.WriteZero(1);
        pWriter.WriteInt(weaponId);
        pWriter.WriteInt(GuidGenerator.Int()); // Item uid

        return(pWriter);
    }
예제 #18
0
    public static PacketWriter SetStats(IFieldActor <Player> player)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.STAT);

        pWriter.WriteInt(player.ObjectId);
        pWriter.WriteByte(); // Unknown (0x00/0x01)
        pWriter.Write(StatsMode.SendStats);
        foreach ((StatId statId, Stat _) in player.Stats.Data)
        {
            pWriter.WriteStat(player.Stats, statId);
        }

        return(pWriter);
    }
예제 #19
0
    /// <summary>
    /// Update specific stats.
    /// </summary>
    public static PacketWriter UpdateStats(IFieldActor actor, params StatAttribute[] attributes)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.Stat);

        pWriter.WriteInt(actor.ObjectId);
        pWriter.WriteByte(); // Unknown when to use 0 or 1
        pWriter.WriteByte((byte)attributes.Length);
        foreach (StatAttribute attribute in attributes)
        {
            pWriter.WriteByte((byte)attribute);
            pWriter.WriteStat(attribute, actor.Stats[attribute]);
        }

        return(pWriter);
    }
예제 #20
0
    private static void HandlePickUp(GameSession session, PacketReader packet)
    {
        string id = packet.ReadString();
        IFieldActor <Player> fieldPlayer = session.Player.FieldPlayer;

        if (id.Contains('_'))
        {
            string coordHexa = long.Parse(id.Split('_')[1]).ToString("X2");
            if (coordHexa.Length == 5)
            {
                coordHexa = "0" + coordHexa;
            }

            CoordB coordB = CoordB.From(
                (sbyte)Convert.ToByte(coordHexa[4..], 16),
예제 #21
0
    /// <summary>
    /// Update all stats.
    /// </summary>
    public static PacketWriter SetStats(IFieldActor actor)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.Stat);

        pWriter.WriteInt(actor.ObjectId);
        pWriter.WriteByte(); // Unknown (0x00/0x01)
        pWriter.Write(StatsMode.SendAllStats);
        for (int i = 0; i < (int)StatsMode.SendAllStats; i++)
        {
            StatAttribute statAttribute = (StatAttribute)i;
            pWriter.WriteStat(statAttribute, actor.Stats.Data[statAttribute]);
        }

        return(pWriter);
    }
예제 #22
0
    public static PacketWriter UpdateStats(IFieldActor <Player> player, IEnumerable <StatId> statIds)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.STAT);

        pWriter.WriteInt(player.ObjectId);
        pWriter.Write(StatsMode.UpdateStats);
        pWriter.WriteByte((byte)statIds.Count());
        foreach (StatId statId in statIds)
        {
            pWriter.Write(statId);
            pWriter.WriteStat(player.Stats, statId);
        }

        return(pWriter);
    }
    public static PacketWriter RequestEnter(IFieldActor <Player> fieldPlayer)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.REQUEST_FIELD_ENTER);

        pWriter.WriteByte();
        pWriter.WriteInt(fieldPlayer.Value.MapId);
        pWriter.WriteByte();
        pWriter.WriteByte();
        pWriter.WriteInt();
        pWriter.WriteInt();
        pWriter.Write(fieldPlayer.Coord);
        pWriter.Write(fieldPlayer.Rotation);
        pWriter.WriteInt(); // Whatever is here seems to be repeated by client in FIELD_ENTER response.

        return(pWriter);
    }
예제 #24
0
    public static PacketWriter UpdatePlayer(IFieldActor <Player> player)
    {
        FieldObjectUpdate flag    = FieldObjectUpdate.Move | FieldObjectUpdate.Animate;
        PacketWriter      pWriter = PacketWriter.Of(SendOp.FIELD_OBJECT);

        pWriter.Write(FieldObjectMode.UpdateEntity);
        pWriter.WriteInt(player.ObjectId);
        pWriter.WriteByte((byte)flag);

        if (flag.HasFlag(FieldObjectUpdate.Type1))
        {
            pWriter.WriteByte();
        }

        if (flag.HasFlag(FieldObjectUpdate.Move))
        {
            pWriter.Write(player.Coord);
        }

        if (flag.HasFlag(FieldObjectUpdate.Level))
        {
            pWriter.WriteShort(player.Value.Levels.Level);
        }

        if (flag.HasFlag(FieldObjectUpdate.Type4))
        {
            pWriter.WriteShort();
            pWriter.WriteInt();
        }

        if (flag.HasFlag(FieldObjectUpdate.Type5))
        {
            pWriter.WriteUnicodeString("Unknown");
        }

        if (flag.HasFlag(FieldObjectUpdate.Type6))
        {
            pWriter.WriteInt();
        }

        if (flag.HasFlag(FieldObjectUpdate.Animate))
        {
            pWriter.WriteShort(player.Animation);
        }

        return(pWriter);
    }
예제 #25
0
    private static bool RollCrit(IFieldActor source, IFieldActor target, double luckCoefficient)
    {
        // used to weigh crit rate in the formula, like how class luck coefficients weigh luck
        const double CritConstant = 5.3;

        // used to convert a percent value to a decimal value
        const double PercentageConversion = 0.015;

        const double MaxCritRate = 0.4;

        double luck        = source.Stats[StatAttribute.Luk].Total * luckCoefficient;
        double critRate    = source.Stats[StatAttribute.CritRate].Total * CritConstant;
        double critEvasion = Math.Max(target.Stats[StatAttribute.CritEvasion].Total, 1) * 2;
        double critChance  = Math.Min(critRate / critEvasion * PercentageConversion, MaxCritRate);

        return(Random.Shared.Next(1000) < 1000 * critChance);
    }
예제 #26
0
    public static PacketWriter UpdateStats(IFieldActor player, StatId statId, params StatId[] otherIds)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.STAT);

        pWriter.WriteInt(player.ObjectId);
        pWriter.Write(StatsMode.UpdateStats);
        pWriter.WriteByte((byte)(1 + otherIds.Length));
        pWriter.Write(statId);
        pWriter.WriteStat(player.Stats, statId);
        foreach (StatId otherId in otherIds)
        {
            pWriter.Write(otherId);
            pWriter.WriteStat(player.Stats, statId);
        }

        return(pWriter);
    }
예제 #27
0
    public static PacketWriter AddBoss(IFieldActor <NpcMetadata> mob)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.FIELD_ADD_NPC);

        pWriter.WriteInt(mob.ObjectId);
        pWriter.WriteInt(mob.Value.Id);
        pWriter.Write(mob.Coord);
        pWriter.Write(mob.Rotation);
        pWriter.WriteString(mob.Value.Model); // StrA - kfm model string
        // If NPC is not valid, the packet seems to stop here

        pWriter.DefaultStatsMob(mob);

        pWriter.WriteByte();
        pWriter.WriteLong();
        pWriter.WriteLong();
        pWriter.WriteInt();
        pWriter.WriteByte();

        short count = 0;

        pWriter.WriteShort(count);
        for (int i = 0; i < count; i++)
        {
            pWriter.WriteInt();
            pWriter.WriteInt();
            pWriter.WriteInt();
            pWriter.WriteInt();
            pWriter.WriteInt();
            pWriter.WriteInt(); // usually 90000814 (skill id)??
            pWriter.WriteShort();
            pWriter.WriteInt();
            pWriter.WriteByte();
            pWriter.WriteLong();
        }

        pWriter.WriteLong();
        pWriter.WriteByte();
        pWriter.WriteInt(1);
        pWriter.WriteInt();
        pWriter.WriteByte();

        return(pWriter);
    }
예제 #28
0
    public static void UpdatePlayer(GameSession session, SyncState[] syncStates)
    {
        Player player = session.Player;
        IFieldActor <Player> fieldPlayer = player.FieldPlayer;

        CoordF coord = syncStates[0].Coord.ToFloat();

        CoordF coordUnderneath = coord;

        coordUnderneath.Z -= 50;

        CoordF blockUnderneath = Block.ClosestBlock(coordUnderneath);

        if (IsCoordSafe(player, syncStates[0].Coord, blockUnderneath))
        {
            CoordF safeBlock = Block.ClosestBlock(coord);
            // TODO: Knowing the state of the player using the animation is probably not the correct way to do this
            // we will need to know the state of the player for other things like counting time spent on ropes/running/walking/swimming
            if (syncStates[0].Animation2 is 7 or 132) // swimming
            {
                safeBlock.Z += Block.BLOCK_SIZE;      // Without this player will spawn under the water
            }

            safeBlock.Z += 10; // Without this player will spawn inside the block

            player.SafeBlock = safeBlock;
        }

        fieldPlayer.Coord    = coord;
        fieldPlayer.Rotation = new()
        {
            Z = syncStates[0].Rotation / 10
        };

        if (IsOutOfBounds(fieldPlayer.Coord, session.FieldManager.BoundingBox))
        {
            player.Move(player.SafeBlock, fieldPlayer.Rotation);
            player.FallDamage();
        }

        // not sure if this needs to be synced here
        fieldPlayer.Animation = syncStates[0].BoreAnimation;
    }
예제 #29
0
    public static PacketWriter AddMob(IFieldActor <NpcMetadata> mob)
    {
        PacketWriter pWriter = PacketWriter.Of(SendOp.FIELD_ADD_NPC);

        pWriter.WriteInt(mob.ObjectId);
        pWriter.WriteInt(mob.Value.Id);
        pWriter.Write(mob.Coord);
        pWriter.Write(mob.Rotation);
        // If NPC is not valid, the packet seems to stop here

        pWriter.DefaultStatsMob(mob);

        pWriter.WriteLong();
        pWriter.WriteInt();
        pWriter.WriteInt(0x0E); // NPC level
        pWriter.WriteInt();
        pWriter.WriteByte();

        return(pWriter);
    }
예제 #30
0
    public static PacketWriter ControlNpc(IFieldActor <NpcMetadata> npc)
    {
        PacketWriter npcBuffer = new();

        npcBuffer.WriteInt(npc.ObjectId);
        npcBuffer.WriteByte();
        npcBuffer.Write(npc.Coord.ToShort());
        npcBuffer.WriteShort(npc.LookDirection);
        npcBuffer.Write(npc.Velocity.ToShort()); // Target Position's Displacement
        npcBuffer.WriteShort(100);               // Unknown
        npcBuffer.WriteByte(1);                  // Flag ?
        npcBuffer.WriteShort(npc.Animation);
        npcBuffer.WriteShort(1);                 // counter (increments every packet)
        // There can be more to this packet, probably dependent on Flag.

        PacketWriter pWriter = PacketWriter.Of(SendOp.NPC_CONTROL);

        pWriter.WriteShort(1); // Segments
        pWriter.WriteShort((short)npcBuffer.Length);
        pWriter.WriteBytes(npcBuffer.ToArray());
        return(pWriter);
    }