Ejemplo n.º 1
0
        public static void ReloadFMGs()
        {
            if (AssetLocator.Type == GameType.Undefined)
            {
                return;
            }

            if (AssetLocator.Type == GameType.DarkSoulsIISOTFS)
            {
                ReloadFMGsDS2();
                IsLoaded = true;
                return;
            }

            IBinder fmgBinder;
            var     desc = AssetLocator.GetEnglishItemMsgbnd();

            if (AssetLocator.Type == GameType.DemonsSouls || AssetLocator.Type == GameType.DarkSoulsPTDE || AssetLocator.Type == GameType.DarkSoulsRemastered)
            {
                fmgBinder = BND3.Read(desc.AssetPath);
            }
            else
            {
                fmgBinder = BND4.Read(desc.AssetPath);
            }

            _fmgs = new Dictionary <ItemFMGTypes, FMG>();
            foreach (var file in fmgBinder.Files)
            {
                _fmgs.Add((ItemFMGTypes)file.ID, FMG.Read(file.Bytes));
            }
            IsLoaded = true;
        }
Ejemplo n.º 2
0
        public static void Unpack(this FMG fmg, string sourceFile)
        {
            XmlWriterSettings xws = new XmlWriterSettings();

            // You need Indent for it to write newlines
            xws.Indent = true;
            // But don't actually indent so there's more room for the text
            xws.IndentChars = "";
            XmlWriter xw = XmlWriter.Create($"{sourceFile}.xml", xws);

            xw.WriteStartElement("fmg");
            xw.WriteElementString("compression", fmg.Compression.ToString());
            xw.WriteElementString("version", fmg.Version.ToString());
            xw.WriteElementString("bigendian", fmg.BigEndian.ToString());
            xw.WriteStartElement("entries");

            // I think they're sorted already, but whatever
            fmg.Entries.Sort((e1, e2) => e1.ID.CompareTo(e2.ID));
            foreach (FMG.Entry entry in fmg.Entries)
            {
                xw.WriteStartElement("text");
                xw.WriteAttributeString("id", entry.ID.ToString());
                xw.WriteString(entry.Text ?? "%null%");
                xw.WriteEndElement();
            }

            xw.WriteEndElement();
            xw.WriteEndElement();
            xw.Close();
        }
Ejemplo n.º 3
0
 public CloneFmgsAction(FMG fmg, string fstring, List <FMG.Entry> entries, bool setsel)
 {
     Fmg = fmg;
     Clonables.AddRange(entries);
     FmgString    = fstring;
     SetSelection = setsel;
 }
Ejemplo n.º 4
0
        public static void Repack(string sourceFile)
        {
            FMG         fmg = new FMG();
            XmlDocument xml = new XmlDocument();

            xml.Load(sourceFile);
            Enum.TryParse(xml.SelectSingleNode("fmg/compression")?.InnerText ?? "None", out DCX.Type compression);
            fmg.Compression = compression;
            fmg.Version     = (FMG.FMGVersion)Enum.Parse(typeof(FMG.FMGVersion), xml.SelectSingleNode("fmg/version").InnerText);
            fmg.BigEndian   = bool.Parse(xml.SelectSingleNode("fmg/bigendian").InnerText);

            foreach (XmlNode textNode in xml.SelectNodes("fmg/entries/text"))
            {
                int id = int.Parse(textNode.Attributes["id"].InnerText);
                // \r\n is drawn as two newlines ingame
                string text = textNode.InnerText.Replace("\r", "");
                if (text == "%null%")
                {
                    text = null;
                }
                fmg.Entries.Add(new FMG.Entry(id, text));
            }

            string outPath = sourceFile.Replace(".fmg.xml", ".fmg");

            YBUtil.Backup(outPath);
            fmg.Write(outPath);
        }
 private static void RewriteFMG(FMG destFMG, Dictionary <int, string> destDict)
 {
     destFMG.Entries.Clear();
     foreach (var item in destDict)
     {
         destFMG.Entries.Add(new FMG.Entry(item.Key, item.Value));
     }
 }
Ejemplo n.º 6
0
        public ActionResult DeleteConfirmed(int id)
        {
            FMG fMG = db.fmg.Find(id);

            db.fmg.Remove(fMG);
            db.SaveChanges();
            return(RedirectToAction("Index"));
        }
Ejemplo n.º 7
0
 public ActionResult Edit([Bind(Include = "fmgID,fmgAvgHoldTime,fmgAvgSpeedAnswer,fmgMaxHoldTime,fmgAbandoned,fmgNumberOfAcceptedCalls,fmgPercentAbandoned,fmgAvgAbandon,fmgDateTime")] FMG fMG)
 {
     if (ModelState.IsValid)
     {
         db.Entry(fMG).State = EntityState.Modified;
         db.SaveChanges();
         return(RedirectToAction("Index"));
     }
     return(View(fMG));
 }
Ejemplo n.º 8
0
        public static Position Create(FMG.DomainObjects.Position domainObject, bool fullResource = true)
        {
            var resource = new Position
             {
            Name = domainObject.Name,
            Shortname = domainObject.ShortName,
            Line = domainObject.Line.Name
             };

             return resource;
        }
Ejemplo n.º 9
0
        public ActionResult Create([Bind(Include = "fmgID,fmgAvgHoldTime,fmgAvgSpeedAnswer,fmgMaxHoldTime,fmgAbandoned,fmgNumberOfAcceptedCalls,fmgPercentAbandoned,fmgAvgAbandon,fmgDateTime")] FMG fMG)
        {
            if (ModelState.IsValid)
            {
                db.fmg.Add(fMG);
                db.SaveChanges();
                return(RedirectToAction("Confirm"));
            }

            return(View(fMG));
        }
Ejemplo n.º 10
0
        public bool ScrapeMsgs(Universe u)
        {
            if (spec.MsgDir == null)
            {
                return(false);
            }
            foreach (KeyValuePair <string, FMG> entry in editor.LoadBnds(spec.MsgDir, (data, name) => FMG.Read(data)).SelectMany(e => e.Value)
                     .Concat(editor.Load(spec.MsgDir, name => FMG.Read(name), "*.fmg")).OrderBy(e => e.Key))
            {
                if (MsgTypes.ContainsKey(entry.Key))
                {
                    Namespace type = MsgTypes[entry.Key];
                    foreach (FMG.Entry name in entry.Value.Entries)
                    {
                        u.Names[Obj.Of(type, name.ID)] = name.Text;
                    }
                }
            }
            if (Directory.Exists($@"{spec.GameDir}\{spec.MsgDir}\talk"))
            {
                foreach (KeyValuePair <string, FMG> entry in editor.Load(spec.MsgDir + @"\talk", name => FMG.Read(name), "*.fmg"))
                {
                    foreach (FMG.Entry name in entry.Value.Entries)
                    {
                        u.Names[Obj.Talk(name.ID)] = name.Text;
                    }
                }
            }
            if (spec.ParamFile == null || !talkParamMsgId.ContainsKey(spec.Game))
            {
                return(false);
            }
            LoadParams();
            if (!Params.ContainsKey("TalkParam"))
            {
                return(false);
            }
            string msgId = talkParamMsgId[spec.Game];

            foreach (PARAM.Row row in Params["TalkParam"].Rows)
            {
                int dialogue = (int)row[msgId].Value;
                if (dialogue > 0)
                {
                    Obj dialogueObj = Obj.Of(Namespace.DIALOGUE, dialogue);
                    if (u.Names.ContainsKey(dialogueObj))
                    {
                        u.Names[Obj.Talk((int)row.ID)] = u.Names[dialogueObj];
                    }
                }
            }
            return(true);
        }
Ejemplo n.º 11
0
            public FMGHandler(TextHandler th, BND3 bnd, string name)
            {
                IEnumerable <BinderFile> list = bnd.Files.Where(f => Path.GetFileNameWithoutExtension(f.Name) == name);

                FileProper = list.ElementAt(0);
                Proper     = FMG.Read(FileProper.Bytes);
                if (list.Count() > 1)
                {
                    FilePatch = list.ElementAtOrDefault(1);
                    Patch     = FMG.Read(FilePatch.Bytes);
                }
            }
Ejemplo n.º 12
0
 private string GetFromFile(FMG fmg, long id)
 {
     if (fmg == null)
     {
         return(null);
     }
     FMG.Entry entry = fmg.Entries.FirstOrDefault(f => f.ID == id);
     if (entry != null && !string.IsNullOrWhiteSpace(entry.Text))
     {
         return(entry.Text);
     }
     return(null);
 }
        private static Dictionary <int, string> MakeRef(IBinder refBND, int iRef)
        {
            FMG refFMG = null;
            Dictionary <int, string> refDict = null;

            if (refBND != null)
            {
                refFMG  = FMG.Read((refBND.Files[iRef]).Bytes);
                refDict = refFMG.Entries.ToDictionary(t => t.ID, t => t.Text);
            }

            return(refDict);
        }
Ejemplo n.º 14
0
        // GET: FMGs/Edit/5
        public ActionResult Edit(int?id)
        {
            if (id == null)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }
            FMG fMG = db.fmg.Find(id);

            if (fMG == null)
            {
                return(HttpNotFound());
            }
            return(View(fMG));
        }
Ejemplo n.º 15
0
 public static LeagueTablePosition Create(FMG.DomainObjects.LeagueTablePosition domainObject, bool fullResource = true)
 {
     return new LeagueTablePosition
      {
     Position = domainObject.Position,
     Matches = domainObject.Matches,
     Points = domainObject.Points,
     Wins = domainObject.Wins,
     Draws = domainObject.Draws,
     Losses = domainObject.Losses,
     GoalsScored = domainObject.GoalsScored,
     GoalsConceded = domainObject.GoalsConceded,
     GoalDifference = domainObject.GoalDifference,
      };
 }
Ejemplo n.º 16
0
 public static void ReloadFmgs()
 {
     try
     {
         BND4 fmgBnd = BND4.Read(FMGBndPath);
         ItemFMG = FMG.Read(fmgBnd.Files.Find(x => Path.GetFileName(x.Name) == "アイテム名.fmg").Bytes);
     }
     catch (Exception e)
     {
         if (!Failwarn)
         {
             Debug.Log("Failed to load item fmg file. Item names will not be shown.");
             Failwarn = true;
         }
     }
 }
Ejemplo n.º 17
0
 private void SetInFile(FMG fmg, long id, string s)
 {
     if (fmg == null)
     {
         return;
     }
     FMG.Entry entry = fmg.Entries.FirstOrDefault(f => f.ID == id);
     if (entry != null)
     {
         entry.Text = s;
     }
     else
     {
         FMG.Entry newEntry = new FMG.Entry((int)id, s);
         fmg.Entries.Add(newEntry);
     }
 }
Ejemplo n.º 18
0
        public static void LoadMessages(SQLiteConnection con, string messageDir)
        {
            List <string> messageFilepaths = new List <string>();

            messageFilepaths.AddRange(Directory.GetFiles(messageDir, "*.msgbnd.dcx"));

            foreach (string messageFilepath in messageFilepaths)
            {
                var msgbnd = BND3.Read(messageFilepath);
                foreach (BinderFile file in msgbnd.Files)
                {
                    string name = Path.GetFileNameWithoutExtension(file.Name);

                    // Yes, .msgbnd file is FMG
                    FMG msg = FMG.Read(file.Bytes);
                    ReadMessagesIntoDatabase(con, name, msg);
                }
            }
        }
        private static int NullOverwrite(int entriesOverwritten, FMG sourceFMG, FMG destFMG)
        {
            int i = 0;

            foreach (var item in sourceFMG.Entries) //Each entry in the current FMG file
            {
                //ConsoleLog(item.ID); //Debug
                //Batch up dest entries if there are extras
                if ((item.ID > destFMG.Entries[i].ID)) //Catch the count up if the entries in the destination langauge is out of place (Extra entries that shouldn't be there)
                {
                    while ((item.ID > destFMG.Entries[i].ID) && (i < destFMG.Entries.Count - 1))
                    {
                        if (i < destFMG.Entries.Count - 1)
                        {
                            i++;
                        }
                    }
                }
                //If item IDs match, check if the destination is null, then overwrite
                if (item.ID == destFMG.Entries[i].ID) //Overwrite all Null whitespace or empty entries in destination
                {
                    //ConsoleLog(item.ID + " = true"); //Debug
                    if (string.IsNullOrWhiteSpace(destFMG.Entries[i].Text))
                    {
                        destFMG.Entries[i].Text = item.Text;

                        if (!string.IsNullOrWhiteSpace(destFMG.Entries[i].Text)) //Keep track of entries that actually changed
                        {
                            Log.Add($"{ destFMG.Entries[i].ID }: { destFMG.Entries[i].Text }");
                            entriesOverwritten++;
                        }
                        //Debug.WriteLine("writing " + destFMG.Entries[i].Text + " to " + destFMG.Entries[i].ID + " in " + Path.GetFileName(sourceBND.Files[iFile].Name));
                    }
                    if (i < destFMG.Entries.Count - 1)
                    {
                        i++;
                    }
                }
            }

            return(entriesOverwritten);
        }
Ejemplo n.º 20
0
        public static void ReloadFMGsDS2()
        {
            var desc  = AssetLocator.GetEnglishItemMsgbnd(true);
            var files = Directory.GetFileSystemEntries($@"{AssetLocator.GameRootDirectory}\{desc.AssetPath}", @"*.fmg").ToList();

            _ds2fmgs = new Dictionary <string, FMG>();
            foreach (var file in files)
            {
                var modfile = $@"{AssetLocator.GameModDirectory}\{desc.AssetPath}\{Path.GetFileName(file)}";
                if (AssetLocator.GameModDirectory != null && File.Exists(modfile))
                {
                    var fmg = FMG.Read(modfile);
                    _ds2fmgs.Add(Path.GetFileNameWithoutExtension(modfile), fmg);
                }
                else
                {
                    var fmg = FMG.Read(file);
                    _ds2fmgs.Add(Path.GetFileNameWithoutExtension(file), fmg);
                }
            }
        }
