Beispiel #1
0
        public void GenerateNewFace()
        {
            var cg = DatManager.PortalDat.CharGen;

            if (!Heritage.HasValue)
            {
                if (!String.IsNullOrEmpty(HeritageGroup))
                {
                    HeritageGroup parsed = (HeritageGroup)Enum.Parse(typeof(HeritageGroup), HeritageGroup.Replace("'", ""));
                    if (parsed != 0)
                    {
                        Heritage = (int)parsed;
                    }
                }
            }

            if (!Gender.HasValue)
            {
                if (!String.IsNullOrEmpty(Sex))
                {
                    Gender parsed = (Gender)Enum.Parse(typeof(Gender), Sex);
                    if (parsed != 0)
                    {
                        Gender = (int)parsed;
                    }
                }
            }

            if (!Heritage.HasValue || !Gender.HasValue)
            {
                return;
            }

            SexCG sex = cg.HeritageGroups[(uint)Heritage].Genders[(int)Gender];

            PaletteBaseId = sex.BasePalette;

            Appearance appearance = new Appearance();

            appearance.HairStyle = 1;
            appearance.HairColor = 1;
            appearance.HairHue   = 1;

            appearance.EyeColor = 1;
            appearance.Eyes     = 1;

            appearance.Mouth = 1;
            appearance.Nose  = 1;

            appearance.SkinHue = 1;

            // Get the hair first, because we need to know if you're bald, and that's the name of that tune!
            int    size = sex.HairStyleList.Count / 3; // Why divide by 3 you ask? Because AC runtime generated characters didn't have much range in hairstyles.
            Random rand = new Random();

            appearance.HairStyle = (uint)rand.Next(size);

            HairStyleCG hairstyle = sex.HairStyleList[Convert.ToInt32(appearance.HairStyle)];
            bool        isBald    = hairstyle.Bald;

            size = sex.HairColorList.Count;
            appearance.HairColor = (uint)rand.Next(size);
            appearance.HairHue   = rand.NextDouble();

            size = sex.EyeColorList.Count;
            appearance.EyeColor = (uint)rand.Next(size);
            size            = sex.EyeStripList.Count;
            appearance.Eyes = (uint)rand.Next(size);

            size             = sex.MouthStripList.Count;
            appearance.Mouth = (uint)rand.Next(size);

            size            = sex.NoseStripList.Count;
            appearance.Nose = (uint)rand.Next(size);

            appearance.SkinHue = rand.NextDouble();

            //// 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;

            if (!EyesTextureDID.HasValue)
            {
                EyesTextureDID = sex.GetEyeTexture(appearance.Eyes, isBald);
            }
            if (!DefaultEyesTextureDID.HasValue)
            {
                DefaultEyesTextureDID = sex.GetDefaultEyeTexture(appearance.Eyes, isBald);
            }
            if (!NoseTextureDID.HasValue)
            {
                NoseTextureDID = sex.GetNoseTexture(appearance.Nose);
            }
            if (!DefaultNoseTextureDID.HasValue)
            {
                DefaultNoseTextureDID = sex.GetDefaultNoseTexture(appearance.Nose);
            }
            if (!MouthTextureDID.HasValue)
            {
                MouthTextureDID = sex.GetMouthTexture(appearance.Mouth);
            }
            if (!DefaultMouthTextureDID.HasValue)
            {
                DefaultMouthTextureDID = sex.GetDefaultMouthTexture(appearance.Mouth);
            }
            if (!HairTextureDID.HasValue)
            {
                HairTextureDID = sex.GetHairTexture(appearance.HairStyle);
            }
            if (!DefaultHairTextureDID.HasValue)
            {
                DefaultHairTextureDID = sex.GetDefaultHairTexture(appearance.HairStyle);
            }
            if (!HeadObjectDID.HasValue)
            {
                HeadObjectDID = 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
            var skinPalSet = DatManager.PortalDat.ReadFromDat <PaletteSet>(sex.SkinPalSet);

            if (!SkinPaletteDID.HasValue)
            {
                SkinPaletteDID = 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
            var hairPalSet = DatManager.PortalDat.ReadFromDat <PaletteSet>(sex.HairColorList[Convert.ToInt32(appearance.HairColor)]);

            if (!HairPaletteDID.HasValue)
            {
                HairPaletteDID = hairPalSet.GetPaletteID(appearance.HairHue);
            }

            // Eye Color
            if (!EyesPaletteDID.HasValue)
            {
                EyesPaletteDID = sex.EyeColorList[Convert.ToInt32(appearance.EyeColor)];
            }
        }
        private static void CharacterCreateEx(ClientMessage message, Session session, uint id)
        {
            var          cg        = DatManager.PortalDat.CharGen;
            var          reader    = message.Payload;
            AceCharacter character = new AceCharacter(id);

            reader.Skip(4);   /* Unknown constant (1) */
            character.Heritage = (int)reader.ReadUInt32();

            // Disable OlthoiPlay characters for now. They're not implemented yet.
            // FIXME: Restore OlthoiPlay characters when properly handled.
            if (character.Heritage == (int)HeritageGroup.Olthoi || character.Heritage == (int)HeritageGroup.OlthoiAcid)
            {
                SendCharacterCreateResponse(session, CharacterGenerationVerificationResponse.Pending);
                return;
            }

            character.HeritageGroup = cg.HeritageGroups[(uint)character.Heritage].Name;
            character.Gender        = (int)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[(uint)character.Heritage].Genders[(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
            var skinPalSet = DatManager.PortalDat.ReadFromDat <PaletteSet>(sex.SkinPalSet);

            character.SkinPalette = skinPalSet.GetPaletteID(appearance.SkinHue);
            character.Shade       = appearance.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(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
            {
                var hat = GetClothingObject(id, sex.GetHeadgearWeenie(appearance.HeadgearStyle), appearance.HeadgearColor, appearance.HeadgearHue);
                if (hat != null)
                {
                    character.WieldedItems.Add(new ObjectGuid(hat.AceObjectId), hat);
                }
                else
                {
                    CreateIOU(character, sex.GetHeadgearWeenie(appearance.HeadgearStyle));
                }
            }

            var shirt = GetClothingObject(id, sex.GetShirtWeenie(appearance.ShirtStyle), appearance.ShirtColor, appearance.ShirtHue);

            if (shirt != null)
            {
                character.WieldedItems.Add(new ObjectGuid(shirt.AceObjectId), shirt);
            }
            else
            {
                CreateIOU(character, sex.GetShirtWeenie(appearance.ShirtStyle));
            }

            var pants = GetClothingObject(id, sex.GetPantsWeenie(appearance.PantsStyle), appearance.PantsColor, appearance.PantsHue);

            if (pants != null)
            {
                character.WieldedItems.Add(new ObjectGuid(pants.AceObjectId), pants);
            }
            else
            {
                CreateIOU(character, sex.GetPantsWeenie(appearance.PantsStyle));
            }

            var shoes = GetClothingObject(id, sex.GetFootwearWeenie(appearance.FootwearStyle), appearance.FootwearColor, appearance.FootwearHue);

            if (shoes != null)
            {
                character.WieldedItems.Add(new ObjectGuid(shoes.AceObjectId), shoes);
            }
            else
            {
                CreateIOU(character, sex.GetFootwearWeenie(appearance.FootwearStyle));
            }

            // Profession (Adventurer, Bow Hunter, etc)
            // TODO - Add this title to the available titles for this character.
            var    templateOption = reader.ReadInt32();
            string templateName   = cg.HeritageGroups[(uint)character.Heritage].Templates[templateOption].Name;

            character.Title              = templateName;
            character.Template           = templateName;
            character.CharacterTitleId   = (int)cg.HeritageGroups[(uint)character.Heritage].Templates[templateOption].Title;
            character.NumCharacterTitles = 1;

            // stats
            uint totalAttributeCredits = cg.HeritageGroups[(uint)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 = (int)cg.HeritageGroups[(uint)character.Heritage].SkillCredits;

            uint numOfSkills = reader.ReadUInt32();

            for (uint i = 0; i < numOfSkills; i++)
            {
                var skill       = (Skill)i;
                var skillCost   = skill.GetCost();
                var 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)
                    {
                        // Olthoi Spitter is a special case
                        if (character.Heritage == (int)HeritageGroup.OlthoiAcid)
                        {
                            character.SpellIdProperties.Add(new AceObjectPropertiesSpell()
                            {
                                AceObjectId = id, SpellId = spell.SpellId
                            });
                            // Continue to next spell as Olthoi spells do not have the SpecializedOnly field
                            continue;
                        }

                        if (charSkill.Status == SkillStatus.Trained && spell.SpecializedOnly == false)
                        {
                            character.SpellIdProperties.Add(new AceObjectPropertiesSpell()
                            {
                                AceObjectId = id, SpellId = spell.SpellId
                            });
                        }
                        else if (charSkill.Status == SkillStatus.Specialized)
                        {
                            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.AccountId = session.Id;

                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);
                }));
            }));
        }
Beispiel #3
0
        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

            // currently not used
            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.AccountId = session.Id;

                CharacterCreateSetDefaultCharacterOptions(character);
                CharacterCreateSetDefaultCharacterPositions(character);

                // 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);
                }));
            }));
        }
