ddon-server/Arrowgene.Ddon.GameServer/Handler/CharacterSwitchGameModeHandler.cs

441 lines
20 KiB
C#

using Arrowgene.Ddon.GameServer.Characters;
using Arrowgene.Ddon.Server;
using Arrowgene.Ddon.Shared.Entity.PacketStructure;
using Arrowgene.Ddon.Shared.Entity.Structure;
using Arrowgene.Ddon.Shared.Model;
using Arrowgene.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Arrowgene.Ddon.GameServer.Handler
{
public class CharacterSwitchGameModeHandler : GameRequestPacketHandler<C2SCharacterSwitchGameModeReq, S2CCharacterSwitchGameModeRes>
{
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(CharacterSwitchGameModeHandler));
public CharacterSwitchGameModeHandler(DdonGameServer server) : base(server)
{
}
public override S2CCharacterSwitchGameModeRes Handle(GameClient client, C2SCharacterSwitchGameModeReq packet)
{
Logger.Info($"GameMode={packet.GameMode}");
client.GameMode = packet.GameMode;
bool createCharacter = false;
uint originalCharacterId = client.Character.CharacterId;
var previousStorage = client.Character.Storage;
var bbmProgress = client.Character.BbmProgress;
var walletPointList = client.Character.WalletPointList;
var warpPointList = client.Character.ReleasedWarpPoints;
var clanId = client.Character.ClanId;
var clanName = client.Character.ClanName;
var achievements = (client.Character.AchievementStatus, client.Character.AchievementProgress, client.Character.AchievementUniqueCrafts);
var acquirableSkills = client.Character.AcquirableSkills;
var acquirableAbilities = client.Character.AcquirableAbilities;
var dispelSeals = client.Character.DispelSeals;
var msgSetList = client.Character.MsgSetList;
var serverInfo = client.Character.Server;
Server.HubManager.LeaveAllHubs(client);
if (client.GameMode == GameMode.Normal)
{
uint characterId = Server.Database.SelectBBMNormalCharacterId(client.Character.BbmCharacterId);
client.Character = Server.CharacterManager.SelectCharacter(client, characterId);
client.Character.BbmCharacterId = 0;
}
else if (client.GameMode == GameMode.BitterblackMaze)
{
uint bbmCharacterId = Server.Database.SelectBBMCharacterId(client.Character.CharacterId);
if (bbmCharacterId == 0)
{
createCharacter = true;
client.Character = CreateNewCharacter(client.Character);
bbmCharacterId = client.Character.CharacterId;
}
client.Character = Server.CharacterManager.SelectCharacter(client, bbmCharacterId);
client.Character.BbmCharacterId = client.Character.CharacterId;
client.Character.CharacterId = originalCharacterId;
}
else
{
throw new ResponseErrorException(ErrorCode.ERROR_CODE_JOBCHANGE_INTERNAL_ERROR, "Attempted to change game mode to unexpected type");
}
// Populate Equipment
client.Character.Equipment = client.Character.Storage.GetCharacterEquipment();
client.Character.Server = serverInfo;
client.Character.BbmProgress = bbmProgress;
client.Character.WalletPointList = walletPointList;
client.Character.ReleasedWarpPoints = warpPointList;
client.Character.OnlineStatus = OnlineStatus.Online;
client.Character.ClanId = clanId;
client.Character.ClanName = clanName;
client.Character.AchievementStatus = achievements.AchievementStatus;
client.Character.AchievementProgress = achievements.AchievementProgress;
client.Character.AchievementUniqueCrafts = achievements.AchievementUniqueCrafts;
client.Character.AcquirableSkills = acquirableSkills;
client.Character.AcquirableAbilities = acquirableAbilities;
client.Character.DispelSeals = dispelSeals;
client.Character.MsgSetList = msgSetList;
client.Send(new S2CCharacterSwitchGameModeNtc()
{
GameMode = packet.GameMode, // Probably not right? int vs uint
CreateCharacter = createCharacter,
CharacterInfo = client.Character.CDataCharacterInfo,
});
client.Send(new S2CItemUpdateCharacterItemNtc()
{
UpdateType = ItemNoticeType.SwitchingStorage,
UpdateItemList = SwapCharacterInventories(client.Character, previousStorage,
[.. ItemManager.ItemBagStorageTypes,
StorageType.CharacterEquipment
])
});
client.Send(new S2CItemUpdateCharacterItemNtc()
{
UpdateType = ItemNoticeType.SwitchingStorage,
UpdateItemList = SwapCharacterInventories(client.Character, previousStorage,
[StorageType.StorageBoxNormal]
)
});
client.Send(new S2CItemUpdateCharacterItemNtc()
{
UpdateType = ItemNoticeType.SwitchingStorage,
UpdateItemList = SwapCharacterInventories(client.Character, previousStorage,
[StorageType.StorageBoxExpansion]
)
});
client.Send(new S2CEquipChangeCharacterEquipLobbyNtc()
{
CharacterId = client.Character.CharacterId,
Job = client.Character.Job,
EquipItemList = client.Character.Equipment.AsCDataEquipItemInfo(EquipType.Performance),
VisualEquipItemList = client.Character.Equipment.AsCDataEquipItemInfo(EquipType.Visual),
});
client.Send(new S2CItemSortGetItemSortdataBinNtc()
{
SortData = [.. ItemManager.ItemBagStorageTypes.Select(x => new CDataItemSort()
{
StorageType = x,
Bin = client.Character.Storage.GetStorage(x).SortData
})]
});
client.Send(new S2CSkillSetPresetAbilityNtc()
{
CharacterId = client.Character.CharacterId,
AbilityDataList = [.. client.Character.EquippedAbilitiesDictionary[client.Character.Job]
.Where(x => x != null)
.Select((x, i) => x.AsCDataContextAcquirementData((byte)(i + 1)))]
});
return new S2CCharacterSwitchGameModeRes()
{
GameMode = packet.GameMode,
};
}
private Character CreateNewCharacter(Character normalCharacter)
{
Character bbmCharacter = new Character();
bbmCharacter.AccountId = normalCharacter.AccountId;
bbmCharacter.CharacterId = 0;
bbmCharacter.UserId = normalCharacter.UserId;
bbmCharacter.Version = normalCharacter.Version;
bbmCharacter.GameMode = GameMode.BitterblackMaze;
bbmCharacter.FirstName = normalCharacter.FirstName;
bbmCharacter.LastName = normalCharacter.LastName;
bbmCharacter.EditInfo = normalCharacter.EditInfo;
bbmCharacter.StatusInfo = normalCharacter.StatusInfo;
bbmCharacter.Job = normalCharacter.Job;
bbmCharacter.JewelrySlotNum = 1;
bbmCharacter.MyPawnSlotNum = 0;
bbmCharacter.RentalPawnSlotNum = 0;
bbmCharacter.MatchingProfile = normalCharacter.MatchingProfile;
bbmCharacter.CharacterProfile = normalCharacter.CharacterProfile;
bbmCharacter.HideEquipHead = normalCharacter.HideEquipHead;
bbmCharacter.HideEquipLantern = normalCharacter.HideEquipLantern;
bbmCharacter.HideEquipHeadPawn = normalCharacter.HideEquipHeadPawn;
bbmCharacter.HideEquipLanternPawn = normalCharacter.HideEquipLanternPawn;
bbmCharacter.ArisenProfileShareRange = normalCharacter.ArisenProfileShareRange;
bbmCharacter.OnlineStatus = normalCharacter.OnlineStatus;
bbmCharacter.Server = normalCharacter.Server;
bbmCharacter.FavWarpSlotNum = 1;
bbmCharacter.BbmProgress = normalCharacter.BbmProgress;
ArisenCsv ActiveJobPreset = Server.AssetRepository.ArisenAsset.Where(x => x.Job == bbmCharacter.Job).Single();
bbmCharacter.StatusInfo = new CDataStatusInfo()
{
HP = ActiveJobPreset.HP,
Stamina = ActiveJobPreset.Stamina,
RevivePoint = ActiveJobPreset.RevivePoint,
MaxHP = ActiveJobPreset.MaxHP,
MaxStamina = ActiveJobPreset.MaxStamina,
WhiteHP = ActiveJobPreset.WhiteHP,
GainHP = ActiveJobPreset.GainHP,
GainStamina = ActiveJobPreset.GainStamina,
GainAttack = ActiveJobPreset.GainAttack,
GainDefense = ActiveJobPreset.GainDefense,
GainMagicAttack = ActiveJobPreset.GainMagicAttack,
GainMagicDefense = ActiveJobPreset.GainMagicDefense
};
bbmCharacter.CharacterJobDataList = Server.AssetRepository.ArisenAsset.Select(arisenPreset => new CDataCharacterJobData
{
Job = arisenPreset.Job,
Exp = arisenPreset.Exp,
JobPoint = arisenPreset.JobPoint,
Lv = arisenPreset.Lv,
Atk = arisenPreset.PAtk,
Def = arisenPreset.PDef,
MAtk = arisenPreset.MAtk,
MDef = arisenPreset.MDef,
Strength = arisenPreset.Strength,
DownPower = arisenPreset.DownPower,
ShakePower = arisenPreset.ShakePower,
StunPower = arisenPreset.StunPower,
Constitution = arisenPreset.Consitution,
Guts = arisenPreset.Guts,
FireResist = arisenPreset.FireResist,
IceResist = arisenPreset.IceResist,
ThunderResist = arisenPreset.ThunderResist,
HolyResist = arisenPreset.HolyResist,
DarkResist = arisenPreset.DarkResist,
SpreadResist = arisenPreset.SpreadResist,
FreezeResist = arisenPreset.FreezeResist,
ShockResist = arisenPreset.ShockResist,
AbsorbResist = arisenPreset.AbsorbResist,
DarkElmResist = arisenPreset.DarkElmResist,
PoisonResist = arisenPreset.PoisonResist,
SlowResist = arisenPreset.SlowResist,
SleepResist = arisenPreset.SleepResist,
StunResist = arisenPreset.StunResist,
WetResist = arisenPreset.WetResist,
OilResist = arisenPreset.OilResist,
SealResist = arisenPreset.SealResist,
CurseResist = arisenPreset.CurseResist,
SoftResist = arisenPreset.SoftResist,
StoneResist = arisenPreset.StoneResist,
GoldResist = arisenPreset.GoldResist,
FireReduceResist = arisenPreset.FireReduceResist,
IceReduceResist = arisenPreset.IceReduceResist,
ThunderReduceResist = arisenPreset.ThunderReduceResist,
HolyReduceResist = arisenPreset.HolyReduceResist,
DarkReduceResist = arisenPreset.DarkReduceResist,
AtkDownResist = arisenPreset.AtkDownResist,
DefDownResist = arisenPreset.DefDownResist,
MAtkDownResist = arisenPreset.MAtkDownResist,
MDefDownResist = arisenPreset.MDefDownResist
}).ToList();
bbmCharacter.EquipmentTemplate = new EquipmentTemplate(Server.AssetRepository.BitterblackMazeAsset.GenerateStarterEquipment(), Server.AssetRepository.BitterblackMazeAsset.GenerateStarterJobEquipment());
bbmCharacter.EquippedCustomSkillsDictionary = Server.AssetRepository.ArisenAsset.Select(arisenPreset => new Tuple<JobId, List<CustomSkill>>(arisenPreset.Job, new List<CustomSkill>() {
// Main Palette
new CustomSkill() {
Job = arisenPreset.Job,
SkillId = arisenPreset.Cs1MpId,
SkillLv = arisenPreset.Cs1MpLv
},
new CustomSkill() {
Job = arisenPreset.Job,
SkillId = arisenPreset.Cs2MpId,
SkillLv = arisenPreset.Cs2MpLv
},
new CustomSkill() {
Job = arisenPreset.Job,
SkillId = arisenPreset.Cs3MpId,
SkillLv = arisenPreset.Cs3MpLv
},
new CustomSkill() {
Job = arisenPreset.Job,
SkillId = arisenPreset.Cs4MpId,
SkillLv = arisenPreset.Cs4MpLv
},
null, null, null, null, null, null, null, null, null, null, null, null, // Padding from slots 0x04 (Main Palette slot 4) to 0x11 (Sub Palette slot 1)
// Sub Palette
new CustomSkill() {
Job = arisenPreset.Job,
SkillId = arisenPreset.Cs1SpId,
SkillLv = arisenPreset.Cs1SpLv
},
new CustomSkill() {
Job = arisenPreset.Job,
SkillId = arisenPreset.Cs2SpId,
SkillLv = arisenPreset.Cs2SpLv
},
new CustomSkill() {
Job = arisenPreset.Job,
SkillId = arisenPreset.Cs3SpId,
SkillLv = arisenPreset.Cs3SpLv
},
new CustomSkill() {
Job = arisenPreset.Job,
SkillId = arisenPreset.Cs4SpId,
SkillLv = arisenPreset.Cs4SpLv
}
}.Select(skill => skill?.SkillId == 0 ? null : skill).ToList()
)).ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2);
// In BBM, all custom skills are already learned so add them
bbmCharacter.LearnedCustomSkills = [.. Server.AssetRepository.SkillData.AllSkills.Select(x => new CustomSkill()
{
Job = x.Job,
SkillId = x.SkillNo,
SkillLv = x.Params.Max(x => x.Lv)
})];
bbmCharacter.Storage = new Storages(Server.AssetRepository.StorageAsset.ToDictionary(x => x.StorageType, x => x.SlotMax));
bbmCharacter.WalletPointList = [.. Enum.GetValues<WalletType>().Select(x => new CDataWalletPoint() { Type = x, Value = 0 })];
// Add starting storage items
foreach (var tuple in Server.AssetRepository.StorageItemAsset)
{
if (tuple.Item1 == StorageType.ItemBagConsumable)
{
continue;
}
if (tuple.Item3.ItemId != 0)
{
bbmCharacter.Storage.GetStorage(tuple.Item1).AddItem(new Item(tuple.Item3), tuple.Item2);
}
}
// Add current job's equipment to the equipment storage
List<Item?> performanceEquipItems = bbmCharacter.EquipmentTemplate.GetEquipment(bbmCharacter.Job, EquipType.Performance);
for (int i = 0; i < performanceEquipItems.Count; i++)
{
Item? item = performanceEquipItems[i];
ushort slot = (ushort)(i + 1);
if (item == null || item.ItemId == 0)
{
bbmCharacter.Storage.GetStorage(StorageType.CharacterEquipment).SetItem(null, 1, slot);
}
else
{
bbmCharacter.Storage.GetStorage(StorageType.CharacterEquipment).SetItem(item, 1, slot);
}
}
foreach (var jobId in Enum.GetValues(typeof(JobId)).Cast<JobId>())
{
bbmCharacter.ExtendedJobParams[jobId] = new CDataOrbGainExtendParam();
}
Server.CharacterManager.UpdateCharacterExtendedParams(bbmCharacter, true);
bbmCharacter.GreenHp = CharacterCommon.BBM_BASE_HEALTH;
bbmCharacter.WhiteHp = CharacterCommon.BBM_BASE_HEALTH;
if (!Database.CreateCharacter(bbmCharacter))
{
return null;
}
// In BBM, all skills are already learned so add them
Dictionary<JobId, List<LearnedNormalSkill>> learnedNormalSkillsMap = Server.AssetRepository.LearnedNormalSkillsAsset.LearnedNormalSkills;
foreach (var (jobId, skills) in learnedNormalSkillsMap)
{
for (int i = 0; i < skills.Count; i++)
{
var learnedSkill = skills[i];
foreach (var skillNo in learnedSkill.SkillNo)
{
CDataNormalSkillParam newSkill = new CDataNormalSkillParam()
{
Job = jobId,
Index = (uint)(i + 1),
SkillNo = skillNo,
PreSkillNo = 0
};
bbmCharacter.LearnedNormalSkills.Add(newSkill);
Server.Database.InsertIfNotExistsNormalSkillParam(bbmCharacter.CommonId, newSkill);
}
}
}
// Populate extra tables for the characters
CDataOrbGainExtendParam ExtendParams = new CDataOrbGainExtendParam()
{
JewelrySlot = 3,
};
if (!Database.InsertGainExtendParam(bbmCharacter.CommonId, ExtendParams))
{
return null;
}
Enum.GetValues(typeof(JobId)).Cast<JobId>().Select(job => new CDataJobPlayPoint()
{
Job = job,
PlayPoint = new CDataPlayPointData()
{
ExpMode = ExpMode.Experience, // EXP
PlayPoint = 0
}
}).ToList().ForEach((Action<CDataJobPlayPoint>)(x => {
Database.ReplaceCharacterPlayPointData((uint)bbmCharacter.ContentCharacterId, x);
bbmCharacter.PlayPointList.Add(x);
}));
if (!Server.Database.InsertBBMCharacterId(normalCharacter.CharacterId, bbmCharacter.CharacterId))
{
// Failed to save character
return null;
}
return bbmCharacter;
}
/**
* @brief Swaps character inventories for content where the player looses access to their normal inventory such as Bitterblack Maze.
* This swap is a visual swap in the client UI -- by this we mean the server will use different inventory storage which shows up in
* the players storage in the client UI. It doesn't impact the actual storage during normal gameplay.
*/
private List<CDataItemUpdateResult> SwapCharacterInventories(Character character, Storages previousStorage, List<StorageType> storageTypes)
{
var results = new List<CDataItemUpdateResult>();
foreach (var storageType in storageTypes)
{
for (int i = 0; i < character.Storage.GetStorage(storageType).Items.Count; i++)
{
ushort slotNo = (ushort)(i + 1);
var previousItem = previousStorage.GetStorage(storageType).GetItem(slotNo);
if (previousItem != null)
{
results.Add(Server.ItemManager.CreateItemUpdateResult(character, previousItem.Item1, storageType, slotNo, 0, 0));
}
var currentItem = character.Storage.GetStorage(storageType).GetItem(slotNo);
if (currentItem != null)
{
results.Add(Server.ItemManager.CreateItemUpdateResult(null, currentItem.Item1, storageType, slotNo, currentItem.Item2, currentItem.Item2));
}
else if (previousItem != null)
{
results.Add(Server.ItemManager.CreateItemUpdateResult(null, new Item() { ItemId = 0, UId = ""}, storageType, slotNo, previousItem.Item2, previousItem.Item2));
}
}
}
return results;
}
}
}