Ejemplo n.º 21
0
 public void DumpMessages(string dir)
 {
     foreach (string path in Directory.GetFiles(dir, "*.msgbnd*"))
     {
         string name = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(path));
         try
         {
             IBinder bnd = BND3.Read(path);
             foreach (BinderFile file in bnd.Files)
             {
                 string fileName = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(file.Name));
                 string uname    = System.Text.RegularExpressions.Regex.Replace(fileName, @"[^\x00-\x7F]", c => string.Format(@"u{0:x4}", (int)c.Value[0]));
                 string fname    = $"{name}_{uname}.txt";
                 // Console.WriteLine(fname);
                 // string fileName = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(file.Name));
                 FMG fmg = FMG.Read(file.Bytes);
                 if (fmg.Entries != null)
                 {
                     File.WriteAllLines($@"{dir}\{fname}", fmg.Entries.Select(e => $"{e.ID} {(e.Text == null ? "" : e.Text.Replace("\r", "").Replace("\n", "\\n"))}"));
                 }
             }
         }
         catch (Exception ex)
         {
             throw new Exception($"Failed to load file: {name}: {path}\r\n\r\n{ex}");
         }
     }
     foreach (string path in Directory.GetFiles(dir, "*.fmg"))
     {
         FMG    fmg   = FMG.Read(path);
         string fname = Path.GetFileNameWithoutExtension(path);
         if (fmg.Entries != null)
         {
             File.WriteAllLines($@"{dir}\{fname}.txt", fmg.Entries.Select(e => $"{e.ID} {(e.Text == null ? "" : e.Text.Replace("\r", "").Replace("\n", "\\n"))}"));
         }
     }
 }