Beispiel #4
0
        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
            {
                // TODO - Create Inventory Item
                uint          headgearWeenie = sex.GetHeadgearWeenie(appearance.HeadgearStyle);
                ClothingTable headCT         = ClothingTable.ReadFromDat(sex.GetHeadgearClothingTable(appearance.HeadgearStyle));
                uint          headgearIconId = headCT.GetIcon(appearance.HeadgearColor);
                // TODO - Apply the chosen color palette(s) (read from the ClothingTable)
            }

            // TODO - Create Inventory Item
            uint          shirtWeenie = sex.GetShirtWeenie(appearance.ShirtStyle);
            ClothingTable shirtCT     = ClothingTable.ReadFromDat(sex.GetShirtClothingTable(appearance.ShirtStyle));
            uint          shirtIconId = shirtCT.GetIcon(appearance.ShirtColor);
            // TODO - Apply the chosen color palette(s) (read from the ClothingTable)

            // TODO - Create Inventory Item
            uint          pantsWeenie = sex.GetPantsWeenie(appearance.PantsStyle);
            ClothingTable pantsCT     = ClothingTable.ReadFromDat(sex.GetPantsClothingTable(appearance.PantsStyle));
            uint          pantsIconId = pantsCT.GetIcon(appearance.PantsColor);
            // TODO - Apply the chosen color palette(s) (read from the ClothingTable)

            // TODO - Create Inventory Item
            uint          footwearWeenie = sex.GetFootwearWeenie(appearance.FootwearStyle);
            ClothingTable footwearCT     = ClothingTable.ReadFromDat(sex.GetFootwearClothingTable(appearance.FootwearStyle));
            uint          footwearIconId = footwearCT.GetIcon(appearance.FootwearColor);
            // TODO - Apply the chosen color palette(s) (read from the ClothingTable)

            // 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();
                // character.TrainSkill(skill, skillCost.TrainingCost);
                // if (skillStatus == SkillStatus.Specialized)
                //     character.SpecializeSkill(skill, skillCost.SpecializationCost);
                if (skillStatus == SkillStatus.Specialized)
                {
                    character.TrainSkill(skill, skillCost.TrainingCost);
                    character.SpecializeSkill(skill, skillCost.SpecializationCost);
                }
                if (skillStatus == SkillStatus.Trained)
                {
                    character.TrainSkill(skill, skillCost.TrainingCost);
                    // TODO : Train to rank 5, the training "bonus", total of 526 XP
                }
                if (skillCost != null && skillStatus == SkillStatus.Untrained)
                {
                    character.UntrainSkill(skill, skillCost.TrainingCost);
                }
            }

            character.Name        = reader.ReadString16L();
            character.DisplayName = character.Name; // unsure

            // currently not used
            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.AccountId = session.Id;

                CharacterCreateSetDefaultCharacterOptions(character);
                CharacterCreateSetDefaultCharacterPositions(character);

                // 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);
                }));
            }));
        }