private static void CharacterCreateEx(ClientMessage message, Session session) { var characterCreateInfo = new CharacterCreateInfo(); characterCreateInfo.Unpack(message.Payload); // TODO: Check for Banned Name Here //DatabaseManager.Shard.IsCharacterNameBanned(characterCreateInfo.Name, isBanned => //{ // if (!isBanned) // { // SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.NameBanned); // return; // } //}); // Disable OlthoiPlay characters for now. They're not implemented yet. // FIXME: Restore OlthoiPlay characters when properly handled. if (characterCreateInfo.Heritage == (int)HeritageGroup.Olthoi || characterCreateInfo.Heritage == (int)HeritageGroup.OlthoiAcid) { SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.Pending); return; } var cg = DatManager.PortalDat.CharGen; var isAdmin = characterCreateInfo.IsAdmin && (session.AccessLevel >= AccessLevel.Developer); var isEnvoy = characterCreateInfo.IsEnvoy && (session.AccessLevel >= AccessLevel.Sentinel); Weenie weenie; if (ConfigManager.Config.Server.Accounts.OverrideCharacterPermissions) { if (session.AccessLevel >= AccessLevel.Developer && session.AccessLevel <= AccessLevel.Admin) { weenie = DatabaseManager.World.GetCachedWeenie("admin"); } else if (session.AccessLevel >= AccessLevel.Sentinel && session.AccessLevel <= AccessLevel.Envoy) { weenie = DatabaseManager.World.GetCachedWeenie("sentinel"); } else { weenie = DatabaseManager.World.GetCachedWeenie("human"); } if (characterCreateInfo.Heritage == (int)HeritageGroup.Olthoi && weenie.Type == (int)WeenieType.Admin) { weenie = DatabaseManager.World.GetCachedWeenie("olthoiadmin"); } if (characterCreateInfo.Heritage == (int)HeritageGroup.OlthoiAcid && weenie.Type == (int)WeenieType.Admin) { weenie = DatabaseManager.World.GetCachedWeenie("olthoiacidadmin"); } } else { weenie = DatabaseManager.World.GetCachedWeenie("human"); } if (characterCreateInfo.Heritage == (int)HeritageGroup.Olthoi && weenie.Type == (int)WeenieType.Creature) { weenie = DatabaseManager.World.GetCachedWeenie("olthoiplayer"); } if (characterCreateInfo.Heritage == (int)HeritageGroup.OlthoiAcid && weenie.Type == (int)WeenieType.Creature) { weenie = DatabaseManager.World.GetCachedWeenie("olthoiacidplayer"); } if (isEnvoy) { weenie = DatabaseManager.World.GetCachedWeenie("sentinel"); } if (isAdmin) { weenie = DatabaseManager.World.GetCachedWeenie("admin"); } if (weenie == null) { weenie = DatabaseManager.World.GetCachedWeenie("human"); // Default catch-all } if (weenie == null) // If it is STILL null after the above catchall, the database is missing critical data and cannot continue with character creation. { SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.DatabaseDown); log.Error($"Database does not contain the weenie for human (1). Characters cannot be created until the missing weenie is restored."); return; } var guid = GuidManager.NewPlayerGuid(); // If Database didn't have Sentinel/Admin weenies, alter the weenietype coming in. if (ConfigManager.Config.Server.Accounts.OverrideCharacterPermissions) { if (session.AccessLevel >= AccessLevel.Developer && session.AccessLevel <= AccessLevel.Admin && weenie.Type != (int)WeenieType.Admin) { weenie.Type = (int)WeenieType.Admin; } else if (session.AccessLevel >= AccessLevel.Sentinel && session.AccessLevel <= AccessLevel.Envoy && weenie.Type != (int)WeenieType.Sentinel) { weenie.Type = (int)WeenieType.Sentinel; } } var player = new Player(weenie, guid, session); player.SetProperty(PropertyInt.HeritageGroup, (int)characterCreateInfo.Heritage); player.SetProperty(PropertyString.HeritageGroup, cg.HeritageGroups[characterCreateInfo.Heritage].Name); player.SetProperty(PropertyInt.Gender, (int)characterCreateInfo.Gender); player.SetProperty(PropertyString.Sex, characterCreateInfo.Gender == 1 ? "Male" : "Female"); //player.SetProperty(PropertyDataId.Icon, cg.HeritageGroups[characterCreateInfo.Heritage].IconImage); // I don't believe this is used anywhere in the client, but it might be used by a future custom launcher // pull character data from the dat file var sex = cg.HeritageGroups[characterCreateInfo.Heritage].Genders[(int)characterCreateInfo.Gender]; player.SetProperty(PropertyDataId.MotionTable, sex.MotionTable); player.SetProperty(PropertyDataId.SoundTable, sex.SoundTable); player.SetProperty(PropertyDataId.PhysicsEffectTable, sex.PhysicsTable); player.SetProperty(PropertyDataId.Setup, sex.SetupID); player.SetProperty(PropertyDataId.PaletteBase, sex.BasePalette); player.SetProperty(PropertyDataId.CombatTable, sex.CombatTable); // Check the character scale if (sex.Scale != 100u) { player.SetProperty(PropertyFloat.DefaultScale, (sex.Scale / 100f)); // Scale is stored as a percentage } // Get the hair first, because we need to know if you're bald, and that's the name of that tune! var hairstyle = sex.HairStyleList[Convert.ToInt32(characterCreateInfo.Apperance.HairStyle)]; // Certain races (Undead, Tumeroks, Others?) have multiple body styles available. This is controlled via the "hair style". if (hairstyle.AlternateSetup > 0) { player.SetProperty(PropertyDataId.Setup, hairstyle.AlternateSetup); } player.SetProperty(PropertyDataId.EyesTexture, sex.GetEyeTexture(characterCreateInfo.Apperance.Eyes, hairstyle.Bald)); player.SetProperty(PropertyDataId.DefaultEyesTexture, sex.GetDefaultEyeTexture(characterCreateInfo.Apperance.Eyes, hairstyle.Bald)); player.SetProperty(PropertyDataId.NoseTexture, sex.GetNoseTexture(characterCreateInfo.Apperance.Nose)); player.SetProperty(PropertyDataId.DefaultNoseTexture, sex.GetDefaultNoseTexture(characterCreateInfo.Apperance.Nose)); player.SetProperty(PropertyDataId.MouthTexture, sex.GetMouthTexture(characterCreateInfo.Apperance.Mouth)); player.SetProperty(PropertyDataId.DefaultMouthTexture, sex.GetDefaultMouthTexture(characterCreateInfo.Apperance.Mouth)); player.SetProperty(PropertyDataId.HairTexture, sex.GetHairTexture(characterCreateInfo.Apperance.HairStyle)); player.SetProperty(PropertyDataId.DefaultHairTexture, sex.GetDefaultHairTexture(characterCreateInfo.Apperance.HairStyle)); player.SetProperty(PropertyDataId.HeadObject, sex.GetHeadObject(characterCreateInfo.Apperance.HairStyle)); // Skin is stored as PaletteSet (list of Palettes), so we need to read in the set to get the specific palette var skinPalSet = DatManager.PortalDat.ReadFromDat <PaletteSet>(sex.SkinPalSet); player.SetProperty(PropertyDataId.SkinPalette, skinPalSet.GetPaletteID(characterCreateInfo.Apperance.SkinHue)); player.SetProperty(PropertyFloat.Shade, characterCreateInfo.Apperance.SkinHue); // Hair is stored as PaletteSet (list of Palettes), so we need to read in the set to get the specific palette var hairPalSet = DatManager.PortalDat.ReadFromDat <PaletteSet>(sex.HairColorList[Convert.ToInt32(characterCreateInfo.Apperance.HairColor)]); player.SetProperty(PropertyDataId.HairPalette, hairPalSet.GetPaletteID(characterCreateInfo.Apperance.HairHue)); // Eye Color player.SetProperty(PropertyDataId.EyesPalette, sex.EyeColorList[Convert.ToInt32(characterCreateInfo.Apperance.EyeColor)]); if (characterCreateInfo.Apperance.HeadgearStyle < 0xFFFFFFFF) // No headgear is max UINT { var hat = GetClothingObject(sex.GetHeadgearWeenie(characterCreateInfo.Apperance.HeadgearStyle), characterCreateInfo.Apperance.HeadgearColor, characterCreateInfo.Apperance.HeadgearHue); if (hat != null) { player.TryEquipObject(hat, hat.GetProperty(PropertyInt.ValidLocations) ?? 0); } else { CreateIOU(player, sex.GetHeadgearWeenie(characterCreateInfo.Apperance.HeadgearStyle)); } } var shirt = GetClothingObject(sex.GetShirtWeenie(characterCreateInfo.Apperance.ShirtStyle), characterCreateInfo.Apperance.ShirtColor, characterCreateInfo.Apperance.ShirtHue); if (shirt != null) { player.TryEquipObject(shirt, shirt.GetProperty(PropertyInt.ValidLocations) ?? 0); } else { CreateIOU(player, sex.GetShirtWeenie(characterCreateInfo.Apperance.ShirtStyle)); } var pants = GetClothingObject(sex.GetPantsWeenie(characterCreateInfo.Apperance.PantsStyle), characterCreateInfo.Apperance.PantsColor, characterCreateInfo.Apperance.PantsHue); if (pants != null) { player.TryEquipObject(pants, pants.GetProperty(PropertyInt.ValidLocations) ?? 0); } else { CreateIOU(player, sex.GetPantsWeenie(characterCreateInfo.Apperance.PantsStyle)); } var shoes = GetClothingObject(sex.GetFootwearWeenie(characterCreateInfo.Apperance.FootwearStyle), characterCreateInfo.Apperance.FootwearColor, characterCreateInfo.Apperance.FootwearHue); if (shoes != null) { player.TryEquipObject(shoes, shoes.GetProperty(PropertyInt.ValidLocations) ?? 0); } else { CreateIOU(player, sex.GetFootwearWeenie(characterCreateInfo.Apperance.FootwearStyle)); } string templateName = cg.HeritageGroups[characterCreateInfo.Heritage].Templates[characterCreateInfo.TemplateOption].Name; //player.SetProperty(PropertyString.Title, templateName); player.SetProperty(PropertyString.Template, templateName); player.AddTitle(cg.HeritageGroups[characterCreateInfo.Heritage].Templates[characterCreateInfo.TemplateOption].Title, true); // stats uint totalAttributeCredits = cg.HeritageGroups[characterCreateInfo.Heritage].AttributeCredits; uint usedAttributeCredits = 0; player.Strength.StartingValue = ValidateAttributeCredits(characterCreateInfo.StrengthAbility, usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += player.Strength.StartingValue; player.Endurance.StartingValue = ValidateAttributeCredits(characterCreateInfo.EnduranceAbility, usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += player.Endurance.StartingValue; player.Coordination.StartingValue = ValidateAttributeCredits(characterCreateInfo.CoordinationAbility, usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += player.Coordination.StartingValue; player.Quickness.StartingValue = ValidateAttributeCredits(characterCreateInfo.QuicknessAbility, usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += player.Quickness.StartingValue; player.Focus.StartingValue = ValidateAttributeCredits(characterCreateInfo.FocusAbility, usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += player.Focus.StartingValue; player.Self.StartingValue = ValidateAttributeCredits(characterCreateInfo.SelfAbility, usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += player.Self.StartingValue; // Validate this is equal to actual attribute credits. 330 for all but "Olthoi", which have 60 if (usedAttributeCredits > 330 || ((characterCreateInfo.Heritage == (int)HeritageGroup.Olthoi || characterCreateInfo.Heritage == (int)HeritageGroup.OlthoiAcid) && usedAttributeCredits > 60)) { SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.Corrupt); return; } // data we don't care about //characterCreateInfo.CharacterSlot; //characterCreateInfo.ClassId; // characters start with max vitals player.Health.Current = player.Health.Base; player.Stamina.Current = player.Stamina.Base; player.Mana.Current = player.Mana.Base; // set initial skill credit amount. 52 for all but "Olthoi", which have 68 player.SetProperty(PropertyInt.AvailableSkillCredits, (int)cg.HeritageGroups[characterCreateInfo.Heritage].SkillCredits); for (int i = 0; i < characterCreateInfo.SkillStatuses.Count; i++) { var skill = (Skill)i; var skillCost = skill.GetCost(); var skillStatus = characterCreateInfo.SkillStatuses[i]; if (skillStatus == SkillStatus.Specialized) { player.TrainSkill(skill, skillCost.TrainingCost); player.SpecializeSkill(skill, skillCost.SpecializationCost); player.GetCreatureSkill(skill).InitLevel = 10; } else if (skillStatus == SkillStatus.Trained) { player.TrainSkill(skill, skillCost.TrainingCost); player.GetCreatureSkill(skill).InitLevel = 5; } else if (skillCost != null && skillStatus == SkillStatus.Untrained) { player.UntrainSkill(skill, skillCost.TrainingCost); } } // grant starter items based on skills var starterGearConfig = StarterGearFactory.GetStarterGearConfiguration(); var grantedWeenies = new List <uint>(); foreach (var skillGear in starterGearConfig.Skills) { var charSkill = player.Skills[(Skill)skillGear.SkillId]; if (charSkill.Status == SkillStatus.Trained || charSkill.Status == SkillStatus.Specialized) { foreach (var item in skillGear.Gear) { if (grantedWeenies.Contains(item.WeenieId)) { var existingItem = player.Inventory.Values.FirstOrDefault(i => i.WeenieClassId == item.WeenieId); if (existingItem == null || (existingItem.MaxStackSize ?? 1) <= 1) { continue; } existingItem.StackSize += item.StackSize; continue; } var loot = WorldObjectFactory.CreateNewWorldObject(item.WeenieId); if (loot != null) { if (loot.StackSize.HasValue && loot.MaxStackSize.HasValue) { loot.StackSize = (item.StackSize <= loot.MaxStackSize) ? item.StackSize : loot.MaxStackSize; } } if (loot == null) { CreateIOU(player, item.WeenieId); continue; } if (player.TryAddToInventory(loot)) { grantedWeenies.Add(item.WeenieId); } } var heritageLoot = skillGear.Heritage.FirstOrDefault(sh => sh.HeritageId == characterCreateInfo.Heritage); if (heritageLoot != null) { foreach (var item in heritageLoot.Gear) { if (grantedWeenies.Contains(item.WeenieId)) { var existingItem = player.Inventory.Values.FirstOrDefault(i => i.WeenieClassId == item.WeenieId); if (existingItem == null || (existingItem.MaxStackSize ?? 1) <= 1) { continue; } existingItem.StackSize += item.StackSize; continue; } var loot = WorldObjectFactory.CreateNewWorldObject(item.WeenieId); if (loot != null) { if (loot.StackSize.HasValue && loot.MaxStackSize.HasValue) { loot.StackSize = (item.StackSize <= loot.MaxStackSize) ? item.StackSize : loot.MaxStackSize; } } if (loot == null) { CreateIOU(player, item.WeenieId); continue; } if (player.TryAddToInventory(loot)) { grantedWeenies.Add(item.WeenieId); } } } foreach (var spell in skillGear.Spells) { // Olthoi Spitter is a special case if (characterCreateInfo.Heritage == (int)HeritageGroup.OlthoiAcid) { player.AddKnownSpell(spell.SpellId); // Continue to next spell as Olthoi spells do not have the SpecializedOnly field continue; } if (charSkill.Status == SkillStatus.Trained && spell.SpecializedOnly == false) { player.AddKnownSpell(spell.SpellId); } else if (charSkill.Status == SkillStatus.Specialized) { player.AddKnownSpell(spell.SpellId); } } } } player.Name = characterCreateInfo.Name; //player.SetProperty(PropertyString.DisplayName, characterCreateInfo.Name); // unsure // Index used to determine the starting location uint startArea = characterCreateInfo.StartArea; player.SetProperty(PropertyBool.Attackable, true); player.SetProperty(PropertyFloat.CreationTimestamp, Time.GetTimestamp()); player.SetProperty(PropertyInt.CreationTimestamp, (int)player.GetProperty(PropertyFloat.CreationTimestamp)); player.SetProperty(PropertyString.DateOfBirth, $"{DateTime.UtcNow:dd MMMM yyyy}"); DatabaseManager.Shard.IsCharacterNameAvailable(characterCreateInfo.Name, isAvailable => { if (!isAvailable) { SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.NameInUse); return; } // player.SetProperty(PropertyInstanceId.Account, (int)session.Id); var character = new Character(); character.AccountId = session.Id; character.Name = player.GetProperty(PropertyString.Name); character.BiotaId = player.Guid.Full; character.IsDeleted = false; CharacterCreateSetDefaultCharacterOptions(player); player.Location = new Position(cg.StarterAreas[(int)startArea].Locations[0].ObjCellID, cg.StarterAreas[(int)startArea].Locations[0].Frame.Origin.X, cg.StarterAreas[(int)startArea].Locations[0].Frame.Origin.Y, cg.StarterAreas[(int)startArea].Locations[0].Frame.Origin.Z, cg.StarterAreas[(int)startArea].Locations[0].Frame.Orientation.X, cg.StarterAreas[(int)startArea].Locations[0].Frame.Orientation.Y, cg.StarterAreas[(int)startArea].Locations[0].Frame.Orientation.Z, cg.StarterAreas[(int)startArea].Locations[0].Frame.Orientation.W); player.Instantiation = player.Location; player.Sanctuary = player.Location; var possessions = player.GetAllPossessions(); var possessedBiotas = new Collection <Biota>(); foreach (var possession in possessions) { possessedBiotas.Add(possession.Biota); } // We must await here -- DatabaseManager.Shard.AddCharacter(character, player.Biota, possessedBiotas, saveSuccess => { if (!saveSuccess) { SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.DatabaseDown); return; } session.AccountCharacters.Add(character); SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.Ok, player.Guid, characterCreateInfo.Name); }); }); }
private static void CharacterCreateEx(ClientMessage message, Session session, uint id) { CharGen cg = CharGen.ReadFromDat(); var reader = message.Payload; AceCharacter character = new AceCharacter(id); reader.Skip(4); /* Unknown constant (1) */ character.Heritage = reader.ReadUInt32(); character.HeritageGroup = cg.HeritageGroups[(int)character.Heritage].Name; character.Gender = reader.ReadUInt32(); if (character.Gender == 1) { character.Sex = "Male"; } else { character.Sex = "Female"; } Appearance appearance = Appearance.FromNetwork(reader); // character.IconId = cg.HeritageGroups[(int)character.Heritage].IconImage; // pull character data from the dat file SexCG sex = cg.HeritageGroups[(int)character.Heritage].SexList[(int)character.Gender]; character.MotionTableId = sex.MotionTable; character.SoundTableId = sex.SoundTable; character.PhysicsTableId = sex.PhysicsTable; character.SetupTableId = sex.SetupID; character.PaletteId = sex.BasePalette; character.CombatTableId = sex.CombatTable; // Check the character scale if (sex.Scale != 100u) { character.DefaultScale = (sex.Scale / 100f); // Scale is stored as a percentage } // Get the hair first, because we need to know if you're bald, and that's the name of that tune! HairStyleCG hairstyle = sex.HairStyleList[Convert.ToInt32(appearance.HairStyle)]; bool isBald = hairstyle.Bald; // Certain races (Undead, Tumeroks, Others?) have multiple body styles available. This is controlled via the "hair style". if (hairstyle.AlternateSetup > 0) { character.SetupTableId = hairstyle.AlternateSetup; } character.EyesTexture = sex.GetEyeTexture(appearance.Eyes, isBald); character.DefaultEyesTexture = sex.GetDefaultEyeTexture(appearance.Eyes, isBald); character.NoseTexture = sex.GetNoseTexture(appearance.Nose); character.DefaultNoseTexture = sex.GetDefaultNoseTexture(appearance.Nose); character.MouthTexture = sex.GetMouthTexture(appearance.Mouth); character.DefaultMouthTexture = sex.GetDefaultMouthTexture(appearance.Mouth); character.HairTexture = sex.GetHairTexture(appearance.HairStyle); character.DefaultHairTexture = sex.GetDefaultHairTexture(appearance.HairStyle); character.HeadObject = sex.GetHeadObject(appearance.HairStyle); // Skin is stored as PaletteSet (list of Palettes), so we need to read in the set to get the specific palette PaletteSet skinPalSet = PaletteSet.ReadFromDat(sex.SkinPalSet); character.SkinPalette = skinPalSet.GetPaletteID(appearance.SkinHue); // Hair is stored as PaletteSet (list of Palettes), so we need to read in the set to get the specific palette PaletteSet hairPalSet = PaletteSet.ReadFromDat(sex.HairColorList[Convert.ToInt32(appearance.HairColor)]); character.HairPalette = hairPalSet.GetPaletteID(appearance.HairHue); // Eye Color character.EyesPalette = sex.EyeColorList[Convert.ToInt32(appearance.EyeColor)]; if (appearance.HeadgearStyle < 0xFFFFFFFF) // No headgear is max UINT { uint headgearWeenie = sex.GetHeadgearWeenie(appearance.HeadgearStyle); ClothingTable headCT = ClothingTable.ReadFromDat(sex.GetHeadgearClothingTable(appearance.HeadgearStyle)); uint headgearIconId = headCT.GetIcon(appearance.HeadgearColor); var hat = (AceObject)DatabaseManager.World.GetAceObjectByWeenie(headgearWeenie).Clone(GuidManager.NewItemGuid().Full); hat.PaletteOverrides = new List <PaletteOverride>(); // wipe any existing overrides hat.TextureOverrides = new List <TextureMapOverride>(); hat.AnimationOverrides = new List <AnimationOverride>(); hat.SpellIdProperties = new List <AceObjectPropertiesSpell>(); hat.IconDID = headgearIconId; hat.Placement = 0; hat.CurrentWieldedLocation = hat.ValidLocations; hat.WielderIID = id; if (headCT.ClothingBaseEffects.ContainsKey(sex.SetupID)) { // Add the model and texture(s) ClothingBaseEffect headCBE = headCT.ClothingBaseEffects[sex.SetupID]; for (int i = 0; i < headCBE.CloObjectEffects.Count; i++) { byte partNum = (byte)headCBE.CloObjectEffects[i].Index; hat.AnimationOverrides.Add(new AnimationOverride() { AceObjectId = hat.AceObjectId, AnimationId = headCBE.CloObjectEffects[i].ModelId, Index = (byte)headCBE.CloObjectEffects[i].Index }); for (int j = 0; j < headCBE.CloObjectEffects[i].CloTextureEffects.Count; j++) { hat.TextureOverrides.Add(new TextureMapOverride() { AceObjectId = hat.AceObjectId, Index = (byte)headCBE.CloObjectEffects[i].Index, OldId = (ushort)headCBE.CloObjectEffects[i].CloTextureEffects[j].OldTexture, NewId = (ushort)headCBE.CloObjectEffects[i].CloTextureEffects[j].NewTexture }); } } // Apply the proper palette(s). Unlike character skin/hair, clothes can have several palette ranges! CloSubPalEffect headSubPal = headCT.ClothingSubPalEffects[appearance.HeadgearColor]; for (int i = 0; i < headSubPal.CloSubPalettes.Count; i++) { PaletteSet headgearPalSet = PaletteSet.ReadFromDat(headSubPal.CloSubPalettes[i].PaletteSet); ushort headgearPal = (ushort)headgearPalSet.GetPaletteID(appearance.HeadgearHue); for (int j = 0; j < headSubPal.CloSubPalettes[i].Ranges.Count; j++) { uint palOffset = headSubPal.CloSubPalettes[i].Ranges[j].Offset / 8; uint numColors = headSubPal.CloSubPalettes[i].Ranges[j].NumColors / 8; hat.PaletteOverrides.Add(new PaletteOverride() { AceObjectId = hat.AceObjectId, SubPaletteId = headgearPal, Length = (ushort)(numColors), Offset = (ushort)(palOffset) }); } } } character.WieldedItems.Add(new ObjectGuid(hat.AceObjectId), hat); } uint shirtWeenie = sex.GetShirtWeenie(appearance.ShirtStyle); ClothingTable shirtCT = ClothingTable.ReadFromDat(sex.GetShirtClothingTable(appearance.ShirtStyle)); uint shirtIconId = shirtCT.GetIcon(appearance.ShirtColor); var shirt = (AceObject)DatabaseManager.World.GetAceObjectByWeenie(shirtWeenie).Clone(GuidManager.NewItemGuid().Full); shirt.PaletteOverrides = new List <PaletteOverride>(); // wipe any existing overrides shirt.TextureOverrides = new List <TextureMapOverride>(); shirt.AnimationOverrides = new List <AnimationOverride>(); shirt.SpellIdProperties = new List <AceObjectPropertiesSpell>(); shirt.IconDID = shirtIconId; shirt.Placement = 0; shirt.CurrentWieldedLocation = shirt.ValidLocations; shirt.WielderIID = id; if (shirtCT.ClothingBaseEffects.ContainsKey(sex.SetupID)) { ClothingBaseEffect shirtCBE = shirtCT.ClothingBaseEffects[sex.SetupID]; for (int i = 0; i < shirtCBE.CloObjectEffects.Count; i++) { byte partNum = (byte)shirtCBE.CloObjectEffects[i].Index; shirt.AnimationOverrides.Add(new AnimationOverride() { AceObjectId = shirt.AceObjectId, AnimationId = shirtCBE.CloObjectEffects[i].ModelId, Index = (byte)shirtCBE.CloObjectEffects[i].Index }); for (int j = 0; j < shirtCBE.CloObjectEffects[i].CloTextureEffects.Count; j++) { shirt.TextureOverrides.Add(new TextureMapOverride() { AceObjectId = shirt.AceObjectId, Index = (byte)shirtCBE.CloObjectEffects[i].Index, OldId = (ushort)shirtCBE.CloObjectEffects[i].CloTextureEffects[j].OldTexture, NewId = (ushort)shirtCBE.CloObjectEffects[i].CloTextureEffects[j].NewTexture }); } } // Apply the proper palette(s). Unlike character skin/hair, clothes can have several palette ranges! if (shirtCT.ClothingSubPalEffects.ContainsKey(appearance.ShirtColor)) { CloSubPalEffect shirtSubPal = shirtCT.ClothingSubPalEffects[appearance.ShirtColor]; for (int i = 0; i < shirtSubPal.CloSubPalettes.Count; i++) { PaletteSet shirtPalSet = PaletteSet.ReadFromDat(shirtSubPal.CloSubPalettes[i].PaletteSet); ushort shirtPal = (ushort)shirtPalSet.GetPaletteID(appearance.ShirtHue); if (shirtPal > 0) // shirtPal will be 0 if the palette set is empty/not found { for (int j = 0; j < shirtSubPal.CloSubPalettes[i].Ranges.Count; j++) { uint palOffset = shirtSubPal.CloSubPalettes[i].Ranges[j].Offset / 8; uint numColors = shirtSubPal.CloSubPalettes[i].Ranges[j].NumColors / 8; shirt.PaletteOverrides.Add(new PaletteOverride() { AceObjectId = shirt.AceObjectId, SubPaletteId = shirtPal, Offset = (ushort)palOffset, Length = (ushort)numColors }); } } } } } character.WieldedItems.Add(new ObjectGuid(shirt.AceObjectId), shirt); uint pantsWeenie = sex.GetPantsWeenie(appearance.PantsStyle); ClothingTable pantsCT = ClothingTable.ReadFromDat(sex.GetPantsClothingTable(appearance.PantsStyle)); uint pantsIconId = pantsCT.GetIcon(appearance.PantsColor); var pants = (AceObject)DatabaseManager.World.GetAceObjectByWeenie(pantsWeenie).Clone(GuidManager.NewItemGuid().Full); pants.PaletteOverrides = new List <PaletteOverride>(); // wipe any existing overrides pants.TextureOverrides = new List <TextureMapOverride>(); pants.AnimationOverrides = new List <AnimationOverride>(); pants.SpellIdProperties = new List <AceObjectPropertiesSpell>(); pants.IconDID = pantsIconId; pants.Placement = 0; pants.CurrentWieldedLocation = pants.ValidLocations; pants.WielderIID = id; // Get the character's initial pants if (pantsCT.ClothingBaseEffects.ContainsKey(sex.SetupID)) { ClothingBaseEffect pantsCBE = pantsCT.ClothingBaseEffects[sex.SetupID]; for (int i = 0; i < pantsCBE.CloObjectEffects.Count; i++) { byte partNum = (byte)pantsCBE.CloObjectEffects[i].Index; pants.AnimationOverrides.Add(new AnimationOverride() { AceObjectId = pants.AceObjectId, AnimationId = pantsCBE.CloObjectEffects[i].ModelId, Index = (byte)pantsCBE.CloObjectEffects[i].Index }); for (int j = 0; j < pantsCBE.CloObjectEffects[i].CloTextureEffects.Count; j++) { pants.TextureOverrides.Add(new TextureMapOverride() { AceObjectId = pants.AceObjectId, Index = (byte)pantsCBE.CloObjectEffects[i].Index, OldId = (ushort)pantsCBE.CloObjectEffects[i].CloTextureEffects[j].OldTexture, NewId = (ushort)pantsCBE.CloObjectEffects[i].CloTextureEffects[j].NewTexture }); } } // Apply the proper palette(s). Unlike character skin/hair, clothes can have several palette ranges! CloSubPalEffect pantsSubPal = pantsCT.ClothingSubPalEffects[appearance.PantsColor]; for (int i = 0; i < pantsSubPal.CloSubPalettes.Count; i++) { PaletteSet pantsPalSet = PaletteSet.ReadFromDat(pantsSubPal.CloSubPalettes[i].PaletteSet); ushort pantsPal = (ushort)pantsPalSet.GetPaletteID(appearance.PantsHue); for (int j = 0; j < pantsSubPal.CloSubPalettes[i].Ranges.Count; j++) { uint palOffset = pantsSubPal.CloSubPalettes[i].Ranges[j].Offset / 8; uint numColors = pantsSubPal.CloSubPalettes[i].Ranges[j].NumColors / 8; pants.PaletteOverrides.Add(new PaletteOverride() { AceObjectId = pants.AceObjectId, SubPaletteId = pantsPal, Offset = (ushort)palOffset, Length = (ushort)numColors }); } } } // end pants character.WieldedItems.Add(new ObjectGuid(pants.AceObjectId), pants); uint footwearWeenie = sex.GetFootwearWeenie(appearance.FootwearStyle); ClothingTable footwearCT = ClothingTable.ReadFromDat(sex.GetFootwearClothingTable(appearance.FootwearStyle)); uint footwearIconId = footwearCT.GetIcon(appearance.FootwearColor); var shoes = (AceObject)DatabaseManager.World.GetAceObjectByWeenie(footwearWeenie).Clone(GuidManager.NewItemGuid().Full); shoes.PaletteOverrides = new List <PaletteOverride>(); // wipe any existing overrides shoes.TextureOverrides = new List <TextureMapOverride>(); shoes.AnimationOverrides = new List <AnimationOverride>(); shoes.SpellIdProperties = new List <AceObjectPropertiesSpell>(); shoes.IconDID = footwearIconId; shoes.Placement = 0; shoes.CurrentWieldedLocation = shoes.ValidLocations; shoes.WielderIID = id; if (footwearCT.ClothingBaseEffects.ContainsKey(sex.SetupID)) { ClothingBaseEffect footwearCBE = footwearCT.ClothingBaseEffects[sex.SetupID]; for (int i = 0; i < footwearCBE.CloObjectEffects.Count; i++) { byte partNum = (byte)footwearCBE.CloObjectEffects[i].Index; shoes.AnimationOverrides.Add(new AnimationOverride() { AceObjectId = shoes.AceObjectId, AnimationId = footwearCBE.CloObjectEffects[i].ModelId, Index = (byte)footwearCBE.CloObjectEffects[i].Index }); for (int j = 0; j < footwearCBE.CloObjectEffects[i].CloTextureEffects.Count; j++) { shoes.TextureOverrides.Add(new TextureMapOverride() { AceObjectId = shoes.AceObjectId, Index = (byte)footwearCBE.CloObjectEffects[i].Index, OldId = (ushort)footwearCBE.CloObjectEffects[i].CloTextureEffects[j].OldTexture, NewId = (ushort)footwearCBE.CloObjectEffects[i].CloTextureEffects[j].NewTexture }); } } // Apply the proper palette(s). Unlike character skin/hair, clothes can have several palette ranges! CloSubPalEffect footwearSubPal = footwearCT.ClothingSubPalEffects[appearance.FootwearColor]; for (int i = 0; i < footwearSubPal.CloSubPalettes.Count; i++) { PaletteSet footwearPalSet = PaletteSet.ReadFromDat(footwearSubPal.CloSubPalettes[i].PaletteSet); ushort footwearPal = (ushort)footwearPalSet.GetPaletteID(appearance.FootwearHue); for (int j = 0; j < footwearSubPal.CloSubPalettes[i].Ranges.Count; j++) { uint palOffset = footwearSubPal.CloSubPalettes[i].Ranges[j].Offset / 8; uint numColors = footwearSubPal.CloSubPalettes[i].Ranges[j].NumColors / 8; pants.PaletteOverrides.Add(new PaletteOverride() { AceObjectId = shoes.AceObjectId, SubPaletteId = footwearPal, Offset = (ushort)palOffset, Length = (ushort)numColors }); } } } // end footwear character.WieldedItems.Add(new ObjectGuid(shoes.AceObjectId), shoes); // Profession (Adventurer, Bow Hunter, etc) // TODO - Add this title to the available titles for this character. var templateOption = reader.ReadInt32(); string templateName = cg.HeritageGroups[(int)character.Heritage].TemplateList[templateOption].Name; character.Title = templateName; character.Template = templateName; character.CharacterTitleId = cg.HeritageGroups[(int)character.Heritage].TemplateList[templateOption].Title; character.NumCharacterTitles = 1; // stats uint totalAttributeCredits = cg.HeritageGroups[(int)character.Heritage].AttributeCredits; uint usedAttributeCredits = 0; // Validate this is equal to actual attribute credits (330 for all but "Olthoi", which have 60 character.StrengthAbility.Base = ValidateAttributeCredits(reader.ReadUInt32(), usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += character.StrengthAbility.Base; character.EnduranceAbility.Base = ValidateAttributeCredits(reader.ReadUInt32(), usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += character.EnduranceAbility.Base; character.CoordinationAbility.Base = ValidateAttributeCredits(reader.ReadUInt32(), usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += character.CoordinationAbility.Base; character.QuicknessAbility.Base = ValidateAttributeCredits(reader.ReadUInt32(), usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += character.QuicknessAbility.Base; character.FocusAbility.Base = ValidateAttributeCredits(reader.ReadUInt32(), usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += character.FocusAbility.Base; character.SelfAbility.Base = ValidateAttributeCredits(reader.ReadUInt32(), usedAttributeCredits, totalAttributeCredits); usedAttributeCredits += character.SelfAbility.Base; // data we don't care about uint characterSlot = reader.ReadUInt32(); uint classId = reader.ReadUInt32(); // characters start with max vitals character.Health.Current = character.Health.MaxValue; character.Stamina.Current = character.Stamina.MaxValue; character.Mana.Current = character.Mana.MaxValue; // set initial skill credit amount. 52 for all but "Olthoi", which have 68 character.AvailableSkillCredits = cg.HeritageGroups[(int)character.Heritage].SkillCredits; uint numOfSkills = reader.ReadUInt32(); Skill skill; SkillStatus skillStatus; SkillCostAttribute skillCost; for (uint i = 0; i < numOfSkills; i++) { skill = (Skill)i; skillCost = skill.GetCost(); skillStatus = (SkillStatus)reader.ReadUInt32(); if (skillStatus == SkillStatus.Specialized) { character.TrainSkill(skill, skillCost.TrainingCost); character.SpecializeSkill(skill, skillCost.SpecializationCost); // oddly enough, specialized skills don't get any free ranks like trained do } if (skillStatus == SkillStatus.Trained) { character.TrainSkill(skill, skillCost.TrainingCost); character.AceObjectPropertiesSkills[skill].Ranks = 5; character.AceObjectPropertiesSkills[skill].ExperienceSpent = 526; } if (skillCost != null && skillStatus == SkillStatus.Untrained) { character.UntrainSkill(skill, skillCost.TrainingCost); } } // grant starter items based on skills var starterGearConfig = StarterGearFactory.GetStarterGearConfiguration(); List <uint> grantedItems = new List <uint>(); foreach (var skillGear in starterGearConfig.Skills) { var charSkill = character.AceObjectPropertiesSkills[(Skill)skillGear.SkillId]; if (charSkill.Status == SkillStatus.Trained || charSkill.Status == SkillStatus.Specialized) { foreach (var item in skillGear.Gear) { if (grantedItems.Contains(item.WeenieId)) { var existingItem = character.Inventory.Values.FirstOrDefault(i => i.WeenieClassId == item.WeenieId); if ((existingItem?.MaxStackSize ?? 1) <= 1) { continue; } existingItem.StackSize += item.StackSize; continue; } var loot = (AceObject)DatabaseManager.World.GetAceObjectByWeenie(item.WeenieId).Clone(GuidManager.NewItemGuid().Full); loot.Placement = 0; loot.ContainerIID = id; loot.StackSize = item.StackSize > 1 ? (ushort?)item.StackSize : null; character.Inventory.Add(new ObjectGuid(loot.AceObjectId), loot); grantedItems.Add(item.WeenieId); } var heritageLoot = skillGear.Heritage.FirstOrDefault(sh => sh.HeritageId == character.Heritage); if (heritageLoot != null) { foreach (var item in heritageLoot.Gear) { if (grantedItems.Contains(item.WeenieId)) { var existingItem = character.Inventory.Values.FirstOrDefault(i => i.WeenieClassId == item.WeenieId); if ((existingItem?.MaxStackSize ?? 1) <= 1) { continue; } existingItem.StackSize += item.StackSize; continue; } var loot = (AceObject)DatabaseManager.World.GetAceObjectByWeenie(item.WeenieId).Clone(GuidManager.NewItemGuid().Full); loot.Placement = 0; loot.ContainerIID = id; loot.StackSize = item.StackSize > 1 ? (ushort?)item.StackSize : null; character.Inventory.Add(new ObjectGuid(loot.AceObjectId), loot); grantedItems.Add(item.WeenieId); } } foreach (var spell in skillGear.Spells) { character.SpellIdProperties.Add(new AceObjectPropertiesSpell() { AceObjectId = id, SpellId = spell.SpellId }); } } } character.Name = reader.ReadString16L(); character.DisplayName = character.Name; // unsure // Index used to determine the starting location uint startArea = reader.ReadUInt32(); character.IsAdmin = Convert.ToBoolean(reader.ReadUInt32()); character.IsEnvoy = Convert.ToBoolean(reader.ReadUInt32()); DatabaseManager.Shard.IsCharacterNameAvailable(character.Name, ((bool isAvailable) => { if (!isAvailable) { SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.NameInUse); return; } character.SubscriptionId = session.SubscriptionId; CharacterCreateSetDefaultCharacterOptions(character); CharacterCreateSetDefaultCharacterPositions(character, startArea); // We must await here -- DatabaseManager.Shard.SaveObject(character, ((bool saveSuccess) => { if (!saveSuccess) { SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.DatabaseDown); return; } // DatabaseManager.Shard.SaveCharacterOptions(character); // DatabaseManager.Shard.InitCharacterPositions(character); var guid = new ObjectGuid(character.AceObjectId); session.AccountCharacters.Add(new CachedCharacter(guid, (byte)session.AccountCharacters.Count, character.Name, 0)); SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.Ok, guid, character.Name); })); })); }