public static void Init(short width, short height, bool withEditor) //Метод инициализации { AssetManager.LoadAssetsByPath(GetDirectoryPath() + GAME_SPRITES_PATH); //Загружаются ассеты InputManager.Init(); //Запускается управление GraphicsManager.Init(width, height); //Запускается графика if (File.Exists(GAME_SCENES_PATH + "defalutScene.scn")) { SceneManager.LoadScene(GetDirectoryPath() + GAME_SCENES_PATH + "defalutScene.scn"); } else { SceneManager.CreateNewScene(); //Создаётся пустая сцена SceneManager.SaveScene(GetDirectoryPath() + GAME_SCENES_PATH + "defalutScene.scn"); } GameEngine.withEditor = withEditor; OnGameStart(); if (!withEditor) { Application.Run(); } else { GameEditor.Init(); } }
public void LoadEditor() //djelomična inicijializacija game editora { LevelLoader.Load("Empty.xml"); //učitaj prazni level //postavke scene i igre... PlayerCamera.main.mode = PlayerCamera.PlayerCameraMode.Editor; Player.playing = false; //isključi nepotrebne UI objekte... for (int i = 0; i < GameData.main.mainButtons.Length; i++) { GameData.main.mainButtons[i].gameObject.SetActive(false); } for (int i = 0; i < GameData.main.inGameStuff.Length; i++) { GameData.main.inGameStuff[i].SetActive(false); } for (int i = 0; i < GameData.main.optionsStuff.Length; i++) { GameData.main.optionsStuff[i].SetActive(false); } //uključi UI editora for (int i = 0; i < GameData.main.editorStuff.Length; i++) { GameData.main.editorStuff[i].SetActive(true); } currentGameState = GameState.editing; GameEditor.Setup(); //editor se treba inicijalizirati Player.main.anim.SetBool("Idle", false); //dino ne smije biti u ikakvoj animaciji Player.main.anim.Play("Nothing", 0); }
private void GenerateObstacleTag() { GameEditor.RemoveAllTag(); //var entitys = Directory.GetFiles(PathRecoder.EntityRoot, "*", SearchOption.AllDirectories); var obstacles = Directory.GetFiles(PathRecoder.ObstacleRoot, "*", SearchOption.AllDirectories); var files = new List <string>(); //files.AddRange(entitys); files.AddRange(obstacles); foreach (var file in files) { var fullPath = file.Replace('\\', '/'); if (fullPath.EndsWith(".meta")) { continue; } var tag = fullPath.Replace(Application.dataPath + "/", ""); var path = "Assets/" + tag; var assets = AssetDatabase.LoadAssetAtPath <GameObject>(path); GameEditor.AddTag(tag); assets.tag = tag; foreach (var t in assets.GetComponentsInChildren <Transform>(true)) { t.tag = tag; } } Debug.Log("finish!"); }
static void Init() { // Get existing open window or if none, make a new one: GameEditor window = (GameEditor)EditorWindow.GetWindow(typeof(GameEditor)); window.Show(); }
static void Init() { // Get existing open window or if none, make a new one: GameEditor window = (GameEditor)EditorWindow.GetWindow(typeof(GameEditor)); window.devMode = bool.Parse(PlayerPrefs.GetString(Constants.cst_DeveloperMode, "false")); window.Show(); }
// Use this for initialization void Start () { Debug.Log (Application.persistentDataPath); gridContainer = new GameObject ("GridContainer"); Instance = this; //Load ("zxczczx"); //CreateGridBackground (); }
//Responsible for saving each specific level based on set level number function public static void SaveLevelData(GameEditor data) { BinaryFormatter binaryFormat = new BinaryFormatter(); FileStream fileStream = new FileStream(Application.persistentDataPath + levelData + saveNum + extension, FileMode.Create); LevelData newData = new LevelData(data); binaryFormat.Serialize(fileStream, newData); fileStream.Close(); }
public CommandRunner(ESDOptions options, ESDOptions.Option opt) { this.options = options; this.opt = opt; this.spec = options.Spec; this.editor = new GameEditor(spec); this.scraper = new Scraper(spec); this.u = new Universe(); }
public void LoadMenu() //učitavanje meni-ja { Cursor.visible = true; if (currentGameState == GameState.editing) //ako je igrač prije bio u level editor-u, počisti sve što je od toga ostalo { GameEditor.Clear(); } if (LevelLoader.currentLevel != "Menu.xml") //učitaj Level menija (ako već nije učitan) { LevelLoader.Load("Menu.xml"); } //iskluči sve ostale UI grupe objekata for (int i = 0; i < GameData.main.youDiedStuff.Length; i++) { GameData.main.youDiedStuff[i].SetActive(false); } for (int i = 0; i < GameData.main.youWonStuff.Length; i++) { GameData.main.youWonStuff[i].SetActive(false); } for (int i = 0; i < GameData.main.inGameStuff.Length; i++) { GameData.main.inGameStuff[i].SetActive(false); } for (int i = 0; i < GameData.main.pauseStuff.Length; i++) { GameData.main.pauseStuff[i].SetActive(false); } for (int i = 0; i < GameData.main.editorStuff.Length; i++) { GameData.main.editorStuff[i].SetActive(false); } for (int i = 0; i < GameData.main.levelSelectionStuff.Length; i++) { GameData.main.levelSelectionStuff[i].gameObject.SetActive(false); } for (int i = 0; i < GameData.main.optionsStuff.Length; i++) { GameData.main.optionsStuff[i].SetActive(false); } GameData.main.helpUIStuff.SetActive(false); Time.timeScale = 1f; //za uklanjanje pauze (ako je bila) //općenite postavke scene i igre... PlayerCamera.main.mode = PlayerCamera.PlayerCameraMode.Stand; Player.playing = false; currentGameState = GameState.menu; //uključi UI menija for (int i = 0; i < GameData.main.mainButtons.Length; i++) { GameData.main.mainButtons[i].gameObject.SetActive(true); } if (Player.main) { Player.main.anim.Play("Idles", 0); //dino treba biti u idle animaciji } }
static void EditGame() { TextAsset file = Selection.activeObject as TextAsset; if (file != null) { GameEditor window = EditorWindow.GetWindow <GameEditor>("Game Editor"); window.SetFile(file); window.Show(); } }
public GameData(string dir, bool sekiro) { this.dir = dir; Sekiro = sekiro; Editor = new GameEditor(sekiro ? GameSpec.FromGame.SDT : GameSpec.FromGame.DS3); Editor.Spec.GameDir = $@"{dir}"; Editor.Spec.NameDir = $@"{dir}\Names"; Editor.Spec.LayoutDir = $@"{dir}\Layouts"; Locations = Sekiro ? sekiroLocationNames : ds3LocationNames; RevLocations = Locations.ToDictionary(e => e.Value, e => e.Key); LocationNames = Sekiro ? sekiroMapName : null; }
public int[] sizeData; //stores all other values within the level like height and width public LevelData(GameEditor data) { allLevelData = new int[3][]; sizeData = new int[2]; sizeData[0] = data.SavedWidth; sizeData[1] = data.SavedHeight; paths = data.NumOfPath.ToArray(); blocks = data.BlockIds.ToArray(); allLevelData[0] = sizeData; allLevelData[1] = blocks; allLevelData[2] = paths; }
protected override void OnClick(EventArgs e) { if (ControlList.data.current_game == null) { MessageBox.Show("Please Select a Game"); } else { GameEditor ge = new GameEditor(ControlList.data.current_game); ge.ShowDialog(); ge.Dispose(); } }
public void FogConfig() { AnnotationData ret = new AnnotationData(); ret.SetGame(FromGame.DS3); GameEditor editor = new GameEditor(FromGame.DS3); Dictionary <string, MSB3> maps = editor.Load(@"map\MapStudio", path => ret.Specs.ContainsKey(GameEditor.BaseName(path)) ? MSB3.Read(path) : null, "*.msb.dcx"); ret.Entrances = new List <Entrance>(); foreach (KeyValuePair <string, MSB3> entry in maps) { if (!ret.Specs.TryGetValue(entry.Key, out MapSpec spec)) { continue; } // Console.WriteLine(spec.Name); MSB3 msb = entry.Value; Dictionary <string, MSB3.Part.Object> objs = new Dictionary <string, MSB3.Part.Object>(); foreach (MSB3.Part.Object e in msb.Parts.Objects) { objs[e.Name] = e; int model = int.Parse(e.ModelName.Substring(1)); if (model >= spec.Start && model <= spec.End) { ret.Entrances.Add(new Entrance { Area = spec.Name, Name = e.Name, ID = e.EventEntityID, Text = "Between", Tags = "pvp boss", ASide = new Side { Area = spec.Name }, BSide = new Side { Area = spec.Name }, }); } } } ISerializer serializer = new SerializerBuilder().DisableAliases().Build(); using (TextWriter writer = File.CreateText("fog.txt")) { serializer.Serialize(writer, ret); } }
private void OnEditGamesListClicked(object sender, RoutedEventArgs e) { if (this.Splitter.EmulationManager.IsEmulationStarted) { Controls.MessageBox.Show( "You can not edit games, when emulation is running!", ApplicationInfo.AppName, MessageBoxButton.OK, MessageBoxImage.Error); return; } var gameEditor = new GameEditor(); gameEditor.Owner = this; gameEditor.WindowStartupLocation = WindowStartupLocation.CenterOwner; gameEditor.ShowDialog(); }
/// <summary> /// Initializes server-side world. /// </summary> /// <param name="maxPlayers"></param> /// <param name="maxEntities"></param> public MapEditor(GameEditor editor, string map) { this.mapName = map; Log.Verbose("game editor"); this.Game = editor.Game; this.rs = Game.RenderSystem; Content = new ContentManager(Game); Config = Game.Config.GetConfig <EditorConfig>(); camera = new EditorCamera(this); hud = new EditorHud(this); manipulator = new NullTool(this); world = new GameWorld(Game, true, new Guid()); world.InitServerAtoms(); SetupUI(); Game.Keyboard.ScanKeyboard = true; fullPath = Builder.GetFullPath(@"maps\" + map + ".map"); }
//public WorkspaceService workspace => desktopRunner.workspaceService; public override void ConfigureScript(Script luaScript) { base.ConfigureScript(luaScript); // File APIs luaScript.Globals["UniqueFilePath"] = new Func <WorkspacePath, WorkspacePath>(workspace.UniqueFilePath); luaScript.Globals["CreateDirectory"] = new Action <WorkspacePath>(workspace.CreateDirectoryRecursive); luaScript.Globals["MoveTo"] = new Action <WorkspacePath, WorkspacePath>(workspace.Move); luaScript.Globals["CopyTo"] = new Action <WorkspacePath, WorkspacePath>(workspace.Copy); luaScript.Globals["Delete"] = new Action <WorkspacePath>(workspace.Delete); luaScript.Globals["PathExists"] = new Func <WorkspacePath, bool>(workspace.Exists); luaScript.Globals["GetEntities"] = new Func <WorkspacePath, List <WorkspacePath> >(path => workspace.GetEntities(path).OrderBy(o => o.EntityName, StringComparer.OrdinalIgnoreCase).ToList()); luaScript.Globals["GetEntitiesRecursive"] = new Func <WorkspacePath, List <WorkspacePath> >(path => workspace.GetEntitiesRecursive(path).ToList()); luaScript.Globals["PlayWav"] = new Action <WorkspacePath>(PlayWav); luaScript.Globals["StopWav"] = new Action(StopWav); luaScript.Globals["CreateDisk"] = new Func <string, WorkspacePath[], WorkspacePath, int, string[], Dictionary <string, object> >(CreateDisk); luaScript.Globals["CreateExe"] = new Func <string, WorkspacePath[], WorkspacePath, WorkspacePath, string[], Dictionary <string, object> >( CreateExe); luaScript.Globals["ClearLog"] = new Action(workspace.ClearLog); luaScript.Globals["ReadLogItems"] = new Func <List <string> >(workspace.ReadLogItems); // TODO these are deprecated luaScript.Globals["ReadTextFile"] = new Func <string, string>(ReadTextFile); luaScript.Globals["SaveTextToFile"] = (SaveTextToFileDelegator)SaveTextToFile; // File helpers luaScript.Globals["NewImage"] = new Func <int, int, string[], int[], Image>(NewImage); // Read file helpers luaScript.Globals["ReadJson"] = new Func <WorkspacePath, Dictionary <string, object> >(ReadJson); luaScript.Globals["ReadText"] = new Func <WorkspacePath, string>(ReadText); luaScript.Globals["ReadImage"] = new Func <WorkspacePath, string, Image>(ReadImage); // Save file helpers luaScript.Globals["SaveText"] = new Action <WorkspacePath, string>(SaveText); luaScript.Globals["SaveImage"] = new Action <WorkspacePath, Image>(SaveImage); // // // Expose Bios APIs // luaScript.Globals["ReadBiosData"] = new Func<string, string, string>((key, defaultValue) => // desktopRunner.bios.ReadBiosData(key, defaultValue)); // luaScript.Globals["WriteBiosData"] = new Action<string, string>(desktopRunner.bios.UpdateBiosData); // luaScript.Globals["RemapKey"] = new Action<string, int>(RemapKey); luaScript.Globals["NewWorkspacePath"] = new Func <string, WorkspacePath>(WorkspacePath.Parse); UserData.RegisterType <WorkspacePath>(); UserData.RegisterType <Image>(); // Register the game editor with the lua service gameEditor = new GameEditor(desktopRunner, locator); UserData.RegisterType <GameEditor>(); luaScript.Globals["gameEditor"] = gameEditor; }
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); }
private void Btn_CreateOwnGame_Click(object sender, RoutedEventArgs e) { gameEditor = new GameEditor(); ChangeMainUserControl(gameEditor); }
public override bool Initialize() { viewer = GameViewer.Instance; if (viewer == null) return false; GameViewer.GameDb = GameDb.Instance; GameDbBrowser browser = new GameDbBrowser (); CsBoard.CsBoardApp.Instance.AddApp (browser); editor = new GameEditor (viewer); viewer.ChessGameDetailsBox.PackStart (editor, false, true, 2); viewer.AddToFileMenu (saveItem); //viewer.AddToViewMenu (openDbItem); return true; }
static void ShowMyWindow() { GameEditor window = EditorWindow.GetWindow <GameEditor>(); window.Show(); }
override public void OnInspectorGUI() { gameEditor = target as GameEditor; #region PlayerPrefs GUILayout.Label("PlayerPrefs", headerText); #region PlayerPrefs Edit EditorGUILayout.LabelField("PlayerPrefs Edit", EditorStyles.boldLabel); gameEditor.playerPrefsState = (PlayerPrefsState)EditorGUILayout.EnumPopup("Mode", gameEditor.playerPrefsState); gameEditor.editKey = EditorGUILayout.TextField("Key Name", gameEditor.editKey); GUILayout.BeginHorizontal(); if (gameEditor.playerPrefsState == PlayerPrefsState.Int) { gameEditor.intValue = EditorGUILayout.IntField("Value", gameEditor.intValue); if (GUILayout.Button($"Set '{gameEditor.editKey}' Key")) { PlayerPrefs.SetInt(gameEditor.editKey, gameEditor.intValue); Debug.Log($"{gameEditor.editKey} is {(PlayerPrefs.GetInt(gameEditor.editKey)).ToString()} now"); } } else if (gameEditor.playerPrefsState == PlayerPrefsState.String) { gameEditor.stringValue = EditorGUILayout.TextField("Value", gameEditor.stringValue); if (GUILayout.Button($"Set '{gameEditor.editKey}' Key")) { PlayerPrefs.SetString(gameEditor.editKey, gameEditor.stringValue); Debug.Log($"{gameEditor.editKey} is {(PlayerPrefs.GetString(gameEditor.editKey)).ToString()} now"); } } else if (gameEditor.playerPrefsState == PlayerPrefsState.Float) { gameEditor.floatValue = EditorGUILayout.FloatField("Value", gameEditor.floatValue); if (GUILayout.Button($"Set '{gameEditor.editKey}' Key")) { PlayerPrefs.SetFloat(gameEditor.editKey, gameEditor.floatValue); Debug.Log($"{gameEditor.editKey} is {(PlayerPrefs.GetFloat(gameEditor.editKey)).ToString()} now"); } } GUILayout.EndHorizontal(); #endregion GUILayout.Space(15); #region PlayerPrefs Learn EditorGUILayout.LabelField("PlayerPrefs Learn", EditorStyles.boldLabel); GUILayout.BeginHorizontal(); gameEditor.learnKey = EditorGUILayout.TextField("Key Name", gameEditor.learnKey); GUILayout.Space(10); if (gameEditor.playerPrefsState == PlayerPrefsState.Int) { if (GUILayout.Button($"Learn '{gameEditor.learnKey}' Key")) { Debug.Log($"'{gameEditor.learnKey}' Key's value is '{(PlayerPrefs.GetInt(gameEditor.learnKey))}'"); } } else if (gameEditor.playerPrefsState == PlayerPrefsState.String) { if (GUILayout.Button($"Learn '{gameEditor.learnKey}' Key")) { Debug.Log($"'{gameEditor.learnKey}' Key's value is '{(PlayerPrefs.GetString(gameEditor.learnKey))}'"); } } else if (gameEditor.playerPrefsState == PlayerPrefsState.Float) { if (GUILayout.Button($"Learn '{gameEditor.learnKey}' Key")) { Debug.Log($"'{gameEditor.learnKey}' Key's value is '{(PlayerPrefs.GetFloat(gameEditor.learnKey))}'"); } } GUILayout.EndHorizontal(); #endregion GUILayout.Space(15); #region PlayerPrefs Delete EditorGUILayout.LabelField("PlayerPrefs Delete", EditorStyles.boldLabel); GUILayout.BeginHorizontal(); gameEditor.deleteKey = EditorGUILayout.TextField("Key Name", gameEditor.deleteKey); if (GUILayout.Button("Delete Key")) { Debug.Log($"{gameEditor.deleteKey} Key Deleted"); PlayerPrefs.DeleteKey(gameEditor.deleteKey); } GUILayout.EndHorizontal(); GUILayout.Space(15); if (GUILayout.Button("Delete All Keys")) { Debug.Log("All Keys Deleted"); PlayerPrefs.DeleteAll(); } EditorGUILayout.HelpBox("Please be careful when pressing this button. It is an irreversible action.", MessageType.Warning); #endregion #endregion GUILayout.Space(20); #region Language GUILayout.Label("Language", headerText); #region Language Change GUILayout.Space(5); GUILayout.Label("Language Change", EditorStyles.boldLabel); CreateButtons(); GUILayout.Label("Current Language: " + PlayerPrefs.GetString("language").ToUpper(), infoText); #endregion GUILayout.Space(15); #region Language Add GUILayout.Label("Language Add", EditorStyles.boldLabel); GUILayout.BeginHorizontal(); gameEditor.addLanguage = EditorGUILayout.TextField("Language Add", gameEditor.addLanguage); if (GUILayout.Button("Add")) { MultiLang.AddLanguage(gameEditor.addLanguage); } GUILayout.EndHorizontal(); #endregion GUILayout.Space(15); #region Language Delete GUILayout.Label("Language Delete", EditorStyles.boldLabel); GUILayout.BeginHorizontal(); gameEditor.deleteLanguage = EditorGUILayout.TextField("Language Delete", gameEditor.deleteLanguage); if (GUILayout.Button("Delete")) { MultiLang.DeleteLanguage(gameEditor.deleteLanguage); } GUILayout.EndHorizontal(); #endregion #endregion serializedObject.ApplyModifiedProperties(); }
static void Main() { GameEditor gameEditor = new GameEditor(); gameEditor.Launch(); }
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;
public Result FindItems(RandomizerOptions opt, AnnotationData ann, Graph g, Events events, string gameDir, FromGame game) { Dictionary <string, string> itemsById = ann.KeyItems.ToDictionary(item => item.ID, item => item.Name); Dictionary <string, List <string> > itemAreas = g.ItemAreas; if (ann.LotLocations != null) { GameEditor editor = new GameEditor(game); editor.Spec.GameDir = gameDir; Dictionary <string, PARAM.Layout> layouts = editor.LoadLayouts(); Dictionary <string, PARAM> Params = editor.LoadParams(layouts); Dictionary <int, PARAM.Row> lots = Params["ItemLotParam"].Rows.ToDictionary(r => (int)r.ID, r => r); foreach (KeyValuePair <int, string> entry in ann.LotLocations) { int lot = entry.Key; if (!g.Areas.ContainsKey(entry.Value)) { throw new Exception($"Internal error in lot config for {entry.Key}: {entry.Value} does not exist"); } while (true) { // It's also fine to not have a lot defined as long as all key items are found if (!lots.TryGetValue(lot, out PARAM.Row row)) { break; } for (int i = 1; i <= 8; i++) { int item = (int)row[$"lotItemId0{i}"].Value; if (item == 0) { continue; } int category = (int)row[$"lotItemCategory0{i}"].Value; category = Universe.LotTypes.TryGetValue((uint)category, out int value) ? value : -1; if (category == -1) { continue; } string id = $"{category}:{item}"; if (opt["debuglots"]) { Console.WriteLine($"{entry.Key} in {entry.Value} has {id}"); } if (itemsById.TryGetValue(id, out string name)) { if (itemAreas[name].Count > 0 && itemAreas[name][0] != entry.Value) { throw new Exception($"Item {name} found in both {itemAreas[name][0]} and {entry.Value}"); } itemAreas[name] = new List <string> { entry.Value }; } } lot++; } } } if (ann.Locations != null) { // It's a bit hacky, but should work from anywhere, probably GameEditor editor = new GameEditor(game); editor.Spec.GameDir = $@"fogdist"; editor.Spec.LayoutDir = $@"fogdist\Layouts"; editor.Spec.NameDir = $@"fogdist\Names"; Dictionary <string, PARAM.Layout> layouts = editor.LoadLayouts(); int dragonFlag = -1; Dictionary <string, PARAM> Params = editor.LoadParams(@"fogdist\Base\Data0.bdt", layouts, true); if (gameDir != null) { string paramPath = $@"{gameDir}\Data0.bdt"; if (File.Exists(paramPath)) { Params = editor.LoadParams(paramPath, layouts, true); } string commonEmevdPath = $@"{gameDir}\event\common.emevd.dcx"; if (File.Exists(commonEmevdPath)) { EMEVD commonEmevd = EMEVD.Read(commonEmevdPath); EMEVD.Event flagEvent = commonEmevd.Events.Find(e => e.ID == 13000904); if (flagEvent != null) { Events.Instr check = events.Parse(flagEvent.Instructions[1]); dragonFlag = (int)check[3]; if (opt["debuglots"]) { Console.WriteLine($"Dragon flag: {dragonFlag}"); } } } } Dictionary <int, PARAM.Row> lots = Params["ItemLotParam"].Rows.ToDictionary(r => (int)r.ID, r => r); Dictionary <int, PARAM.Row> shops = Params["ShopLineupParam"].Rows.ToDictionary(r => (int)r.ID, r => r); void setArea(string itemName, List <string> areas) { if (opt["debuglots"]) { Console.WriteLine($"-- name: {itemName}"); } if (itemAreas[itemName].Count > 0 && !itemAreas[itemName].SequenceEqual(areas)) { throw new Exception($"Item {itemName} found in both {string.Join(",", itemAreas[itemName])} and {string.Join(",", areas)}"); } itemAreas[itemName] = areas; } foreach (KeyItemLoc loc in ann.Locations.Items) { List <string> areas = loc.Area.Split(' ').ToList(); if (!areas.All(a => g.Areas.ContainsKey(a) || itemAreas.ContainsKey(a))) { // Currently happens with multi-area intersection lots/shops throw new Exception($"Warning: Areas not found for {loc.Area} - {loc.DebugText[0]}"); } List <int> lotIds = loc.Lots == null ? new List <int>() : loc.Lots.Split(' ').Select(i => int.Parse(i)).ToList(); foreach (int baseLot in lotIds) { int lot = baseLot; while (true) { // It's also fine to not have a lot defined as long as all key items are found if (!lots.TryGetValue(lot, out PARAM.Row row)) { break; } for (int i = 1; i <= 8; i++) { int item = (int)row[$"ItemLotId{i}"].Value; if (item == 0) { continue; } uint category = (uint)row[$"LotItemCategory0{i}"].Value; if (!Universe.LotTypes.TryGetValue(category, out int catVal)) { continue; } string id = $"{catVal}:{item}"; if (opt["debuglots"]) { Console.WriteLine($"lot {lot} in {loc.Area} has {id}"); } if (itemsById.TryGetValue(id, out string name)) { setArea(name, areas); } } if (dragonFlag > 0 && (int)row["getItemFlagId"].Value == dragonFlag) { setArea("pathofthedragon", areas); } lot++; } } List <int> shopIds = loc.Shops == null ? new List <int>() : loc.Shops.Split(' ').Select(i => int.Parse(i)).ToList(); foreach (int shopId in shopIds) { // Not as fine for a shop to be missing, but also whatever if (!shops.TryGetValue(shopId, out PARAM.Row row)) { continue; } int item = (int)row["EquipId"].Value; int catVal = (byte)row["equipType"].Value; string id = $"{catVal}:{item}"; if (opt["debuglots"]) { Console.WriteLine($"shop {shopId} in {loc.Area} has {id}"); } if (itemsById.TryGetValue(id, out string name)) { setArea(name, areas); } if (dragonFlag > 0 && (int)row["EventFlag"].Value == dragonFlag) { setArea("pathofthedragon", areas); } } } } // lots:.*[1-9]\r // Iterative approach for items which depend simply on other items // Recursion would look a lot nicer but lazy bool itemExpanded; do { itemExpanded = false; foreach (KeyValuePair <string, List <string> > entry in itemAreas) { foreach (string dep in entry.Value.ToList()) { if (itemAreas.TryGetValue(dep, out List <string> deps)) { entry.Value.Remove(dep); entry.Value.AddRange(deps); itemExpanded = true; } } } }while (itemExpanded); if (opt["explain"] || opt["debuglots"]) { foreach (Item item in ann.KeyItems) { Console.WriteLine($"{item.Name} {item.ID}: default {item.Area}, found [{string.Join(", ", itemAreas[item.Name])}]"); } } // Collect items in graph SortedSet <string> itemRecord = new SortedSet <string>(); bool randomized = false; foreach (Item item in ann.KeyItems) { if (itemAreas[item.Name].Count == 0) { if (item.HasTag("randomonly")) { itemAreas[item.Name] = new List <string> { item.Area }; } else if (item.HasTag("hard") && !opt["hard"]) { continue; } else { throw new Exception($"Couldn't find {item.Name} in item lots"); } } List <string> areas = itemAreas[item.Name]; foreach (string area in areas) { g.Nodes[area].Items.Add(item.Name); } if (!item.HasTag("randomonly")) { if (areas.Count > 1 || areas[0] != item.Area) { randomized = true; } itemRecord.Add($"{item.Name}={string.Join(",", areas)}"); } } return(new Result { Randomized = randomized, ItemHash = (RandomizerOptions.JavaStringHash($"{string.Join(";", itemRecord)}") % 99999).ToString().PadLeft(5, '0') }); }
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]}"); } } } }
public void RandomizeTrees(Random random, Permutation permutation, SkillSplitter.Assignment split) { // >= 700: prosthetics // < 400: skills before mushin GameEditor editor = game.Editor; PARAM param = game.Params["SkillParam"]; // Orderings for skills which completely supersede each other. (For prosthetics, just use their natural id ordering) Dictionary <int, int> skillOrderings = new Dictionary <int, int> { [110] = 111, // Nightjar slash [210] = 211, // Ichimonji [310] = 311, // Praying Strikes }; 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"), }; SortedDictionary <ItemKey, string> names = game.Names(); string descName(int desc) { return(names[new ItemKey(ItemType.WEAPON, desc)]); } Dictionary <int, SkillData> allData = new Dictionary <int, SkillData>(); Dictionary <int, SkillSlot> allSlots = new Dictionary <int, SkillSlot>(); Dictionary <ItemKey, SkillData> skillItems = new Dictionary <ItemKey, SkillData>(); List <SkillData> skills = new List <SkillData>(); List <SkillSlot> skillSlots = new List <SkillSlot>(); List <SkillData> prosthetics = new List <SkillData>(); List <SkillSlot> prostheticSlots = new List <SkillSlot>(); bool explain = false; foreach (PARAM.Row r in param.Rows) { SkillData data = new SkillData { ID = (int)r.ID, Item = (int)r["SkilLDescriptionId"].Value, Equip = (int)r["Unk1"].Value, Flag = (int)r["EventFlagId"].Value, Placeholder = (int)r["Unk5"].Value, SpEffects = new[] { (int)r["Unk2"].Value, (int)r["Unk3"].Value }, EmblemChange = (byte)r["Unk10"].Value != 0, }; data.Key = new ItemKey(ItemType.WEAPON, data.Item); skillItems[data.Key] = data; SkillSlot slot = new SkillSlot { ID = (int)r.ID, Col = (short)r["MenuDisplayPositionIndexXZ"].Value, Row = (short)r["MenuDisplayPositionIndexY"].Value, Text = data.ID < 400 && texts.TryGetValue((byte)r["Unk7"].Value, out ItemKey text) ? text : null, }; if (explain) { Console.WriteLine($"{r.ID}: {data.Item}, {data.Equip}, {descName(data.Item)}"); } if (data.ID < 400) { skills.Add(data); skillSlots.Add(slot); } else if (data.ID >= 700) { prosthetics.Add(data); prostheticSlots.Add(slot); } allData[data.ID] = data; allSlots[slot.ID] = slot; } void applyData(PARAM.Row r, SkillData data) { r["SkilLDescriptionId"].Value = data.Item; r["EventFlagId"].Value = data.Flag; r["Unk1"].Value = data.Equip; r["Unk2"].Value = data.SpEffects[0]; r["Unk3"].Value = data.SpEffects[0]; r["Unk5"].Value = data.Placeholder; r["Unk10"].Value = (byte)(data.EmblemChange ? 1 : 0); } Shuffle(random, skills); Shuffle(random, skillSlots); Shuffle(random, prosthetics); Shuffle(random, prostheticSlots); // Skills rando if (split == null) { Dictionary <ItemKey, string> textWeight = new Dictionary <ItemKey, string>(); Dictionary <ItemKey, string> textLocations = texts.Values.ToDictionary(t => t, t => { SlotKey target = permutation.GetFiniteTargetKey(t); textWeight[t] = permutation.GetLogOrder(target); SlotAnnotation sn = ann.Slot(data.Location(target).LocScope); if (explain) { Console.WriteLine($"{game.Name(t)} in {sn.Area} - {sn.Text}. Lateness {(permutation.ItemLateness.TryGetValue(t, out double val) ? val : -1)}"); } return(sn.Area); });