public void ReadFromPacket(IPacketReader reader) { MobID = reader.ReadInt(); HitAction = reader.ReadByte(); var info = reader.ReadByte(); ForeAction = (byte)(info & 0x7F); IsMobFacingLeft = (info >> 7 & 1) > 0; FrameIdx = reader.ReadByte(); _ = reader.ReadByte(); // v258 & 0x7F | v25 _ = reader.ReadShort(); // dAx[5] _ = reader.ReadShort(); // dAx[4] _ = reader.ReadShort(); // dAx[3] _ = reader.ReadShort(); // dAx[2] Delay = reader.ReadShort(); for (var i = 0; i < Damage.Length; i++) { Damage[i] = reader.ReadInt(); } _ = reader.ReadInt(); // mob crc }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { _ = packet.ReadInt(); var type = (GroupMessageType)packet.ReadByte(); var recipientCount = packet.ReadByte(); var recipients = new int[recipientCount]; for (var i = 0; i < recipientCount; i++) { recipients[i] = packet.ReadInt(); } var text = packet.ReadString(); switch (type) { case GroupMessageType.Party: { if (user.Party == null) { return; } var partyChat = new UnstructuredOutgoingPacket(PacketSendOperations.GroupMessage); partyChat.WriteByte((byte)GroupMessageType.Party); partyChat.WriteString(user.Character.Name); partyChat.WriteString(text); var dispatchRequest = new DispatchToCharactersRequest { Data = ByteString.CopyFrom(partyChat.Buffer) }; dispatchRequest.Characters.Add(user.Party.Members .Select(m => m.ID) .Where(m => m != user.ID) ); await stageUser.Stage.DispatchService.DispatchToCharacters(dispatchRequest); break; } default: stageUser.Stage.Logger.LogWarning($"Unhandled group message type: {type}"); break; } }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { _ = packet.ReadInt(); var targetID = packet.ReadInt(); var target = user.Watching .SelectMany(w => w.GetObjects <IFieldObjUser>()) .Where(o => o.ID == targetID) .FirstOrDefault(); if (target == null) { return; } var response = new UnstructuredOutgoingPacket(PacketSendOperations.CharacterInfo); response.WriteInt(target.ID); response.WriteByte(target.Character.Level); response.WriteShort(target.Character.Job); response.WriteShort(target.Character.POP); response.WriteByte(0); response.WriteString(target?.Guild?.Name ?? ""); response.WriteString(""); // sAlliance response.WriteByte(0); // Medal? response.WriteBool(false); // Pets response.WriteByte(0); // TamingMobInfo response.WriteByte(0); // Wishlist response.WriteInt(0); // MedalAchievementInfo response.WriteShort(0); var chairs = target.Character.Inventories[ItemInventoryType.Install].Items .Select(kv => kv.Value) .Select(i => i.TemplateID) .Where(i => i / 10000 == 301) .ToList(); response.WriteInt(chairs.Count); chairs.ForEach(i => response.WriteInt(i)); await user.Dispatch(response); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { var emotion = packet.ReadInt(); var duration = packet.ReadInt(); var byItemOption = packet.ReadBool(); // TODO item option check var response = new UnstructuredOutgoingPacket(PacketSendOperations.UserEmotion); response.WriteInt(user.ID); response.WriteInt(emotion); response.WriteInt(duration); response.WriteBool(byItemOption); await user.FieldSplit.Dispatch(user, response); }
protected override Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { for (var i = 0; i < 8; i++) { user.Character.QuickSlotKeys[i] = new CharacterQuickSlotKey(packet.ReadInt()); } return(Task.CompletedTask); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { _ = packet.ReadInt(); var type = (ItemInventoryType)packet.ReadByte(); var response = new UnstructuredOutgoingPacket(PacketSendOperations.GatherItemResult); response.WriteBool(false); response.WriteByte((byte)type); await user.ModifyInventory(i => i[type].Gather(), true); await user.Dispatch(response); }
protected override async Task Handle( GameStageUser stageUser, IFieldObjUser user, IPacketReader packet ) { _ = packet.ReadLong(); _ = packet.ReadByte(); _ = packet.ReadLong(); _ = packet.ReadInt(); _ = packet.ReadInt(); _ = packet.ReadInt(); var path = packet.Read(new MovePath()); var movement = new UnstructuredOutgoingPacket(PacketSendOperations.UserMove); movement.WriteInt(user.ID); movement.Write(path); await user.Move(path); await user.FieldSplit.Dispatch(user, movement); }
protected override Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { var v3 = packet.ReadInt(); if (v3 > 0) { return(Task.CompletedTask); } var count = packet.ReadInt(); for (var i = 0; i < count; i++) { var key = packet.ReadInt(); user.Character.FunctionKeys[key] = new CharacterFunctionKey { Type = packet.ReadByte(), Action = packet.ReadInt() }; } return(Task.CompletedTask); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { _ = packet.ReadInt(); var type = (ItemInventoryType)packet.ReadByte(); var from = packet.ReadShort(); var to = packet.ReadShort(); var number = packet.ReadShort(); if (to == 0) { await user.ModifyInventory(exclRequest : true); // TODO: drops return; } // TODO: checks await user.ModifyInventory(i => i[type].Move(from, to), true); }
public override async Task Handle(LoginStageUser user, IPacketReader packet) { var spw = packet.ReadString(); var characterID = packet.ReadInt(); var result = LoginResultCode.Success; var response = new UnstructuredOutgoingPacket(PacketSendOperations.DeleteCharacterResult); var character = await user.Stage.CharacterRepository.Retrieve(characterID); if (!BCrypt.Net.BCrypt.EnhancedVerify(spw, user.Account.SPW)) { result = LoginResultCode.IncorrectSPW; } if (character == null || character.AccountWorldID != user.AccountWorld.ID) { result = LoginResultCode.DBFail; } var guild = (await user.Stage.GuildService.LoadByCharacter(new GuildLoadByCharacterRequest { Character = characterID })).Guild; var party = (await user.Stage.PartyService.LoadByCharacter(new PartyLoadByCharacterRequest { Character = characterID })).Party; if (result == LoginResultCode.Success) { // TODO: guild withdraw if not master if (party != null) { _ = user.Stage.PartyService.Withdraw(new PartyWithdrawRequest { Character = characterID }); } await user.Stage.CharacterRepository.Delete(characterID); } response.WriteInt(characterID); response.WriteByte((byte)result); await user.Dispatch(response); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { var objID = packet.ReadInt(); var obj = user.Field.GetPool(Type).GetObject(objID); if (obj is not T controlled) { return; } if (controlled == null) { return; } if (controlled.Controller != user) { return; } await Handle(stageUser, user, controlled, packet); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { _ = packet.ReadInt(); var message = packet.ReadString(); var onlyBalloon = packet.ReadBool(); if (message.StartsWith("!") || message.StartsWith("@")) // TODO: config? { await stageUser.Stage.CommandProcessor.Process(user, message.Substring(1)); return; } var chatPacket1 = new UnstructuredOutgoingPacket(PacketSendOperations.UserChat); chatPacket1.WriteInt(user.ID); chatPacket1.WriteBool(user.Account.GradeCode > 0 || user.Account.SubGradeCode > 0); // TODO: proper gm chat checks chatPacket1.WriteString(message); chatPacket1.WriteBool(onlyBalloon); await user.FieldSplit.Dispatch(chatPacket1); if (onlyBalloon) { return; } var chatPacket2 = new UnstructuredOutgoingPacket(PacketSendOperations.UserChatNLCPQ); chatPacket2.WriteInt(user.ID); chatPacket2.WriteBool(user.Account.GradeCode > 0 || user.Account.SubGradeCode > 0); // TODO: proper gm chat checks chatPacket2.WriteString(message); chatPacket2.WriteBool(onlyBalloon); chatPacket2.WriteString(user.Character.Name); await Task.WhenAll(user.Field .GetUsers() .Except(user.FieldSplit.GetWatchers()) .Select(u => u.Dispatch(chatPacket2))); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { var npc = user.Field.GetPool(FieldObjType.NPC).GetObject <IFieldObjNPC>(packet.ReadInt()); if (npc == null) { return; } if (!user.Watching.Any(w => w == npc.FieldSplit)) { return; } var scriptName = npc.Info.Script; if (string.IsNullOrWhiteSpace(scriptName)) { return; } try { var script = await stageUser.Stage.ScriptEngine.CreateFromFile(scriptName); var context = new ConversationContext(user); var conversation = new ScriptedConversation( context, new BasicSpeaker(context, templateID: npc.Info.ID), new BasicSpeaker(context, flags: ConversationSpeakerFlags.NPCReplacedByUser), script ); await user.Converse(conversation); } catch (FileNotFoundException) { await user.Message($"The '{scriptName}' script does not exist."); } }
public void ReadFromPacket(IPacketReader reader) { _ = reader.ReadByte(); // FieldKey _ = reader.ReadInt(); // dr0 _ = reader.ReadInt(); // dr1 var attackInfo = reader.ReadByte(); DamagePerMob = attackInfo >> 0 & 0xF; MobCount = attackInfo >> 4 & 0xF; _ = reader.ReadInt(); // dr2 _ = reader.ReadInt(); // dr3 SkillID = reader.ReadInt(); _ = reader.ReadBool(); // unk _ = reader.ReadInt(); // dr rand _ = reader.ReadInt(); // crc _ = reader.ReadInt(); // skillLevel crc _ = reader.ReadInt(); // skillLevel crc Keydown = 0; // TODO keydownskill check - int keydown var skillFlags = reader.ReadByte(); IsFinalAfterSlashBlast = (skillFlags & 0x0) > 0; IsShadowPartner = (skillFlags & 8) > 0; IsSerialAttack = (skillFlags & 32) > 0; var actionInfo = reader.ReadShort(); Action = actionInfo & 0x7FFF; IsFacingLeft = (actionInfo >> 15 & 1) > 0; _ = reader.ReadInt(); // action crc? ActionType = reader.ReadByte(); ActionSpeed = reader.ReadByte(); ActionTime = reader.ReadInt(); Phase = reader.ReadInt(); // BattleMage? for (var i = 0; i < MobCount; i++) { Mobs.Add(reader.Read(new ClientAttackMobInfo(DamagePerMob))); } _ = reader.ReadPoint2D(); // unk // TODO grenade readpoint2d }
protected override async Task Handle( GameStageUser stageUser, IFieldObjUser controller, IFieldObjMob controlled, IPacketReader packet ) { if (controlled.HP <= 0) { return; } var mobCtrlSN = packet.ReadShort(); var v7 = packet.ReadByte(); //v85 = nDistance | 4 * (v184 | 2 * ((unsigned __int8)retaddr | 2 * v72)); [ CONFIRMED ] var oldSplit = (v7 & 0xF0) != 0; //this is a type of CFieldSplit var mobMoveStartResult = (v7 & 0xF) != 0; var curSplit = packet.ReadByte(); var illegalVelocity = packet.ReadInt(); var v8 = packet.ReadByte(); var cheatedRandom = (v8 & 0xF0) != 0; var cheatedCtrlMove = (v8 & 0xF) != 0; var multiTargetForBall = packet.ReadInt(); for (var i = 0; i < multiTargetForBall; i++) { packet.ReadLong(); // int, int } var randTimeForAreaAttack = packet.ReadInt(); for (var i = 0; i < randTimeForAreaAttack; i++) { packet.ReadInt(); } packet.ReadInt(); // HackedCode packet.ReadInt(); // idk packet.ReadInt(); // HackedCodeCrc packet.ReadInt(); // idk var path = packet.Read(new MovePath()); await controlled.Move(path); var response = new UnstructuredOutgoingPacket(PacketSendOperations.MobCtrlAck); response.WriteInt(controlled.ID); response.WriteShort(mobCtrlSN); response.WriteBool(mobMoveStartResult); response.WriteShort((short)controlled.MP); // nMP response.WriteByte(0); // SkillCommand response.WriteByte(0); // SLV await controller.Dispatch(response); var movement = new UnstructuredOutgoingPacket(PacketSendOperations.MobMove); movement.WriteInt(controlled.ID); movement.WriteBool(false); // NotForceLandingWhenDiscard movement.WriteBool(false); // NotChangeAction movement.WriteBool(false); // NextAttackPossible movement.WriteBool(false); // Left movement.WriteInt(illegalVelocity); movement.WriteInt(0); // MultiTargetForBall movement.WriteInt(0); // RandTimeForAreaAttack movement.Write(path); await controlled.Field.Dispatch(controller, movement); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { var stage = stageUser.Stage; var flag = (WhisperFlags)packet.ReadByte(); switch (flag) { case WhisperFlags.Whisper | WhisperFlags.Request: { _ = packet.ReadInt(); var name = packet.ReadString(); var text = packet.ReadString(); if (name.Equals(user.Character.Name, StringComparison.CurrentCultureIgnoreCase)) { return; } var allowed = false; var target = await stage.CharacterRepository.RetrieveByName(name); var response = new UnstructuredOutgoingPacket(PacketSendOperations.Whisper); if (target != null) { var sessionRequest = new DescribeSessionByCharacterRequest { Character = target.ID }; var sessionResponse = await stage.SessionRegistry.DescribeByCharacter(sessionRequest); if (sessionResponse.Session.State == SessionState.LoggedIn) { allowed = true; } } response.WriteByte((byte)(WhisperFlags.Whisper | WhisperFlags.Result)); response.WriteString(allowed ? target.Name : name); response.WriteBool(allowed); if (allowed) { var whisper = new UnstructuredOutgoingPacket(PacketSendOperations.Whisper) .WriteByte((byte)(WhisperFlags.Whisper | WhisperFlags.Receive)) .WriteString(user.Character.Name) .WriteByte((byte)stage.ChannelID) .WriteBool(false) // bFromAdmin .WriteString(text); var whisperRequest = new DispatchToCharactersRequest { Data = ByteString.CopyFrom(whisper.Buffer) }; whisperRequest.Characters.Add(target.ID); await stage.DispatchService.DispatchToCharacters(whisperRequest); } await user.Dispatch(response); break; } default: stage.Logger.LogWarning($"Unhandled whisper flag: {flag}"); break; } }
public override async Task Handle(LoginStageUser user, IPacketReader packet) { var name = packet.ReadString(); var race = packet.ReadInt(); var subJob = packet.ReadShort(); var face = packet.ReadInt(); var hair = packet.ReadInt(); var hairColor = packet.ReadInt(); var skin = packet.ReadInt(); var coat = packet.ReadInt(); var pants = packet.ReadInt(); var shoes = packet.ReadInt(); var weapon = packet.ReadInt(); var gender = packet.ReadByte(); var result = LoginResultCode.Success; var response = new UnstructuredOutgoingPacket(PacketSendOperations.CreateNewCharacterResult); var world = await user.Stage.WorldTemplates.Retrieve((int)user.SelectedWorldID); if (world.BlockCharCreation) { result = LoginResultCode.NotConnectableWorld; } var serverRequest = new DescribeServerByMetadataRequest(); serverRequest.Metadata.Add("Type", Enum.GetName(ServerStageType.Game)); serverRequest.Metadata.Add("WorldID", user.SelectedWorldID.ToString()); serverRequest.Metadata.Add("ChannelID", user.SelectedChannelID.ToString()); var server = (await user.Stage.ServerRegistry.DescribeByMetadata(serverRequest)).Servers.FirstOrDefault(); if (server == null) { result = LoginResultCode.NotConnectableWorld; } response.WriteByte((byte)result); if (result == LoginResultCode.Success) { var character = new Character { AccountWorldID = user.AccountWorld.ID, Name = name, Job = 0, // TODO: race -> job Face = face, Hair = hair + hairColor, Skin = (byte)skin, Gender = gender, FieldID = 310000000, // TODO: start maps FieldPortal = 0, SubJob = 0 // TODO: race -> subjob }; var context = new ModifyMultiInventoryContext(character.Inventories, user.Stage.ItemTemplates); context.Set(BodyPart.Clothes, coat); context.Set(BodyPart.Shoes, shoes); context.Set(BodyPart.Weapon, weapon); if (pants > 0) { context.Set(BodyPart.Pants, pants); } await user.Stage.CharacterRepository.Insert(character); user.Character = character; user.Stage.Logger.LogDebug($"Created new {race} character: {name} (ID: {character.ID})"); response.WriteCharacterStats(character); response.WriteCharacterLook(character); response.WriteBool(false); response.WriteBool(false); } else { response.WriteInt(0); } await user.Dispatch(response); }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { var type = (ConversationRequestType)packet.ReadByte(); if (!user.IsConversing) { return; } var context = user.CurrentConversation.Context; if (type == ConversationRequestType.AskQuiz || type == ConversationRequestType.AskSpeedQuiz) { await context.Respond( new ConversationResponse <string>(type, packet.ReadString()) ); return; } var answer = packet.ReadByte(); if ( type != ConversationRequestType.Say && type != ConversationRequestType.AskYesNo && type != ConversationRequestType.AskAccept && answer == byte.MinValue || (type == ConversationRequestType.Say || type == ConversationRequestType.AskYesNo || type == ConversationRequestType.AskAccept) && answer == byte.MaxValue ) { await user.EndConversation(); return; } switch (type) { case ConversationRequestType.AskText: case ConversationRequestType.AskBoxText: await context.Respond( new ConversationResponse <string>(type, packet.ReadString()) ); break; case ConversationRequestType.AskNumber: case ConversationRequestType.AskMenu: case ConversationRequestType.AskSlideMenu: await context.Respond( new ConversationResponse <int>(type, packet.ReadInt()) ); break; case ConversationRequestType.AskAvatar: case ConversationRequestType.AskMemberShopAvatar: await context.Respond( new ConversationResponse <byte>(type, packet.ReadByte()) ); break; case ConversationRequestType.AskYesNo: case ConversationRequestType.AskAccept: await context.Respond( new ConversationResponse <bool>(type, Convert.ToBoolean(answer)) ); break; default: await context.Respond( new ConversationResponse <byte>(type, answer) ); break; } }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { var stage = stageUser.Stage; var service = stage.PartyService; var type = (PartyRequestCode)packet.ReadByte(); switch (type) { case PartyRequestCode.CreateNewParty: { if (user.Party != null) { return; } var result = PartyResultCode.CreateNewParty_Done; var response = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult); var contract = new PartyCreateRequest { Member = new PartyMemberContract { Id = user.ID, Name = user.Character.Name, Job = user.Character.Job, Level = user.Character.Level, Channel = stage.ChannelID, Field = user.Field.ID } }; var serviceResponse = await service.Create(contract); var serviceResult = serviceResponse.Result; if (serviceResult == PartyServiceResult.FailedAlreadyInParty) { result = PartyResultCode.CreateNewParty_AlreadyJoined; } else if (serviceResult != PartyServiceResult.Ok) { result = PartyResultCode.CreateNewParty_Unknown; } response.WriteByte((byte)result); if (result == PartyResultCode.CreateNewParty_Done) { response.WriteInt(serviceResponse.Party.Id); response.WriteInt(0); // TownPortal-TownID response.WriteInt(0); // TownPortal-FieldID response.WriteInt(0); // TownPortal-SkillID response.WritePoint2D(new Point2D(0, 0)); //TownPortal-Position } await user.Dispatch(response); break; } case PartyRequestCode.WithdrawParty: { if (user.Party == null) { return; } var result = PartyResultCode.WithdrawParty_Done; var response = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult); var contract = new PartyWithdrawRequest { Character = user.ID, IsKick = false }; var serviceResponse = await service.Withdraw(contract); var serviceResult = serviceResponse.Result; if (serviceResult == PartyServiceResult.FailedNotInParty) { result = PartyResultCode.WithdrawParty_NotJoined; } else if (serviceResult != PartyServiceResult.Ok) { result = PartyResultCode.WithdrawParty_Unknown; } response.WriteByte((byte)result); if (result == PartyResultCode.WithdrawParty_Done) { return; } await user.Dispatch(response); break; } case PartyRequestCode.InviteParty: { if (user.Party == null) { var contract = new PartyCreateRequest { Member = new PartyMemberContract { Id = user.ID, Name = user.Character.Name, Job = user.Character.Job, Level = user.Character.Level, Channel = stage.ChannelID, Field = user.Field.ID } }; var serviceResponse = await service.Create(contract); var serviceResult = serviceResponse.Result; if (serviceResult != PartyServiceResult.Ok) { return; } var createResponse = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult); createResponse.WriteByte((byte)PartyResultCode.CreateNewParty_Done); createResponse.WriteInt(serviceResponse.Party.Id); createResponse.WriteInt(0); // TownPortal-TownID createResponse.WriteInt(0); // TownPortal-FieldID createResponse.WriteInt(0); // TownPortal-SkillID createResponse.WritePoint2D(new Point2D(0, 0)); //TownPortal-Position await user.Dispatch(createResponse); } if (user?.Party?.Boss != user.ID) { return; } var name = packet.ReadString(); var result = PartyResultCode.InviteParty_Sent; var response = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult); var character = await stage.CharacterRepository.RetrieveByName(name); if (character == null || user.Character.ID == character.ID) { result = PartyResultCode.InviteParty_BlockedUser; } if ((await service.LoadByCharacter(new PartyLoadByCharacterRequest { Character = character.ID })).Party != null) { result = PartyResultCode.JoinParty_AlreadyJoined; } else { var serviceResponse = await stage.InviteService.Register(new InviteRegisterRequest { Invite = new InviteContract { Type = InviteType.Party, Invited = character.ID, Inviter = user.ID } }); if (serviceResponse.Result != InviteServiceResult.Ok) { result = PartyResultCode.InviteParty_AlreadyInvited; } } response.WriteByte((byte)result); if (result == PartyResultCode.InviteParty_Sent) { var invitation = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult); var invitationRequest = new DispatchToCharactersRequest(); invitation.WriteByte((byte)PartyRequestCode.InviteParty); invitation.WriteInt(user.Character.ID); invitation.WriteString(user.Character.Name); invitation.WriteInt(user.Character.Level); invitation.WriteInt(user.Character.Job); invitation.WriteByte(0); // PartyOpt invitationRequest.Data = ByteString.CopyFrom(invitation.Buffer); invitationRequest.Characters.Add(character.ID); await stage.DispatchService.DispatchToCharacters(invitationRequest); response.WriteString(character.Name); } await user.Dispatch(response); break; } case PartyRequestCode.KickParty: { if (user.Party == null) { return; } if (user.Party.Boss != user.ID) { return; } var id = packet.ReadInt(); if (!user.Party.Members.Any(m => m.ID == id)) { return; } if (id == user.ID) { return; } var result = PartyResultCode.WithdrawParty_Done; var response = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult); var contract = new PartyWithdrawRequest { Character = id, IsKick = true }; var serviceResponse = await service.Withdraw(contract); var serviceResult = serviceResponse.Result; if (serviceResult == PartyServiceResult.FailedNotInParty) { result = PartyResultCode.WithdrawParty_NotJoined; } else if (serviceResult != PartyServiceResult.Ok) { result = PartyResultCode.WithdrawParty_Unknown; } response.WriteByte((byte)result); if (result == PartyResultCode.WithdrawParty_Done) { return; } await user.Dispatch(response); break; } case PartyRequestCode.ChangePartyBoss: { if (user.Party == null || user.Party.Boss != user.ID) { return; } var id = packet.ReadInt(); if (!user.Party.Members.Any(m => m.ID == id)) { return; } if (id == user.ID) { return; } var result = PartyResultCode.ChangePartyBoss_Done; var response = new UnstructuredOutgoingPacket(PacketSendOperations.PartyResult); var contract = new PartyChangeBossRequest { Character = id }; var serviceResponse = await service.ChangeBoss(contract); var serviceResult = serviceResponse.Result; if (serviceResult != PartyServiceResult.Ok) { result = PartyResultCode.WithdrawParty_Unknown; } response.WriteByte((byte)result); if (result == PartyResultCode.ChangePartyBoss_Done) { return; } await user.Dispatch(response); break; } default: stage.Logger.LogWarning($"Unhandled party request type: {type}"); break; } }
protected override async Task Handle(GameStageUser stageUser, IFieldObjUser user, IPacketReader packet) { _ = packet.ReadInt(); var templateID = packet.ReadInt(); var template = await stageUser.Stage.SkillTemplates.Retrieve(templateID); if (template == null) { return; } var job = template.ID / 10000; var jobLevel = (byte)GameConstants.GetJobLevel(job); // TODO: job checks if (jobLevel == 0) { var sp = Math.Min(user.Character.Level - 1, job == 3000 ? 9 : 6); for (var i = 0; i < 3; i++) { sp -= user.Character.GetSkillLevel(job * 1000 + 1000 + i); } if (sp > 0) { await user.ModifySkills(s => s.Add(templateID), true); } return; } if (GameConstants.IsExtendSPJob(job) && user.Character.GetExtendSP(jobLevel) <= 0) { return; } if (!GameConstants.IsExtendSPJob(job) && user.Character.SP <= 0) { return; } var maxLevel = template.MaxLevel; if (GameConstants.IsSkillNeedMasterLevel(templateID)) { maxLevel = (short)user.Character.GetSkillMasterLevel(templateID); } if (user.Character.GetSkillLevel(templateID) >= maxLevel) { return; } var increment = 1; await user.ModifyStats(s => { if (GameConstants.IsExtendSPJob(job)) { s.SetExtendSP(jobLevel, (byte)(s.GetExtendSP(jobLevel) - increment)); } else { s.SP--; } }); await user.ModifySkills(s => s.Add(templateID, increment), true); }