Ejemplo n.º 22
0
        public static void LoadAllFMG(bool forceReload)
        {
            if (!forceReload && GameTypeCurrentFmgsAreLoadedFrom == GameDataManager.GameType)
            {
                return;
            }

            List <FMG> weaponNameFMGs = new List <FMG>();
            List <FMG> armorNameFMGs  = new List <FMG>();

            /*
             *  [ptde]
             *  weapon 1
             *  armor 2
             *
             *  [ds1r]
             *  weapon 1, 30
             *  armor 2, 32
             *
             *  [ds3]
             *  weapon 1
             *  armor 2
             *  weapon_dlc1 18
             *  armor_dlc1 19
             *  weapon_dlc2 33
             *  armor_dlc2 34
             *
             *  [bb]
             *  weapon 1
             *  armor 2
             *
             *  [sdt]
             *  weapon 1
             *  armor 2
             */

            void TryToLoadFromMSGBND(string language, string msgbndName, int weaponNamesIdx, int armorNamesIdx)
            {
                var     msgbndRelativePath = $@"msg\{language}\{msgbndName}";
                var     fullMsgbndPath     = GameDataManager.GetInterrootPath(msgbndRelativePath);
                IBinder msgbnd             = null;

                if (File.Exists(fullMsgbndPath))
                {
                    if (BND3.Is(fullMsgbndPath))
                    {
                        msgbnd = BND3.Read(fullMsgbndPath);
                    }
                    else if (BND4.Is(fullMsgbndPath))
                    {
                        msgbnd = BND4.Read(fullMsgbndPath);
                    }

                    weaponNameFMGs.Add(FMG.Read(msgbnd.Files.First(x => x.ID == weaponNamesIdx).Bytes));
                    armorNameFMGs.Add(FMG.Read(msgbnd.Files.First(x => x.ID == armorNamesIdx).Bytes));
                }

                if (msgbnd == null)
                {
                    System.Windows.Forms.MessageBox.Show(
                        $"Unable to find text file '{msgbndRelativePath}'. Some player equipment may not show names.",
                        "Unable to find asset", System.Windows.Forms.MessageBoxButtons.OK,
                        System.Windows.Forms.MessageBoxIcon.Warning);

                    return;
                }
            }

            if (GameDataManager.GameType == GameDataManager.GameTypes.DS1)
            {
                TryToLoadFromMSGBND("ENGLISH", "item.msgbnd", 11, 12);
                TryToLoadFromMSGBND("ENGLISH", "menu.msgbnd", 115, 117); //Patch
            }
            else if (GameDataManager.GameType == GameDataManager.GameTypes.DS1R)
            {
                TryToLoadFromMSGBND("ENGLISH", "item.msgbnd.dcx", 11, 12);
                TryToLoadFromMSGBND("ENGLISH", "item.msgbnd.dcx", 115, 117); //Patch
            }
            else if (GameDataManager.GameType == GameDataManager.GameTypes.DS3)
            {
                TryToLoadFromMSGBND("engus", "item_dlc1.msgbnd.dcx", 11, 12);
                TryToLoadFromMSGBND("engus", "item_dlc1.msgbnd.dcx", 211, 212); //DLC1

                TryToLoadFromMSGBND("engus", "item_dlc2.msgbnd.dcx", 11, 12);
                TryToLoadFromMSGBND("engus", "item_dlc2.msgbnd.dcx", 211, 212); //DLC1
                TryToLoadFromMSGBND("engus", "item_dlc2.msgbnd.dcx", 251, 252); //DLC2
            }
            else if (GameDataManager.GameType == GameDataManager.GameTypes.SDT)
            {
                TryToLoadFromMSGBND("engus", "item.msgbnd.dcx", 11, 12);
            }
            else if (GameDataManager.GameType == GameDataManager.GameTypes.BB)
            {
                TryToLoadFromMSGBND("engus", "item.msgbnd.dcx", 11, 12);
            }

            WeaponNames.Clear();

            void DoWeaponEntry(FMG.Entry entry)
            {
                if (string.IsNullOrWhiteSpace(entry.Text) ||
                    !ParamManager.EquipParamWeapon.ContainsKey(entry.ID))
                {
                    return;
                }

                //if (GameDataManager.GameType == GameDataManager.GameTypes.DS3 && (entry.ID % 10000) != 0)
                //    return;
                //else if ((entry.ID % 1000) != 0)
                //    return;
                string val = entry.Text + $" <{entry.ID}>";

                if (WeaponNames.ContainsKey(entry.ID))
                {
                    WeaponNames[entry.ID] = val;
                }
                else
                {
                    WeaponNames.Add(entry.ID, val);
                }
            }

            foreach (var wpnNameFmg in weaponNameFMGs)
            {
                foreach (var entry in wpnNameFmg.Entries)
                {
                    DoWeaponEntry(entry);
                }
            }

            ProtectorNames_HD.Clear();
            ProtectorNames_BD.Clear();
            ProtectorNames_AM.Clear();
            ProtectorNames_LG.Clear();

            void DoProtectorParamEntry(FMG.Entry entry)
            {
                if (string.IsNullOrWhiteSpace(entry.Text))
                {
                    return;
                }
                //if (entry.ID < 1000000 && GameDataManager.GameType == GameDataManager.GameTypes.DS3)
                //    return;
                if (ParamManager.EquipParamProtector.ContainsKey(entry.ID))
                {
                    string val            = entry.Text + $" <{entry.ID}>";
                    var    protectorParam = ParamManager.EquipParamProtector[entry.ID];
                    if (protectorParam.HeadEquip)
                    {
                        if (ProtectorNames_HD.ContainsKey(entry.ID))
                        {
                            ProtectorNames_HD[entry.ID] = val;
                        }
                        else
                        {
                            ProtectorNames_HD.Add(entry.ID, val);
                        }
                    }
                    else if (protectorParam.BodyEquip)
                    {
                        if (ProtectorNames_BD.ContainsKey(entry.ID))
                        {
                            ProtectorNames_BD[entry.ID] = val;
                        }
                        else
                        {
                            ProtectorNames_BD.Add(entry.ID, entry.Text + $" <{entry.ID}>");
                        }
                    }
                    else if (protectorParam.ArmEquip)
                    {
                        if (ProtectorNames_AM.ContainsKey(entry.ID))
                        {
                            ProtectorNames_AM[entry.ID] = val;
                        }
                        else
                        {
                            ProtectorNames_AM.Add(entry.ID, entry.Text + $" <{entry.ID}>");
                        }
                    }
                    else if (protectorParam.LegEquip)
                    {
                        if (ProtectorNames_LG.ContainsKey(entry.ID))
                        {
                            ProtectorNames_LG[entry.ID] = val;
                        }
                        else
                        {
                            ProtectorNames_LG.Add(entry.ID, entry.Text + $" <{entry.ID}>");
                        }
                    }
                }
            }

            foreach (var armorNameFmg in armorNameFMGs)
            {
                foreach (var entry in armorNameFmg.Entries)
                {
                    DoProtectorParamEntry(entry);
                }
            }

            WeaponNames       = WeaponNames.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_HD = ProtectorNames_HD.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_BD = ProtectorNames_BD.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_AM = ProtectorNames_AM.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_LG = ProtectorNames_LG.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

            foreach (var protector in ParamManager.EquipParamProtector)
            {
                if (protector.Value.HeadEquip && !ProtectorNames_HD.ContainsKey((int)protector.Key))
                {
                    ProtectorNames_HD.Add((int)protector.Key, $"<{protector.Key}>");
                }
                else if (protector.Value.BodyEquip && !ProtectorNames_BD.ContainsKey((int)protector.Key))
                {
                    ProtectorNames_BD.Add((int)protector.Key, $"<{protector.Key}>");
                }
                else if (protector.Value.ArmEquip && !ProtectorNames_AM.ContainsKey((int)protector.Key))
                {
                    ProtectorNames_AM.Add((int)protector.Key, $"<{protector.Key}>");
                }
                else if (protector.Value.LegEquip && !ProtectorNames_LG.ContainsKey((int)protector.Key))
                {
                    ProtectorNames_LG.Add((int)protector.Key, $"<{protector.Key}>");
                }
            }

            foreach (var weapon in ParamManager.EquipParamWeapon)
            {
                if (!WeaponNames.ContainsKey((int)weapon.Key))
                {
                    WeaponNames.Add((int)weapon.Key, $"<{weapon.Key}>");
                }
            }

            ProtectorNames_HD = ProtectorNames_HD.OrderBy(kvp => kvp.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_BD = ProtectorNames_BD.OrderBy(kvp => kvp.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_AM = ProtectorNames_AM.OrderBy(kvp => kvp.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_LG = ProtectorNames_LG.OrderBy(kvp => kvp.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            WeaponNames       = WeaponNames.OrderBy(kvp => kvp.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

            GameTypeCurrentFmgsAreLoadedFrom = GameDataManager.GameType;
        }
Ejemplo n.º 23
0
    public static void ReloadFmgs()
    {
        BND4 fmgBnd = BND4.Read(FMGBndPath);

        ItemFMG = FMG.Read(fmgBnd.Files.Find(x => Path.GetFileName(x.Name) == "アイテム名.fmg").Bytes);
    }
        public void WriteList(GameData game, Dictionary <int, EnemyInfo> fullInfos)
        {
            // Generate things
            HashSet <int> allBonfires = new HashSet <int>
            {
                1001950,  // Dragonspring - Hirata Estate
                1001951,  // Estate Path
                1001952,  // Bamboo Thicket Slope
                1001953,  // Hirata Estate - Main Hall
                1001955,  // Hirata Audience Chamber
                1001954,  // Hirata Estate - Hidden Temple
                1101950,  // Dilapidated Temple
                1101956,  // Ashina Outskirts
                1101951,  // Outskirts Wall - Gate Path
                1101952,  // Outskirts Wall - Stairway
                1101953,  // Underbridge Valley
                1101954,  // Ashina Castle Gate Fortress
                1101955,  // Ashina Castle Gate
                1101957,  // Flames of Hatred
                1111950,  // Ashina Castle
                1111951,  // Upper Tower - Antechamber
                1111957,  // Upper Tower - Ashina Dojo
                1111952,  // Castle Tower Lookout
                1111953,  // Upper Tower - Kuro's Room
                1111956,  // Old Grave
                1111954,  // Great Serpent Shrine
                1111955,  // Abandoned Dungeon Entrance
                1121951,  // Ashina Reservoir
                1121950,  // Near Secret Passage
                1301950,  // Underground Waterway
                1301951,  // Bottomless Hole
                1701955,  // Ashina Depths
                1701954,  // Poison Pool
                1701956,  // Guardian Ape's Burrow
                1501950,  // Hidden Forest
                1501951,  // Mibu Village
                1501952,  // Water Mill
                1501953,  // Wedding Cave Door
                1701957,  // Under-Shrine Valley
                1701950,  // Sunken Valley
                1701951,  // Gun Fort
                1701952,  // Riven Cave
                1701958,  // Bodhisattva Valley
                1701953,  // Guardian Ape's Watering Hole
                2001950,  // Senpou Temple,  Mt. Kongo
                2001951,  // Shugendo
                2001952,  // Temple Grounds
                2001953,  // Main Hall
                2001954,  // Inner Sanctum
                2001955,  // Sunken Valley Cavern
                2001956,  // Bell Demon's Temple
                2501950,  // Fountainhead Palace
                2501951,  // Vermilion Bridge
                2501956,  // Mibu Manor
                2501952,  // Flower Viewing Stage
                2501953,  // Great Sakura
                2501954,  // Palace Grounds
                2501957,  // Feeding Grounds
                2501958,  // Near Pot Noble
                2501955,  // Sanctuary
            };

            // Probably shouldn't use tuples, but too late now
            List <(int, List <string>, List <int>, List <int>)> paths = new List <(int, List <string>, List <int>, List <int>)>
            {
                // Tutorial
                (1, new List <string> {
                    "ashinareservoir", "ashinacastle"
                },
                 new List <int> {
                    8306, 0
                }, new List <int>
                {
                    1121951,  // Ashina Reservoir
                    1121950,  // Near Secret Passage
                }),
                // First stretch of Ashina Outskirts
                (1, new List <string> {
                    "ashinaoutskirts"
                },
                 new List <int> {
                    8302, 1, 8302, -1, 1100330, 1
                }, new List <int>
                {
                    1101956,  // Ashina Outskirts
                    1101951,  // Outskirts Wall - Gate Path
                    1101952,  // Outskirts Wall - Stairway
                }),
                // Ashina Outskirts up to Blazing Bull
                (1, new List <string> {
                    "ashinaoutskirts", "ashinacastle"
                },
                 new List <int> {
                    8302, 1, 8302, -1, 8301, 1, 8301, -1, 1100330, 1
                }, new List <int>
                {
                    1101952,  // Outskirts Wall - Stairway
                    1101953,  // Underbridge Valley
                    1101954,  // Ashina Castle Gate Fortress
                    1101955,  // Ashina Castle Gate
                    // 1111950,  // Ashina Castle
                }),
                // Hirata 1
                (1, new List <string> {
                    "hirata"
                },
                 new List <int> {
                    1000353, 1, 1005601, 1, 1000301, 1, 1000900, 1
                }, new List <int>
                {
                    1001950,  // Dragonspring - Hirata Estate
                    1001951,  // Estate Path
                    1001952,  // Bamboo Thicket Slope
                    1001953,  // Hirata Estate - Main Hall
                    1001955,  // Hirata Audience Chamber
                    1001954,  // Hirata Estate - Hidden Temple
                }),
                // Ashina Castle to Genichiro
                (2, new List <string> {
                    "ashinacastle"
                },
                 new List <int> {
                    8301, 1, 8302, 1, 8302, -1
                }, new List <int>
                {
                    1111950,  // Ashina Castle
                    1111951,  // Upper Tower - Antechamber
                    1111957,  // Upper Tower - Ashina Dojo
                    1111952,  // Castle Tower Lookout
                }),
                // Ashina Castle to Reservoir to Dungeon
                (2, new List <string> {
                    "ashinareservoir"
                },
                 new List <int> {
                    8302, 1, 1120300, 0
                }, new List <int>
                {
                    1111950,  // Ashina Castle
                    1121951,  // Ashina Reservoir
                    1301951,  // Bottomless Hole
                }),
                // Dungeon
                (2, new List <string> {
                    "dungeon"
                },
                 new List <int> {
                }, new List <int>
                {
                    1111955,  // Abandoned Dungeon Entrance
                    1301950,  // Underground Waterway
                    1301951,  // Bottomless Hole
                }),
                // Senpou temple
                (2, new List <string> {
                    "senpou"
                },
                 new List <int> {
                }, new List <int>
                {
                    2001950,  // Senpou Temple,  Mt. Kongo
                    2001951,  // Shugendo
                    2001952,  // Temple Grounds
                    2001953,  // Main Hall
                }),
                // Hidden Forest to Water Mill
                (3, new List <string> {
                    "mibuvillage"
                },
                 new List <int> {
                    1700850, 1, 1700520, 1
                }, new List <int>
                {
                    1501950,  // Hidden Forest
                    1501951,  // Mibu Village
                    1501952,  // Water Mill
                }),
                // End of Ashina Depths
                (3, new List <string> {
                    "mibuvillage"
                },
                 new List <int> {
                }, new List <int>
                {
                    1501952,  // Water Mill
                    1501953,  // Wedding Cave Door
                }),
                // Most of Sunken Valley
                (3, new List <string> {
                    "ashinacastle", "sunkenvalley"
                },
                 new List <int> {
                    8301, 1, 8301, -1, 8302, 1, 8302, -1
                }, new List <int>
                {
                    1111952,  // Castle Tower Lookout
                    1111956,  // Old Grave
                    1111954,  // Great Serpent Shrine
                    1701957,  // Under-Shrine Valley
                    1701950,  // Sunken Valley
                    1701951,  // Gun Fort
                    1701952,  // Riven Cave
                    1701958,  // Bodhisattva Valley
                    1701953,  // Guardian Ape's Watering Hole
                }),
                // Sunken Valley to Poison Pool path
                (3, new List <string> {
                    "sunkenvalley"
                },
                 new List <int> {
                    1700850, 0, 1700520, 0
                }, new List <int>
                {
                    1701958,  // Bodhisattva Valley
                    1701954,  // Poison Pool
                    1701956,  // Guardian Ape's Burrow
                }),
                // Ashina Castle Revisited, also down to Masanaga
                (4, new List <string> {
                    "ashinacastle"
                },
                 new List <int> {
                    8301, 0, 8302, 1, 8302, -1
                }, new List <int>
                {
                    1111955,  // Abandoned Dungeon Entrance
                    1111950,  // Ashina Castle
                    1111951,  // Upper Tower - Antechamber
                    1111957,  // Upper Tower - Ashina Dojo
                    1111952,  // Castle Tower Lookout
                    1111956,  // Old Grave
                    1111954,  // Great Serpent Shrine
                }),
                // Fountainhead
                (5, new List <string> {
                    "fountainhead"
                },
                 new List <int> {
                }, new List <int>
                {
                    2501950,  // Fountainhead Palace
                    2501951,  // Vermilion Bridge
                    2501956,  // Mibu Manor
                    2501952,  // Flower Viewing Stage
                    2501958,  // Near Pot Noble
                    2501953,  // Great Sakura
                    2501954,  // Palace Grounds
                    2501955,  // Sanctuary
                }),
                // Hirata Revisited
                (5, new List <string> {
                    "hirata"
                },
                 new List <int> {
                    1000353, 0, 1005601, 0, 1000301, 0, 1000900, 0
                }, new List <int>
                {
                    1001952,  // Bamboo Thicket Slope
                    1001953,  // Hirata Estate - Main Hall
                    1001955,  // Hirata Audience Chamber
                    1001954,  // Hirata Estate - Hidden Temple
                }),
                // Ashina Castle End to Outskirts
                (5, new List <string> {
                    "ashinacastle", "ashinaoutskirts"
                },
                 new List <int> {
                    8302, 0
                }, new List <int>
                {
                    1111953,  // Upper Tower - Kuro's Room
                    1111956,  // Old Grave
                    1101952,  // Outskirts Wall - Stairway
                    1101951,  // Outskirts Wall - Gate Path
                }),
                // Ashina Castle End to Reservoir
                (5, new List <string> {
                    "ashinacastle", "ashinareservoir"
                },
                 new List <int> {
                    8302, 0
                }, new List <int>
                {
                    1111953,  // Upper Tower - Kuro's Room
                    1111957,  // Upper Tower - Ashina Dojo
                    1111951,  // Upper Tower - Antechamber
                    1111950,  // Ashina Castle
                    1121951,  // Ashina Reservoir
                    1121950,  // Near Secret Passage
                }),
            };
            FMG bonfires = new GameEditor(GameSpec.FromGame.SDT).LoadBnd(@"C:\Program Files (x86)\Steam\steamapps\common\Sekiro\msg\engus\menu.msgbnd.dcx", (p, n) => FMG.Read(p))["NTC_\u30e1\u30cb\u30e5\u30fc\u30c6\u30ad\u30b9\u30c8"];
            Dictionary <int, string> names = new Dictionary <int, string>();

            foreach (PARAM.Row r in game.Params["BonfireWarpParam"].Rows)
            {
                // break;
                int    entity  = (int)r["WarpEventId"].Value;
                string bonfire = bonfires[(int)r["BonfireNameId"].Value];
                if (bonfire != null && entity > 0)
                {
                    names[entity] = bonfire;
                    // Console.WriteLine($"{entity},  // {bonfire}");
                }
            }
            Dictionary <int, Vector3> points = new Dictionary <int, Vector3>();

            // Find location of all bonfires
            foreach (KeyValuePair <string, MSBS> entry in game.Smaps)
            {
                if (!game.Locations.ContainsKey(entry.Key))
                {
                    continue;
                }
                string map = game.Locations[entry.Key];
                MSBS   msb = entry.Value;
                foreach (MSBS.Part.Object e in msb.Parts.Objects)
                {
                    if (allBonfires.Contains(e.EntityID))
                    {
                        points[e.EntityID] = e.Position;
                    }
                }
            }
            string pathText(int p)
            {
                int first = paths[p].Item4.First();
                int last  = paths[p].Item4.Last();

                return($"#{paths[p].Item1} {names[first]}->{names[last]}");
            }

            bool investigateScaling = false;
            List <List <EnemyClass> > typeGroups = new List <List <EnemyClass> >
            {
                new List <EnemyClass> {
                    EnemyClass.Boss, EnemyClass.TutorialBoss
                },
                new List <EnemyClass> {
                    EnemyClass.Miniboss
                },
                new List <EnemyClass> {
                    EnemyClass.Basic, EnemyClass.FoldingMonkey, EnemyClass.OldDragon
                }
            };
            List <EnemyClass>           types = typeGroups.SelectMany(c => c).ToList();
            Dictionary <int, EnemyInfo> infos = fullInfos.Values.Where(e => types.Contains(e.Class)).ToDictionary(e => e.ID, e => e);

            if (!investigateScaling)
            {
                infos.Remove(1110920);
                infos.Remove(1110900);
                infos.Remove(1120800);
            }
            Dictionary <int, List <int> > possiblePaths = new Dictionary <int, List <int> >();
            bool explainCat = false;

            for (int i = 0; i < paths.Count; i++)
            {
                if (explainCat)
                {
                    Console.WriteLine($"--- Processing {pathText(i)}");
                }
                (int section, List <string> maps, List <int> cond, List <int> order) = paths[i];
                Dictionary <int, List <int> > eventFlags = new Dictionary <int, List <int> >();
                HashSet <int> excludeEntity = new HashSet <int>();
                HashSet <int> expectEntity  = new HashSet <int>();
                HashSet <int> getEntity     = new HashSet <int>();
                for (int j = 0; j < cond.Count; j += 2)
                {
                    int check = cond[j];
                    int val   = cond[j + 1];
                    if (check >= 1000000)
                    {
                        if (val == 0)
                        {
                            expectEntity.Add(check);
                        }
                        else
                        {
                            excludeEntity.Add(check);
                        }
                    }
                    else
                    {
                        AddMulti(eventFlags, check, val);
                    }
                }
                foreach (KeyValuePair <string, MSBS> entry in game.Smaps)
                {
                    if (!game.Locations.ContainsKey(entry.Key))
                    {
                        continue;
                    }
                    string map = game.Locations[entry.Key];
                    MSBS   msb = entry.Value;

                    if (!maps.Contains(map))
                    {
                        continue;
                    }
                    foreach (MSBS.Part.Enemy e in msb.Parts.Enemies)
                    {
                        if (!infos.ContainsKey(e.EntityID))
                        {
                            continue;
                        }
                        points[e.EntityID] = e.Position;
                        names[e.EntityID]  = game.ModelName(e.ModelName);
                        List <int> ids = new List <int> {
                            e.EntityID
                        };
                        ids.AddRange(e.EntityGroupIDs.Where(id => id > 0));
                        if (excludeEntity.Overlaps(ids))
                        {
                            if (explainCat)
                            {
                                Console.WriteLine($"excluded: {string.Join(",", ids)} from {string.Join(",", excludeEntity)}");
                            }
                            continue;
                        }
                        else if (expectEntity.Overlaps(ids))
                        {
                            getEntity.UnionWith(ids);
                        }
                        else if (eventFlags.Count > 0)
                        {
                            // If not explicitly expected, do a check for game progression
                            Dictionary <int, int> flags = new Dictionary <int, int>();
                            if (e.EventFlagID != -1)
                            {
                                flags[e.EventFlagID] = e.EventFlagCompareState;
                            }
                            if (e.UnkT48 != -1)
                            {
                                flags[e.UnkT48] = e.UnkT4C;
                            }
                            if (e.UnkT50 != -1)
                            {
                                flags[e.UnkT50] = 1;
                            }
                            bool mismatch = false;
                            foreach (KeyValuePair <int, List <int> > flagPair in eventFlags)
                            {
                                int        flag = flagPair.Key;
                                List <int> cmps = flagPair.Value;
                                int        cmp  = flags.TryGetValue(flag, out int tmp) ? tmp : -1;
                                if (explainCat && e.EntityID == 9999999)
                                {
                                    Console.WriteLine($"for {e.EntityID} expected {flag} = {string.Join(",", cmps)}, found result {cmp}");
                                }
                                if (!cmps.Contains(cmp))
                                {
                                    if (explainCat)
                                    {
                                        Console.WriteLine($"excluded: {string.Join(",", ids)} with {flag} = {cmp} (not {string.Join(",", cmps)})");
                                    }
                                    mismatch = true;
                                }
                            }
                            if (mismatch)
                            {
                                continue;
                            }
                        }
                        if (explainCat)
                        {
                            Console.WriteLine($"added: {string.Join(",", ids)}");
                        }
                        AddMulti(possiblePaths, e.EntityID, i);
                    }
                }
                List <int> missing = expectEntity.Except(getEntity).ToList();
                if (missing.Count > 0)
                {
                    throw new Exception($"Missing {string.Join(",", missing)} in {string.Join(",", maps)}");
                }
            }
            // Hardcode headless into Senpou, because it is out of the way and sort of a singleton
            possiblePaths[1100330] = new List <int> {
                7
            };

            Console.WriteLine("Categories");
            Dictionary <int, (int, float)> chosenPath = new Dictionary <int, (int, float)>();

            foreach (EnemyInfo info in infos.Values)
            {
                if (!possiblePaths.TryGetValue(info.ID, out List <int> pathList))
                {
                    throw new Exception($"{info.ID} has no categorization: {info.DebugText}");
                }
                if (paths[pathList[0]].Item2.Contains("hirata"))
                {
                    // If Hirata, greedily choose pre-revisited Hirata
                    pathList = new List <int> {
                        pathList[0]
                    };
                }
                float   score = float.PositiveInfinity;
                Vector3 pos   = points[info.ID];
                foreach (int path in pathList)
                {
                    (int section, List <string> maps, List <int> cond, List <int> order) = paths[path];
                    for (int i = 0; i < order.Count - 1; i++)
                    {
                        Vector3 p1    = points[order[i]];
                        Vector3 p2    = points[order[i + 1]];
                        float   dist1 = Vector3.Distance(p1, pos);
                        float   dist2 = Vector3.Distance(p2, pos);
                        float   dist  = dist1 + dist2;
                        if (info.ID == 9999999)
                        {
                            Console.WriteLine($"Found dist {dist1} to {names[order[i]]}, and {dist2} to {names[order[i + 1]]}. TOTAL {dist}");
                        }
                        if (dist < score)
                        {
                            score = dist;
                            chosenPath[info.ID] = (path, i + Vector3.Distance(p1, pos) / dist);
                        }
                    }
                }
                if (float.IsInfinity(score))
                {
                    throw new Exception($"{info.ID} with paths {string.Join(",", pathList.Select(pathText))} had nothing checked for it");
                }
            }

            // Put bosses in phase order
            foreach (int id in chosenPath.Keys.ToList())
            {
                EnemyInfo info = infos[id];
                if (info.Class == EnemyClass.Boss && info.OwnedBy != 0)
                {
                    chosenPath[id] = chosenPath[info.OwnedBy];
                }
            }

            if (investigateScaling)
            {
                Dictionary <int, MSBS.Part.Enemy> enemies = new Dictionary <int, MSBS.Part.Enemy>();
                foreach (KeyValuePair <string, MSBS> entry in game.Smaps)
                {
                    if (!game.Locations.ContainsKey(entry.Key))
                    {
                        continue;
                    }
                    string map = game.Locations[entry.Key];
                    MSBS   msb = entry.Value;
                    foreach (MSBS.Part.Enemy e in msb.Parts.Enemies)
                    {
                        enemies[e.EntityID] = e;
                    }
                }

                // Exclude these from scaling considerations, since they are not really part of the area (meant for when visiting later)
                HashSet <int> phantomGroups = new HashSet <int>
                {
                    // Ashina phantoms
                    1505201, 1505211, 1705200, 1705201, 2005200, 2005201,
                    // Sunken Valley phantoms
                    1505202, 1505212, 2005210, 2005211,
                    // Mibu Village phantoms
                    1705220, 1705221, 2005220, 2005221,
                };

                // haveSoulRate Unk85: NG+ only
                // EventFlagId: used for scaling speffect
                // There are these overall groups: vitality, damage, experience, cash. (is there haveSoulRate for cash/xp? maybe it's Unk85)
                List <string> scaleSp   = "maxHpRate maxStaminaCutRate physAtkPowerRate magicAtkPowerRate fireAtkPowerRate thunderAtkPowerRate staminaAttackRate darkAttackPowerRate NewGameBonusUnk".Split(' ').ToList();
                List <string> scaleNpc  = "Hp getSoul stamina staminaRecoverBaseVal Experience".Split(' ').ToList();
                List <string> allFields = scaleSp.Concat(scaleNpc).ToList();
                // Disp: ModelDispMask0 -> ModelDispMask31
                // Npc param has GameClearSpEffectID
                Dictionary <(string, int, int), List <float> > allScales = new Dictionary <(string, int, int), List <float> >();
                Dictionary <int, int> allSections = new Dictionary <int, int>();
                foreach (List <EnemyClass> typeGroup in typeGroups)
                {
                    // Consider two enemies the same if they have the same think id, or same disp mask
                    // Or for minibosses, if they are just the same model, that's probably fine
                    Dictionary <string, List <int> > thinks = new Dictionary <string, List <int> >();
                    Dictionary <string, List <int> > masks  = new Dictionary <string, List <int> >();
                    Dictionary <string, List <int> > bosses = new Dictionary <string, List <int> >();
                    List <string>         order             = new List <string>();
                    Dictionary <int, int> sections          = new Dictionary <int, int>();
                    foreach (KeyValuePair <int, (int, float)> entry in chosenPath.OrderBy(e => (e.Value, e.Key)))
                    {
                        int       id   = entry.Key;
                        EnemyInfo info = infos[id];
                        if (!typeGroup.Contains(info.Class))
                        {
                            continue;
                        }
                        MSBS.Part.Enemy e       = enemies[id];
                        int             path    = entry.Value.Item1;
                        int             section = paths[path].Item1;
                        sections[id]    = section;
                        allSections[id] = section;
                        if (e.EntityGroupIDs.Any(g => phantomGroups.Contains(g)))
                        {
                            continue;
                        }
                        string model = game.ModelName(e.ModelName);
                        if (typeGroup.Contains(EnemyClass.Miniboss) || typeGroup.Contains(EnemyClass.Boss))
                        {
                            AddMulti(bosses, model, id);
                            continue;
                        }
                        string think = $"{model} {e.ThinkParamID}";
                        AddMulti(thinks, think, id);
                        PARAM.Row npc = game.Params["NpcParam"][e.NPCParamID];
                        if (e.NPCParamID > 0 && npc != null)
                        {
                            uint mask = 0;
                            for (int i = 0; i < 32; i++)
                            {
                                if ((byte)npc[$"ModelDispMask{i}"].Value == 1)
                                {
                                    mask |= ((uint)1 << i);
                                }
                            }
                            string dispMask = $"{model} 0x{mask:X8}";
                            AddMulti(masks, dispMask, id);
                        }
                    }
                    foreach (KeyValuePair <string, List <int> > entry in thinks.Concat(masks.Concat(bosses)))
                    {
                        if (entry.Value.Count == 1)
                        {
                            continue;
                        }
                        List <int> secs = entry.Value.Select(i => sections[i]).Distinct().ToList();
                        if (secs.Count == 1)
                        {
                            continue;
                        }

                        Console.WriteLine($"{entry.Key}: {string.Join(",", entry.Value.Select(i => $"{i}[{sections[i]}]"))}");
                        SortedDictionary <string, List <(int, float)> > fieldValues = new SortedDictionary <string, List <(int, float)> >();
                        foreach (int id in entry.Value)
                        {
                            MSBS.Part.Enemy e   = enemies[id];
                            PARAM.Row       npc = game.Params["NpcParam"][e.NPCParamID];
                            if (e.NPCParamID == 0 || npc == null)
                            {
                                continue;
                            }
                            Dictionary <string, float> values = new Dictionary <string, float>();
                            foreach (string f in scaleNpc)
                            {
                                values[f] = float.Parse(npc[f].Value.ToString());
                            }
                            int       spVal = (int)npc["EventFlagId"].Value; // GameClearSpEffectID is for NG+ only, or time-of-day only, or something like that
                            PARAM.Row sp    = game.Params["SpEffectParam"][spVal];
                            if (spVal > 0 && sp != null)
                            {
                                foreach (string f in scaleSp)
                                {
                                    values[f] = float.Parse(sp[f].Value.ToString());
                                }
                            }
                            foreach (KeyValuePair <string, float> val in values)
                            {
                                AddMulti(fieldValues, val.Key, (sections[id], val.Value));
                            }
                        }
                        foreach (KeyValuePair <string, List <(int, float)> > val in fieldValues)
                        {
                            // Console.WriteLine($"  {val.Key}: {string.Join(", ", val.Value.OrderBy(v => v).Select(v => $"[{v.Item1}]{v.Item2}"))}");
                            Dictionary <int, float> bySection = val.Value.GroupBy(v => v.Item1).ToDictionary(g => g.Key, g => g.Select(v => v.Item2).Average());
                            List <string>           sorts     = new List <string>();
                            foreach (int i in bySection.Keys)
                            {
                                foreach (int j in bySection.Keys)
                                {
                                    if (i >= j)
                                    {
                                        continue;
                                    }
                                    float ratio = bySection[j] / bySection[i];
                                    if (float.IsNaN(ratio) || float.IsInfinity(ratio) || ratio == 1 || ratio == 0)
                                    {
                                        continue;
                                    }
                                    sorts.Add($"{i}{j}: {ratio:f3}x");
                                    AddMulti(allScales, (val.Key, i, j), ratio);
                                    // Can be used for complete table, but easier to leave out for lower diagonal
                                    // AddMulti(allScales, (val.Key, j, i), 1 / ratio);
                                }
                            }
                            if (sorts.Count > 0)
                            {
                                Console.WriteLine($"  {val.Key}: {string.Join(", ", sorts)}");
                            }
                        }
                    }
                }
                foreach (string field in allFields)
                {
                    Console.WriteLine($"-- {field} ({allScales.Where(k => k.Key.Item1 == field).Sum(e => e.Value.Count)})");
                    for (int i = 1; i <= 5; i++)
                    {
                        // row: the target class. column: the source class. value: how much to multiply to place the source in the target.
                        Console.WriteLine("  " + string.Join(" ", Enumerable.Range(1, 5).Select(j => allScales.TryGetValue((field, j, i), out List <float> floats) ? $"{floats.Average():f5}," : "        ")));
                    }
                }
                foreach (EnemyInfo info in fullInfos.Values)
                {
                    if (!allSections.ContainsKey(info.ID) && info.Class == EnemyClass.Helper && allSections.TryGetValue(info.OwnedBy, out int section))
                    {
                        allSections[info.ID] = section;
                    }
                }
                foreach (KeyValuePair <int, int> entry in allSections.OrderBy(e => (e.Value, e.Key)))
                {
                    Console.WriteLine($"  {entry.Key}: {entry.Value}");
                }
            }

            bool debugOutput = false;

            foreach (List <EnemyClass> typeGroup in typeGroups)
            {
                List <string> order = new List <string>();
                foreach (KeyValuePair <int, (int, float)> entry in chosenPath.OrderBy(e => (e.Value, e.Key)))
                {
                    int       id   = entry.Key;
                    EnemyInfo info = infos[id];
                    if (!typeGroup.Contains(info.Class))
                    {
                        continue;
                    }
                    if (debugOutput)
                    {
                        Console.WriteLine($"{info.DebugText}\n- {pathText(entry.Value.Item1)}, progress {entry.Value.Item2}\n");
                    }
                    order.Add($"{info.ExtraName ?? names[id]} {id}");
                }
                for (int i = 0; i < order.Count; i++)
                {
                    if (!debugOutput)
                    {
                        Console.WriteLine($"  {order[i]}: {order[order.Count - 1 - i]}");
                    }
                }
            }
        }
Ejemplo n.º 25
0
        private static void ReadMessagesIntoDatabase(SQLiteConnection con, string name, FMG msgFile)
        {
            var tableName = DesMsgFileNamesToEnglish.TryGetValue(name, out string value) ? value : name;

            Console.WriteLine("Tablename is " + tableName);

            // Create the table to write into
            // Oddly enough, DS1 seems to have multiple copies of the same data in the same file.
            // Not sure if that's a source issue, or bug.
            var sb = new StringBuilder();

            sb.Append(@"CREATE TABLE IF NOT EXISTS'");
            sb.Append(tableName);
            sb.Append("' (");

            sb.Append(@"'id' INTEGER NOT NULL PRIMARY KEY,");
            sb.Append(@"'message' TEXT);");

            using (var cmd = new SQLiteCommand(sb.ToString(), con))
            {
                cmd.ExecuteNonQuery();
            }

            // Actually insert our data
            sb.Clear();
            sb.Append(@"INSERT OR IGNORE INTO '");
            sb.Append(tableName);
            sb.Append(@"' (id, message) VALUES($id, $message);");

            using (var cmd = new SQLiteCommand(sb.ToString(), con))
            {
                var idParam = cmd.CreateParameter();
                idParam.ParameterName = @"$id";
                cmd.Parameters.Add(idParam);

                var msgParam = cmd.CreateParameter();
                msgParam.IsNullable    = true;
                msgParam.ParameterName = @"$message";
                cmd.Parameters.Add(msgParam);

                foreach (FMG.Entry entry in msgFile.Entries)
                {
                    idParam.Value  = entry.ID;
                    msgParam.Value = entry.Text;

                    cmd.ExecuteNonQuery();
                }
            }
        }
Ejemplo n.º 26
0
        public static void LoadAllFMG(bool forceReload)
        {
            if (!forceReload && GameTypeCurrentFmgsAreLoadedFrom == GameDataManager.GameType)
            {
                return;
            }

            FMG weaponNamesFmg    = null;
            FMG protectorNamesFmg = null;

            FMG weaponNamesFmg_dlc1    = null;
            FMG protectorNamesFmg_dlc1 = null;

            FMG weaponNamesFmg_dlc2    = null;
            FMG protectorNamesFmg_dlc2 = null;

            if (GameDataManager.GameType == GameDataManager.GameTypes.DS1)
            {
                var msgbnd = BND3.Read($@"{GameDataManager.InterrootPath}\msg\ENGLISH\item.msgbnd");

                weaponNamesFmg    = FMG.Read(msgbnd.Files.Last(f => f.Name.EndsWith("武器名.fmg")).Bytes);
                protectorNamesFmg = FMG.Read(msgbnd.Files.Last(f => f.Name.EndsWith("防具名.fmg")).Bytes);
            }
            else if (GameDataManager.GameType == GameDataManager.GameTypes.DS1R)
            {
                var msgbnd = BND3.Read($@"{GameDataManager.InterrootPath}\msg\ENGLISH\item.msgbnd.dcx");

                weaponNamesFmg    = FMG.Read(msgbnd.Files.Last(f => f.Name.EndsWith("Weapon_name_.fmg")).Bytes);
                protectorNamesFmg = FMG.Read(msgbnd.Files.Last(f => f.Name.EndsWith("Armor_name_.fmg")).Bytes);
            }
            else if (GameDataManager.GameType == GameDataManager.GameTypes.DS3)
            {
                var msgbnd = BND4.Read($@"{GameDataManager.InterrootPath}\msg\engus\item_dlc2.msgbnd.dcx");

                weaponNamesFmg    = FMG.Read(msgbnd.Files.Last(f => f.Name.EndsWith("武器名.fmg")).Bytes);
                protectorNamesFmg = FMG.Read(msgbnd.Files.Last(f => f.Name.EndsWith("防具名.fmg")).Bytes);

                var weaponNamesFmg_dlc1_entry = msgbnd.Files.LastOrDefault(f => f.Name.EndsWith("武器名_dlc1.fmg"));
                if (weaponNamesFmg_dlc1_entry != null)
                {
                    weaponNamesFmg_dlc1 = FMG.Read(weaponNamesFmg_dlc1_entry.Bytes);
                }

                var weaponNamesFmg_dlc2_entry = msgbnd.Files.LastOrDefault(f => f.Name.EndsWith("武器名_dlc2.fmg"));
                if (weaponNamesFmg_dlc2_entry != null)
                {
                    weaponNamesFmg_dlc2 = FMG.Read(weaponNamesFmg_dlc2_entry.Bytes);
                }

                var protectorNamesFmg_dlc1_entry = msgbnd.Files.LastOrDefault(f => f.Name.EndsWith("防具名_dlc1.fmg"));
                if (protectorNamesFmg_dlc1_entry != null)
                {
                    protectorNamesFmg_dlc1 = FMG.Read(protectorNamesFmg_dlc1_entry.Bytes);
                }

                var protectorNamesFmg_dlc2_entry = msgbnd.Files.LastOrDefault(f => f.Name.EndsWith("防具名_dlc2.fmg"));
                if (protectorNamesFmg_dlc2_entry != null)
                {
                    protectorNamesFmg_dlc2 = FMG.Read(protectorNamesFmg_dlc2_entry.Bytes);
                }
            }
            else if (GameDataManager.GameType == GameDataManager.GameTypes.BB)
            {
                var msgbnd = BND4.Read($@"{GameDataManager.InterrootPath}\msg\engus\item.msgbnd.dcx");

                weaponNamesFmg    = FMG.Read(msgbnd.Files.Last(f => f.Name.EndsWith("武器名.fmg")).Bytes);
                protectorNamesFmg = FMG.Read(msgbnd.Files.Last(f => f.Name.EndsWith("防具名.fmg")).Bytes);
            }

            WeaponNames.Clear();

            void DoWeaponEntry(FMG.Entry entry)
            {
                if (string.IsNullOrWhiteSpace(entry.Text) ||
                    !ParamManager.EquipParamWeapon.ContainsKey(entry.ID))
                {
                    return;
                }

                if (GameDataManager.GameType == GameDataManager.GameTypes.DS3 && (entry.ID % 10000) != 0)
                {
                    return;
                }
                else if ((entry.ID % 1000) != 0)
                {
                    return;
                }

                WeaponNames.Add(entry.ID, entry.Text);
            }

            if (weaponNamesFmg != null)
            {
                foreach (var entry in weaponNamesFmg.Entries)
                {
                    DoWeaponEntry(entry);
                }
            }

            if (weaponNamesFmg_dlc1 != null)
            {
                foreach (var entry in weaponNamesFmg_dlc1.Entries)
                {
                    DoWeaponEntry(entry);
                }
            }

            if (weaponNamesFmg_dlc2 != null)
            {
                foreach (var entry in weaponNamesFmg_dlc2.Entries)
                {
                    DoWeaponEntry(entry);
                }
            }

            ProtectorNames_HD.Clear();
            ProtectorNames_BD.Clear();
            ProtectorNames_AM.Clear();
            ProtectorNames_LG.Clear();

            void DoProtectorParamEntry(FMG.Entry entry)
            {
                if (string.IsNullOrWhiteSpace(entry.Text))
                {
                    return;
                }
                if (entry.ID < 1000000 && GameDataManager.GameType == GameDataManager.GameTypes.DS3)
                {
                    return;
                }
                if (ParamManager.EquipParamProtector.ContainsKey(entry.ID))
                {
                    var protectorParam = ParamManager.EquipParamProtector[entry.ID];
                    if (protectorParam.HeadEquip)
                    {
                        ProtectorNames_HD.Add(entry.ID, entry.Text);
                    }
                    else if (protectorParam.BodyEquip)
                    {
                        ProtectorNames_BD.Add(entry.ID, entry.Text);
                    }
                    else if (protectorParam.ArmEquip)
                    {
                        ProtectorNames_AM.Add(entry.ID, entry.Text);
                    }
                    else if (protectorParam.LegEquip)
                    {
                        ProtectorNames_LG.Add(entry.ID, entry.Text);
                    }
                }
            }

            if (protectorNamesFmg != null)
            {
                foreach (var entry in protectorNamesFmg.Entries)
                {
                    DoProtectorParamEntry(entry);
                }
            }

            if (protectorNamesFmg_dlc1 != null)
            {
                foreach (var entry in protectorNamesFmg_dlc1.Entries)
                {
                    DoProtectorParamEntry(entry);
                }
            }

            if (protectorNamesFmg_dlc2 != null)
            {
                foreach (var entry in protectorNamesFmg_dlc2.Entries)
                {
                    DoProtectorParamEntry(entry);
                }
            }

            WeaponNames       = WeaponNames.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_HD = ProtectorNames_HD.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_BD = ProtectorNames_BD.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_AM = ProtectorNames_AM.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            ProtectorNames_LG = ProtectorNames_LG.OrderBy(x => x.Key).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

            foreach (var protector in ParamManager.EquipParamProtector)
            {
                if (protector.Value.HeadEquip && !ProtectorNames_HD.ContainsKey((int)protector.Key))
                {
                    ProtectorNames_HD.Add((int)protector.Key, $"<Head {protector.Key}>");
                }
                else if (protector.Value.BodyEquip && !ProtectorNames_BD.ContainsKey((int)protector.Key))
                {
                    ProtectorNames_BD.Add((int)protector.Key, $"<Body {protector.Key}>");
                }
                else if (protector.Value.ArmEquip && !ProtectorNames_AM.ContainsKey((int)protector.Key))
                {
                    ProtectorNames_AM.Add((int)protector.Key, $"<Arms {protector.Key}>");
                }
                else if (protector.Value.LegEquip && !ProtectorNames_LG.ContainsKey((int)protector.Key))
                {
                    ProtectorNames_LG.Add((int)protector.Key, $"<Legs {protector.Key}>");
                }
            }

            foreach (var weapon in ParamManager.EquipParamWeapon)
            {
                if (!WeaponNames.ContainsKey((int)weapon.Key))
                {
                    WeaponNames.Add((int)weapon.Key, $"<Weapon {weapon.Key}>");
                }
            }

            GameTypeCurrentFmgsAreLoadedFrom = GameDataManager.GameType;
        }
Ejemplo n.º 27
0
    void handleLevelSelect(FMG.LevelSelect ls)
    {
        if(ls)
        {
            Debug.Log ("found levelselect");
            GameObject objectToReplace = ls.levelButton;
            UnityEngine.UI.Image img = objectToReplace.GetComponent<UnityEngine.UI.Image>();
            if(img)
            {
                img.sprite = buttonSprite;
                Debug.Log ("found image");

            }
            UnityEngine.UI.Text text = objectToReplace.GetComponentInChildren<UnityEngine.UI.Text>();
            if(text)
            {
                text.font = font;
                text.color = fontColor;

                Debug.Log ("found text");

            }
            //PrefabUtility.ReplacePrefab(objectToReplace, objectToReplace, ReplacePrefabOptions.ConnectToPrefab);
        }
    }
Ejemplo n.º 28
0
        private static bool UnpackFile(string sourceFile)
        {
            string sourceDir = Path.GetDirectoryName(sourceFile);
            string filename  = Path.GetFileName(sourceFile);
            string targetDir = $"{sourceDir}\\{filename.Replace('.', '-')}";

            if (File.Exists(targetDir))
            {
                targetDir += "-ybr";
            }

            if (DCX.Is(sourceFile))
            {
                Console.WriteLine($"Decompressing DCX: {filename}...");
                byte[] bytes = DCX.Decompress(sourceFile, out DCX.Type compression);
                if (BND3.Is(bytes))
                {
                    Console.WriteLine($"Unpacking BND3: {filename}...");
                    BND3 bnd = BND3.Read(bytes);
                    bnd.Compression = compression;
                    bnd.Unpack(filename, targetDir);
                }
                else if (BND4.Is(bytes))
                {
                    Console.WriteLine($"Unpacking BND4: {filename}...");
                    BND4 bnd = BND4.Read(bytes);
                    bnd.Compression = compression;
                    bnd.Unpack(filename, targetDir);
                }
                else if (TPF.Is(bytes))
                {
                    Console.WriteLine($"Unpacking TPF: {filename}...");
                    TPF tpf = TPF.Read(bytes);
                    tpf.Compression = compression;
                    tpf.Unpack(filename, targetDir);
                }
                else if (sourceFile.EndsWith(".gparam.dcx"))
                {
                    Console.WriteLine($"Unpacking GPARAM: {filename}...");
                    GPARAM gparam = GPARAM.Read(bytes);
                    gparam.Unpack(sourceFile);
                }
                else
                {
                    Console.WriteLine($"File format not recognized: {filename}");
                    return(true);
                }
            }
            else
            {
                if (BND3.Is(sourceFile))
                {
                    Console.WriteLine($"Unpacking BND3: {filename}...");
                    BND3 bnd = BND3.Read(sourceFile);
                    bnd.Unpack(filename, targetDir);
                }
                else if (BND4.Is(sourceFile))
                {
                    Console.WriteLine($"Unpacking BND4: {filename}...");
                    BND4 bnd = BND4.Read(sourceFile);
                    bnd.Unpack(filename, targetDir);
                }
                else if (BXF3.IsBHD(sourceFile))
                {
                    string bdtExtension = Path.GetExtension(filename).Replace("bhd", "bdt");
                    string bdtFilename  = $"{Path.GetFileNameWithoutExtension(filename)}{bdtExtension}";
                    string bdtPath      = $"{sourceDir}\\{bdtFilename}";
                    if (File.Exists(bdtPath))
                    {
                        Console.WriteLine($"Unpacking BXF3: {filename}...");
                        BXF3 bxf = BXF3.Read(sourceFile, bdtPath);
                        bxf.Unpack(filename, bdtFilename, targetDir);
                    }
                    else
                    {
                        Console.WriteLine($"BDT not found for BHD: {filename}");
                        return(true);
                    }
                }
                else if (BXF4.IsBHD(sourceFile))
                {
                    string bdtExtension = Path.GetExtension(filename).Replace("bhd", "bdt");
                    string bdtFilename  = $"{Path.GetFileNameWithoutExtension(filename)}{bdtExtension}";
                    string bdtPath      = $"{sourceDir}\\{bdtFilename}";
                    if (File.Exists(bdtPath))
                    {
                        Console.WriteLine($"Unpacking BXF4: {filename}...");
                        BXF4 bxf = BXF4.Read(sourceFile, bdtPath);
                        bxf.Unpack(filename, bdtFilename, targetDir);
                    }
                    else
                    {
                        Console.WriteLine($"BDT not found for BHD: {filename}");
                        return(true);
                    }
                }
                else if (TPF.Is(sourceFile))
                {
                    Console.WriteLine($"Unpacking TPF: {filename}...");
                    TPF tpf = TPF.Read(sourceFile);
                    tpf.Unpack(filename, targetDir);
                }
                else if (sourceFile.EndsWith(".fmg"))
                {
                    Console.WriteLine($"Unpacking FMG: {filename}...");
                    FMG fmg = FMG.Read(sourceFile);
                    fmg.Unpack(sourceFile);
                }
                else if (sourceFile.EndsWith(".fmg.xml"))
                {
                    Console.WriteLine($"Repacking FMG: {filename}...");
                    YFMG.Repack(sourceFile);
                }
                else if (sourceFile.EndsWith(".gparam"))
                {
                    Console.WriteLine($"Unpacking GPARAM: {filename}...");
                    GPARAM gparam = GPARAM.Read(sourceFile);
                    gparam.Unpack(sourceFile);
                }
                else if (sourceFile.EndsWith(".gparam.xml") || sourceFile.EndsWith(".gparam.dcx.xml"))
                {
                    Console.WriteLine($"Repacking GPARAM: {filename}...");
                    YGPARAM.Repack(sourceFile);
                }
                else if (sourceFile.EndsWith(".luagnl"))
                {
                    Console.WriteLine($"Unpacking LUAGNL: {filename}...");
                    LUAGNL gnl = LUAGNL.Read(sourceFile);
                    gnl.Unpack(sourceFile);
                }
                else if (sourceFile.EndsWith(".luagnl.xml"))
                {
                    Console.WriteLine($"Repacking LUAGNL: {filename}...");
                    YLUAGNL.Repack(sourceFile);
                }
                else if (LUAINFO.Is(sourceFile))
                {
                    Console.WriteLine($"Unpacking LUAINFO: {filename}...");
                    LUAINFO info = LUAINFO.Read(sourceFile);
                    info.Unpack(sourceFile);
                }
                else if (sourceFile.EndsWith(".luainfo.xml"))
                {
                    Console.WriteLine($"Repacking LUAINFO: {filename}...");
                    YLUAINFO.Repack(sourceFile);
                }
                else
                {
                    Console.WriteLine($"File format not recognized: {filename}");
                    return(true);
                }
            }
            return(false);
        }
        private static void PatchFMG(IBinder sourceBND, IBinder destBND, IBinder refBND, string destLang)
        {
            int iFile              = 0; //File counter
            int iRef               = 0; //Reference index counter
            int entriesAdded       = 0; //Total added per file
            int entriesOverwritten = 0; //Total overwritten per file

            Log.Add($"{ new DirectoryInfo(Path.GetDirectoryName(destLang)).Name } { Path.GetFileName(destLang) } start");

            foreach (var file in sourceBND.Files)      //For each FMG in the source BND file
            {
                if (file.ID > destBND.Files[iFile].ID) //Compatability for using program from non-English folders. Does nothing in the English folder.
                {
                    while ((file.ID > destBND.Files[iFile].ID) && (iFile < destBND.Files.Count - 1))
                    {
                        //ConsoleLog("Skipped " + destBND.Files[iFile].ID); //Debug
                        if (iFile < destBND.Files.Count - 1)
                        {
                            iFile++;
                        }
                    }
                }

                if (file.ID == destBND.Files[iFile].ID) //If the file IDs match, update. If not, skip until they do match
                {
                    //Add the source and destination FMG
                    Log.Add($"Destination: { Path.GetFileName(destBND.Files[iFile].Name) } / Source: { Path.GetFileName(file.Name) }");
                    //ConsoleLog(file.ID + " = true"); // Debug
                    FMG sourceFMG = FMG.Read(file.Bytes);
                    FMG destFMG   = FMG.Read((destBND.Files[iFile]).Bytes);

                    //Make dictionaries out of the FMG files
                    Dictionary <int, string> sourceDict = sourceFMG.Entries.GroupBy(x => x.ID).Select(x => x.First()).ToDictionary(x => x.ID, x => x.Text);
                    Dictionary <int, string> destDict   = destFMG.Entries.GroupBy(x => x.ID).Select(x => x.First()).ToDictionary(x => x.ID, x => x.Text);

                    entriesAdded = AddNew(entriesAdded, sourceDict, destDict);

                    //Make a refFMG and refDict if refBND isn't null
                    Dictionary <int, string> refDict = MakeRef(refBND, iRef);

                    //Make dicitonary based on comparing sourceDict to refDict
                    if (refDict != null)
                    {
                        entriesOverwritten = UpdateText(entriesOverwritten, refDict, sourceDict, destDict);
                    }

                    //Clear and rewrite FMG file
                    RewriteFMG(destFMG, destDict);

                    //Replace old null entries if no reference.
                    if (NoRef)
                    {
                        Log.Add("Changed:");
                        entriesOverwritten = NullOverwrite(entriesOverwritten, sourceFMG, destFMG);
                    }

                    #region Debug Stuff
                    //foreach (var entry in destFMG.Entries)
                    //{
                    //    ConsoleLog(entry);
                    //}
                    #endregion
                    //Write the new files
                    destBND.Files[iFile].Bytes = destFMG.Write();

                    //Add to counter if it's not already maxed
                    if (iFile < destBND.Files.Count - 1)
                    {
                        iFile++;
                    }
                }
                #region Debug Stuff
                //else //Debug
                //{
                //    ConsoleLog(file.ID + " = false");
                //}
                #endregion
                iRef++;
            }
            //Print stats for entire BND file
            ConsoleLog($"Patched: { new DirectoryInfo(Path.GetDirectoryName(destLang)).Name } { Path.GetFileName(destLang) }: { entriesAdded } entries added and { entriesOverwritten } entries overwritten");

            //Append log file
            Log.Add($"{ new DirectoryInfo(Path.GetDirectoryName(destLang)).Name } { Path.GetFileName(destLang) } end");
            File.AppendAllLines($@"{ Directory.GetCurrentDirectory() }\LangPatchLog.txt", Log);
            Log.Clear();
        }
Ejemplo n.º 30
0
        public void Write(RandomizerOptions opt, Permutation permutation)
        {
            // Overall: 9020, and 9200 to 9228
            // Exclude 9209 and 9215 and 9228 bc it's useful
            // Also, 9221 9223 9225 are 'purchase to read'
            // Can replace everything in quotes
            // Or after the first :\n
            // Or after the last \n\n
            // If the message ends with '               -?Name' can leave that. space or ideographic space (3000). multiple whitespace either way
            //
            // Alternatively: replace individual text, like 'moon-view tower'
            // defeat a named enemy
            // defeat a formidable foe
            // defeat a powerful enemy
            FMG itemDesc  = game.ItemFMGs["アイテム説明"];
            FMG eventText = game.MenuFMGs["イベントテキスト"];

            if (opt["writehints"])
            {
                List <int> eventIds = new List <int>
                {
                    12000000, 12000331, 12000021, 12000261, 12000275, 12000321, 12000285,
                    12000241, 12000011, 12000311, 12000231, 12000291, 12000341,
                };
                eventIds.Sort();
                HintData write = new HintData();
                void createHint(int id, string name, string text)
                {
                    Hint hint = new Hint
                    {
                        ID       = id,
                        Name     = name,
                        Versions = new List <HintTemplate>
                        {
                            new HintTemplate
                            {
                                Type = "default",
                                Text = text.Split('\n').ToList(),
                            }
                        },
                    };

                    write.Hints.Add(hint);
                }

                foreach (KeyValuePair <ItemKey, string> entry in game.Names())
                {
                    ItemKey key = entry.Key;
                    if (!(key.Type == ItemType.GOOD && (key.ID == 9020 || key.ID >= 9200 && key.ID <= 9228)))
                    {
                        continue;
                    }
                    createHint(key.ID, entry.Value, itemDesc[key.ID]);
                }
                foreach (int id in eventIds)
                {
                    createHint(id, null, eventText[id]);
                }
                ISerializer serializer = new SerializerBuilder().DisableAliases().Build();
                using (var writer = File.CreateText("hints.txt"))
                {
                    serializer.Serialize(writer, write);
                }
                return;
            }

            IDeserializer deserializer = new DeserializerBuilder().Build();
            HintData      hints;

            using (var reader = File.OpenText("dists/Base/hints.txt"))
            {
                hints = deserializer.Deserialize <HintData>(reader);
            }

            // Preprocess some items
            Dictionary <HintType, TypeHint>            typeNames    = hints.Types.ToDictionary(e => e.Name, e => e);
            Dictionary <ItemCategory, List <ItemKey> > categories   = ((ItemCategory[])Enum.GetValues(typeof(ItemCategory))).ToDictionary(e => e, e => new List <ItemKey>());
            Dictionary <ItemCategory, string>          categoryText = new Dictionary <ItemCategory, string>();

            foreach (ItemHint cat in hints.ItemCategories)
            {
                if (cat.Includes != null)
                {
                    categories[cat.Name].AddRange(cat.Includes.Split(' ').Select(i => ann.Items.TryGetValue(i, out ItemKey key) ? key : throw new Exception($"Unrecognized name {i}")));
                }
                if (cat.IncludesName != null)
                {
                    categories[cat.Name].AddRange(phraseRe.Split(cat.IncludesName).Select(i => game.ItemForName(i)));
                }
                if (cat.Text != null)
                {
                    categoryText[cat.Name] = cat.Text;
                }
            }
            if (opt["earlyhirata"])
            {
                categories[ItemCategory.ExcludeHints].Add(ann.Items["younglordsbellcharm"]);
            }
            categories[ItemCategory.ExcludeHints].AddRange(permutation.NotRequiredKeyItems);

            // TODO: Exclude non-technically-required items... calculate this in key item permutations
            List <ItemKey> allItems = permutation.KeyItems.ToList();

            if (opt["norandom_skills"])
            {
                categories[ItemCategory.ImportantTool].Clear();
            }
            else
            {
                allItems.AddRange(categories[ItemCategory.ImportantTool]);
            }
            allItems.AddRange(categories[ItemCategory.HintFodder]);

            Dictionary <string, ItemHintName> specialItemNames = hints.ItemNames.ToDictionary(n => n.Name, n => n);

            // Process areas
            Dictionary <string, AreaHint> areas = new Dictionary <string, AreaHint>();
            HashSet <string> gameAreas          = new HashSet <string>(ann.Areas.Keys);

            List <string> getAreasForName(string names)
            {
                List <string> nameList = new List <string>();

                foreach (string name in names.Split(' '))
                {
                    if (name.EndsWith("*"))
                    {
                        string        prefix   = name.Substring(0, name.Length - 1);
                        List <string> matching = gameAreas.Where(n => n.StartsWith(prefix)).ToList();
                        if (matching.Count == 0)
                        {
                            throw new Exception($"Unrecognized area in hint config: {name}");
                        }
                        nameList.AddRange(matching);
                    }
                    else
                    {
                        if (!gameAreas.Contains(name))
                        {
                            throw new Exception($"Unrecognized area in hint config: {name}");
                        }
                        nameList.Add(name);
                    }
                }
                return(nameList);
            }

            foreach (AreaHint area in hints.Areas)
            {
                if (area.Name == null || area.Includes == null)
                {
                    throw new Exception($"Missing data in area hint grouping {area.Name}");
                }
                areas[area.Name] = area;
                area.Areas.UnionWith(getAreasForName(area.Includes));
                if (area.Excludes != null)
                {
                    area.Areas.ExceptWith(getAreasForName(area.Excludes));
                }
                if (area.LaterIncludes != null)
                {
                    area.LaterAreas.UnionWith(getAreasForName(area.LaterIncludes));
                    if (!area.LaterAreas.IsSubsetOf(area.Areas))
                    {
                        throw new Exception($"Error in hint config: later areas of {area.Name} are not a subset of all areas");
                    }
                }
                area.EarlyAreas.UnionWith(area.Areas.Except(area.LaterAreas));
                if (area.Parent != null)
                {
                    if (!areas.TryGetValue(area.Parent, out AreaHint parent))
                    {
                        throw new Exception($"Error in hint config: parent of {area.Name} does not exist: {area.Parent}");
                    }
                    area.Parents.Add(parent);
                    area.Parents.UnionWith(parent.Parents);
                }
                if (area.Present != null)
                {
                    area.Types = area.Present.Split(' ').Select(t => (HintType)Enum.Parse(typeof(HintType), t)).ToList();
                }
            }

            bool printText = opt["hinttext"];

            // Process items to search for
            List <ItemCategory> categoryOverrides = new List <ItemCategory> {
                ItemCategory.RequiredKey, ItemCategory.RequiredAbility, ItemCategory.ImportantTool, ItemCategory.HintFodder
            };
            HashSet <string> chests = new HashSet <string> {
                "o005300", "o005400", "o255300"
            };
            List <Placement> placements = new List <Placement>();
            Dictionary <ItemKey, Placement> itemPlacement = new Dictionary <ItemKey, Placement>();

            foreach (ItemKey key in allItems)
            {
                if (categories[ItemCategory.ExcludeHints].Contains(key))
                {
                    continue;
                }
                if (!permutation.SkillAssignment.TryGetValue(key, out ItemKey lookup))
                {
                    lookup = key;
                }
                SlotKey      targetKey = permutation.GetFiniteTargetKey(lookup);
                ItemLocation itemLoc   = data.Location(targetKey);
                if (!ann.Slots.TryGetValue(itemLoc.LocScope, out SlotAnnotation slot))
                {
                    continue;
                }
                ItemCategory category = ItemCategory.RequiredItem;
                foreach (ItemCategory cat in categoryOverrides)
                {
                    // Use the last applicable category
                    if (categories[cat].Contains(key))
                    {
                        category = cat;
                    }
                }
                List <HintType> types = new List <HintType>();
                if (slot.HasTag("boss") || slot.HasTag("bosshint"))
                {
                    types.Add(HintType.Boss);
                    types.Add(HintType.Enemy);
                }
                else if (slot.HasTag("miniboss"))
                {
                    types.Add(HintType.Miniboss);
                    types.Add(HintType.Enemy);
                }
                else if (slot.HasTag("enemyhint"))
                {
                    types.Add(HintType.Enemy);
                }
                else if (slot.HasTag("carp"))
                {
                    types.Add(HintType.Carp);
                }
                else if (itemLoc.Keys.Any(k => k.Type == LocationKey.LocationType.SHOP && k.ID / 100 != 11005))
                {
                    types.Add(HintType.Shop);
                }
                else
                {
                    if (slot.Area.EndsWith("_underwater") || slot.HasTag("underwater"))
                    {
                        types.Add(HintType.Underwater);
                    }
                    if (itemLoc.Keys.Any(k => k.Type == LocationKey.LocationType.LOT && k.Entities.Any(e => chests.Contains(e.ModelName))))
                    {
                        types.Add(HintType.Chest);
                    }
                    types.Add(HintType.Treasure);
                }

                string    name      = game.Name(key);
                Placement placement = new Placement
                {
                    Item         = key,
                    FullName     = name,
                    Category     = category,
                    LateEligible = categories[ItemCategory.LatenessHints].Contains(key),
                    Important    = category != ItemCategory.HintFodder,
                    Area         = slot.Area,
                    Types        = types,
                };
                if (placement.Important)
                {
                    placements.Add(placement);
                }
                itemPlacement[key] = placement;
                if (printText)
                {
                    Console.WriteLine(placement);
                }
            }

            foreach (Hint hint in hints.Hints)  // Lovely
            {
                hint.Types = hint.Versions.Where(v => v.Type != "default").ToDictionary(v => v.Type, v => v);
            }

            // Classify early and late areas
            HashSet <string> early = new HashSet <string>(permutation.IncludedAreas.Where(e => !e.Value.Contains("ashinacastle")).Select(e => e.Key));
            HashSet <string> late  = new HashSet <string>(permutation.IncludedAreas.Where(e => e.Value.Contains("fountainhead_bridge")).Select(e => e.Key));

            // Start hints
            Random      random  = new Random((int)opt.Seed);
            List <Hint> sources = hints.Hints.Where(s => s.Types.Any(e => e.Key != "default")).ToList();

            Shuffle(random, sources);
            sources = sources.OrderBy(s => (s.HasInfix("bad") && s.Types.ContainsKey("hint")) ? 1 : 0).ToList();
            string choose(List <string> items)
            {
                return(items.Count == 1 ? items[0] : Choice(random, items));
            }

            // Process all hint types. There are 20 item locations in the entire game, plus 13 fixed texts, for a total of 33
            Regex format = new Regex(@"\(([^\)]*)\)");

            if (printText)
            {
                Console.WriteLine($"No hint items: {string.Join(", ", categories[ItemCategory.ExcludeHints].Select(k => game.Name(k)))}");
            }
            void addHint(Hint hint, HintTemplate t, Placement mainPlacement, Placement otherPlacement = null)
            {
                string text     = printText ? string.Join(" ", t.Text.Select(l => l.Trim())) : string.Join("\n", t.Text);
                bool   positive = !t.Type.Contains("bad");

                foreach (Match m in format.Matches(text))
                {
                    string    variable = m.Groups[1].Value;
                    string[]  parts    = variable.Split('_');
                    string    kind     = parts[0].ToLowerInvariant();
                    bool      upper    = char.IsUpper(parts[0][0]);
                    string    subkind  = parts.Length > 1 ? parts[1] : null;
                    string    value;
                    Placement placement = kind == "location2" && otherPlacement != null ? otherPlacement : mainPlacement;
                    if (kind == "item")
                    {
                        if (positive)
                        {
                            if (placement.LateHint || !categoryText.ContainsKey(placement.Category))
                            {
                                value = placement.FullName;
                                if (specialItemNames.TryGetValue(value, out ItemHintName vagueName))
                                {
                                    value = choose(vagueName.GetNames());
                                }
                            }
                            else
                            {
                                value = categoryText[placement.Category];
                            }
                        }
                        else
                        {
                            // Shouldn't be used in this context, but fall back
                            value = "nothing";
                        }
                    }
                    else if (kind == "type")
                    {
                        HintType type = placement.Types.FirstOrDefault();
                        value = choose(typeNames[placement.Types[0]].GetNames(subkind));
                    }
                    else if (kind == "location" || kind == "location2")
                    {
                        bool prep;
                        if (subkind == null)
                        {
                            prep = false;
                        }
                        else if (subkind == "preposition")
                        {
                            prep = true;
                        }
                        else
                        {
                            throw new Exception($"Unknown hint config variable {variable}");
                        }
                        if (positive || placement.Types.Count == 0)
                        {
                            value = placement.AreaHint == null ? (positive ? "somewhere" : "anywhere") : choose(placement.AreaHint.GetNames(prep));
                        }
                        else
                        {
                            if (prep || placement.AreaHint != null)
                            {
                                value = (prep ? "from " : "") + choose(typeNames[placement.Types[0]].GetNames("noun")) + " " + (placement.AreaHint == null ? (positive ? "somewhere" : "anywhere") : choose(placement.AreaHint.GetNames(true)));
                            }
                            else
                            {
                                value = value = choose(typeNames[placement.Types[0]].GetNames("gerund"));
                            }
                        }
                    }
                    else
                    {
                        throw new Exception($"Unknown hint variable {variable}");
                    }
                    if (upper)
                    {
                        value = value[0].ToString().ToUpperInvariant() + value.Substring(1);
                    }
                    text = text.Replace($"({variable})", value);
                }
                if (printText)
                {
                    Console.WriteLine(text + "\n");
                }
                if (hint.ID < 10000)
                {
                    itemDesc[hint.ID] = text;
                }
                else
                {
                    eventText[hint.ID] = text;
                }
            }

            AreaHint mostSpecificArea(string name)
            {
                AreaHint selected = null;

                foreach (AreaHint area in hints.Areas)
                {
                    if (area.EarlyAreas.Contains(name))
                    {
                        if (selected == null || area.Parents.Contains(selected))
                        {
                            selected = area;
                        }
                    }
                }
                return(selected);
            }

            T pop <T>(List <T> list)
            {
                T ret = list[list.Count - 1];

                list.RemoveAt(list.Count - 1);
                return(ret);
            }

            // In priority order:
            // Item hints: Find item at (type) and (location). Always filled in. 1 of these.
            HashSet <ItemKey> exactKey = new HashSet <ItemKey>();

            foreach (Hint hint in sources.Where(s => s.Types.ContainsKey("itemhint")).ToList())
            {
                HintTemplate t = hint.Types["itemhint"];
                if (!ann.Items.TryGetValue(t.Req, out ItemKey key))
                {
                    throw new Exception($"Unrecognized name {t.Req}");
                }
                if (!itemPlacement.TryGetValue(key, out Placement placement))
                {
                    continue;
                }
                exactKey.Add(key);
                placement          = placement.Copy();
                placement.AreaHint = mostSpecificArea(placement.Area);
                addHint(hint, t, placement);
                sources.Remove(hint);
            }

            // Location hints: Find (item/item category/nothing) at (location). Always filled in. 2 of these.
            foreach (Hint hint in sources.Where(s => s.Types.ContainsKey("locationhint")).ToList())
            {
                HintTemplate     t        = hint.Types["locationhint"];
                List <string>    reqAreas = t.Req.Split(' ').ToList();
                List <Placement> places   = placements.Where(p => reqAreas.Contains(p.Area)).ToList();
                if (places.Count == 0 && hint.Types.TryGetValue("locationbadhint", out HintTemplate t2))
                {
                    addHint(hint, t2, null);
                }
                else
                {
                    Placement placement = places[0].Copy();
                    addHint(hint, t, placement);
                }
                sources.Remove(hint);
            }

            // Global negative hint: There is nothing at (type). Always include as many as applicable for likely types, treasure/chest/boss/miniboss/enemy/underwater/shop (but more like 1-2)
            List <HintType> present = placements.SelectMany(p => p.Types).Distinct().ToList();
            List <HintType> absent  = new List <HintType>
            {
                HintType.Treasure, HintType.Chest, HintType.Boss, HintType.Miniboss, HintType.Enemy, HintType.Underwater, HintType.Shop
            }
            .Except(present).ToList();

            foreach (Hint hint in sources.Where(s => s.Types.ContainsKey("badhint")).ToList())
            {
                if (absent.Count == 0)
                {
                    break;
                }
                HintType     type = pop(absent);
                HintTemplate t    = hint.Types["badhint"];
                addHint(hint, t, new Placement
                {
                    Types = new List <HintType> {
                        type
                    },
                });
                sources.Remove(hint);
            }

            // Positive hint: There is (item/item category) at (type) in (location). Include one per key item, up to 13, and special items, currently 3.
            List <Placement> toPlace = placements.Where(p => !exactKey.Contains(p.Item)).ToList();

            foreach (Hint hint in sources.Where(s => s.Types.ContainsKey("hint")).ToList())
            {
                if (toPlace.Count == 0)
                {
                    break;
                }
                HintTemplate t         = hint.Types["hint"];
                Placement    placement = pop(toPlace);
                placement          = placement.Copy();
                placement.AreaHint = mostSpecificArea(placement.Area);
                addHint(hint, t, placement);
                sources.Remove(hint);
            }

            // Lateness hint: There is (item) at (type) (late/early). Include one per location item, up to 6.
            toPlace = placements.Where(p => p.LateEligible).ToList();
            foreach (Hint hint in sources.Where(s => s.Types.ContainsKey("hint")).ToList())
            {
                if (toPlace.Count == 0)
                {
                    break;
                }
                HintTemplate t         = hint.Types["hint"];
                Placement    placement = pop(toPlace);
                placement          = placement.Copy();
                placement.LateHint = true;
                string info = early.Contains(placement.Area) ? "an early game location" : (late.Contains(placement.Area) ? "a late game location" : "a mid game location");
                string prep = early.Contains(placement.Area) ? "in the early game" : (late.Contains(placement.Area) ? "in the late game" : "in the mid game");
                placement.AreaHint = new AreaHint {
                    Name = info, Vague = info, VaguePrep = prep
                };
                addHint(hint, t, placement);
                sources.Remove(hint);
            }

            // So far, around 25 hints created, with ~10 left to go. At this point, pick randomly from either of these categories.
            List <AreaHint> withoutRedundantChildren(List <AreaHint> allAreas)
            {
                return(allAreas.Where(area => area.Parents.Count == 0 || !area.Parents.Any(p => allAreas.Contains(p))).ToList());
            }

            List <AreaHint> areasWithoutPlacements(HintType type = HintType.None)
            {
                HashSet <string> importantAreas = new HashSet <string>(placements.Where(p => type == HintType.None || p.Types.Contains(type)).Select(p => p.Area));

                return(hints.Areas.Where(h => (type == HintType.None || h.Types.Contains(type)) && !importantAreas.Overlaps(h.Areas)).ToList());
            }

            // Area negative hint: There is nothing in (location). Can be included for all such areas, but eliminate a hint if its parent also applies.
            Dictionary <HintType, List <AreaHint> > negativeHints = new Dictionary <HintType, List <AreaHint> >();
            List <AreaHint> unimportantAreas = areasWithoutPlacements();

            negativeHints[HintType.None] = withoutRedundantChildren(unimportantAreas);
            foreach (HintType noType in new[] { HintType.Boss, HintType.Miniboss, HintType.Treasure })
            {
                negativeHints[noType] = withoutRedundantChildren(areasWithoutPlacements(noType)).Except(unimportantAreas).ToList();
            }
            negativeHints[HintType.Enemy] = new List <AreaHint>();
            foreach (AreaHint allEnemy in negativeHints[HintType.Boss].Intersect(negativeHints[HintType.Miniboss]).ToList())
            {
                // Add 'no powerful enemy' hints if no boss and no miniboss.
                negativeHints[HintType.Enemy].Add(allEnemy);
                negativeHints[HintType.Boss].Remove(allEnemy);
                negativeHints[HintType.Miniboss].Remove(allEnemy);
            }
            List <Placement> negatives = new List <Placement>();

            foreach (KeyValuePair <HintType, List <AreaHint> > entry in negativeHints)
            {
                List <HintType> types = entry.Key == HintType.None ? new List <HintType>() : new List <HintType> {
                    entry.Key
                };
                foreach (AreaHint area in entry.Value)
                {
                    negatives.Add(new Placement
                    {
                        AreaHint = area,
                        Types    = types,
                    });
                }
            }
            // Area type hint: There is nothing at (type) in (location). Include this for treasure and for miniboss/boss/enemy, eliminating when parent (or parent type) applies.
            Shuffle(random, negatives);
            negatives = negatives.OrderBy(a => a.AreaHint.AreaRank).ToList();
            // If there are at least 4 negative hints, allow up to 3 of the remainder to become fodder.
            List <Hint> negativeHintTemplates = sources.Where(s => s.Types.ContainsKey("badhint") || s.Types.ContainsKey("badhint2")).ToList();

            if (negatives.Count >= 4 && negativeHintTemplates.Count > 4)
            {
                int cutoffIndex = Math.Max(negativeHintTemplates.Count - 3, 4);
                negativeHintTemplates.RemoveRange(cutoffIndex, negativeHintTemplates.Count - cutoffIndex);
            }
            foreach (Hint hint in negativeHintTemplates)
            {
                if (negatives.Count == 0)
                {
                    break;
                }
                HintTemplate t              = hint.Types.ContainsKey("badhint") ? hint.Types["badhint"] : hint.Types["badhint2"];
                Placement    placement      = pop(negatives);
                Placement    otherPlacement = t.Type == "badhint2" && negatives.Count > 0 ? pop(negatives) : null;
                addHint(hint, t, placement, otherPlacement);
                sources.Remove(hint);
            }

            if (printText)
            {
                Console.WriteLine($"{sources.Count} remaining hints: [{string.Join(", ", sources.Select(s => string.Join("/", s.Types.Keys)))}]");
            }
            if (sources.Count > 0)
            {
                // At this point, pull in misc hints for somewhat useful items
                toPlace = categories[ItemCategory.HintFodder].Select(k => itemPlacement[k]).ToList();
                Shuffle(random, toPlace);
                foreach (Hint hint in sources.Where(s => s.Types.ContainsKey("hint")).ToList())
                {
                    if (toPlace.Count == 0)
                    {
                        break;
                    }
                    HintTemplate t         = hint.Types["hint"];
                    Placement    placement = pop(toPlace);
                    placement          = placement.Copy();
                    placement.AreaHint = mostSpecificArea(placement.Area);
                    addHint(hint, t, placement);
                    sources.Remove(hint);
                }
            }

            // Need to figure out which items are strictly required to beat the game
            // Also, for bad hints, find all strictly required items plus key items
            // List all locations which can have hints scoped to them. The entirety of Sunken Valley, or just lower/upper, or burrow
            // Most key items get minimum specificity
            // Required side area items will get early/lateness specificity when that applies
            // Skills/prosthetics will get maximum specificity, maybe even two for Mikiri

            // Mortal Blade is excluded, since it has its own explicit hint
            // Young Lord Bell Charm is excluded if earlyhirata
        }
Ejemplo n.º 31
0
        public void WriteEventConfig(AnnotationData ann, Events events, RandomizerOptions opt)
        {
            GameEditor editor = new GameEditor(FromGame.DS3);

            editor.Spec.GameDir = "fogdist";
            Dictionary <string, MSB3>  maps   = editor.Load(@"Base", path => ann.Specs.ContainsKey(GameEditor.BaseName(path)) ? MSB3.Read(path) : null, "*.msb.dcx");
            Dictionary <string, EMEVD> emevds = editor.Load(@"Base", path => ann.Specs.ContainsKey(GameEditor.BaseName(path)) || path.Contains("common") ? EMEVD.Read(path) : null, "*.emevd.dcx");

            void deleteEmpty <K, V>(Dictionary <K, V> d)
            {
                foreach (K key in d.Keys.ToList())
                {
                    if (d[key] == null)
                    {
                        d.Remove(key);
                    }
                }
            }

            // Should this be in GameEditor?
            deleteEmpty(maps);
            deleteEmpty(emevds);

            editor.Spec.NameDir = @"fogdist\Names";
            Dictionary <string, string>    modelNames = editor.LoadNames("ModelName", n => n);
            SortedDictionary <int, string> chars      = new SortedDictionary <int, string>(editor.LoadNames("CharaInitParam", n => int.Parse(n)));

            Dictionary <string, List <string> > description          = new Dictionary <string, List <string> >();
            Dictionary <int, string>            entityNames          = new Dictionary <int, string>();
            Dictionary <int, List <int> >       groupIds             = new Dictionary <int, List <int> >();
            Dictionary <(string, string), MSB3.Event.ObjAct> objacts = new Dictionary <(string, string), MSB3.Event.ObjAct>();

            HashSet <int> highlightIds = new HashSet <int>();
            HashSet <int> selectIds    = new HashSet <int>();

            foreach (Entrance e in ann.Warps.Concat(ann.Entrances))
            {
                int id = e.ID;
                AddMulti(description, id.ToString(), (ann.Warps.Contains(e) ? "" : "fog gate ") + e.Text);
                selectIds.Add(e.ID);
                highlightIds.Add(e.ID);
            }
            HashSet <string> gameObjs = new HashSet <string>();

            foreach (GameObject obj in ann.Objects)
            {
                if (int.TryParse(obj.ID, out int id))
                {
                    AddMulti(description, id.ToString(), obj.Text);
                    selectIds.Add(id);
                    highlightIds.Add(id);
                }
                else
                {
                    gameObjs.Add($"{obj.Area}_{obj.ID}");
                }
            }

            Dictionary <string, Dictionary <string, FMG> > fmgs = new GameEditor(FromGame.DS3).LoadBnds($@"msg\engus", (data, name) => FMG.Read(data), ext: "*_dlc2.msgbnd.dcx");

            void addFMG(FMG fmg, string desc)
            {
                foreach (FMG.Entry e in fmg.Entries)
                {
                    if (e.ID > 25000 && !string.IsNullOrWhiteSpace(e.Text))
                    {
                        highlightIds.Add(e.ID);
                        AddMulti(description, e.ID.ToString(), desc + " " + "\"" + e.Text.Replace("\r", "").Replace("\n", "\\n") + "\"");
                    }
                }
            }

            addFMG(fmgs["item_dlc2"]["NPC名"], "name");
            addFMG(fmgs["menu_dlc2"]["イベントテキスト"], "text");

            foreach (KeyValuePair <string, MSB3> entry in maps)
            {
                string map = ann.Specs[entry.Key].Name;
                MSB3   msb = entry.Value;

                foreach (MSB3.Part e in msb.Parts.GetEntries())
                {
                    string shortName = $"{map}_{e.Name}";
                    if (modelNames.TryGetValue(e.ModelName, out string modelDesc))
                    {
                        if (e is MSB3.Part.Enemy en && modelDesc == "Human NPC" && en.CharaInitID > 0)
                        {
                            modelDesc = CharacterName(chars, en.CharaInitID);
                        }
                        else if (e is MSB3.Part.Player)
                        {
                            modelDesc = "Warp Point";
                        }
                        AddMulti(description, shortName, modelDesc);
                    }
                    AddMulti(description, shortName, $"{map} {e.GetType().Name.ToString().ToLowerInvariant()} {e.Name}");  // {(e.EventEntityID > 0 ? $" {e.EventEntityID}" : "")}
                    if (e.EventEntityID > 10)
                    {
                        highlightIds.Add(e.EventEntityID);
                        string idStr = e.EventEntityID.ToString();
                        if (description.ContainsKey(idStr))
                        {
                            AddMulti(description, shortName, description[idStr]);
                        }
                        description[idStr] = description[shortName];
                        if (e is MSB3.Part.Player || e.ModelName == "o000100")
                        {
                            selectIds.Add(e.EventEntityID);
                        }
                        if (selectIds.Contains(e.EventEntityID))
                        {
                            gameObjs.Add(shortName);
                        }

                        foreach (int id in e.EventEntityGroups)
                        {
                            if (id > 0)
                            {
                                AddMulti(groupIds, id, e.EventEntityID);
                                highlightIds.Add(id);
                            }
                        }
                    }
                }
                foreach (MSB3.Region r in msb.Regions.GetEntries())
                {
                    if (r.EventEntityID < 1000000)
                    {
                        continue;
                    }
                    AddMulti(description, r.EventEntityID.ToString(), $"{map} {r.GetType().Name.ToLowerInvariant()} region {r.Name}");
                    highlightIds.Add(r.EventEntityID);
                }
                foreach (MSB3.Event e in msb.Events.GetEntries())
                {
                    if (e is MSB3.Event.ObjAct oa)
                    {
                        // It can be null, basically for commented out objacts
                        string part = oa.PartName ?? oa.PartName2;
                        if (part == null)
                        {
                            continue;
                        }
                        string desc = description.TryGetValue($"{map}_{part}", out List <string> p) ? string.Join(" - ", p) : throw new Exception($"{map} {oa.Name}");
                        objacts[(map, part)] = oa;
Ejemplo n.º 32
0
        public Assignment SplitAll()
        {
            // Add skills as item drops in the world. Esoteric texts still work, but they are removed from randomizer pool.
            Assignment ret = new Assignment();

            // First, create skill items, including editing item fmgs
            SortedDictionary <int, ItemKey> newSkills = new SortedDictionary <int, ItemKey>();
            SortedDictionary <int, ItemKey> oldSkills = new SortedDictionary <int, ItemKey>();

            Dictionary <int, ItemKey> texts = new Dictionary <int, ItemKey>
            {
                [0] = game.ItemForName("Shinobi Esoteric Text"),
                [1] = game.ItemForName("Prosthetic Esoteric Text"),
                [2] = game.ItemForName("Ashina Esoteric Text"),
                [3] = game.ItemForName("Senpou Esoteric Text"),
                [4] = game.ItemForName("Mushin Esoteric Text"),
            };

            // Note: there are some events in common which can be used to detect skills which are already granted by emevd.
            // But they all have a text of -1 so it's unnecessary to scan emevd for this.
            // For example:
            // Initialize Event (Event Slot ID: 4, Event ID: 450, Parameters: 6719){, 3, 2450, 620}
            // Initialize Event (Event Slot ID: 0, Event ID: 460, Parameters: 6710){, 2470, 600}

            PARAM.Row baseGood = game.Params["EquipParamGoods"][2470];
            int       baseId   = 6405;

            FMG itemName   = game.ItemFMGs["アイテム名"];
            FMG itemDesc   = game.ItemFMGs["アイテム説明"];
            FMG weaponName = game.ItemFMGs["武器名"];
            FMG weaponDesc = game.ItemFMGs["武器説明"];
            SortedDictionary <ItemKey, string> gameNames = game.Names();

            bool          explain       = false;
            HashSet <int> copiedWeapons = new HashSet <int>();

            foreach (PARAM.Row r in game.Params["SkillParam"].Rows)
            {
                int skillId = (int)r.ID;
                if (skillId >= 700)
                {
                    continue;
                }

                int text = (byte)r["Unk7"].Value;
                if (!texts.ContainsKey(text))
                {
                    continue;
                }

                int descItem = (int)r["SkilLDescriptionId"].Value;
                if (copiedWeapons.Contains(descItem))
                {
                    continue;
                }
                copiedWeapons.Add(descItem);

                PARAM.Row weaponRow = game.Params["EquipParamWeapon"][descItem];
                int       sortId    = (int)weaponRow["sortId"].Value;
                short     iconId    = (short)weaponRow["iconId"].Value;

                int       good    = baseId++;
                PARAM.Row newGood = game.AddRow("EquipParamGoods", good);
                GameEditor.CopyRow(baseGood, newGood);
                ItemKey goodKey = new ItemKey(ItemType.GOOD, good);
                newSkills[skillId] = goodKey;

                gameNames[goodKey] = weaponName[descItem];
                itemName[good]     = weaponName[descItem];
                itemDesc[good]     = weaponDesc[descItem];

                newGood["sortId"].Value = sortId;
                newGood["iconId"].Value = iconId;
                // These should be set in base row, but do this just in case
                // Don't show up in inventory
                newGood["goodsType"].Value = (byte)7;
                // But pop up on acquisition
                newGood["Unk20"].Value = (byte)6;

                if (explain)
                {
                    Console.WriteLine($"-- {r.ID} -> {good}: {descItem}, {weaponName[descItem]}");
                }
                ret.Assign[new ItemKey(ItemType.WEAPON, descItem)] = goodKey;
            }
            game.Params["EquipParamGoods"].Rows.Sort((a, b) => a.ID.CompareTo(b.ID));

            // Second, add event scripting to grant skills, with new common_func
            EMEVD common  = game.Emevds["common"];
            int   grantId = 11615600;

            EMEVD.Event   grantEv   = new EMEVD.Event(grantId);
            List <string> grantCmds = new List <string> {
                "IF Player Has/Doesn't Have Item (0,3,X0_4,1)", "Grant Skill (X4_4)"
            };

            for (int i = 0; i < grantCmds.Count; i++)
            {
                (EMEVD.Instruction ins, List <EMEVD.Parameter> ps) = events.ParseAddArg(grantCmds[i], i);
                grantEv.Instructions.Add(ins);
                grantEv.Parameters.AddRange(ps);
            }
            common.Events.Add(grantEv);

            int slot = 0;

            foreach (KeyValuePair <int, ItemKey> entry in newSkills)
            {
                common.Events[0].Instructions.Add(new EMEVD.Instruction(2000, 0, new List <object> {
                    slot++, grantId, entry.Value.ID, entry.Key
                }));
            }

            // Third, edit drops
            // Remove text drops
            ann.ItemGroups["remove"].AddRange(texts.Values);

            // Add skill drops
            foreach (ItemKey item in newSkills.Values)
            {
                data.AddLocationlessItem(item);
            }

            // Copy restrictions from the weapons over to the goods
            ann.CopyRestrictions(ret.Assign);
            foreach (KeyValuePair <ItemKey, ItemKey> entry in ret.Assign)
            {
                ItemKey weapon = entry.Key;
                ItemKey good   = entry.Value;
                // Mikiri Counter in hint log
                if (weapon.ID == 200300)
                {
                    ann.ItemGroups["upgradehints"].Add(good);
                }
                // Carp scalesmen
                if (!ann.ExcludeTags.ContainsKey(weapon))
                {
                    ann.ItemGroups["premium"].Add(good);
                }
            }

            // For balancing Dancing Dragon Mask, greatly reduce enemy xp drops
            // All NG bosses together give 93k XP. This gives enough for 45 skill points.
            // So allow 15 levels/3 AP upgrades, or 9 XP. (Next threshhold: 4 AP upgrades, 13k XP)
            // (Or not, since only got 9 levels in a full run, just double it.)
            foreach (PARAM.Row row in game.Params["NpcParam"].Rows)
            {
                row["Experience"].Value = (int)row["Experience"].Value / 5;
            }
            foreach (PARAM.Row row in game.Params["GameAreaParam"].Rows)
            {
                row["BonusExperience"].Value = (int)row["BonusExperience"].Value / 5;
            }

            // Also in this mode, acquire skills option is removed from Sculptor's Idols, in case it has been there from previous runs. Done in PermutationWriter.

            return(ret);
        }
Ejemplo n.º 33
0
 private void LoadText()
 {
     if (Sekiro)
     {
         ItemFMGs = Editor.LoadBnd($@"{dir}\Base\item.msgbnd.dcx", (data, path) => FMG.Read(data));
         ItemFMGs = MaybeOverrideFromModDir(ItemFMGs, @"msg\engus\item.msgbnd.dcx", path => Editor.LoadBnd(path, (data, path2) => FMG.Read(data)));
         MenuFMGs = Editor.LoadBnd($@"{dir}\Base\menu.msgbnd.dcx", (data, path) => FMG.Read(data));
         MenuFMGs = MaybeOverrideFromModDir(MenuFMGs, @"msg\engus\menu.msgbnd.dcx", path => Editor.LoadBnd(path, (data, path2) => FMG.Read(data)));
         foreach (string lang in MiscSetup.Langs)
         {
             if (lang == "engus")
             {
                 continue;
             }
             OtherItemFMGs[lang] = Editor.LoadBnd($@"{dir}\Base\msg\{lang}\item.msgbnd.dcx", (data, path) => FMG.Read(data));
             OtherItemFMGs[lang] = MaybeOverrideFromModDir(OtherItemFMGs[lang], $@"msg\{lang}\item.msgbnd.dcx", path => Editor.LoadBnd(path, (data, path2) => FMG.Read(data)));
         }
     }
     else
     {
         ItemFMGs = Editor.LoadBnd($@"{dir}\Base\item_dlc2.msgbnd.dcx", (data, path) => FMG.Read(data));
         ItemFMGs = MaybeOverrideFromModDir(ItemFMGs, @"msg\engus\item_dlc2.msgbnd.dcx", path => Editor.LoadBnd(path, (data, path2) => FMG.Read(data)));
     }
 }