Пример #1
0
        static void Main(string[] args)
        {
            Utility.WriteExeDetails();

            Utility.LoadStrings(root);

            Utility.LoadClientNpcs(root);

            Utility.LoadItems(root);

            Utility.LoadSkills(root);

            Utility.LoadSkillLearns(root);

            Utility.LoadUltraSkills(root);



            string outputPath = Path.Combine(root, @".\output");

            if (!Directory.Exists(outputPath))
            {
                Directory.CreateDirectory(outputPath);
            }

            var outputFile = new SkillData();

            var settings = new XmlWriterSettings()
            {
                CheckCharacters = false,
                CloseOutput     = false,
                Indent          = true,
                IndentChars     = "\t",
                NewLineChars    = "\n",
                Encoding        = new UTF8Encoding(false)
            };

            #region Pet UltraSkill parsing

            PetSkillTemplates petTemplates = new PetSkillTemplates();
            List <PetSkill>   ultraSkills  = new List <PetSkill>();

            foreach (var ultra in Utility.UltraSkillIndex.UltraSkillList)
            {
                PetSkill petSkill = new PetSkill();
                petSkill.skill_id = Utility.SkillIndex[ultra.ultra_skill].id;
                if (String.Compare("Light_Summon_MagmaElemental_G1", ultra.pet_name) == 0)
                {
                    ultra.pet_name = "Dark_Summon_MagmaElemental_G1";
                }
                else if (String.Compare("Dark_Summon_TempestElemental_G1", ultra.pet_name) == 0)
                {
                    ultra.pet_name = "Light_Summon_TempestElemental_G1";
                }
                petSkill.pet_id = Utility.ClientNpcIndex[ultra.pet_name];
                if (petSkill.pet_id == -1)
                {
                    petSkill.missing_pet_id = ultra.pet_name;
                    petSkill.pet_id         = 0;
                }
                ClientSkill skill = Utility.SkillIndex[ultra.order_skill];
                if (skill != null)
                {
                    petSkill.order_skill = Utility.SkillIndex[ultra.order_skill].id;
                }
                //else
                //    petSkill.missing_order_skill = ultra.order_skill;
                ultraSkills.Add(petSkill);
            }

            petTemplates.SkillList = ultraSkills.OrderBy(s => s.skill_id).ToList();

            try
            {
                using (var fs = new FileStream(Path.Combine(outputPath, "pet_skills.xml"),
                                               FileMode.Create, FileAccess.Write))
                    using (var writer = XmlWriter.Create(fs, settings))
                    {
                        XmlSerializer ser = new XmlSerializer(typeof(PetSkillTemplates));
                        ser.Serialize(writer, petTemplates);
                    }
            }
            catch (Exception ex)
            {
                Debug.Print(ex.ToString());
            }

            #endregion

            /* Old
             *          var skillsetEx = new Dictionary<int, int>() {
             *  { 1566, 1564 }, { 1586, 1564 }, { 2210, 1564 }, { 1574, 1564 }, { 1575, 1564 }, { 1576, 1564 },
             *  { 1596, 1564 }, { 1564, 1564 }, { 1565, 1564 }, { 1590, 1564 },
             *  { 1554, 1554 }, { 1588, 1554 },
             *  { 969, 969 }, { 970, 969 }, { 971, 969 }, { 989, 969 }, { 2181, 969 }, { 1220, 969 },
             *  { 1221, 969 }, { 1222, 969 }, { 1267, 969 }, { 1318, 969 }, { 1319, 969 }, { 1342, 969 },
             *  { 2183, 969 },
             *  { 1105, 1105 }, { 1107, 1105 }, { 1108, 1105 }, { 1187, 1105 }, { 1112, 1105 }, { 1186, 1105 }
             *          };
             */

            // skillset_exception == client_skills.delay_id (All client skills have delay id but skills on same cooldown/exception list have the same delay id)
            var skillsetEx2 = new Dictionary <int, int>()
            {
                { 1564, 2 }, { 1565, 2 }, { 1566, 2 }, { 1574, 2 }, { 1575, 2 }, { 1586, 2 }, { 1590, 2 }, { 1596, 2 }, { 2210, 2 },
                { 1105, 4 }, { 1107, 4 }, { 1108, 4 }, { 1112, 4 }, { 1186, 4 }, { 1187, 4 },
                { 969, 5 }, { 970, 5 }, { 971, 5 }, { 989, 5 }, { 1220, 5 }, { 1221, 5 }, { 1222, 5 }, { 1267, 5 }, { 1318, 5 }, { 1319, 5 }, { 1342, 5 }, { 2181, 5 }, { 2183, 5 },
                { 540, 6 }, { 1636, 6 }, { 1637, 6 }, { 1685, 6 }, { 1708, 6 }, { 1781, 6 }, { 1782, 6 }, { 1935, 6 }, { 2008, 6 }, { 2062, 6 }, { 2231, 6 }, { 2232, 6 }, { 8345, 6 },
                { 173, 7 }, { 524, 7 },
                { 1554, 8 }, { 1555, 8 }, { 1556, 8 }, { 1587, 8 }, { 1588, 8 },
                { 955, 9 }, { 956, 9 }, { 957, 9 }, { 986, 9 }, { 1230, 9 }, { 2159, 9 }, { 2165, 9 }, { 2178, 9 },
                { 559, 175 }, { 582, 175 }, { 851, 175 }, { 858, 175 }, { 948, 175 },
                { 646, 2005 }, { 684, 2005 }, { 693, 2005 }, { 694, 2005 }, { 776, 2005 }, { 777, 2005 }, { 786, 2005 }, { 2075, 2005 }
            };


            // Limit skills with same delay_id to a max # of occurances
            var skillset_maxoccurs = new Dictionary <int, int>()
            {
                { 646, 2 }, { 684, 2 }, { 693, 2 }, { 776, 2 }, { 777, 2 }, { 786, 2 }, { 2075, 2 }
            };

            var delayIdOverrides = new Dictionary <int, int>()
            {
                { 11885, 11885 }, { 11886, 11885 }, { 11887, 11885 }, { 11888, 11885 }, { 11889, 11885 },
                { 11890, 11890 }, { 11891, 11890 }, { 11892, 11890 }, { 11893, 11890 }, { 11894, 11890 }
            };

            //  skills which have 0 cast time or cooldown is smaller and which exclude each other
            var delayIdsForSkills = new Dictionary <int, int>()
            {
                { 1178, 1178 }, { 2148, 1178 }, { 1177, 1178 }, { 2147, 1178 }
            };

            #region Finding the chainDelayIdsInclude

            //Dictionary<int, int> diffDelays = Utility.SkillIndex.SkillList.ToDictionary(s => s.id, s => s.delay_id);
            //Dictionary<int, bool> diffCount = diffDelays.Values.GroupBy(x => x).ToDictionary(x => x.Key, g => g.Count() > 1);

            /*
             * //test lookups
             * int skill_id = 2075; //known to have listed exception
             * int delay_id = 2005; //delay id associated with client skill
             *
             * if (diffCount.ContainsKey(delay_id) && diffCount[delay_id]) {
             *  int skill_exception_test = diffDelays[skill_id];
             * }
             */

            /*
             * var diffDelays = Utility.SkillIndex.SkillList.Where(s => s.delay_id > 0 &&
             *                                                  s.sub_type == SkillSubType.buff &&
             *                                                  s.chain_category_level > 0)
             *                                           .ToLookup(s => s.delay_id, s => s);
             * var chainCategories = new Dictionary<string, List<ClientSkill>>();
             *
             * foreach (var group in diffDelays) {
             *  var skills = diffDelays[group.Key];
             *  string[] chains = skills.Select(s => s.chain_category_priority).Distinct().ToArray();
             *  string categoryName = chains.Where(s => s != null).FirstOrDefault();
             *  if (categoryName == null) {
             *      chains = skills.Select(s => s.prechain_skillname).Distinct().ToArray();
             *      categoryName = chains.Where(s => s != null).FirstOrDefault();
             *  }
             *
             *  if (categoryName == null)
             *      continue;
             *
             *  if (chainCategories.ContainsKey(categoryName)) {
             *      Debug.Print("Different delay id for chain: {0}", categoryName);
             *      chainCategories[categoryName].AddRange(skills.ToList());
             *  } else {
             *      chainCategories.Add(categoryName, skills.ToList());
             *  }
             * }
             *
             * chainCategories = chainCategories.Where(pair => pair.Value.Count > 1)
             *                               .OrderBy(p => p.Value.First().delay_id)
             *                               .ToDictionary(pair => pair.Key, pair => pair.Value);
             *
             * // StringBuilder sb = new StringBuilder();
             * foreach (var pair in chainCategories) {
             *  // sb.AppendFormat("SkillSet by delay: {0}\r\n", pair.Key);
             *  foreach (var skill in pair.Value) {
             *      // string desc = Utility.StringIndex.GetString(skill.desc);
             *      if (skill.casting_delay == 0) {
             *          int dur = 0;
             *          if (skill.effect1_reserved_cond1 == null ||
             *              skill.effect1_reserved_cond1_prob2 == 100 &&
             *              skill.effect1_reserved_cond1 == "EveryHit")
             *              dur = Math.Max(dur, skill.effect1_remain2);
             *          if (skill.effect2_reserved_cond1 == null ||
             *              skill.effect2_reserved_cond1_prob2 == 100 &&
             *              skill.effect2_reserved_cond1 == "EveryHit")
             *              dur = Math.Max(dur, skill.effect2_remain2);
             *          if (skill.effect3_reserved_cond1 == null ||
             *              skill.effect3_reserved_cond1_prob2 == 100 &&
             *              skill.effect3_reserved_cond1 == "EveryHit")
             *              dur = Math.Max(dur, skill.effect3_remain2);
             *          if (skill.effect4_reserved_cond1 == null ||
             *              skill.effect4_reserved_cond1_prob2 == 100 &&
             *              skill.effect4_reserved_cond1 == "EveryHit")
             *              dur = Math.Max(dur, skill.effect4_remain2);
             *          skill.casting_delay = dur;
             *      }
             *
             *      if (skill.casting_delay == 0 || skill.casting_delay / 1000 > skill.delay_time / 100) {
             *          sb.AppendFormat("\tSkill: id={0}; name={1}; delayId={2}; r10={3}/{4}/{5}/{6}; delay={7}; cooldown={8}\r\n",
             *                          skill.id, desc,
             *                          skill.delay_id, skill.effect1_reserved10, skill.effect2_reserved10,
             *                          skill.effect3_reserved10, skill.effect4_reserved10,
             *                          skill.casting_delay, skill.delay_time / 100);
             *      }
             *  }
             * }
             */

            #endregion

            Dictionary <ClientSkill, List <ClientEffect> > list     = new Dictionary <ClientSkill, List <ClientEffect> >();
            Dictionary <string, HashSet <string> >         statData = new Dictionary <string, HashSet <string> >();

            const EffectType filterEffect = EffectType.None;
            const Stat       filterStat   = Stat.None;
            StringBuilder    sb           = new StringBuilder();

            //for (var filterEffect = EffectType.None + 1;
            //     filterEffect <= EffectType.XPBoost; filterEffect++) {

            foreach (var sk in Utility.SkillIndex.SkillList)
            {
                var template = new SkillTemplate();
                template.skill_id = sk.id;
                template.name     = sk.desc;

                // Check Skill Strings for Name ID
                template.nameId = Utility.StringIndex[sk.desc.ToUpper()] * 2 + 1;

                // SKill Name ID is Not in Skill Strings, Check Item Stings
                if (template.nameId == -1)
                {
                    template.nameId = Utility.StringIndex[sk.desc.ToUpper()] * 2 + 1;
                }

                // SKill Name ID is Not in Item Strings, Check Main Strings
                if (template.nameId == -1)
                {
                    template.nameId = Utility.StringIndex[sk.desc.ToUpper()] * 2 + 1;
                }

                // SKill Name ID is Not in General Strings, Check UI Strings
                if (template.nameId == -1)
                {
                    template.nameId = Utility.StringIndex[sk.desc.ToUpper()] * 2 + 1;
                }

                string stack;
                int    level = -1;
                if (level == -1)
                {
                    level = Utility.GetSkillLevelFromName(sk.name, out stack);
                }
                //if (level == -1 && !String.IsNullOrEmpty(sk.skillicon_name))
                //    level = Utility.GetSkillLevelFromName(sk.skillicon_name, out stack);
                if (level == -1)
                {
                    level = 1;
                }
                template.lvl   = level;
                level          = Utility.GetSkillLevelFromName(sk.desc, out stack);
                template.stack = stack.ToUpper();
                // some overides for stack.
                if (sk.id == 251 || sk.id == 258 || sk.id == 385)
                {
                    template.stack = "SKILL_GL_251_258_385";
                }


                template.skilltype    = (skillType)sk.type;
                template.skillsubtype = (skillSubType)sk.sub_type;
                template.tslot        = (TargetSlot)sk.target_slot;
                template.tslot_level  = sk.target_slot_level;
                template.activation   = (activationAttribute)sk.activation_attribute;
                template.cooldown     = sk.delay_time / 100;
                template.cancel_rate  = sk.cancel_rate;
                if (sk.casting_delay > 0)
                {
                    template.duration = sk.casting_delay;
                }
                template.pvp_duration     = sk.pvp_remain_time_ratio;
                template.chain_skill_prob = sk.chain_skill_prob1 == 0 && sk.chain_skill_prob2 != 0 ? sk.chain_skill_prob2 : 0;
                //template.dispel_category = sk.dispel_category.ToString().ToUpper();
                template.dispel_category = sk.getDispellCategory();
                //template.dispel_level = sk.required_dispel_level;
                //template.delay_id = sk.delay_id;

                if (!String.IsNullOrEmpty(sk.penalty_skill_succ))
                {
                    var penaltySkill = Utility.SkillIndex[sk.penalty_skill_succ];
                    if (penaltySkill == null)
                    {
                        Debug.Print("Missing penalty skill: {0}", sk.penalty_skill_succ);
                    }
                    else
                    {
                        template.penalty_skill_id = penaltySkill.id;
                    }
                }

                if (sk.change_stance != Stance.none)
                {
                    template.stance = true;
                }
                template.pvp_damage = sk.pvp_damage_ratio;

                if (sk.first_target != FirstTarget.None)
                {
                    //template.setproperties = new Properties();
                    var properties = new Properties();

                    properties.firsttarget       = new FirstTargetProperty();
                    properties.firsttarget.value = (FirstTargetAttribute)sk.first_target;
                    if (sk.first_target_valid_distance > 0)
                    {
                        properties.firsttargetrange                = new FirstTargetRangeProperty();
                        properties.firsttargetrange.value          = sk.first_target_valid_distance;
                        properties.firsttargetrange.valueSpecified = true;
                    }
                    //else if (sk.first_target == FirstTarget.Target)
                    //	properties.firsttargetrange = new FirstTargetRangeProperty();

                    if (sk.target_range != TargetRange.None)
                    {
                        properties.target_range = new TargetRangeProperty();
                        if (sk.target_range_opt1 != 0)
                        {
                            properties.target_range.distance          = sk.target_range_opt1;
                            properties.target_range.distanceSpecified = true;
                        }
                        if (sk.target_maxcount != 0)
                        {
                            properties.target_range.maxcount          = sk.target_maxcount;
                            properties.target_range.maxcountSpecified = true;
                        }
                        if (sk.target_range != TargetRange.None)
                        {
                            properties.target_range.value = (TargetRangeAttribute)sk.target_range;
                        }
                    }


                    if (sk.target_relation_restriction != RelationRestriction.None)
                    {
                        properties.targetrelation       = new TargetRelationProperty();
                        properties.targetrelation.value = (TargetRelationAttribute)sk.target_relation_restriction;
                    }


                    if (sk.target_species_restriction != SpeciesRestriction.None)
                    {
                        properties.targetspecies       = new TargetSpeciesProperty();
                        properties.targetspecies.value = (TargetSpeciesAttribute)sk.target_species_restriction;
                    }


                    var           importUtil = Utility <ClientSkill> .Instance;
                    List <string> states     = new List <string>();
                    importUtil.Export <string>(sk, "target_valid_status", states);
                    TargetState state = 0;
                    foreach (string s in states)
                    {
                        try
                        {
                            TargetState s1 = (TargetState)Enum.Parse(typeof(TargetState), s, true);
                            state |= s1;
                        }
                        catch (Exception e) { }
                    }
                    //if (state != TargetState.NONE)
                    //{
                    //    states = states.ConvertAll(s => s.ToUpper());
                    //    List<TargetStatusProperty> statusProperties = states.ConvertAll(s => { TargetStatusProperty t = new TargetStatusProperty(); t.value = s; return t; });
                    //    properties.targetstatus = statusProperties;
                    //}

                    if (sk.add_wpn_range)
                    {
                        template.initproperties = new Properties();
                        template.initproperties.addweaponrange = new AddWeaponRangeProperty();
                    }

                    template.setproperties = properties;
                }

                template.useconditions   = new Conditions();
                template.startconditions = new Conditions();
                template.actions         = new Actions();

                List <Condition> useList = new List <Condition>();
                List <Condition> useEquipmentConditionsList = new List <Condition>();
                List <Condition> startList  = new List <Condition>();
                List <Action>    actionList = new List <Action>();

                //TargetFlyingCondition targetflyingcondition = null;
                TargetCondition targetcondition = null;

                /*
                 * if (sk.target_species_restriction != SpeciesRestriction.None && sk.target_species_restriction != SpeciesRestriction.All)
                 * {
                 *  targetcondition = new TargetCondition((FlyingRestriction)sk.target_species_restriction);
                 *  startList.Add(targetcondition);
                 * }*/

                /*
                 * if (sk.required_leftweapon != LeftWeapon.None)
                 * {
                 *  useEquipmentConditionsList.Add(new ArmorCondition(sk.required_leftweapon.ToString().ToUpper()));
                 * }
                 */

                // Periodic Actions
                if (sk.cost_checktime > 0 || sk.cost_toggle > 0)
                {
                    PeriodicAction periodicAction = new PeriodicAction();
                    CostType       parameter      = sk.cost_checktime_parameter;

                    if (parameter == CostType.NONE)
                    {
                        parameter = sk.cost_parameter;
                    }

                    if (parameter == CostType.MP || parameter == CostType.MP_RATIO)
                    {
                        var mpUseAction = new MpUseAction();
                        periodicAction.checktime = sk.cost_time;
                        mpUseAction.percent      = parameter == CostType.MP_RATIO;

                        if (sk.cost_checktime_lv > 0 || sk.cost_checktime > 0)
                        {
                            mpUseAction.value = sk.cost_checktime;
                            mpUseAction.delta = sk.cost_checktime_lv;
                        }
                        else if (sk.cost_toggle_lv > 0 || sk.cost_toggle > 0)
                        {
                            mpUseAction.value = sk.cost_toggle;
                            mpUseAction.delta = sk.cost_toggle_lv;
                        }
                        periodicAction.mpuse = mpUseAction;
                    }
                    else if (parameter == CostType.HP || parameter == CostType.HP_RATIO)
                    {
                        var hpUseAction = new HpUseAction();
                        periodicAction.checktime = sk.cost_time;
                        hpUseAction.percent      = parameter == CostType.HP_RATIO;

                        if (sk.cost_checktime_lv > 0 || sk.cost_checktime > 0)
                        {
                            hpUseAction.value = sk.cost_checktime;
                            hpUseAction.delta = sk.cost_checktime_lv;
                        }
                        else if (sk.cost_toggle_lv > 0 || sk.cost_toggle > 0)
                        {
                            hpUseAction.value = sk.cost_toggle;
                            hpUseAction.delta = sk.cost_toggle_lv;
                        }

                        periodicAction.hpuse = hpUseAction;
                    }

                    //template.periodicactions = periodicAction;
                }
                else
                {
                    // Non Periodic Actions
                    if (sk.cost_parameter != CostType.NONE && sk.cost_checktime == 0)
                    {
                        CostType parameter = sk.cost_checktime_parameter;

                        if (parameter == CostType.NONE)
                        {
                            parameter = sk.cost_parameter;
                        }

                        if (parameter == CostType.MP || parameter == CostType.MP_RATIO)
                        {
                            var mpUseAction = new MpUseAction();
                            mpUseAction.percent = parameter == CostType.MP_RATIO;

                            if (sk.cost_end > 0 || sk.cost_end_lv > 0)
                            {
                                mpUseAction.value = sk.cost_end;
                                mpUseAction.delta = sk.cost_end_lv;
                                actionList.Add(mpUseAction);
                            }
                            else if (sk.cost_toggle_lv > 0 || sk.cost_toggle > 0)
                            {
                                mpUseAction.value = sk.cost_toggle;
                                mpUseAction.delta = sk.cost_toggle_lv;
                                actionList.Add(mpUseAction);
                            }
                        }
                        else if (parameter == CostType.HP || parameter == CostType.HP_RATIO)
                        {
                            var hpUseAction = new HpUseAction();
                            hpUseAction.percent = parameter == CostType.HP_RATIO;

                            if (sk.cost_end > 0 || sk.cost_end_lv > 0)
                            {
                                hpUseAction.value = sk.cost_end;
                                hpUseAction.delta = sk.cost_end_lv;
                                actionList.Add(hpUseAction);
                            }
                            else if (sk.cost_toggle_lv > 0 || sk.cost_toggle > 0)
                            {
                                hpUseAction.value = sk.cost_toggle;
                                hpUseAction.delta = sk.cost_toggle_lv;
                                actionList.Add(hpUseAction);
                            }
                        }
                    }
                }

                if (sk.cost_end != 0)
                {
                    startList.Add(new MpCondition(sk.cost_end, sk.cost_end_lv));
                }

                /*
                 * if (sk.required_leftweapon != LeftWeapon.None)
                 * {
                 *  startList.Add(new ArmorCondition(sk.required_leftweapon.ToString().ToUpper()));
                 * }*/

                if (sk.use_arrow != null && sk.use_arrow != "0")
                { // 3 arena skills have FX_pow, FX_HIT and weaponbody
                    startList.Add(new ArrowCheckCondition());
                }
                else
                {
                    if (sk.use_arrow_count != 0)
                    {
                        startList.Add(new ArrowCheckCondition());
                    }
                }

                if (sk.cost_dp > 0)
                {
                    startList.Add(new DpCondition(sk.cost_dp));
                    actionList.Add(new DpUseAction(sk.cost_dp, sk.cost_dp_lv));
                }

                /*
                 * string required_weapons = getRequiredWeapons(sk);
                 * if (required_weapons != null)
                 * {
                 *  startList.Add(new WeaponCondition(required_weapons));
                 * }
                 *
                 * // Target Flying Start Condition
                 * FlyRestriction restriction = (FlyRestriction)sk.target_flying_restriction;
                 * if (restriction != FlyRestriction.NONE)
                 * {
                 *  if (targetflyingcondition == null)
                 *      startList.Add(new TargetFlyingCondition(restriction));
                 *  else
                 *      targetflyingcondition.restriction = restriction;
                 * }
                 *
                 * if (sk.chain_category_name != null)
                 * {
                 *  ChainCondition chain = new ChainCondition();
                 *  chain.category = sk.chain_category_name.ToUpper();
                 *
                 *  if (sk.prechain_category_name != null) chain.precategory = sk.prechain_category_name.ToUpper();
                 *  if (sk.chain_time != null) chain.time = Int32.Parse(sk.chain_time);
                 *
                 *  startList.Add(chain);
                 * }
                 */

                // Self Flying Start Condition

                /*
                 * restriction = (FlyRestriction)sk.self_flying_restriction;
                 * if (restriction != FlyRestriction.NONE)
                 *  startList.Add(new SelfCondition(restriction));
                 *
                 * if (sk.nouse_combat_state == 1)
                 * {
                 *  startList.Add(new CombatCheckCondition());
                 * }
                 */

                if (sk.component != null)
                {
                    Item item = Utility.ItemIndex.GetItem(sk.component);
                    if (item == null)
                    {
                        Debug.Print("Missing item for skill {0}", sk.id);
                    }
                    else
                    {
                        actionList.Add(new ItemUseAction(item.id, sk.component_count));
                    }
                }

                if (!sk.move_casting)
                {
                    useList.Add(new PlayerMovedCondition(false));
                }

                #region Fill conditions and actions

                // Use Conditions
                if (useList.Count == 0)
                {
                    template.useconditions = null;
                }
                else
                {
                    template.useconditions.ConditionList = useList;
                }

                // Start Conditions
                if (startList.Count == 0)
                {
                    template.startconditions = null;
                }
                else
                {
                    template.startconditions.ConditionList = startList;
                }


                // Skill Actions
                if (actionList.Count == 0)
                {
                    template.actions = null;
                }
                else
                {
                    template.actions.ActionList = actionList;
                }

                #endregion

                if (skillsetEx2.ContainsKey(sk.id))
                {
                    template.skillset_exception = skillsetEx2[sk.id];
                }

                /*
                 * if (sk.delay_id != 0)
                 *  template.skillset_exception = diffCount.ContainsKey(sk.delay_id) && diffCount[sk.delay_id] == true ? diffDelays[sk.id] : 0;
                 */


                //if (delayIdsForSkills.ContainsKey(sk.id))
                //{
                //    template.delay_id = delayIdsForSkills[sk.id];
                //}
                //else if (delayIdOverrides.ContainsKey(sk.id))
                //{
                //    template.delay_id = delayIdOverrides[sk.id];
                //}

                string descrStr = sk.desc_long == null ? sk.desc : sk.desc_long;
                var    desc     = Utility.StringIndex.GetStringDescription(descrStr);
                string desc_2nd;
                if (!String.IsNullOrEmpty(sk.desc_long_2nd))
                {
                    desc_2nd = Utility.StringIndex.GetStringDescription(sk.desc_long_2nd).body;
                }

                if (sk.motion_name != ClientMotion.None)
                {
                    Motion motion = new Motion();
                    motion.name          = sk.motion_name != ClientMotion.None ? sk.motion_name.ToString().ToLower() : null;
                    motion.instant_skill = sk.instant_skill != null && sk.instant_skill == "1" ? true : false;
                    motion.speed         = sk.motion_play_speed != 0 && sk.motion_play_speed != 100 ? sk.motion_play_speed : 0;
                    template.motion      = motion;
                }


                #region Effect processing

                var utility = Utility <ClientSkill> .Instance;
                List <ClientEffect> effects = new List <ClientEffect>();
                utility.Export(sk, "effect", effects);

                var  validEffects = effects.Where(e => e.type != EffectType.None).ToArray();
                bool save         = validEffects.Where(e => e.type == filterEffect).Any();
                // var validEffects = effects.Where(e => e.changeStat != Stat.None).ToArray();
                // bool save = validEffects.Where(e => e.changeStat == filterStat).Any();

                string text = desc == null ? String.Empty : desc.body;
                int    idx  = 0;
                var    vars = (from v in Utility.GetVarStrings(text)
                               let parts = v.Split('.')
                                           let parsed = Int32.TryParse(parts[0].Remove(0, 1), out idx)
                                                        let name = parsed ? parts[1] : v
                                                                   let var = parsed ? parts[2] : String.Empty
                                                                             select new { Id = idx, Data = new StatData(name, var) })
                              .ToLookup(a => a.Id, a => a);

                if (save)
                {
                    sb.Append("\r\n");
                    sb.AppendFormat("---Skill Id = {0}, Name = '{1}' ---\r\n",
                                    sk.id, Utility.StringIndex.GetString(sk.desc));
                    if (desc == null)
                    {
                        sb.AppendFormat("NO DESCRIPTION\r\n");
                    }
                    else
                    {
                        sb.AppendFormat("{0}: {1}\r\n", desc.name, desc.body);
                    }
                    if (sk.desc_abnormal != null)
                    {
                        sb.AppendFormat("ABNORMAL: {0}\r\n", Utility.StringIndex.GetString(sk.desc_abnormal));
                    }
                }

                foreach (var eff in validEffects)
                {
                    EffectClass @class = (EffectClass)eff.type;

                    #region Overrides


                    #endregion

                    Type type = effectsAssembly.GetType(String.Format("{0}.{1}", typeof(Effect).Namespace,
                                                                      @class.ToString()));
                    Effect ourEffect = null;

                    if (save)
                    {
                        sb.AppendFormat("Data for {0}:\r\n", @class);
                    }

                    if (vars.Any())
                    {
                        if (vars.Contains(idx + 1))
                        {
                            foreach (var v in vars[idx + 1])
                            {
                                if (!statData.ContainsKey(v.Data.Name))
                                {
                                    statData.Add(v.Data.Name, new HashSet <string>());
                                }
                                statData[v.Data.Name].Add(v.Data.Var);
                                if (save)
                                {
                                    sb.AppendFormat("\tEffect = {0}, Var = {1}\r\n",
                                                    v.Data.Name, v.Data.Var);
                                }
                            }
                        }
                    }
                    if (save)
                    {
                        sb.AppendFormat("\tE{0} Reserved: ", eff.e);
                        using (TextWriter wr = new StringWriter(sb))
                        {
                            ObjectDumper.Write(eff.reserved, wr);
                            sb.Append("\r\n");
                            if (!String.IsNullOrEmpty(eff.reserved_cond1))
                            {
                                sb.AppendFormat("\tCondition = {0}, Prob. = ", eff.reserved_cond1);
                                ObjectDumper.Write(eff.reserved_cond1_prob, wr);
                                sb.Append("\r\n");
                            }
                            if (!String.IsNullOrEmpty(eff.reserved_cond2))
                            {
                                sb.AppendFormat("\tCondition = {0}, Prob. = ", eff.reserved_cond2);
                                ObjectDumper.Write(eff.reserved_cond2_prob, wr);
                                sb.Append("\r\n");
                            }
                        }
                    }

                    if (type == null)
                    {
                        string skillName = Utility.StringIndex.GetString(sk.desc);
                        Debug.Print("Effect {0} not handled (skillId={1}; name='{2}')", @class, sk.id, skillName);
                        continue;
                    }
                    else
                    {
                        ourEffect = (Effect)Activator.CreateInstance(type);
                    }

                    if (template.effects == null)
                    {
                        template.effects = new Effects();/*
                                                          * if (sk.skillicon_name != null)
                                                          * template.effects.food = sk.skillicon_name.EndsWith("_food"); */
                    }

                    eff.Skill    = sk;
                    eff.Template = template;
                    ourEffect.Import(eff, null);

                    template.effects.EffectList.Add(ourEffect);
                }

                #endregion

                //if (template.effects != null && template.effects.EffectList != null) {
                //    if (template.effects.EffectList.Count == 0)
                //        template.effects.EffectList = null;
                //    else if (template.activation != activationAttribute.PASSIVE) {
                //        foreach (var eff in template.effects.EffectList) {
                //            if (eff is DeformEffect || eff is PolymorphEffect || eff is ShapeChangeEffect ||
                //                eff is WeaponDualEffect) {
                //            } else
                //                eff.basiclvl = 0; // not used for active skills
                //        }
                //    }
                //}
                if (template.effects != null && template.effects.EffectList == null)
                {
                    template.effects = null;
                }

                outputFile.SkillList.Add(template);

                if (filterEffect != EffectType.None)
                {
                    string fileName = /*"stat_" + filterStat*/ filterEffect.ToString() + ".txt";
                    using (var fs = new FileStream(Path.Combine(outputPath, fileName), FileMode.Create,
                                                   FileAccess.Write))
                    {
                        using (TextWriter wr = new StreamWriter(fs))
                        {
                            wr.Write(sb.ToString());
                        }
                    }
                }

                sb.Length = 0;
            }

            try
            {
                using (var fs = new FileStream(Path.Combine(outputPath, "skill_templates.xml"),
                                               FileMode.Create, FileAccess.Write))
                    using (var writer = XmlWriter.Create(fs, settings))
                    {
                        XmlSerializer ser = new XmlSerializer(typeof(SkillData));
                        ser.UnknownAttribute += new XmlAttributeEventHandler(OnUnknownAttribute);
                        ser.UnknownElement   += new XmlElementEventHandler(OnUnknownElement);
                        ser.Serialize(writer, outputFile);
                    }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            Console.WriteLine("Process completed");
            Console.Read();
        }
Пример #2
0
        static void Main(string[] args)
        {
            Utility.WriteExeDetails();
            Console.WriteLine("Loading strings...");
            Utility.LoadStrings(root);

            Console.WriteLine("Loading NPCs...");
            Utility.LoadClientNpcs(root);

            Console.WriteLine("Loading items...");
            Utility.LoadItems(root);

            Console.WriteLine("Loading skills...");
            Utility.LoadSkills(root);

            StringBuilder sb1 = new StringBuilder();

            foreach (var sk in Utility.SkillIndex.SkillList)
            {
                sb1.AppendLine(sk.id.ToString());
            }

            Console.WriteLine("Loading Ultra Skills...");
            Utility.LoadUltraSkills(root);

            string outputPath = Path.Combine(root, @".\output");

            if (!Directory.Exists(outputPath))
            {
                Directory.CreateDirectory(outputPath);
            }

            var outputFile = new SkillData();

            var settings = new XmlWriterSettings()
            {
                CheckCharacters = false,
                CloseOutput     = false,
                Indent          = true,
                IndentChars     = "\t",
                NewLineChars    = "\n",
                Encoding        = new UTF8Encoding(false)
            };

            #region Pet UltraSkill parsing

            PetSkillTemplates petTemplates = new PetSkillTemplates();
            List <PetSkill>   ultraSkills  = new List <PetSkill>();

            foreach (var ultra in Utility.UltraSkillIndex.UltraSkillList)
            {
                PetSkill petSkill = new PetSkill();
                petSkill.skill_id = Utility.SkillIndex[ultra.ultra_skill].id;
                if (String.Compare("Light_Summon_MagmaElemental_G1", ultra.pet_name) == 0)
                {
                    ultra.pet_name = "Dark_Summon_MagmaElemental_G1";
                }
                else if (String.Compare("Dark_Summon_TempestElemental_G1", ultra.pet_name) == 0)
                {
                    ultra.pet_name = "Light_Summon_TempestElemental_G1";
                }
                petSkill.pet_id = Utility.ClientNpcIndex[ultra.pet_name];
                if (petSkill.pet_id == -1)
                {
                    petSkill.missing_pet_id = ultra.pet_name;
                    petSkill.pet_id         = 0;
                }
                ClientSkill skill = Utility.SkillIndex[ultra.order_skill];
                if (skill != null)
                {
                    petSkill.order_skill = Utility.SkillIndex[ultra.order_skill].id;
                }
                //else
                //    petSkill.missing_order_skill = ultra.order_skill;
                ultraSkills.Add(petSkill);
            }

            petTemplates.SkillList = ultraSkills.OrderBy(s => s.skill_id).ToList();

            try {
                using (var fs = new FileStream(Path.Combine(outputPath, "pet_skills.xml"),
                                               FileMode.Create, FileAccess.Write))
                    using (var writer = XmlWriter.Create(fs, settings)) {
                        XmlSerializer ser = new XmlSerializer(typeof(PetSkillTemplates));
                        ser.Serialize(writer, petTemplates);
                    }
            } catch (Exception ex) {
                Debug.Print(ex.ToString());
            }

            #endregion


            var skillsetEx = new Dictionary <int, int>()
            {
                { 1566, 1564 }, { 1586, 1564 }, { 2210, 1564 }, { 1574, 1564 }, { 1575, 1564 }, { 1576, 1564 },
                { 1596, 1564 }, { 1564, 1564 }, { 1565, 1564 }, { 1590, 1564 },
                { 1554, 1554 }, { 1588, 1554 },
                { 969, 969 }, { 970, 969 }, { 971, 969 }, { 989, 969 }, { 2181, 969 }, { 1220, 969 },
                { 1221, 969 }, { 1222, 969 }, { 1267, 969 }, { 1318, 969 }, { 1319, 969 }, { 1342, 969 },
                { 2183, 969 },
                { 1105, 1105 }, { 1107, 1105 }, { 1108, 1105 }, { 1187, 1105 }, { 1112, 1105 }, { 1186, 1105 }
            };

            var delayIdOverrides = new Dictionary <int, int>()
            {
                { 11885, 11885 }, { 11886, 11885 }, { 11887, 11885 }, { 11888, 11885 }, { 11889, 11885 },
                { 11890, 11890 }, { 11891, 11890 }, { 11892, 11890 }, { 11893, 11890 }, { 11894, 11890 }
            };

            //  skills which have 0 cast time or cooldown is smaller and which exclude each other
            var delayIdsForSkills = new Dictionary <int, int>()
            {
                { 1178, 1178 }, { 2148, 1178 }, { 1177, 1178 }, { 2147, 1178 }
            };

            #region Finding the chainDelayIdsInclude

            /*
             * var diffDelays = Utility.SkillIndex.SkillList.Where(s => s.delay_id > 0 &&
             *                                                  s.sub_type == SkillSubType.buff &&
             *                                                  s.chain_category_level > 0)
             *                                           .ToLookup(s => s.delay_id, s => s);
             * var chainCategories = new Dictionary<string, List<ClientSkill>>();
             *
             * foreach (var group in diffDelays) {
             *  var skills = diffDelays[group.Key];
             *  string[] chains = skills.Select(s => s.chain_category_priority).Distinct().ToArray();
             *  string categoryName = chains.Where(s => s != null).FirstOrDefault();
             *  if (categoryName == null) {
             *      chains = skills.Select(s => s.prechain_skillname).Distinct().ToArray();
             *      categoryName = chains.Where(s => s != null).FirstOrDefault();
             *  }
             *
             *  if (categoryName == null)
             *      continue;
             *
             *  if (chainCategories.ContainsKey(categoryName)) {
             *      Debug.Print("Different delay id for chain: {0}", categoryName);
             *      chainCategories[categoryName].AddRange(skills.ToList());
             *  } else {
             *      chainCategories.Add(categoryName, skills.ToList());
             *  }
             * }
             *
             * chainCategories = chainCategories.Where(pair => pair.Value.Count > 1)
             *                               .OrderBy(p => p.Value.First().delay_id)
             *                               .ToDictionary(pair => pair.Key, pair => pair.Value);
             *
             * // StringBuilder sb = new StringBuilder();
             * foreach (var pair in chainCategories) {
             *  // sb.AppendFormat("SkillSet by delay: {0}\r\n", pair.Key);
             *  foreach (var skill in pair.Value) {
             *      // string desc = Utility.StringIndex.GetString(skill.desc);
             *      if (skill.casting_delay == 0) {
             *          int dur = 0;
             *          if (skill.effect1_reserved_cond1 == null ||
             *              skill.effect1_reserved_cond1_prob2 == 100 &&
             *              skill.effect1_reserved_cond1 == "EveryHit")
             *              dur = Math.Max(dur, skill.effect1_remain2);
             *          if (skill.effect2_reserved_cond1 == null ||
             *              skill.effect2_reserved_cond1_prob2 == 100 &&
             *              skill.effect2_reserved_cond1 == "EveryHit")
             *              dur = Math.Max(dur, skill.effect2_remain2);
             *          if (skill.effect3_reserved_cond1 == null ||
             *              skill.effect3_reserved_cond1_prob2 == 100 &&
             *              skill.effect3_reserved_cond1 == "EveryHit")
             *              dur = Math.Max(dur, skill.effect3_remain2);
             *          if (skill.effect4_reserved_cond1 == null ||
             *              skill.effect4_reserved_cond1_prob2 == 100 &&
             *              skill.effect4_reserved_cond1 == "EveryHit")
             *              dur = Math.Max(dur, skill.effect4_remain2);
             *          skill.casting_delay = dur;
             *      }
             *
             *      if (skill.casting_delay == 0 || skill.casting_delay / 1000 > skill.delay_time / 100) {
             *          sb.AppendFormat("\tSkill: id={0}; name={1}; delayId={2}; r10={3}/{4}/{5}/{6}; delay={7}; cooldown={8}\r\n",
             *                          skill.id, desc,
             *                          skill.delay_id, skill.effect1_reserved10, skill.effect2_reserved10,
             *                          skill.effect3_reserved10, skill.effect4_reserved10,
             *                          skill.casting_delay, skill.delay_time / 100);
             *      }
             *  }
             * }
             */

            #endregion

            Dictionary <ClientSkill, List <ClientEffect> > list     = new Dictionary <ClientSkill, List <ClientEffect> >();
            Dictionary <string, HashSet <string> >         statData = new Dictionary <string, HashSet <string> >();

            const EffectType filterEffect = EffectType.None;
            const Stat       filterStat   = Stat.None;
            StringBuilder    sb           = new StringBuilder();

            //for (var filterEffect = EffectType.None + 1;
            //     filterEffect <= EffectType.XPBoost; filterEffect++) {

            foreach (var sk in Utility.SkillIndex.SkillList)
            {
                var template = new SkillTemplate();
                template.skill_id = sk.id;
                template.name     = sk.desc;
                template.nameId   = Utility.StringIndex[sk.desc] * 2 + 1;

                string stack;
                int    level = -1;
                if (level == -1)
                {
                    level = Utility.GetSkillLevelFromName(sk.name, out stack);
                }
                //if (level == -1 && !String.IsNullOrEmpty(sk.skillicon_name))
                //    level = Utility.GetSkillLevelFromName(sk.skillicon_name, out stack);
                if (level == -1)
                {
                    level = 1;
                }
                template.lvl   = level;
                level          = Utility.GetSkillLevelFromName(sk.desc, out stack);
                template.stack = stack.ToUpper();

                template.skilltype    = (skillType)sk.type;
                template.skillsubtype = (skillSubType)sk.sub_type;
                template.tslot        = (TargetSlot)sk.target_slot;
                template.tslot_level  = sk.target_slot_level;
                if (sk.id == 417)
                {
                    template.activation = activationAttribute.ACTIVE;
                }
                else
                {
                    template.activation = (activationAttribute)sk.activation_attribute;
                }
                template.cooldown    = sk.delay_time / 100;
                template.cancel_rate = sk.cancel_rate;
                if (sk.casting_delay > 0)
                {
                    template.duration = sk.casting_delay;
                }
                template.pvp_duration     = sk.pvp_remain_time_ratio;
                template.chain_skill_prob = sk.chain_skill_prob2;
                template.dispel_category  = (DispelCategory)sk.dispel_category;
                template.dispel_level     = sk.required_dispel_level;
                template.delay_id         = sk.delay_id;

                if (!String.IsNullOrEmpty(sk.penalty_skill_succ))
                {
                    var penaltySkill = Utility.SkillIndex[sk.penalty_skill_succ];
                    if (penaltySkill == null)
                    {
                        Debug.Print("Missing penalty skill: {0}", sk.penalty_skill_succ);
                    }
                    else
                    {
                        template.penalty_skill_id = penaltySkill.id;
                    }
                }

                if (sk.change_stance != Stance.none)
                {
                    template.stance = true;
                }
                template.pvp_damage = sk.pvp_damage_ratio;

                if (sk.first_target != FirstTarget.None)
                {
                    var properties = new Properties();
                    properties.firsttarget = new FirstTargetProperty((FirstTargetAttribute)sk.first_target);
                    if (sk.first_target_valid_distance > 0)
                    {
                        properties.firsttargetrange =
                            new FirstTargetRangeProperty(sk.first_target_valid_distance);
                    }
                    else if (sk.first_target == FirstTarget.Target)
                    {
                        properties.firsttargetrange = new FirstTargetRangeProperty();
                    }

                    if (sk.target_range != TargetRange.None)
                    {
                        properties.targetrange =
                            new TargetRangeProperty(sk.target_maxcount, sk.target_range_opt1,
                                                    (TargetRangeAttribute)sk.target_range);
                    }

                    if (sk.target_relation_restriction != RelationRestriction.None)
                    {
                        properties.targetrelation =
                            new TargetRelationProperty((TargetRelationAttribute)sk.target_relation_restriction);
                    }

                    if (sk.target_species_restriction != SpeciesRestriction.None)
                    {
                        properties.targetspecies =
                            new TargetSpeciesProperty((TargetAttribute)sk.target_species_restriction);
                    }

                    var           importUtil = Utility <ClientSkill> .Instance;
                    List <string> states     = new List <string>();
                    importUtil.Export <string>(sk, "target_valid_status", states);
                    TargetState state = TargetState.NONE;
                    foreach (string s in states)
                    {
                        TargetState s1 = (TargetState)Enum.Parse(typeof(TargetState), s, true);
                        state |= s1;
                    }
                    if (state != TargetState.NONE)
                    {
                        properties.targetstatus = new TargetStatusProperty(state);
                    }

                    template.setproperties = properties;
                }

                if (sk.add_wpn_range)
                {
                    template.initproperties = new Properties();
                    template.initproperties.addweaponrange = new AddWeaponRangeProperty();
                }

                template.useconditions   = new Conditions();
                template.startconditions = new Conditions();
                template.actions         = new Actions();

                List <Condition> useList    = new List <Condition>();
                List <Condition> startList  = new List <Condition>();
                List <Action>    actionList = new List <Action>();

                TargetCondition targetcondition = null;

                /*
                 * if (sk.target_species_restriction != SpeciesRestriction.None) {
                 * targetcondition = new TargetCondition((TargetAttribute)sk.target_species_restriction);
                 * startList.Add(targetcondition);
                 * }
                 */

                FlyRestriction restriction = (FlyRestriction)sk.target_flying_restriction;
                if (restriction != FlyRestriction.NONE)
                {
                    if (targetcondition == null)
                    {
                        startList.Add(new TargetCondition(restriction));
                    }
                    else
                    {
                        targetcondition.restriction = restriction;
                    }
                }
                restriction = (FlyRestriction)sk.self_flying_restriction;
                if (restriction != FlyRestriction.NONE)
                {
                    startList.Add(new SelfCondition(restriction));
                }

                int startCost = sk.activation_attribute == Activation.Toggle ? sk.cost_toggle : sk.cost_start;
                int startLvl  = sk.activation_attribute == Activation.Toggle ? sk.cost_toggle_lv : sk.cost_start_lv;
                int endCost   = sk.cost_end;
                if (endCost == 0 && sk.activation_attribute == Activation.Toggle)
                {
                    endCost = sk.cost_toggle;
                }
                int endLvl = sk.cost_end_lv;
                if (endLvl == 0 && sk.activation_attribute == Activation.Toggle)
                {
                    endLvl = sk.cost_toggle_lv;
                }

                if (sk.cost_checktime > 0 || sk.cost_toggle > 0)
                {
                    OverTimeEffect ot        = null;
                    CostType       parameter = sk.cost_checktime_parameter;
                    if (parameter == CostType.NONE)
                    {
                        parameter = sk.cost_parameter;
                    }

                    if (parameter == CostType.MP || parameter == CostType.MP_RATIO)
                    {
                        var mpEff = new MpUseOverTimeEffect();
                        ot = mpEff;
                        mpEff.checktime  = sk.cost_time;
                        mpEff.percent    = parameter == CostType.MP_RATIO;
                        mpEff.cost_start = sk.cost_start;
                        if (sk.cost_checktime_lv > 0 || sk.cost_checktime > 0)
                        {
                            mpEff.value = sk.cost_checktime;
                            mpEff.delta = sk.cost_checktime_lv;
                        }
                        else if (sk.cost_toggle_lv > 0 || sk.cost_toggle > 0)
                        {
                            mpEff.value = sk.cost_toggle;
                            mpEff.delta = sk.cost_toggle_lv;
                        }
                        mpEff.cost_end = endCost;
                        if (endCost > 0 || startLvl > 0)
                        {
                            if (sk.cost_parameter == CostType.MP)
                            {
                                startList.Add(new MpCondition(endCost, startLvl));
                            }
                            else if (sk.cost_parameter == CostType.HP)
                            {
                                startList.Add(new HpCondition(endCost, startLvl));
                            }
                            else
                            {
                                Debug.Print("Cost parameter not handled: {0}", sk.cost_parameter);
                            }
                        }
                    }
                    else if (parameter == CostType.HP || parameter == CostType.HP_RATIO)
                    {
                        var hpEff = new HpUseOverTimeEffect();
                        ot = hpEff;
                        hpEff.checktime  = sk.cost_time;
                        hpEff.percent    = parameter == CostType.HP_RATIO;
                        hpEff.cost_start = sk.cost_start;
                        if (sk.cost_checktime_lv > 0 || sk.cost_checktime > 0)
                        {
                            hpEff.value = sk.cost_checktime;
                            hpEff.delta = sk.cost_checktime_lv;
                        }
                        else if (sk.cost_toggle_lv > 0 || sk.cost_toggle > 0)
                        {
                            hpEff.value = sk.cost_toggle;
                            hpEff.delta = sk.cost_toggle_lv;
                        }
                        hpEff.cost_end = endCost;
                        if (endCost > 0 || startLvl > 0)
                        {
                            if (sk.cost_parameter == CostType.MP)
                            {
                                startList.Add(new MpCondition(endCost, startLvl));
                            }
                            else if (sk.cost_parameter == CostType.HP)
                            {
                                startList.Add(new HpCondition(endCost, startLvl));
                            }
                            else
                            {
                                Debug.Print("Cost parameter not handled: {0}", sk.cost_parameter);
                            }
                        }
                    }
                    if (ot != null)
                    {
                        if (sk.effect1_hop_type == null)
                        {
                            Debug.Print("Missing HOP data for {0}: id={1}", sk.cost_parameter, sk.id);
                        }
                        else
                        {
                            ot.hoptype = (HopType)Enum.Parse(typeof(HopType), sk.effect1_hop_type, true);
                            ot.hopb    = sk.effect1_hop_b;
                        }
                        if (template.effects == null)
                        {
                            template.effects = new Effects();
                        }
                        if (template.effects.EffectList == null)
                        {
                            template.effects.EffectList = new List <Effect>();
                        }
                        template.effects.EffectList.Add(ot);
                    }
                }
                else
                {
                    // cost_checktime_parameter is always absent
                    if (sk.cost_parameter == CostType.MP || sk.cost_parameter == CostType.MP_RATIO)
                    {
                        if (endCost != 0 || endLvl != 0)
                        {
                            var action = new MpUseAction(endCost, endLvl);
                            action.percent = sk.cost_parameter == CostType.MP_RATIO;
                            actionList.Add(action);
                        }
                        if (sk.cost_parameter == CostType.MP)
                        {
                            if (startCost > 0 || startLvl > 0)
                            {
                                startList.Add(new MpCondition(startCost, startLvl));
                            }
                            else if (endCost > 0 || endLvl > 0)
                            {
                                startList.Add(new MpCondition(endCost, endLvl));
                            }
                        }
                    }
                    else if (sk.cost_parameter == CostType.HP || sk.cost_parameter == CostType.HP_RATIO)
                    {
                        if (endCost != 0 || endLvl != 0)
                        {
                            var action = new HpUseAction(endCost, endLvl);
                            action.percent = sk.cost_parameter == CostType.HP_RATIO;
                            actionList.Add(action);
                        }
                        if (sk.cost_parameter == CostType.HP)
                        {
                            if (startCost > 0 || startLvl > 0)
                            {
                                startList.Add(new HpCondition(startCost, startLvl));
                            }
                            else if (endCost > 0 || endLvl > 0)
                            {
                                startList.Add(new HpCondition(endCost, endLvl));
                            }
                        }
                    }
                }

                if (sk.cost_dp > 0)
                {
                    startList.Add(new DpCondition(sk.cost_dp));
                    actionList.Add(new DpUseAction(sk.cost_dp, sk.cost_dp_lv));
                }

                if (sk.component != null)
                {
                    Item item = Utility.ItemIndex.GetItem(sk.component);
                    if (item == null)
                    {
                        Debug.Print("Missing item for skill {0}", sk.id);
                    }
                    else
                    {
                        actionList.Add(new ItemUseAction(item.id, sk.component_count));
                    }
                }

                if (!sk.move_casting)
                {
                    useList.Add(new PlayerMovedCondition(false));
                }

                if (sk.use_arrow != null && sk.use_arrow != "0") // 3 arena skills have FX_pow, FX_HIT and weaponbody
                {
                    startList.Add(new ArrowCheckCondition());
                }

                #region Fill conditions and actions

                if (useList.Count == 0)
                {
                    template.useconditions = null;
                }
                else
                {
                    template.useconditions.ConditionList = useList;
                }

                if (startList.Count == 0)
                {
                    template.startconditions = null;
                }
                else
                {
                    template.startconditions.ConditionList = startList;
                }

                if (actionList.Count == 0)
                {
                    template.actions = null;
                }
                else
                {
                    template.actions.ActionList = actionList;
                }

                #endregion

                if (skillsetEx.ContainsKey(sk.id))
                {
                    template.skillset_exception = skillsetEx[sk.id];
                }

                if (delayIdsForSkills.ContainsKey(sk.id))
                {
                    template.delay_id = delayIdsForSkills[sk.id];
                }
                else if (delayIdOverrides.ContainsKey(sk.id))
                {
                    template.delay_id = delayIdOverrides[sk.id];
                }

                string descrStr = sk.desc_long == null ? sk.desc : sk.desc_long;
                var    desc     = Utility.StringIndex.GetStringDescription(descrStr);
                string desc_2nd;
                if (!String.IsNullOrEmpty(sk.desc_long_2nd))
                {
                    desc_2nd = Utility.StringIndex.GetStringDescription(sk.desc_long_2nd).body;
                }

                #region Effect processing

                var utility = Utility <ClientSkill> .Instance;
                List <ClientEffect> effects = new List <ClientEffect>();
                utility.Export(sk, "effect", effects);

                var  validEffects = effects.Where(e => e.type != EffectType.None).ToArray();
                bool save         = validEffects.Where(e => e.type == filterEffect).Any();
                // var validEffects = effects.Where(e => e.changeStat != Stat.None).ToArray();
                // bool save = validEffects.Where(e => e.changeStat == filterStat).Any();

                string text = desc == null ? String.Empty : desc.body;
                int    idx  = 0;
                var    vars = (from v in Utility.GetVarStrings(text)
                               let parts = v.Split('.')
                                           let parsed = Int32.TryParse(parts[0].Remove(0, 1), out idx)
                                                        let name = parsed ? parts[1] : v
                                                                   let var = parsed ? parts[2] : String.Empty
                                                                             select new { Id = idx, Data = new StatData(name, var) })
                              .ToLookup(a => a.Id, a => a);

                if (save)
                {
                    sb.Append("\r\n");
                    sb.AppendFormat("---Skill Id = {0}, Name = '{1}' ---\r\n",
                                    sk.id, Utility.StringIndex.GetString(sk.desc));
                    if (desc == null)
                    {
                        sb.AppendFormat("NO DESCRIPTION\r\n");
                    }
                    else
                    {
                        sb.AppendFormat("{0}: {1}\r\n", desc.name, desc.body);
                    }
                    if (sk.desc_abnormal != null)
                    {
                        sb.AppendFormat("ABNORMAL: {0}\r\n", Utility.StringIndex.GetString(sk.desc_abnormal));
                    }
                }

                foreach (var eff in validEffects)
                {
                    EffectClass @class = (EffectClass)eff.type;

                    #region Overrides


                    #endregion

                    Type type = effectsAssembly.GetType(String.Format("{0}.{1}", typeof(Effect).Namespace,
                                                                      @class.ToString()));
                    Effect ourEffect = null;

                    if (save)
                    {
                        sb.AppendFormat("Data for {0}:\r\n", @class);
                    }

                    if (vars.Any())
                    {
                        if (vars.Contains(idx + 1))
                        {
                            foreach (var v in vars[idx + 1])
                            {
                                if (!statData.ContainsKey(v.Data.Name))
                                {
                                    statData.Add(v.Data.Name, new HashSet <string>());
                                }
                                statData[v.Data.Name].Add(v.Data.Var);
                                if (save)
                                {
                                    sb.AppendFormat("\tEffect = {0}, Var = {1}\r\n",
                                                    v.Data.Name, v.Data.Var);
                                }
                            }
                        }
                    }
                    if (save)
                    {
                        sb.AppendFormat("\tE{0} Reserved: ", eff.e);
                        using (TextWriter wr = new StringWriter(sb)) {
                            ObjectDumper.Write(eff.reserved, wr);
                            sb.Append("\r\n");
                            if (!String.IsNullOrEmpty(eff.reserved_cond1))
                            {
                                sb.AppendFormat("\tCondition = {0}, Prob. = ", eff.reserved_cond1);
                                ObjectDumper.Write(eff.reserved_cond1_prob, wr);
                                sb.Append("\r\n");
                            }
                            if (!String.IsNullOrEmpty(eff.reserved_cond2))
                            {
                                sb.AppendFormat("\tCondition = {0}, Prob. = ", eff.reserved_cond2);
                                ObjectDumper.Write(eff.reserved_cond2_prob, wr);
                                sb.Append("\r\n");
                            }
                        }
                    }

                    if (type == null)
                    {
                        string skillName = Utility.StringIndex.GetString(sk.desc);
                        Debug.Print("Effect {0} not handled (skillId={1}; name='{2}')", @class, sk.id, skillName);
                        continue;
                    }
                    else
                    {
                        ourEffect = (Effect)Activator.CreateInstance(type);
                    }

                    if (template.effects == null)
                    {
                        template.effects = new Effects();
                        if (sk.skillicon_name != null)
                        {
                            template.effects.food = sk.skillicon_name.EndsWith("_food");
                        }
                    }

                    eff.Skill    = sk;
                    eff.Template = template;
                    ourEffect.Import(eff, null);

                    template.effects.EffectList.Add(ourEffect);
                }

                #endregion

                //if (template.effects != null && template.effects.EffectList != null) {
                //    if (template.effects.EffectList.Count == 0)
                //        template.effects.EffectList = null;
                //    else if (template.activation != activationAttribute.PASSIVE) {
                //        foreach (var eff in template.effects.EffectList) {
                //            if (eff is DeformEffect || eff is PolymorphEffect || eff is ShapeChangeEffect ||
                //                eff is WeaponDualEffect) {
                //            } else
                //                eff.basiclvl = 0; // not used for active skills
                //        }
                //    }
                //}
                if (template.effects != null && template.effects.EffectList == null)
                {
                    template.effects = null;
                }

                outputFile.SkillList.Add(template);

                if (filterEffect != EffectType.None)
                {
                    string fileName = /*"stat_" + filterStat*/ filterEffect.ToString() + ".txt";
                    using (var fs = new FileStream(Path.Combine(outputPath, fileName), FileMode.Create,
                                                   FileAccess.Write)) {
                        using (TextWriter wr = new StreamWriter(fs)) {
                            wr.Write(sb.ToString());
                        }
                    }
                }

                sb.Length = 0;
            }

            try {
                using (var fs = new FileStream(Path.Combine(outputPath, "skill_templates.xml"),
                                               FileMode.Create, FileAccess.Write))
                    using (var writer = XmlWriter.Create(fs, settings)) {
                        XmlSerializer ser = new XmlSerializer(typeof(SkillData));
                        // ser.UnknownAttribute += new XmlAttributeEventHandler(OnUnknownAttribute);
                        // ser.UnknownElement += new XmlElementEventHandler(OnUnknownElement);
                        ser.Serialize(writer, outputFile);
                    }
            } catch (Exception ex) {
                Debug.Print(ex.ToString());
            }
        }