mirror of
https://github.com/haven1433/HexManiacAdvance.git
synced 2026-04-26 08:18:18 -05:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
9b59c3128b
28
src/HexManiac.Core/Core/AutoDictionary.cs
Normal file
28
src/HexManiac.Core/Core/AutoDictionary.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HavenSoft.HexManiac.Core;
|
||||
|
||||
/// <summary>
|
||||
/// If used as AutoDictionary or IDictionary,
|
||||
/// requesting a value from an unused key will create and add a default value.
|
||||
/// Casting to a Dictionary will act like a Dictionary.
|
||||
/// Contains and TryGet still work the normal way.
|
||||
/// </summary>
|
||||
public class AutoDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IDictionary<TKey, TValue> where TKey : notnull {
|
||||
private readonly Func<TKey, TValue> factory;
|
||||
|
||||
/// <param name="factory">A method for creating a new value based on a key.</param>
|
||||
public AutoDictionary(Func<TKey, TValue> factory) => this.factory = factory;
|
||||
|
||||
TValue IDictionary<TKey, TValue>.this[TKey key] {
|
||||
get => this[key];
|
||||
set => this[key] = value;
|
||||
}
|
||||
|
||||
public new TValue this[TKey key] {
|
||||
get => TryGetValue(key, out var value) ? value : base[key] = factory(key);
|
||||
set => base[key] = value;
|
||||
}
|
||||
}
|
||||
23
src/HexManiac.Core/Core/IDelayWorkTimer.cs
Normal file
23
src/HexManiac.Core/Core/IDelayWorkTimer.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
|
||||
namespace HavenSoft.HexManiac.Core;
|
||||
|
||||
public enum DelayWorkResult { WorkScheduled, WorkScheduledAndPreviousWorkCleared }
|
||||
|
||||
public interface IDelayWorkTimer {
|
||||
/// <summary>
|
||||
/// Returns true if 'DelayCall' has been called, but the work has not yet been executed.
|
||||
/// </summary>
|
||||
bool HasScheduledWork { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Will call the action after the delay.
|
||||
/// Returns 'WorkScheduledAndPreviousWorkCleared' if this delay work timer was already tracking work.
|
||||
/// </summary>
|
||||
DelayWorkResult DelayCall(TimeSpan delay, Action action);
|
||||
|
||||
/// <summary>
|
||||
/// Clears any currently scheduled work.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
}
|
||||
|
|
@ -1,105 +1,114 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<ImplicitUsings>false</ImplicitUsings>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<Nullable>annotations</Nullable>
|
||||
<CodeGenDir>$(SolutionDir)artifacts\$(AssemblyName)\codegen\</CodeGenDir>
|
||||
<AutoImplement>$(SolutionDir)artifacts\AutoImplement.Tool\bin\$(Configuration)\$(TargetFramework)\AutoImplement.Tool</AutoImplement>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(SolutionDir)src\SharedAssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Models\Code\default.axve.axpe.bpee.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.axve.axpe.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.axve0.axpe0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.axve1.axpe1.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpee.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpge0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpge1.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpre.bpge.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpre0.bpge0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpre0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpre1.bpge1.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpre1.toml">
|
||||
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpei0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\expand_levelup_moves_code.hma">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bprf0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpef.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\armReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\animationScriptReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\battleScriptReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\battleAIScriptReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\constantReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\docReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\pcsReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\scriptReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\tableReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\hma.py">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Crc32.NET" Version="1.2.0" />
|
||||
<PackageReference Include="DynamicLanguageRuntime" Version="1.3.3" />
|
||||
<PackageReference Include="IronPython" Version="3.4.0" />
|
||||
<PackageReference Include="IronPython.StdLib" Version="3.4.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<ImplicitUsings>false</ImplicitUsings>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
|
||||
<Nullable>annotations</Nullable>
|
||||
<CodeGenDir>$(SolutionDir)artifacts\$(AssemblyName)\codegen\</CodeGenDir>
|
||||
<AutoImplement>$(SolutionDir)artifacts\AutoImplement.Tool\bin\$(Configuration)\$(TargetFramework)\AutoImplement.Tool</AutoImplement>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(SolutionDir)src\SharedAssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Models\Code\default.axve.axpe.bpee.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.axve.axpe.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.axve0.axpe0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.axve1.axpe1.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpee.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpge0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpge1.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpre.bpge.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpre0.bpge0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpre0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpre1.bpge1.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpre1.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpri.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpei0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\expand_levelup_moves_code.hma">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bprf0.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\default.bpef.toml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\armReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\animationScriptReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\battleScriptReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\battleAIScriptReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\constantReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\constantReference.it.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\docReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\pcsReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\scriptReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\tableReference.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\tableReference.it.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Models\Code\hma.py">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Crc32.NET" Version="1.2.0" />
|
||||
<PackageReference Include="DynamicLanguageRuntime" Version="1.3.3" />
|
||||
<PackageReference Include="IronPython" Version="3.4.0" />
|
||||
<PackageReference Include="IronPython.StdLib" Version="3.4.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
178
src/HexManiac.Core/Models/Code/constantReference.it.txt
Normal file
178
src/HexManiac.Core/Models/Code/constantReference.it.txt
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
- Shiny -------------------------------------------------------------------------------------------------------------------------
|
||||
# -1: CreateBoxMon,GetMonSpritePalFromSpeciesAndPersonality,GetMonSpritePalStructFromOtIdPersonality,IsShinyOtIdPersonality,sub_XXXXXX0
|
||||
# 0: apparently used for pokedex pokemon palette choosing, although I can't find the function in pokefirered
|
||||
BPRI0.scripts.shiny.odds 104C78 # The odds of a pokemon being shiny are n/65535. For example, if you set the odds to 250, then the final odds are about 1 in 262.
|
||||
BPRI0.scripts.shiny.odds-1 03DA4A,04400C,044082,04439C,0F19FE # The odds of a pokemon being shiny are n/65535. For example, if you set the odds to 250, then the final odds are about 1 in 262.
|
||||
|
||||
# additional shiny constants found by AGSMG?, used in credits
|
||||
BPRI0.scripts.shiny.odds 0F4462,0F44A0,0F44E0,0F4528
|
||||
|
||||
|
||||
# Credit to Rubiibank for finding this!
|
||||
|
||||
# 0: NewGameBirchSpeech_CreateLotadSprite,CreateMonSpriteFromNationalDexNumber,CreatePokemonFrontSprite
|
||||
|
||||
|
||||
|
||||
|
||||
- Regional Pokedex Length -------------------------------------------------------------------------------------------------------
|
||||
# 0: SpeciesToPokedexNum,CreatePokedexList,CreatePokedexList,GetPokedexRatingText
|
||||
# -1: GetHoennPokedexCount,GetPokedexRatingText
|
||||
# -2: GetPokedexRatingText
|
||||
# -3: HasAllHoennMons,GetPokedexRatingText
|
||||
|
||||
# 0: sub_XXXXXX8,MonCanEvolve
|
||||
# -1: GetKantoPokedexCount,sub_XXXXXX8,sub_XXXXXXC
|
||||
# -2: HasAllKantoMons
|
||||
BPRI0.scripts.pokedex.regional.length 103780,126CDA
|
||||
BPRI0.scripts.pokedex.regional.length-1 088FE8,10384A,104E9E
|
||||
BPRI0.scripts.pokedex.regional.length-2 089046
|
||||
|
||||
|
||||
|
||||
|
||||
- Townmap Button Positions ------------------------------------------------------------------------------------------------------
|
||||
# PlaySEForSelectedMapsec,Task_RegionMap,HandleRegionMapInput,SnapToIconOrButton,Task_FlyMap
|
||||
BPRI0.graphics.townmap.position.cancel.x 0C067A,0C0894,0C3414,0C364C,0C523A
|
||||
BPRI0.graphics.townmap.position.cancel.y 0C0686,0C0704,0C3416,0C3648,0C5246
|
||||
|
||||
# PlaySEForSelectedMapsec,Task_RegionMap,HandleRegionMapInput,SnapToIconOrButton
|
||||
BPRI0.graphics.townmap.position.switch.x 0C064C,0C0860,0C3418,0C3608
|
||||
BPRI0.graphics.townmap.position.switch.y 0C0658,0C086C,0C341A,0C360C
|
||||
|
||||
# BufferRegionMapBg
|
||||
BPRI0.graphics.townmap.position.switch.x+3 0C0F02,0C0F04,0C0F18
|
||||
BPRI0.graphics.townmap.position.switch.y+3 0C0F1A
|
||||
BPRI0.graphics.townmap.position.switch.y+4 0C0F2E
|
||||
BPRI0.graphics.townmap.position.switch.y+5 0C0F30
|
||||
|
||||
|
||||
|
||||
|
||||
- EV caps: how many EVs you can gain from vitamins, or how many EVs you can have in any stat. -----------------------------------
|
||||
BPRI0.scripts.ev.cap.vitamins-1 0419FA
|
||||
BPRI0.scripts.ev.cap.vitamins 041A0A,041A0E
|
||||
BPRI0.scripts.ev.cap.vitamins-1 041FC4
|
||||
BPRI0.scripts.ev.cap.vitamins 041FD4,041FD8
|
||||
BPRI0.scripts.ev.cap.vitamins-1 042810,0429CC
|
||||
BPRI0.scripts.ev.cap.stat 0438E8,0438EE
|
||||
|
||||
- Money cap: The maximum amount of money the player can carry at once. -----------------------------------
|
||||
|
||||
# Coins
|
||||
BPRI0:scripts.coins.cap 0D0768
|
||||
BPRI0:scripts.coins.cap-1 0D0744
|
||||
BPRI0:scripts.coins.cap-9 16C7B5
|
||||
BPRI0:scripts.coins.cap-19 16C86C,16C8CC
|
||||
BPRI0:scripts.coins.cap-49 16C6E6
|
||||
BPRI0:scripts.coins.cap-499 16C6B8
|
||||
|
||||
- Other -------------------------------------------------------------------------------------------------------------------------
|
||||
# TryProduceOrHatchEgg
|
||||
|
||||
# TryProduceOrHatchEgg,ShouldEggHatch
|
||||
BPRI0.scripts.daycare.exp.multiplier 0461D8,0462C4 # Picking an even number will prevent new eggs from being created.
|
||||
|
||||
# data.battle.text length
|
||||
BPRI0:data.battle.textlength+11 0D7968
|
||||
|
||||
|
||||
# Intro
|
||||
BPRI0:scripts.newgame.money 054B6C
|
||||
|
||||
|
||||
BPRI0.scripts.newgame.start.bank 054A10
|
||||
BPRI0.scripts.newgame.start.map 054A12
|
||||
BPRI0.scripts.newgame.start.x 054A14
|
||||
BPRI0.scripts.newgame.start.y 054A0C
|
||||
|
||||
BPRI0.scripts.newgame.heal.bank 0BFF1C
|
||||
BPRI0.scripts.newgame.heal.map 0BFF20
|
||||
BPRI0.scripts.newgame.heal.x 0BFF24
|
||||
BPRI0.scripts.newgame.heal.y 0BFF28
|
||||
|
||||
# Ruby, Sapphire, and Emerald spawn the player in the middle of the truck instead of predefined coordinates.
|
||||
|
||||
BPRI0.scripts.newgame.professor.pokemon 12FBC8,130FD0,130FDC # ID of the pokemon shown during the professor's introduction. Also edit pointers to graphics.pokemon.sprites.front/29 and graphics.pokemon.palettes.normal/29
|
||||
|
||||
|
||||
# Catchmap. See http://sfc.pokefans.net/lesson.php?id=20
|
||||
BPRI0.data.maps.catchmap.conversion.kanto.length 13CB78
|
||||
|
||||
# 2 entries for speed (because double battles), 1 entry each for atk, def, spatk, spdef
|
||||
BPRI0.scripts.battle.badge.boost 014DB0,014E98,03ED22,03ED54,03ED8A,03EDC2
|
||||
|
||||
# lucky egg exp boost, normally 150%
|
||||
BPRI0.scripts.exp.boost.luckyegg 021D46
|
||||
|
||||
# trainer battle exp boost, normall 150%
|
||||
BPRI0.scripts.exp.boost.trainer 021D62
|
||||
|
||||
# Exp Boost for traded pokemon, normally 150%
|
||||
BPRI0.scripts.exp.boost.traded 021D96
|
||||
|
||||
# Nature limiters
|
||||
# AXVE0.data.pokemon.natures.count XXXXXX,XXXXXX,XXXXXX
|
||||
# AXVE0.data.pokemon.natures.count-1 XXXXXX,XXXXXX,XXXXXX,XXXXXX
|
||||
# AXVE0.data.pokemon.natures.count-2 XXXXXX
|
||||
# AXPE0.data.pokemon.natures.count XXXXXX,XXXXXX,XXXXXX
|
||||
# AXPE0.data.pokemon.natures.count-1 XXXXXX,XXXXXX,XXXXXX,XXXXXX
|
||||
# AXPE0.data.pokemon.natures.count-2 XXXXXX
|
||||
# AXVE1.data.pokemon.natures.count XXXXXX,XXXXXX,XXXXXX
|
||||
# AXVE1.data.pokemon.natures.count-1 XXXXXX,XXXXXX,XXXXXX,XXXXXX
|
||||
# AXVE1.data.pokemon.natures.count-2 XXXXXX
|
||||
# AXPE1.data.pokemon.natures.count XXXXXX,XXXXXX,XXXXXX
|
||||
# AXPE1.data.pokemon.natures.count-1 XXXXXX,XXXXXX,XXXXXX,XXXXXX
|
||||
# AXPE1.data.pokemon.natures.count-2 XXXXXX
|
||||
# BPRE0.data.pokemon.natures.count XXXXXX,XXXXXX,XXXXXX
|
||||
# BPGE0.data.pokemon.natures.count XXXXXX,XXXXXX,XXXXXX
|
||||
# BPRE1.data.pokemon.natures.count XXXXXX,XXXXXX,XXXXXX
|
||||
# BPGE1.data.pokemon.natures.count XXXXXX,XXXXXX,XXXXXX
|
||||
# BPEE0.data.pokemon.natures.count XXXXXX,XXXXXX,XXXXXX
|
||||
# BPEE0.data.pokemon.natures.count-1 XXXXXX,XXXXXX,XXXXXX,XXXXXX
|
||||
# BPEE0.data.pokemon.natures.count-2 XXXXXX
|
||||
|
||||
# type limiter. See LoadMoveInfoUI and DrawMoveInfoUIMarkers
|
||||
BPRI0.data.pokemon.type.length 44E058
|
||||
BPRI0.data.pokemon.type.length+1 0E566C,1334E4
|
||||
BPRI0.data.pokemon.type.length+2 0E5678,1334F0
|
||||
BPRI0.data.pokemon.type.length+3 0E5684,1334FC
|
||||
BPRI0.data.pokemon.type.length+4 0E5690,133508
|
||||
BPRI0.data.pokemon.type.length+5 0E569C
|
||||
|
||||
# number of item effects that link to type boost. See sHoldEffectToType
|
||||
BPRI0.data.pokemon.type.holdEffect.length-1 03EE2C
|
||||
|
||||
# trainer phone calls
|
||||
|
||||
# evolution methods limiter
|
||||
BPRI0.scripts.evolution.count-1 042E96
|
||||
|
||||
# item count limiter
|
||||
BPRI0:data.items.count 098A88,09B4A0,10870C,10DAD8,13D504
|
||||
|
||||
# data.abilities.pickup.items limiter
|
||||
BPRI0.data.abilities.pickup.length-2 02CE70
|
||||
|
||||
# data.maps.heal.map limiter
|
||||
BPRI0.data.maps.heal.length-1 0BFE2C
|
||||
BPRI0.data.maps.heal.length 0BFE66
|
||||
|
||||
# maximum flash level
|
||||
BPRI0.scripts.moves.flash.maxlevel-1 3BF8E8
|
||||
|
||||
# number of balls in the safari zone
|
||||
BPRI0.scripts.games.safari.balls.count 0A0F9E
|
||||
|
||||
# overworld table limiters (credit to phoenixbound)
|
||||
BPRI0.graphics.overworld.tablelength-1 05F2CC
|
||||
|
||||
# firstpersonview table length
|
||||
BPRI0.graphics.firstpersonview.count-1 0F8398
|
||||
BPRI0.graphics.firstpersonview.count 0F839C,0F83BA,0F8440,0F87E6,0F880E
|
||||
|
||||
BPRI0.data.items.teachy.count 46E68C
|
||||
|
||||
# not really sure about this...
|
||||
BPRI0.scripts.seagallop.count-1 146E88
|
||||
|
||||
|
|
@ -1421,7 +1421,7 @@ Format = '''[id:: tileset<`lzt4`> tilemap<`lzm4x30x24|table`> palette<`ucp4`>]8'
|
|||
[[NamedAnchors]]
|
||||
Name = '''graphics.overworld.firstpersonview.sprites'''
|
||||
Address = 0x436EBC
|
||||
Format = '''[id.data.maps.names+88 transition.transitiontype worldmapflag: tileset<`lzt4|graphics.firstpersonview.sprites`> tilemap<`lzm4x32x20|graphics.firstpersonview.sprites`> pal<`ucp4:DE`>]graphics.firstpersonview.count'''
|
||||
Format = '''[id.data.maps.names+88 transition.transitiontype worldmapflag: tileset<`lzt4|graphics.overworld.firstpersonview.sprites`> tilemap<`lzm4x32x20|graphics.overworld.firstpersonview.sprites`> pal<`ucp4:DE`>]graphics.firstpersonview.count'''
|
||||
|
||||
[[NamedAnchors]]
|
||||
Name = '''graphics.battle.background.sprites'''
|
||||
|
|
@ -4437,13 +4437,9 @@ Name = '''perstepcallbacks'''
|
|||
Name = '''directions'''
|
||||
1 = [
|
||||
'''South''',
|
||||
'''Down''',
|
||||
'''North''',
|
||||
'''Up''',
|
||||
'''West''',
|
||||
'''Left''',
|
||||
'''East''',
|
||||
'''Right''',
|
||||
'''Southwest''',
|
||||
'''Southeast''',
|
||||
'''Northwest''',
|
||||
|
|
|
|||
1216
src/HexManiac.Core/Models/Code/default.bpri.toml
Normal file
1216
src/HexManiac.Core/Models/Code/default.bpri.toml
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -1174,20 +1174,16 @@ Name = '''perstepcallbacks'''
|
|||
|
||||
[[List]]
|
||||
Name = '''directions'''
|
||||
0 = '''0'''
|
||||
1 = '''South'''
|
||||
1 = '''Down'''
|
||||
2 = '''North'''
|
||||
2 = '''Up'''
|
||||
3 = '''West'''
|
||||
3 = '''Left'''
|
||||
4 = '''East'''
|
||||
4 = '''Right'''
|
||||
5 = [
|
||||
'''Southwest''',
|
||||
'''Southeast''',
|
||||
'''Northwest''',
|
||||
'''Northeast''',
|
||||
0 = [
|
||||
'''0''',
|
||||
'''South''',
|
||||
'''North''',
|
||||
'''West''',
|
||||
'''East''',
|
||||
'''Southwest''',
|
||||
'''Southeast''',
|
||||
'''Northwest''',
|
||||
'''Northeast''',
|
||||
]
|
||||
|
||||
[[List]]
|
||||
|
|
|
|||
|
|
@ -258,6 +258,7 @@ double.battle.continue.silent 5C 08 trainer:data.trainers.stats 00 00 star
|
|||
59 spriteinvisible npc: bank. map. # hides the sprite on the given map by setting its invisibility to true.
|
||||
5A faceplayer # if the script was called by a person event, make that person face the player
|
||||
5B spriteface npc: direction.directions
|
||||
5B turnobject npc: direction.directions
|
||||
|
||||
5C trainerbattle 00 trainer:data.trainers.stats arg: start<""> playerwin<"">
|
||||
5C trainerbattle 01 trainer:data.trainers.stats arg: start<""> playerwin<""> winscript<`xse`> # doesn't play encounter music, continues with winscript
|
||||
|
|
|
|||
650
src/HexManiac.Core/Models/Code/tableReference.it.txt
Normal file
650
src/HexManiac.Core/Models/Code/tableReference.it.txt
Normal file
|
|
@ -0,0 +1,650 @@
|
|||
// Contributed by Leonardo Cariaggi (leqo-c) and Francesco Cariaggi (anferico)
|
||||
// Special thanks to Fred40 for helping with a lot of the FireRed image anchors
|
||||
|
||||
// quick reference for formats
|
||||
// . one byte
|
||||
// : two bytes
|
||||
// :: four bytes
|
||||
// "" pokemon-character-set text, dynamic length
|
||||
// ""10 10 pokemon-character-set characters, the last one should be an 'end' (FF)
|
||||
// |h display as hex
|
||||
// |z signed value (can be negative)
|
||||
// |t a tuple
|
||||
// |s switch/record. Value type depends on other fields
|
||||
// |b[] bit-array (1-bit per value, labeled after an enum or list)
|
||||
// <> pointer
|
||||
// `asc`10 10 ascii characters
|
||||
// `ucp` uncompressed palette
|
||||
// `lzp4` compressed palette
|
||||
// `lzs4` compressed sprite
|
||||
// `lzt4` compressed tiles
|
||||
// `lzm4` compressed tilemap
|
||||
// `ucs4x4x4` uncompressed sprite
|
||||
// `uct` uncompressed tiles
|
||||
// `ucm` uncompressed tilemap
|
||||
// `xse` event script
|
||||
// `bse` battle script
|
||||
// `ase` animation script
|
||||
// `tse` trainer AI script
|
||||
// `pie` pokemon item effect
|
||||
// `tpt` trainer pokemon team
|
||||
// `osl` overworld sprite list
|
||||
// `egg` egg moves
|
||||
// `blm` block-map (which blocks go where in a map)
|
||||
// `bls` block-set (which tiles/palettes make up a set of blocks)
|
||||
// `bld` block-data (flags plus pointers to tileset, palette, blockset, block attributes, and tileset animation)
|
||||
// `bla` block-attributes
|
||||
//games, BPRI0,
|
||||
// FRedIt,
|
||||
|
||||
data.pokemon.names , 000144, [name""11]
|
||||
data.pokemon.moves.names , 000148, ^[name""13]
|
||||
data.abilities.names , 0001C0, [name""13]
|
||||
data.trainers.classes.names , 0D7B0C, [name""13]107
|
||||
data.pokemon.type.names , 0308B4, ^[name""7]data.pokemon.type.length
|
||||
data.items.stats , 0001C8, [name""13 unused. index: price: holdeffect.holdeffects param. description<""> keyitemvalue. bagkeyitem. pocket.pocketid type.|s=pocket(0=itemtype|1=itemtype|2=itemtype|4=itemtype|5=itemtype) fieldeffect<> battleusage:: battleeffect<> battleextra::]data.items.count
|
||||
data.abilities.descriptions , 0001C4, [description<"">]data.abilities.names
|
||||
data.pokemon.moves.descriptions , 0E5664, [description<"">]data.pokemon.moves.names-1
|
||||
scripts.text.multichoice , 09CC28, [options<[text<""> unused::]/count> count::]
|
||||
scripts.text.listmenu , 0CBC00, [option1<""> option2<""> option3<""> option4<""> option5<""> option6<""> option7<""> option8<""> option9<""> optionA<""> optionB<""> optionC<"">]7
|
||||
|
||||
data.pokemon.natures.names , 0487B0, [name<"">]25
|
||||
data.menus.text.options , 088EAC, [text<"">]7
|
||||
data.menus.text.pc , 08FCCC, [text<""> msgvar::messagevars]31
|
||||
data.menus.text.pokemon , 121E6C, [text<"">]27
|
||||
data.menus.text.pcoptions , 0EBA30, [text<""> code<>]3
|
||||
data.menus.namescreen.content , 09F738, [line""8]12
|
||||
data.menus.namescreen.draw , 09FB30, [line<[unknown:|h gap. character""1]!FF>]12
|
||||
|
||||
scripts.newgame.names.male , 131780, [name<"">]19
|
||||
scripts.newgame.names.female , 13177C, [name<"">]19
|
||||
scripts.newgame.names.rival , 1317E0, [name<"">]4
|
||||
|
||||
data.maps.names , 0C0E44, [name<"">]
|
||||
data.maps.banks , 055258, [maps<[map<[layout<[width:: height:: borderblock<> blockmap<`blm`> blockdata1<[isCompressed. isSecondary. padding: tileset<> pal<`ucp4:0123456789ABCDEF`> blockset<> animation<> attributes<>]1> blockdata2<[isCompressed. isSecondary. padding: tileset<> pal<`ucp4:0123456789ABCDEF`> blockset<> animation<> attributes<>]1> borderwidth. borderheight. unused:]1> events<[objectCount.100 warpCount.100 scriptCount.100 signpostCount.100 objects<[id. graphics.graphics.overworld.sprites kind: x:|z y:|z elevation.11 moveType. range:|t|x::|y:: trainerType: trainerRangeOrBerryID: script<`xse`> flag:|h padding:]/objectCount> warps<[x:|z y:|z elevation.11 warpID. map. bank.]/warpCount> scripts<[x:|z y:|z elevation:11 trigger: index:: script<`xse`>]/scriptCount> signposts<[x:|z y:|z elevation.11 kind. unused:1 arg::|s=kind(0=<>|1=<>|2=<>|3=<>|4=<>)]/signpostCount>]1> mapscripts<[type. pointer<>]!00> connections<[count:: connections<[direction::mapdirections offset:: mapGroup. mapNum. unused:]/count>]1> music:songnames layoutID:data.maps.layouts+1 regionSectionID.data.maps.names+88 cave. weather. mapType. allowBiking. flags.|t|allowEscaping.|allowRunning.|showMapName. floorNum. battleType.]1>]?>]43
|
||||
data.maps.layouts , 0551A0, [layout<[width:: height:: borderblock<> blockmap<`blm`> blockdata1<[isCompressed. isSecondary. padding: tileset<> pal<`ucp4:0123456789ABCDEF`> blockset<> animation<> attributes<>]1> blockdata2<[isCompressed. isSecondary. padding: tileset<> pal<`ucp4:0123456789ABCDEF`> blockset<> animation<> attributes<>]1> borderwidth. borderheight. unused:]1>]383
|
||||
data.maps.dungeons.stats , 0C1A68, [id:: name<""> description<"">]19
|
||||
|
||||
data.maps.heal.map , 0BFF30, [bank: map:]data.maps.heal.length
|
||||
data.maps.heal.healerNPC , 0BFFC4, [ID.]data.maps.heal.map
|
||||
|
||||
// TODO Leo: Questo fly.spawn l'ho valorizzato correttamente ma nella rom inglese ci sono anche due copie del puntatore con -8.
|
||||
data.maps.fly.spawn , 0BFE24, [bank. map. x: y: unused:]data.maps.heal.length
|
||||
data.maps.fly.connections , 0C5440, [bank. map. flight.]data.maps.names-0-1
|
||||
|
||||
data.maps.roaming.sets , 141E88, [loc1. loc2. loc3. loc4. loc5. loc6. loc7.]26
|
||||
|
||||
// +28 pokemon are for egg and unown sprites (B-Z, !, ?),
|
||||
graphics.pokemon.sprites.front , 000128, ^[sprite<`lzs4x8x8`> uncompressedLength: index:]data.pokemon.names+28
|
||||
graphics.pokemon.sprites.back , 00012C, [sprite<`lzs4x8x8`> uncompressedLength: index:]data.pokemon.names+28
|
||||
graphics.pokemon.sprites.ghost , 0345A4, `lzs4x8x8|graphics.pokemon.palettes.ghost`
|
||||
graphics.pokemon.palettes.ghost , 0345AC, `lzp4`
|
||||
|
||||
graphics.pokemon.sprites.elevation , 0355E4, [elevation.]data.pokemon.names
|
||||
graphics.pokemon.sprites.footprint , 105ECC, [footprint<`ucs1x2x2`>]data.pokemon.names+1
|
||||
graphics.pokemon.sprites.coordinates.front, 011ED0, [size.|t|width::|height:: yOffset. render|render=graphics.battle.background.sprites/0/battlemap/|0|0|240|112|graphics.pokemon.sprites.front|144|8||yOffset-graphics.pokemon.sprites.elevation/elevation unused:]data.pokemon.names+28
|
||||
graphics.pokemon.sprites.coordinates.back, 074584, [size.|t|width::|height:: yOffset. render|render=graphics.battle.background.sprites/0/battlemap/|0|0|240|112|graphics.pokemon.sprites.back|40|48||yOffset unused:]data.pokemon.names+28
|
||||
graphics.pokemon.sprites.anchor , 12EB70, [x1. y1. yClose. x2. y2.]data.pokemon.names-1+2
|
||||
graphics.pokemon.palettes.normal , 000130, ^[palette<`lzp4`> index: unused:]data.pokemon.names+28
|
||||
graphics.pokemon.palettes.shiny , 000134, [palette<`lzp4`> index: unused:]data.pokemon.names+28
|
||||
graphics.pokemon.icons.sprites , 000138, [icon<`ucs4x4x8|graphics.pokemon.icons.index`>]data.pokemon.names+28
|
||||
graphics.pokemon.icons.index , 00013C, [index.graphics.pokemon.icons.palettes]data.pokemon.names+28
|
||||
graphics.pokemon.icons.palettes , 000140, [palette<`ucp4`> id::]3
|
||||
graphics.pokemon.icons.deoxys , 3CC8D8, `ucs4x4x16|graphics.pokemon.icons.index`
|
||||
graphics.pokemon.shiny.star.sprite , 13AF44, `lzs4x1x2|graphics.pokemon.shiny.star.palette`
|
||||
graphics.pokemon.shiny.star.palette , 13AF48, `ucp4`
|
||||
graphics.pokemon.evolution.scene.palette, 0D04F4, `ucp4:AB`
|
||||
graphics.pokemon.evolution.scene.tileset, 0D05AC, `lzt4|graphics.pokemon.evolution.scene.palette`
|
||||
graphics.pokemon.evolution.scene.tilemap1, 0D05B0, `lzm4x32x32|graphics.pokemon.evolution.scene.tileset`
|
||||
graphics.pokemon.evolution.scene.tilemap2, 0D05B4, `lzm4x32x32|graphics.pokemon.evolution.scene.tileset`
|
||||
graphics.trainers.sprites.front , 034628, [sprite<`lzs4x8x8|graphics.trainers.palettes.front`> uncompressedLength: index:]148
|
||||
|
||||
graphics.trainers.sprites.back.enter , 10BD1C, [sprite<`ucs4x8x8|graphics.trainers.palettes.back`> a. b. c:]6
|
||||
graphics.trainers.animations.back , 03F790, [animationPointer<[animation<[frame: time:]!FFFF0000>]2>]graphics.trainers.sprites.back.enter
|
||||
graphics.trainers.animations.frames , 10BE6C, [animationPointer<[animation<>]1>]graphics.trainers.sprites.front
|
||||
graphics.trainers.coordinates.front , 037F38, [x. y. unused:]graphics.trainers.sprites.front
|
||||
graphics.trainers.palettes.front , 034638, [palette<`lzp4`> index: unused:]graphics.trainers.sprites.front
|
||||
graphics.trainers.sprites.back.throw , 03F78C, [titleTag:|h paletteTag:|h oam<> anims<> sprites<`osl|graphics.trainers.palettes.back:sprite`> affineAnimations<> callback<>]graphics.trainers.sprites.back.enter
|
||||
graphics.trainers.coordinates.back , 032460, [x. y. unused:]graphics.trainers.sprites.back.enter
|
||||
graphics.trainers.palettes.back , 0334C0, [pal<`lzp4`> sprite::]graphics.trainers.sprites.back.enter
|
||||
graphics.trainers.emotes.sprites , 3C039C, [pointer<`ucs4x2x2|graphics.overworld.palettes:id=1100`> length::]15
|
||||
|
||||
|
||||
graphics.pokedex.habitats , 103198, [sprite<`lzs4x8x6`> pal<`ucp4`>]data.pokedex.habitat.names+6
|
||||
graphics.pokedex.minibox , 105118, `lzs4x8x4|graphics.townmap.catchmap.palette`
|
||||
graphics.pokedex.regionaldex.tileset , 1028F8, `lzt4|graphics.townmap.catchmap.palette`
|
||||
graphics.pokedex.nationaldex.tileset , 1027F0, `lzt4|graphics.townmap.catchmap.palette`
|
||||
|
||||
|
||||
graphics.pokemon.moves.animations , 072520, [animation<`ase`>]data.pokemon.moves.names
|
||||
graphics.moves.particles.sprites-13880 , 072808, [ptr<`lzt4`> size: index:move_particles+10000]move_particles
|
||||
graphics.moves.particles.palettes-13880 , 07280C, [ptr<`lzp4`> index:move_particles+10000 unused:]graphics.moves.particles.sprites
|
||||
graphics.moves.backgrounds.all , 0738B0, [tileset<`lzt4`> palette<`lzp4:2`> tilemap<`lzm4x32x32|graphics.moves.backgrounds.all`>]animationbg
|
||||
graphics.moves.backgrounds.psychic , 3A6E4C, `lzm4x32x20|graphics.moves.backgrounds.all`
|
||||
graphics.moves.backgrounds.impact.opponent, 3A6E58, `lzm4x32x20|graphics.moves.backgrounds.all`
|
||||
graphics.moves.backgrounds.impact.player, 3A6E64, `lzm4x32x20|graphics.moves.backgrounds.all`
|
||||
graphics.moves.backgrounds.impact.contest, 3A6E70, `lzm4x32x20|graphics.moves.backgrounds.all`
|
||||
graphics.moves.backgrounds.impact.fissure, 3A6F24, `lzm4x32x64|graphics.moves.backgrounds.all`
|
||||
graphics.moves.surf.palette , 0AB530, `lzp4:8`
|
||||
graphics.moves.surf.tileset , 0AB528, `lzt4|graphics.moves.surf.palette`
|
||||
graphics.moves.surf.opponent , 0AB4D0, `lzm4x32x64|graphics.moves.surf.tileset`
|
||||
graphics.moves.surf.player , 0AB4E0, `lzm4x32x64|graphics.moves.surf.tileset`
|
||||
graphics.moves.surf.contest , 0AB524, `lzm4x32x64|graphics.moves.surf.tileset`
|
||||
|
||||
graphics.items.ball.sprites , 0001D0, [sprite<`lzs4x2x6|graphics.items.ball.palettes`> uncompressedLength: tag:]12
|
||||
graphics.items.ball.palettes , 0001D4, [palette<`lzp4`> a b unused:]graphics.items.ball.sprites
|
||||
graphics.items.ball.trade.sprite , 265EB4, `ucs4x2x24|graphics.items.ball.trade.palette`
|
||||
graphics.items.ball.trade.palette , 265EBC, `ucp4`
|
||||
graphics.items.sprites , 098A8C, [sprite<`lzs4x3x3`> palette<`lzp4`>]data.items.stats+1
|
||||
graphics.items.mail , 0BF060, [pal<`ucp4`> tileset<`lzt4`> tilemap<`lzm4x32x20|graphics.items.mail`> size:: textcolor:|c shadow:|c]12
|
||||
graphics.items.fossils.palette1 , 09D6D4, `ucp4`
|
||||
graphics.items.fossils.palette2 , 09D624, `ucp4`
|
||||
graphics.items.fossils.sprite1 , 3D8690, `ucs4x8x8|graphics.items.fossils.palette1`
|
||||
graphics.items.fossils.sprite2 , 3D8680, `ucs4x8x8|graphics.items.fossils.palette2`
|
||||
|
||||
|
||||
data.pokemon.stats , 0001BC, [hp. attack. def. speed. spatk. spdef. baseStatTotal|=hp+attack+def+speed+spatk+spdef type1.data.pokemon.type.names type2.data.pokemon.type.names catchRate. baseExp. evs:|t|hp:|atk:|def:|spd:|spatk:|spdef: item1:data.items.stats item2:data.items.stats genderratio.genderratiovalues steps2hatch. basehappiness. growthrate.growthrates egg1.egggroups egg2.egggroups ability1.data.abilities.names ability2.data.abilities.names runrate. dex.|t|color:::.bodycolors|noFlip. padding:]data.pokemon.names
|
||||
|
||||
// TODO Leo: dati trovati, nessun anchor
|
||||
data.pokemon.deoxys.stats , 0445F0, [hp: atk: def: spd: spatk: spdef:]1
|
||||
|
||||
data.pokemon.evolutions , 042E58, [method1:evolutionmethods arg1:|s=method1(6=data.items.stats|7=data.items.stats) species1:data.pokemon.names unused1: method2:evolutionmethods arg2:|s=method2(6=data.items.stats|7=data.items.stats) species2:data.pokemon.names unused2: method3:evolutionmethods arg3:|s=method3(6=data.items.stats|7=data.items.stats) species3:data.pokemon.names unused3: method4:evolutionmethods arg4:|s=method4(6=data.items.stats|7=data.items.stats) species4:data.pokemon.names unused4: method5:evolutionmethods arg5:|s=method5(6=data.items.stats|7=data.items.stats) species5:data.pokemon.names unused5:]data.pokemon.names
|
||||
|
||||
// TODO Leo: dati trovati, nessun anchor
|
||||
data.items.pokeball.catchrates , 02D618, [catchrate.]4
|
||||
|
||||
data.items.effects , 03A19C, [data<`pie`>]data.items.stats-13-199
|
||||
data.items.berry.stats , 09C84C, [name""7 firmness. size: maxYield. minYield. description1<""> description2<""> stageDuration. spicy. dry. sweet. bitter. sour. smoothness:]43
|
||||
data.items.teachy.tv , 46E680, [label<""> id::]data.items.teachy.count
|
||||
data.pokemon.moves.stats.battle , 0001CC, [effect.moveeffectoptions power. type.data.pokemon.type.names accuracy. pp. effectAccuracy. target|b[]movetarget priority.|z info|b[]moveinfo unused. unused:]data.pokemon.moves.names
|
||||
data.pokemon.moves.levelup , 03E968, [movesFromLevel<[pair:|t|move::::.data.pokemon.moves.names|level:::.]!FFFF>]data.pokemon.names
|
||||
data.pokemon.moves.tutors , 120C5C, [move:data.pokemon.moves.names]15
|
||||
|
||||
// TODO Leo: dati trovati, nessun anchor
|
||||
data.pokemon.moves.tutorcompatibility , 120CA8, [moves|b[]data.pokemon.moves.tutors]data.pokemon.names
|
||||
|
||||
data.pokemon.moves.tms , 125B1C, [move:data.pokemon.moves.names]58
|
||||
data.pokemon.moves.tmcompatibility , 043B54, [moves|b[]data.pokemon.moves.tms]data.pokemon.names
|
||||
data.pokemon.moves.hms , 0440C8, [move:data.pokemon.moves.names]8
|
||||
data.pokemon.moves.details.flash.radius , 07EFC0, [levelToRadius:]scripts.moves.flash.maxlevel
|
||||
data.pokemon.moves.details.lowkick.power, 02C8B4, [weight: basePower:]!FFFFFFFF
|
||||
data.pokemon.moves.details.singing , 072638, [move:data.pokemon.moves.names]!FFFF
|
||||
|
||||
// TODO Leo: dati trovati, nessun anchor (3F5FD7). Tuttavia, sembra abbia risolto.
|
||||
data.pokemon.moves.details.fallback.names, 0D77C4, [name<"">]data.pokemon.type.names
|
||||
|
||||
data.trainers.stats , 00FB84, [structType.trainerStructType class.data.trainers.classes.names introMusicAndGender.|t|music:::.encountersongs|female. sprite.graphics.trainers.sprites.front name""12 prizeMoney|=4*(data.trainers.money/class=class)/moneyrate*pokemon/last/level item1:data.items.stats item2:data.items.stats item3:data.items.stats item4:data.items.stats doubleBattle::doublebattleflag ai|b[]traineraibits | pokemonCount:: pokemon<`tpt`>]
|
||||
data.trainers.money , 025950, [class.data.trainers.classes.names moneyrate. unused:]105
|
||||
data.trainers.vsseeker , 10C8B0, [match1:data.trainers.stats match2:data.trainers.stats match3:data.trainers.stats match4:data.trainers.stats match5:data.trainers.stats match6:data.trainers.stats mapbank: map:]221
|
||||
data.decorations.stats , 00014C, [id. name""16 permission.decorpermissions shape.decorshape category.decorcategory price: unused: description<""> graphics<>]
|
||||
data.pokemon.wild , 0828E0, [bank. map. unused: grass<[rate:: list<[basic|comment=0|20% common|comment=2|10% uncommon|comment=6|5% rare|comment=8|4% mythic|comment=10|1% lowLevel. highLevel. species:data.pokemon.names]12>]1> surf<[rate:: list<[lowLevel. highLevel. species:data.pokemon.names]5>]1> tree<[rate:: list<[lowLevel. highLevel. species:data.pokemon.names]5>]1> fish<[rate:: list<[old|comment=0|old_rod: good|comment=2|good_rod: super|comment=5|super_rod: lowLevel. highLevel. species:data.pokemon.names]10>]1>]!FFFF
|
||||
data.pokemon.trades , 053AE0, [nickname""12 receive:data.pokemon.names hp. attack. defense. speed. spatk. spdef. abilitynum:: trainerid:: cool. tough. beauty. smart. cute. unused. unused: personality:: nature|=personality%25|data.pokemon.natures.names helditem:data.items.stats mailnum. trainername""11 trainergender.trainergender sheen. give::data.pokemon.names]9
|
||||
data.pokemon.moves.details.mimic.metronome.forbidden, 0294F4, [move:data.pokemon.moves.names]!FFFF
|
||||
data.pokemon.moves.details.protect.successrate, 026FCC, [rate:]4
|
||||
data.pokemon.friendship.changes , 043778, [lowFriendshipChange.|z mediumFriendshipChange.|z highFriendshipChange.|z]friendshipevents
|
||||
data.abilities.pickup.items , 02CE68, [item:data.items.stats chance:]data.abilities.pickup.length
|
||||
data.abilities.soundproof.moves , 01A600, [move:data.pokemon.moves.names]!FFFF
|
||||
data.pokemon.moves.egg , 045B40, `egg`
|
||||
data.pokemon.type.chart , 01E8C8, [attack.data.pokemon.type.names defend.data.pokemon.type.names strength.effectiveness]!FFFF00
|
||||
data.pokemon.type.holdEffects , 03EE24, [effect. type.data.pokemon.type.names]data.pokemon.type.holdEffect.length
|
||||
data.pokemon.natures.stats , 0435C4, [attack.|z defense.|z speed.|z spAttack.|z spDefense.|z]data.pokemon.natures.names
|
||||
scripts.moves.effects , 0162E8, [effect<`bse`>]moveeffectoptions
|
||||
scripts.moves.setupeffects , 1D8F02, [effects.moveeffectoptions]!FF
|
||||
scripts.commands.battle.animationscript , 0727A8, [code<>]48
|
||||
scripts.commands.events.specials , 069EF8, [code<>]444
|
||||
scripts.commands.events.thumb , 069A78, [code<>]213
|
||||
scripts.specials.vars , 06E470, [variable::|h]21
|
||||
scripts.commands.battle.battlescript , 014BA0, [code<>]248
|
||||
scripts.commands.battle.ai_script , 0C7278, [command<>]94
|
||||
scripts.commands.battle.animations , 3A6F64, [code<>]48
|
||||
scripts.battle.ai.hpaware.discourage.when.self.high, 1D8FC7, [effect.moveeffectoptions]!FF
|
||||
scripts.battle.ai.hpaware.discourage.when.self.medium, 1D8FD6, [effect.moveeffectoptions]!FF
|
||||
scripts.battle.ai.hpaware.discourage.when.self.low, 1D8FB8, [effect.moveeffectoptions]!FF
|
||||
scripts.battle.ai.hpaware.discourage.when.target.high, 1D900A, [effect.moveeffectoptions]!FF
|
||||
scripts.battle.ai.hpaware.discourage.when.target.medium, 1D9019, [effect.moveeffectoptions]!FF
|
||||
scripts.battle.ai.hpaware.discourage.when.target.low, 1D8FFB, [effect.moveeffectoptions]!FF
|
||||
scripts.battle.ai.trainer , 0C7250, [ai<`tse`>]traineraibits
|
||||
scripts.newgame.setflags , 054B78, `xse`
|
||||
scripts.newgame.pc.item , 0EB8CC, [item:data.items.stats count:]!00000000
|
||||
scripts.credits.panmap , 0F3E48, [data<[loadmapCommand: bank: map:: x: y: delay:: xspeed: yspeed: length:: end1Command: end2Command: end3Command::]1>]13
|
||||
scripts.credits.text , 0F41A0, [header<""> text<""> bool::|t|unknown.]
|
||||
scripts.evolution.routines , 042EAC, [thumb<>]scripts.evolution.count
|
||||
scripts.commands.events.callstd , 069EFC, [script<`xse`>]10
|
||||
|
||||
graphics.pokemon.castform.sprite.coordinates.front, 074670, [size. yOffset. unused:]4
|
||||
graphics.pokemon.castform.sprite.elevations, 0746D0, [elevation.]graphics.pokemon.castform.sprite.coordinates.front
|
||||
graphics.pokemon.castform.sprite.coordinates.back, 0745A0, [yOffset.]graphics.pokemon.castform.sprite.coordinates.front
|
||||
data.battle.text , 0CF174, [text<"">]data.battle.textlength
|
||||
|
||||
|
||||
data.pokedex.regional , 0430DC, [index:]data.pokemon.names-1
|
||||
data.pokedex.national , 043128, [index:]data.pokemon.names-1
|
||||
// hoenn[treecko] = 1, national[treecko] = 252, HoeennToNationalDex[ 1]= 252
|
||||
// hoenn[bulbasaur]= 203, national[bulbasaur]= 1, HoennToNationalDex[203]= 1
|
||||
// -> this table's values can be determined automatically based on the first two
|
||||
data.pokedex.hoennToNational , 043174, [index:]data.pokemon.names-1
|
||||
data.pokedex.stats , 088F04, [species""12 height: heightInches|=height÷.254 weight: weightLbs|=weight÷4.536 description1<""> description2<""> unused: pokemonScale: pokemonOffset:|z trainerScale: trainerOffset:|z unused:]
|
||||
data.pokedex.search.alpha , 1038E8, [species:data.pokedex.national]data.pokedex.national
|
||||
data.pokedex.search.weight , 103A20, [species:data.pokedex.national]data.pokedex.national-25
|
||||
data.pokedex.search.size , 103ABC, [species:data.pokedex.national]data.pokedex.national-25
|
||||
data.pokedex.search.type , 103988, [species:data.pokemon.names]data.pokedex.national
|
||||
data.pokemon.type.unionroom.options , 44E04C, [type<> index::]data.pokemon.type.names-1+1
|
||||
data.pokedex.habitat.names , 105230, [name<"">]
|
||||
data.pokedex.habitat.pages , 106948, [data<[pokemon<[species:data.pokemon.names]/pokecount> pokecount::]/count> count::]data.pokedex.habitat.names
|
||||
|
||||
data.battletower.items , 0E65A0, [item:data.items.stats]64
|
||||
data.battletower.pokemon.level50 , 0E659C, [species:data.pokemon.names heldItem.data.battletower.items flags.|h move1:data.pokemon.moves.names move2:data.pokemon.moves.names move3:data.pokemon.moves.names move4:data.pokemon.moves.names evSpread|b[]battletowerEvFlags nature.data.pokemon.natures.names unused:]300
|
||||
data.battletower.pokemon.level100 , 0E6540, [species:data.pokemon.names heldItem.data.battletower.items flags.|h move1:data.pokemon.moves.names move2:data.pokemon.moves.names move3:data.pokemon.moves.names move4:data.pokemon.moves.names evSpread|b[]battletowerEvFlags nature.data.pokemon.natures.names unused:]300
|
||||
data.battletower.prizes , 15E0E0, [item:data.items.stats]15
|
||||
data.trainers.trainertower.trainers , 15DA04, [trainer<[id. floor. challengetype. prize. name""11 class. textColor. unused. beforeWord1:|h beforeWord2:|h beforeWord3:|h beforeWord4:|h beforeWord5:|h beforeWord6:|h winWord1:|h winWord2:|h winWord3:|h winWord4:|h winWord5:|h winWord6:|h lostWord1:|h lostWord2:|h lostWord3:|h lostWord4:|h lostWord5:|h lostWord6:|h afterWord1:|h afterWord2:|h afterWord3:|h afterWord4:|h afterWord5:|h afterWord6:|h unused: [species:data.pokemon.names heldItem:data.items.stats move1:data.pokemon.moves.names move2:data.pokemon.moves.names move3:data.pokemon.moves.names move4:data.pokemon.moves.names unknown: hpEv. atkEv. defEv. speedEv. spatkEv. spdefEv. otID::|h IVs::|t|hp::.|atk::.|def::.|spd::.|spatk::.|spdef::.|unused.|2ndAbility. personality:: nickname""11 friendship.]6]1>]32
|
||||
|
||||
|
||||
//data.ec.words+8, ,,,, ,,,, 11EAA0, [list<[text<""> a:: b::]/count> count: other:]21 // before1-lose6 use the high 7 bits to select a group, and the low 9 bits to select a phrase.
|
||||
|
||||
graphics.pokemon.type.icons , 107E70, `ucs4x16x16|graphics.pokemon.type.palettes`
|
||||
graphics.pokemon.type.pokeball.palettes , 107E10, `ucp4`
|
||||
graphics.pokemon.type.palettes , 107E24, `ucp4`
|
||||
graphics.pokemon.type.map , 107E6C, [width. height. xy:|t|:|x:|.|y::]data.pokemon.type.names+1+5
|
||||
graphics.text.importer , 145444, [titleTextPal. bodyTextPal. footerTextPal. stampShadowPal. tileset<`lzt4`> tilemap<`lzm4x30x20|graphics.text.importer`> pal<`ucp4`>]8
|
||||
graphics.text.box.about , 1130D0, `ucs4x5x4|graphics.text.box.palette`
|
||||
graphics.text.box.palette , 150154, `ucp4:01234`
|
||||
graphics.text.box.message , 14FA6C, `uct4x18|graphics.text.box.palette`
|
||||
graphics.text.box.signpost , 14FAA0, `uct4x19|graphics.text.box.palette`
|
||||
graphics.text.boxes , 069778, [sprite<`ucs4x3x3`> pal<`ucp4`>]10
|
||||
|
||||
graphics.bag.male , 3CCCF4, `lzs4x8x8|graphics.bag.palette`
|
||||
graphics.bag.female , 3CCCFC, `lzs4x8x8|graphics.bag.palette`
|
||||
graphics.bag.palette , 3CCD04, `lzp4`
|
||||
graphics.bag.berrycase.palette , 45B49C, `lzp4`
|
||||
graphics.bag.berrycase.sprite , 45B494, `lzs4x8x8|graphics.bag.berrycase.palette`
|
||||
graphics.bag.inside1.palette , 1083C4, `lzp4:012`
|
||||
graphics.bag.inside1.tileset , 108354, `lzt4|graphics.bag.inside1.palette`
|
||||
graphics.bag.inside1.tilemap.item , 10837C, `lzm4x32x32|graphics.bag.inside1.tileset`
|
||||
graphics.bag.inside1.tilemap.deposite , 108390, `lzm4x32x32|graphics.bag.inside1.tileset`
|
||||
graphics.bag.inside1.berry.palette , 10D8C0, `lzp4:012`
|
||||
graphics.bag.inside1.berry.tileset , 10D890, `lzt4|graphics.bag.inside1.berry.palette`
|
||||
graphics.bag.inside1.berry.tilemap , 10D8AC, `lzm4x32x32|graphics.bag.inside1.berry.tileset`
|
||||
graphics.bag.inside2.palette , 13D248, `lzp4:012`
|
||||
graphics.bag.inside2.tileset , 13D208, `lzt4|graphics.bag.inside2.palette`
|
||||
graphics.bag.inside2.tilemap , 13D224, `lzm4x32x32|graphics.bag.inside2.tileset`
|
||||
graphics.bag.inside3.palette , 09B1D8, `lzp4`
|
||||
graphics.bag.inside3.tileset , 09B180, `lzt4|graphics.bag.inside3.palette`
|
||||
graphics.bag.inside3.tilemap1 , 09B188, `lzm4x32x32|graphics.bag.inside3.tileset`
|
||||
graphics.bag.inside3.tilemap2 , 09B1D0, `lzm4x32x32|graphics.bag.inside3.tileset`
|
||||
|
||||
|
||||
graphics.menu.pokeball.palette , 451458, `lzp4`
|
||||
graphics.menu.pokeball.large , 451450, `lzs4x4x8|graphics.menu.pokeball.palette`
|
||||
graphics.menu.pokeball.small , 4514C8, `lzs4x2x4|graphics.menu.pokeball.palette`
|
||||
graphics.menu.status.palette , 451558, `lzp4`
|
||||
graphics.menu.status.sprite , 451550, `lzs4x4x8|graphics.menu.status.palette`
|
||||
graphics.menu.bar.palette , 13AA54, `ucp4`
|
||||
graphics.menu.bar.hp , 13A59C, `lzs4x12x1|graphics.menu.bar.palette`
|
||||
graphics.menu.bar.exp , 13AA50, `lzs4x12x1|graphics.menu.bar.palette`
|
||||
graphics.menu.summaryscreen.palette , 136084, `ucp4:0123456`
|
||||
graphics.menu.summaryscreen.tileset , 1360D8, `lzt4|graphics.menu.summaryscreen.palette`
|
||||
graphics.menu.summaryscreen.tilemap.info, 135E98, `lzm4x32x20|graphics.menu.summaryscreen.tileset`
|
||||
graphics.menu.summaryscreen.tilemap.skills, 135E9C, `lzm4x32x20|graphics.menu.summaryscreen.tileset`
|
||||
graphics.menu.summaryscreen.tilemap.knownmoves, 135E64, `lzm4x32x32|graphics.menu.summaryscreen.tileset`
|
||||
graphics.menu.summaryscreen.tilemap.egg , 135E2C, `lzm4x32x20|graphics.menu.summaryscreen.tileset`
|
||||
graphics.menu.summaryscreen.tilemap.changemoves, 135E68, `lzm4x32x20|graphics.menu.summaryscreen.tileset`
|
||||
graphics.menu.relearner.palette , 0E49A4, `ucp4`
|
||||
graphics.menu.relearner.tileset , 0E49A8, `lzt4|graphics.menu.relearner.palette`
|
||||
graphics.menu.relearner.tilemap , 0E49AC, `lzm4x32x32|graphics.menu.relearner.tileset`
|
||||
graphics.menu.summaryscreen.tilemap.relearner, 135DF0, `lzm4x32x32|graphics.menu.summaryscreen.tileset`
|
||||
graphics.menu.summaryscreen.unknown.tilemap3, 135E08, `lzm4x32x20|graphics.menu.summaryscreen.tileset`
|
||||
graphics.menu.partyscreen.selection.palette, 13A1A4, `ucp4`
|
||||
graphics.menu.partyscreen.selection.sprite1, 13A19C, `lzs4x8x8|graphics.menu.partyscreen.selection.palette`
|
||||
graphics.menu.partyscreen.selection.sprite2, 13A1A0, `lzs4x8x8|graphics.menu.partyscreen.selection.palette`
|
||||
graphics.menu.pokemon.background.palette, 11F06C, `lzp4:0123456789A`
|
||||
graphics.menu.pokemon.background.tileset, 11F028, `lzt4|graphics.menu.pokemon.background.palette`
|
||||
graphics.menu.pokemon.background.tilemap, 11F044, `lzm4x32x32|graphics.menu.pokemon.background.tileset`
|
||||
graphics.menu.pokemon.item.sprite , 451408, `ucs4x1x2|graphics.menu.pokemon.item.palette`
|
||||
graphics.menu.pokemon.item.palette , 451410, `ucp4`
|
||||
graphics.menu.boxes.background , 091A20, [tileset<`lzt4`> tilemap<`lzm4x20x18|graphics.menu.boxes.background`> palette<`ucp4:12`>]16
|
||||
graphics.menu.boxes.picker.party.palette, 08F810, `ucp4`
|
||||
graphics.menu.boxes.picker.cursor.palette, 08F1B4, `ucp4`
|
||||
graphics.menu.boxes.picker.palette , 08F1B8, `ucp4`
|
||||
graphics.menu.boxes.picker.palette2 , 08F1B4, `ucp4`
|
||||
graphics.menu.boxes.picker.tileset , 08F138, `lzt4|graphics.menu.boxes.picker.palette`
|
||||
//graphics.menu.boxes.picker.tilemap, ,,,, 08F038, 08F00C, 08F04C, 08F020, 0CA098, `lzm4x32x20|graphics.menu.boxes.picker.tileset` // BPRE0, needs testing for LG/1.1
|
||||
// this tilemap assumes the tileset is loaded at index 0x100 instead of index 0x00. The tilemap won't show right unless we support that.
|
||||
graphics.menu.boxes.hand.palette.normal , 3C7530, `ucp4`
|
||||
graphics.menu.boxes.hand.palette.catch , 3CBF88, `ucp4`
|
||||
graphics.menu.boxes.hand.sprite , 3CBF70, `ucs4x4x16|graphics.menu.boxes.hand.palette.normal`
|
||||
graphics.menu.tms.tileset , 131C00, `lzt4|graphics.menu.tms.palette`
|
||||
graphics.menu.tms.background , 131C1C, `lzm4x32x32|graphics.menu.tms.tileset`
|
||||
graphics.menu.tms.case , 131C38, `lzm4x32x32|graphics.menu.tms.tileset`
|
||||
graphics.menu.tms.palette , 131C58, `lzp4:0123`
|
||||
graphics.menu.tms.palette2 , 131C68, `lzp4`
|
||||
graphics.menu.tms.hm_logo , 1336B8, `ucs4x2x2`
|
||||
graphics.menu.help.palette , 13BA68, `ucp4`
|
||||
graphics.menu.text.bold.palette , 10B9AC, `ucp4`
|
||||
graphics.menu.storage.background.palette1, 08F1C4, `ucp4:3`
|
||||
graphics.menu.storage.background.palette2, 08F1F0, `ucp4:3`
|
||||
graphics.menu.storage.background.tileset, 08F0C0, `lzs4x4x2|graphics.menu.storage.background.palette1`
|
||||
graphics.menu.storage.background.tilemap, 08F0C4, `lzm4x32x32|graphics.menu.storage.background.tileset`
|
||||
graphics.menu.downarrow.palette , 00AD88, `ucp4`
|
||||
graphics.menu.downarrow.sprite1 , 0054E0, `ucs4x2x4|graphics.menu.downarrow.palette`
|
||||
graphics.menu.downarrow.sprite2 , 00554C, `ucs4x4x6|graphics.menu.downarrow.palette`
|
||||
graphics.menu.downarrow.sprite3 , 1E3730, `ucs4x2x2|graphics.menu.downarrow.palette`
|
||||
graphics.menu.downarrow.sprite4 , 1E3738, `ucs4x2x2|graphics.menu.downarrow.palette`
|
||||
|
||||
|
||||
// from AGSMG
|
||||
graphics.moves.substitute.sprite.front , 034FE0, `lzs4x8x8|graphics.moves.substitute.palette`
|
||||
graphics.moves.substitute.palette , 035038, `lzp4`
|
||||
graphics.moves.substitute.sprite.back , 035030, `lzs4x8x8|graphics.moves.substitute.palette`
|
||||
graphics.moves.tmcase.sprite , 45A1F4, `lzs4x4x4|graphics.moves.tmcase.palette`
|
||||
graphics.moves.tmcase.palette , 1339D4, `lzp4`
|
||||
graphics.moves.tmcase.palette2 , 1339D8, `lzp4`
|
||||
graphics.moves.tmcase.palettemap , 133864, [offset:|t|::|page::::]data.pokemon.type.names
|
||||
data.pokemon.type.camouflage , 02D1EC, [type.data.pokemon.type.names]terrains
|
||||
data.pokemon.moves.details.naturepower.list, 02BDF8, [move:data.pokemon.moves.names]terrains
|
||||
|
||||
graphics.gamecorner.game.palette , 1414AC, `ucp4:01234`
|
||||
graphics.gamecorner.game.tileset , 14149C, `lzt4|graphics.gamecorner.game.palette`
|
||||
graphics.gamecorner.game.tilemap , 1414A8, `lzm4x32x20|graphics.gamecorner.game.tileset`
|
||||
graphics.gamecorner.score.palette , 1414B4, `ucp4:789`
|
||||
graphics.gamecorner.score.tileset , 1414C0, `lzt4|graphics.gamecorner.score.palette`
|
||||
graphics.gamecorner.score.tilemap , 1414C4, `lzm4x32x20|graphics.gamecorner.score.tileset`
|
||||
graphics.gamecorner.sprites , 140D2C, [rolls<`lzs4x4x4`> a:: cheer<`lzs4x4x4`> b:: digits<`lzs4x5x4`> c::]1
|
||||
graphics.gamecorner.palettes , 140D30, [pal<`ucp4`> id::]7
|
||||
data.gamecorner.payout , 140CC8, [payout:]7
|
||||
|
||||
graphics.misc.questionnaire.palette , 100F64, `ucp4`
|
||||
graphics.misc.questionnaire.tileset , 1002B8, `lzt4|graphics.misc.questionnaire.palette`
|
||||
graphics.misc.questionnaire.tilemap , 1002BC, `lzm4x32x20|graphics.misc.questionnaire.tileset`
|
||||
graphics.misc.questionnaire.button.palette, 436E90, `ucp4`
|
||||
graphics.misc.questionnaire.button.sprite, 436EB8, `lzs4x8x8|graphics.misc.questionnaire.button.palette`
|
||||
|
||||
|
||||
graphics.overworld.firstpersonview.sprites, 0F8390, [id.data.maps.names+88 transition.transitiontype worldmapflag: tileset<`lzt4|graphics.overworld.firstpersonview.sprites`> tilemap<`lzm4x32x20|graphics.overworld.firstpersonview.sprites`> pal<`ucp4:DE`>]graphics.firstpersonview.count
|
||||
graphics.battle.background.sprites , 00F224, [battletiles<`lzt4`> battlemap<`lzm4x32x64|graphics.battle.background.sprites|battletiles`> | introtiles<`lzt4`> intromap<`lzm4x32x14|graphics.battle.background.sprites|introtiles`> pal<`lzp4:234`>]
|
||||
graphics.battle.background.fighttype , 00F1D0, [id. entry.graphics.battle.background.sprites unused:]8
|
||||
graphics.battle.hud.palette , 259340, `ucp4`
|
||||
graphics.battle.hud.hpbar.sprite , 047FA4, `ucs4x11x6|graphics.battle.hud.hpbar.palette`
|
||||
graphics.battle.hud.hpbar.palette , 259348, `ucp4`
|
||||
graphics.battle.hud.hpbox.player , 2592E8, `lzs4x8x16|graphics.battle.hud.palette`
|
||||
graphics.battle.hud.hpbox.opponent , 2592F0, `lzs4x8x8|graphics.battle.hud.palette`
|
||||
graphics.battle.hud.hpbox.doublebattle.player, 259300, `lzs4x8x8|graphics.battle.hud.palette`
|
||||
graphics.battle.hud.hpbox.doublebattle.opponent, 259310, `lzs4x8x8|graphics.battle.hud.palette`
|
||||
graphics.battle.hud.hpbox.safarizone , 259318, `lzs4x8x16|graphics.battle.hud.palette`
|
||||
graphics.battle.hud.status , 259584, `ucs4x13x4|graphics.battle.hud.hpbar.palette`
|
||||
graphics.battle.hud.pokeballbar , 25954C, `lzs4x16x1|graphics.battle.hud.palette`
|
||||
graphics.battle.hud.idlelevel.palette , 02644C, `ucp4`
|
||||
graphics.battle.hud.idlelevel.sprite , 026450, `lzs4x12x3|graphics.battle.hud.idlelevel.palette`
|
||||
graphics.battle.textbox.palette , 00F3E0, `lzp4:01`
|
||||
graphics.battle.textbox.tileset , 00F3D8, `lzt4|graphics.battle.textbox.palette`
|
||||
graphics.battle.textbox.tilemap , 00F3DC, `lzm4x32x64|graphics.battle.textbox.tileset`
|
||||
graphics.battle.animations.status , 078800, [animation<`ase`>]statusanimations
|
||||
graphics.battle.animations.special , 03403C, [animation<`ase`>]effectanimations
|
||||
graphics.battle.animations.statchange.palette.p1, 0BB458, `lzp4:8`
|
||||
graphics.battle.animations.statchange.palette.p2, 0BB450, `lzp4:8`
|
||||
graphics.battle.animations.statchange.palette.p3, 0BB460, `lzp4:8`
|
||||
graphics.battle.animations.statchange.palette.p4, 0BB468, `lzp4:8`
|
||||
graphics.battle.animations.statchange.palette.p5, 0BB4C8, `lzp4:8`
|
||||
graphics.battle.animations.statchange.palette.p6, 0BB470, `lzp4:8`
|
||||
graphics.battle.animations.statchange.palette.p7, 0BB478, `lzp4:8`
|
||||
graphics.battle.animations.statchange.palette.p8, 0BB48C, `lzp4:8`
|
||||
graphics.battle.animations.statchange.tileset, 0BB424, `lzt4|graphics.battle.animations.statchange.palette.p1`
|
||||
graphics.battle.animations.statchange.tilemap1, 0BB3D4, `lzm4x32x32|graphics.battle.animations.statchange.tileset`
|
||||
graphics.battle.animations.statchange.tilemap2, 0BB420, `lzm4x32x32|graphics.battle.animations.statchange.tileset`
|
||||
graphics.battle.animations.misc , 034124, [pointer<>]miscanimations
|
||||
graphics.battle.pokemon.shadow , 249AEC, `lzs4x4x1|graphics.battle.hud.palette`
|
||||
|
||||
graphics.titlescreen.background.animation.sprite, 3B8BA4, `lzs4x2x2|graphics.titlescreen.background.animation.palette`
|
||||
graphics.titlescreen.background.animation.palette, 3B8BC4, `ucp4`
|
||||
graphics.titlescreen.logo.palette , 0789E4, `ucp8`
|
||||
graphics.titlescreen.logo.tileset , 0789E8, `lzt8|graphics.titlescreen.logo.palette`
|
||||
graphics.titlescreen.logo.tilemap , 0789EC, `lzm8x32x20|graphics.titlescreen.logo.tileset`
|
||||
graphics.titlescreen.pokemon.palette , 0789F0, `ucp4:D`
|
||||
graphics.titlescreen.pokemon.tileset , 0789F4, `lzt4|graphics.titlescreen.pokemon.palette`
|
||||
graphics.titlescreen.pokemon.tilemap , 0789F8, `lzm4x32x20|graphics.titlescreen.pokemon.tileset`
|
||||
graphics.titlescreen.publisher.palette , 0789FC, `ucp4:F`
|
||||
graphics.titlescreen.publisher.tileset , 078A00, `lzt4|graphics.titlescreen.publisher.palette`
|
||||
graphics.titlescreen.publisher.tilemap , 078A04, `lzm4x32x20|graphics.titlescreen.publisher.tileset`
|
||||
graphics.titlescreen.widescreen.tileset , 078A08, `lzt4|graphics.titlescreen.publisher.palette`
|
||||
graphics.titlescreen.widescreen.tilemap , 078A0C, `lzm4x32x20|graphics.titlescreen.widescreen.tileset`
|
||||
graphics.titlescreen.introscene.gengar.palette, 403644, `ucp4:5`
|
||||
graphics.titlescreen.introscene.nidorino.palette, 40364C, `ucp4`
|
||||
graphics.titlescreen.introscene.grass.palette, 403654, `ucp4`
|
||||
graphics.titlescreen.introscene.gengar.sprite, 40360C, `lzs4x8x8|graphics.titlescreen.introscene.gengar.palette`
|
||||
graphics.titlescreen.introscene.nidorino.sprite, 403614, `lzs4x8x8|graphics.titlescreen.introscene.nidorino.palette`
|
||||
graphics.titlescreen.introscene.nidorino.palette1, 0ED52C, `ucp4:6`
|
||||
graphics.titlescreen.introscene.nidorino.tileset, 0ED53C, `lzt4|graphics.titlescreen.introscene.nidorino.palette1`
|
||||
graphics.titlescreen.introscene.nidorino.tilemap, 0ED540, `lzm4x32x32|graphics.titlescreen.introscene.nidorino.tileset`
|
||||
graphics.titlescreen.introscene.gengar.tileset, 0ED544, `lzt4|graphics.titlescreen.introscene.gengar.palette`
|
||||
graphics.titlescreen.introscene.gengar.tilemap, 0ED548, `lzm4x32x32|graphics.titlescreen.introscene.gengar.tileset`
|
||||
graphics.titlescreen.introscene.gengar.tileset2, 0ED814, `lzt4|graphics.titlescreen.introscene.gengar.palette`
|
||||
graphics.titlescreen.introscene.gengar.tilemap2, 0ED818, `lzm4x32x64|graphics.titlescreen.introscene.gengar.tileset2`
|
||||
graphics.titlescreen.introscene.nidorino.sprite2, 40361C, `lzs4x8x8|graphics.titlescreen.introscene.nidorino.palette`
|
||||
graphics.titlescreen.introscene.grass.sprite, 403624, `lzs4x8x4|graphics.titlescreen.introscene.grass.palette`
|
||||
graphics.titlescreen.introscene.gengar.sprite2, 40362C, `lzs4x8x8|graphics.titlescreen.introscene.gengar.palette`
|
||||
graphics.titlescreen.introscene.slash.tiles, 403634, `lzt4`
|
||||
graphics.titlescreen.introscene.forest.palette, 0ED7AC, `ucp4:12`
|
||||
graphics.titlescreen.introscene.forest.tileset, 0ED7BC, `lzt4|graphics.titlescreen.introscene.forest.palette`
|
||||
graphics.titlescreen.introscene.forest.tilemap, 0ED7C0, `lzm4x32x20|graphics.titlescreen.introscene.forest.tileset`
|
||||
graphics.titlescreen.introscene.forest.palette2, 0ED524, `ucp4:123`
|
||||
graphics.titlescreen.introscene.forest.tileset2, 0ED434, `lzt4|graphics.titlescreen.introscene.forest.palette2`
|
||||
graphics.titlescreen.introscene.forest.tilemap2, 0ED438, `lzm4x32x64|graphics.titlescreen.introscene.forest.tileset2`
|
||||
graphics.titlescreen.introscene.grass.palette1, 0ED170, `ucp4:1`
|
||||
graphics.titlescreen.introscene.grass.tileset, 0ED1C4, `lzt4|graphics.titlescreen.introscene.grass.palette1`
|
||||
graphics.titlescreen.introscene.grass.tilemap, 0ED1C8, `lzm4x32x64|graphics.titlescreen.introscene.grass.tileset`
|
||||
graphics.titlescreen.introscene.grass.tileset2, 0ED534, `lzt4|graphics.titlescreen.introscene.forest.palette2`
|
||||
graphics.titlescreen.introscene.grass.tilemap2, 0ED538, `lzm4x32x20|graphics.titlescreen.introscene.grass.tileset2`
|
||||
graphics.titlescreen.introscene.grass.background.palette, 0ED174, `ucp4:2`
|
||||
graphics.titlescreen.introscene.grass.background.tileset, 0ED180, `lzt4|graphics.titlescreen.introscene.grass.background.palette`
|
||||
graphics.titlescreen.introscene.grass.background.tilemap, 0ED184, `lzm4x32x64|graphics.titlescreen.introscene.grass.background.tileset`
|
||||
graphics.titlescreen.developer.palette1 , 403318, `ucp4`
|
||||
graphics.titlescreen.developer.palette2 , 403320, `ucp4`
|
||||
graphics.titlescreen.developer.palette3 , 403328, `ucp4`
|
||||
graphics.titlescreen.developer.bigstar , 4032F0, `lzs4x2x2|graphics.titlescreen.developer.palette1`
|
||||
graphics.titlescreen.developer.littlestar, 4032F8, `lzs4x1x1|graphics.titlescreen.developer.palette2`
|
||||
graphics.titlescreen.developer.mediumstar, 403300, `lzs4x4x4|graphics.titlescreen.developer.palette2`
|
||||
graphics.titlescreen.developer.logo , 403308, `lzs4x4x8|graphics.titlescreen.developer.palette3`
|
||||
graphics.titlescreen.developer.presents , 403310, `lzs4x8x1|graphics.titlescreen.developer.palette3`
|
||||
graphics.titlescreen.developer.text , 0ECD90, `lzs4x18x2|graphics.titlescreen.developer.palette3`
|
||||
graphics.titlescreen.copyright.tileset , 0EC834, `lzt4|graphics.titlescreen.copyright.palette`
|
||||
graphics.titlescreen.copyright.tilemap , 0EC838, `lzm4x32x32|graphics.titlescreen.copyright.tileset`
|
||||
graphics.titlescreen.copyright.palette , 0EC83C, `ucp4`
|
||||
|
||||
graphics.newgame.platform.palette , 459F00, `ucp4`
|
||||
graphics.newgame.platform.sprite , 459EF0, `lzs4x4x12|graphics.newgame.platform.palette`
|
||||
graphics.newgame.pikachu.palette , 459EF8, `ucp4`
|
||||
graphics.newgame.pikachu.body , 459ED8, `lzs4x4x8|graphics.newgame.pikachu.palette`
|
||||
graphics.newgame.pikachu.ears , 459EE0, `lzs4x4x4|graphics.newgame.pikachu.palette`
|
||||
graphics.newgame.pikachu.eyes , 459EE8, `lzs4x2x2|graphics.newgame.pikachu.palette`
|
||||
graphics.newgame.player.male.palette , 1312B8, `ucp4:45`
|
||||
graphics.newgame.player.male.sprite , 1312BC, `lzs8x8x12|graphics.newgame.player.male.palette`
|
||||
graphics.newgame.player.female.palette , 1312D0, `ucp4:45`
|
||||
graphics.newgame.player.female.sprite , 1312D4, `lzs8x8x12|graphics.newgame.player.female.palette`
|
||||
graphics.newgame.rival.palette , 1312F0, `ucp4:67`
|
||||
graphics.newgame.rival.sprite , 1312F4, `lzs8x8x12|graphics.newgame.rival.palette`
|
||||
graphics.newgame.professor.palette , 131390, `ucp4:67`
|
||||
graphics.newgame.professor.sprite , 131394, `lzs8x8x12|graphics.newgame.professor.palette`
|
||||
graphics.newgame.background.palette , 130E5C, `ucp4:0123`
|
||||
graphics.newgame.background.tileset1 , 12EDF8, `lzt4|graphics.newgame.background.palette`
|
||||
graphics.newgame.background.tilemap1 , 12F518, `lzm4x30x18|graphics.newgame.background.tileset1`
|
||||
graphics.newgame.background.tileset2 , 130E74, `lzt4|graphics.newgame.background.palette`
|
||||
graphics.newgame.background.tilemap2 , 130EC4, `lzm4x32x20|graphics.newgame.background.tileset2`
|
||||
graphics.newgame.menu.naming.palette1 , 09F9CC, `ucp4:0123`
|
||||
graphics.newgame.menu.naming.palette2 , 3D95C4, `ucp4:01`
|
||||
graphics.newgame.menu.naming.palette3 , 09F9D0, `ucp4`
|
||||
graphics.newgame.menu.naming.sprite1 , 3D954C, `ucs4x5x18|graphics.newgame.menu.naming.palette2`
|
||||
graphics.newgame.menu.naming.sprite2 , 3D93F4, `ucs4x2x6|graphics.newgame.menu.naming.palette1`
|
||||
graphics.newgame.menu.naming.tileset , 09F980, `lzt4|graphics.newgame.menu.naming.palette1`
|
||||
// graphics.newgame.menu.naming.tilemap.m1, , , , , 09DF08, 09DEDC, 09DF1C, 09DEF0, , `lzm4x32x20|graphics.newgame.menu.naming.tileset` // has palette issue: wants to use palette 15?
|
||||
graphics.newgame.menu.naming.tilemap.m2 , 09DFE8, `lzm4x32x20|graphics.newgame.menu.naming.tileset`
|
||||
graphics.newgame.menu.naming.tilemap.m3 , 09DFE4, `lzm4x32x20|graphics.newgame.menu.naming.tileset`
|
||||
graphics.newgame.menu.naming.tilemap.m4 , 3D9314, `lzm4x32x20|graphics.newgame.menu.naming.tileset`
|
||||
graphics.newgame.menu.choosesave.palette, 00C43C, `ucp4`
|
||||
|
||||
// Introductory Speeches
|
||||
scripts.newgame.professor.speeches.intro.welcome, 12F980, ""
|
||||
scripts.newgame.professor.speeches.intro.preShowcaseMon, 12FA24, ""
|
||||
scripts.newgame.professor.speeches.intro.showcaseMon, 12FB98, ""
|
||||
scripts.newgame.professor.speeches.intro.mainSpeech, 12FC2C, ""
|
||||
scripts.newgame.professor.speeches.intro.introduceSelf, 12FDB4, ""
|
||||
scripts.newgame.professor.speeches.prompt.gender, 12FEC4, ""
|
||||
scripts.newgame.professor.speeches.prompt.name, 13019C, ""
|
||||
scripts.newgame.professor.speeches.prompt.confirmName, 130540, ""
|
||||
scripts.newgame.professor.speeches.prompt.repromptRivalName, 13036C, ""
|
||||
scripts.newgame.professor.speeches.prompt.confirmRival, 130588, ""
|
||||
scripts.newgame.professor.speeches.rememberRivalName, 13068C, ""
|
||||
scripts.newgame.professor.speeches.intro.rival, 130804, ""
|
||||
scripts.newgame.professor.speeches.ready, 130954, ""
|
||||
|
||||
graphics.townmap.map.palette , 0C049C, `ucp4:01234`
|
||||
graphics.townmap.map.tileset , 0C04E0, `lzt4|graphics.townmap.map.palette`
|
||||
graphics.townmap.map.tilemap , 0C050C, `lzm4x30x20|graphics.townmap.map.tileset`
|
||||
graphics.townmap.islands.tilemap1 , 0C0520, `lzm4x30x20|graphics.townmap.map.tileset`
|
||||
graphics.townmap.islands.tilemap2 , 0C0538, `lzm4x30x20|graphics.townmap.map.tileset`
|
||||
graphics.townmap.islands.tilemap3 , 0C0554, `lzm4x30x20|graphics.townmap.map.tileset`
|
||||
graphics.townmap.border.tileset , 0C04EC, `lzt4|graphics.townmap.map.palette`
|
||||
graphics.townmap.border.tilemap , 0C0570, `lzm4x30x20|graphics.townmap.border.tileset`
|
||||
graphics.townmap.border.tileset2 , 0C25F0, `lzt4|graphics.townmap.map.palette`
|
||||
graphics.townmap.border.tilemap2 , 0C2608, `lzm4x30x20|graphics.townmap.border.tileset2`
|
||||
graphics.townmap.namesoverlay.xy , 0C3ED4, [x: y:]data.maps.names
|
||||
graphics.townmap.namesoverlay.widthheight, 0C3ED0, [width: height:]data.maps.names
|
||||
graphics.townmap.icon.palette , 0C4790, `ucp4`
|
||||
graphics.townmap.icon.area , 0C4594, `lzs4x1x1|graphics.townmap.icon.palette`
|
||||
graphics.townmap.icon.fly , 0C4598, `lzs4x2x2|graphics.townmap.icon.palette`
|
||||
graphics.townmap.icon.indicator , 0C3224, `lzs4x2x2|graphics.townmap.icon.palette`
|
||||
graphics.townmap.icon.head.male.palette , 0C4498, `ucp4`
|
||||
graphics.townmap.icon.head.female.palette, 0C44B4, `ucp4`
|
||||
graphics.townmap.icon.head.male.sprite , 0C43D4, `lzs4x2x2|graphics.townmap.icon.head.male.palette`
|
||||
graphics.townmap.icon.head.female.sprite, 0C43A4, `lzs4x2x2|graphics.townmap.icon.head.female.palette`
|
||||
graphics.townmap.catchmap.palette , 102914, `ucp4:0123456789ABCDEF`
|
||||
graphics.townmap.catchmap.kanto , 106514, `lzs4x12x9|graphics.townmap.catchmap.palette`
|
||||
graphics.townmap.catchmap.island , 106518, [data<[a b c width height d e f]1> map<`lzt4|graphics.townmap.catchmap.palette`>]7
|
||||
graphics.townmap.catchmap.shape , 1345B8, [shape.catchmap_shape x. y. unused.]80
|
||||
graphics.townmap.catchmap.conversion.sevii, 13CBA8, [data<[worldmap:data.maps.names+88 catchmap:]/length> length::]7
|
||||
graphics.townmap.catchmap.conversion.kanto, 13CBA4, [worldmap:data.maps.names+88 catchmap:]data.maps.catchmap.conversion.kanto.length
|
||||
graphics.townmap.selector.tilemap , 0C10D4, `lzm4x30x20|graphics.townmap.selector.tileset`
|
||||
graphics.townmap.selector.tilemap2 , 0C10F4, `lzm4x30x20|graphics.townmap.selector.tileset`
|
||||
graphics.townmap.selector.tilemap3 , 0C116C, `lzm4x30x20|graphics.townmap.selector.tileset`
|
||||
graphics.townmap.selector.tileset , 0C1170, `lzt4`
|
||||
graphics.townmap.opening.left.top , 0C2538, `lzs4x4x8|graphics.townmap.map.palette`
|
||||
graphics.townmap.opening.left.center , 0C2558, `lzs4x4x8|graphics.townmap.map.palette`
|
||||
graphics.townmap.opening.left.down , 0C2578, `lzs4x4x8|graphics.townmap.map.palette`
|
||||
graphics.townmap.opening.right.top , 0C2598, `lzs4x4x8|graphics.townmap.map.palette`
|
||||
graphics.townmap.opening.right.center , 0C25B8, `lzs4x4x8|graphics.townmap.map.palette`
|
||||
graphics.townmap.opening.right.down , 0C25D8, `lzs4x4x8|graphics.townmap.map.palette`
|
||||
graphics.townmap.annotations.kanto , 0C432C, [worldmap.data.maps.names+88]graphics.townmap.map.tilemap*2-4-4-4-1
|
||||
graphics.townmap.annotations.island1 , 0C4334, [worldmap.data.maps.names+88]graphics.townmap.islands.tilemap1*2-4-4-4-1
|
||||
graphics.townmap.annotations.island2 , 0C433C, [worldmap.data.maps.names+88]graphics.townmap.islands.tilemap2*2-4-4-4-1
|
||||
graphics.townmap.annotations.island3 , 0C4364, [worldmap.data.maps.names+88]graphics.townmap.islands.tilemap3*2-4-4-4-1
|
||||
|
||||
graphics.credits.trainer.male.palette , 0F4CB0, `ucp4`
|
||||
graphics.credits.trainer.female.palette , 0F4CDC, `ucp4`
|
||||
graphics.credits.trainer.rival.palette , 0F4D70, `ucp4`
|
||||
graphics.credits.trainer.male.sprite , 0F4CA4, `lzs4x8x8|graphics.credits.trainer.male.palette`
|
||||
graphics.credits.trainer.female.sprite , 0F4CD8, `lzs4x8x8|graphics.credits.trainer.female.palette`
|
||||
graphics.credits.trainer.rival.sprite , 0F4D6C, `lzs4x8x8|graphics.credits.trainer.rival.palette`
|
||||
graphics.credits.pokemon.Starter1.idle , 0F4480, `lzs4x10x10|graphics.pokemon.palettes.normal:index=6`
|
||||
graphics.credits.pokemon.Starter1.animate, 0F4484, `lzs4x12x13|graphics.pokemon.palettes.normal:index=6`
|
||||
graphics.credits.pokemon.Starter2.idle , 0F44C0, `lzs4x10x10|graphics.pokemon.palettes.normal:index=3`
|
||||
graphics.credits.pokemon.Starter2.animate, 0F44C4, `lzs4x12x10|graphics.pokemon.palettes.normal:index=3`
|
||||
graphics.credits.pokemon.Starter3.idle , 0F4508, `lzs4x10x10|graphics.pokemon.palettes.normal:index=9`
|
||||
graphics.credits.pokemon.Starter3.animate, 0F450C, `lzs4x10x12|graphics.pokemon.palettes.normal:index=9`
|
||||
graphics.credits.pokemon.Mascot.idle , 0F4570, `lzs4x10x10|graphics.pokemon.palettes.normal:index=25`
|
||||
graphics.credits.pokemon.Mascot.animate , 0F4574, `lzs4x12x12|graphics.pokemon.palettes.normal:index=25`
|
||||
|
||||
graphics.trainercard.badges.palette , 08AEA4, `ucp4`
|
||||
graphics.trainercard.stickers.palette , 08AEB0, `ucp4`
|
||||
graphics.trainercard.badges.sprite , 089600, `lzs4x16x2|graphics.trainercard.stickers.palette`
|
||||
graphics.trainercard.stickers.sprite , 089664, `lzs4x2x8|graphics.trainercard.stickers.palette`
|
||||
graphics.trainercard.palettes.palette1 , 3C63A0, `ucp4:012`
|
||||
graphics.trainercard.palettes.palette2 , 3C63A4, `ucp4:012`
|
||||
graphics.trainercard.palettes.palette3 , 3C63AC, `ucp4:012`
|
||||
graphics.trainercard.tileset , 089638, `lzt4|graphics.trainercard.palettes.palette1`
|
||||
graphics.trainercard.tileset2 , 089628, `lzt4|graphics.trainercard.palettes.palette1`
|
||||
graphics.trainercard.front.tilemap , 0895C4, `lzm4x30x20|graphics.trainercard.tileset`
|
||||
graphics.trainercard.front.nobadges.tilemap, 0895EC, `lzm4x30x20|graphics.trainercard.tileset`
|
||||
graphics.trainercard.front.unknown.tilemap, 0895B8, `lzm4x30x20|graphics.trainercard.tileset2`
|
||||
graphics.trainercard.front.unknown.nobadges.tilemap, 0895E0, `lzm4x30x20|graphics.trainercard.tileset2`
|
||||
graphics.trainercard.back.tilemap , 08958C, `lzm4x30x20|graphics.trainercard.tileset`
|
||||
graphics.trainercard.back.unknown.tilemap, 08957C, `lzm4x30x20|graphics.trainercard.tileset2`
|
||||
graphics.trainercard.background.tilemap , 089558, `lzm4x30x20|graphics.trainercard.tileset`
|
||||
graphics.trainercard.background.tilemap2, 089548, `lzm4x30x20|graphics.trainercard.tileset2`
|
||||
|
||||
graphics.overworld.palettes , 05F4C4, [pal<`ucp4`> id:|h unused:]!0000000000000000
|
||||
graphics.overworld.palettes2 , 1D6C98, [pal<`ucp4`> id:|h unused:]2
|
||||
graphics.overworld.sprites , 05F2E0, [data<[starterbytes:|h paletteid:|h secondid:|h length: width: height: info.|t|palSlot::|shadowSize:|inanimate.|reflectionPalette. footprint.owfootprints unused: distribution<> sizedraw<> animation<> sprites<`osl`> ramstore<>]1>]graphics.overworld.tablelength
|
||||
graphics.overworld.sprites2 , 05DF14, [data<[starterbytes:|h paletteid:|h a<> b<> sprites<`osl|graphics.overworld.palettes2:id=`> d<> e<>]1>]36
|
||||
graphics.overworld.textcolor , 13CE20, [data.|t|low::|high::]76
|
||||
graphics.overworld.reflection.palettes.player, 05F61C, [normalPalette:|h padding: pointer<[normalReflection:|h multiplayerReflection:|h unused: unused:]1>]!FF11
|
||||
graphics.overworld.reflection.palettes.slot10, 05F694, [normalPalette:|h padding: pointer<[normalReflection:|h multiplayerReflection:|h unused: unused:]1>]!FF11
|
||||
|
||||
# these should be loaded after the overworld palettes because some hacks change these semi-overworld sprites to use overworld palettes (example, Vega)
|
||||
graphics.newgame.rival.nameselection.palette, 3D9288, `ucp4`
|
||||
graphics.newgame.rival.nameselection.sprite, 3D9280, `ucs4x2x36|graphics.newgame.rival.nameselection.palette`
|
||||
|
||||
graphics.text.font.other.characters , 00650C, `ucs2x2x1024`
|
||||
graphics.text.font.other.width , 006514, [width.]512
|
||||
graphics.text.font.black.characters , 00666C, `ucs2x2x1024`
|
||||
graphics.text.font.black.width , 006618, [width.]512
|
||||
graphics.text.font.blue.characters , 0068A0, `ucs2x2x1024`
|
||||
graphics.text.font.blue.width , 00684C, [width.]512
|
||||
graphics.text.font.red.characters , 006A0C, `ucs2x2x1024`
|
||||
graphics.text.font.red.width , 0069B8, [width.]512
|
||||
|
||||
|
||||
graphics.text.font.short.characters , 006444, `ucs2x1x1024`
|
||||
graphics.text.font.short.width , 00644C, [width.]512
|
||||
graphics.text.font.japan.short.japanese.characters, 006408, `ucs2x16x64`
|
||||
graphics.text.font.japan.japan1.characters, 0064B4, `ucs2x16x64`
|
||||
graphics.text.font.japan.japan2.characters, 0065D0, `ucs2x16x128`
|
||||
graphics.text.font.japan.japan2.width , 0065D8, [width.]280
|
||||
graphics.text.font.japan.japan3.characters, 006804, `ucs2x16x128`
|
||||
graphics.text.font.japan.japan3.width , 00680C, [width.]280
|
||||
graphics.text.font.japan.japan4.characters, 006970, `ucs2x16x128`
|
||||
graphics.text.font.japan.japan4.width , 006978, [width.]280
|
||||
graphics.text.font.japan.japan5.characters, 006A88, `ucs2x32x16`
|
||||
|
||||
graphics.text.font.buttons.characters , 006394, `ucs4x16x4|graphics.menu.downarrow.palette`
|
||||
graphics.text.font.buttons.data , 006390, [tileOffset: width. height.]13
|
||||
|
||||
data.text.menu.pokemon.battle , 032B24, ""
|
||||
data.text.menu.pause , 06EEEC, [text<""> code<>]
|
||||
data.text.menu.pokemon.options , 120FEC, [text<""> code<>]
|
||||
data.text.menu.itemStorage , 0EBCA8, [text<""> thumb<>]
|
||||
|
||||
sound.fanfares , 071B58, [songID:songnames duration:]14
|
||||
sound.tracks , 1DA588, [pointer<> musicplayer: unknown:]songnames
|
||||
sound.pokemon.cry.growl , 072054, ^[type.|h key. length. pan_sweep. p<> attack. decay. sustain. release.]data.pokemon.names-24
|
||||
sound.pokemon.cry.normal , 072064, ^[type.|h key. length. pan_sweep. p<> attack. decay. sustain. release.]data.pokemon.names-24
|
||||
sound.pokemon.cry.hoennconversion , 043214, [index:]data.pokemon.names-277
|
||||
|
||||
// From Shiny Till Dawn:
|
||||
data.pokemon.moves.details.flail.chart , 02A450, [hpbenchmark. power.]6
|
||||
scripts.trig.sinetable.radian , 044D38, [sine:|z]320
|
||||
scripts.trig.sinetable.degree , 044D90, [sine:|z]180
|
||||
scripts.commands.buffercommands.player , 02E37C, [thumb<>]57
|
||||
scripts.commands.buffercommands.opponent, 0359C8, [thumb<>]57
|
||||
scripts.commands.buffercommands.linkopponent, 03A5B4, [thumb<>]57
|
||||
scripts.commands.events.onstep.callbacks, 06E788, [thumb<>]perstepcallbacks
|
||||
scripts.commands.weather.functions , 079CA4, [thumb<>]60
|
||||
scripts.text.stringvars , 06BC6C, [ram::|h]3
|
||||
graphics.items.ball.trade.palette , 265EBC, `ucp4`
|
||||
graphics.items.ball.trade.sprite , 265EB4, `ucs4x2x24|graphics.items.ball.trade.palette`
|
||||
scripts.text.daycare.compatibility.messages, 0465B4, [pointer<"">]4
|
||||
graphics.pokemon.palettes.egg , 25920C, `ucp4`
|
||||
graphics.pokemon.sprites.egg , 2591FC, `ucs4x4x17|graphics.pokemon.palettes.egg`
|
||||
data.text.trade.messages , 124600, [text<"">]9
|
||||
data.statstages.default , 014E58, [numerator. denominator. ratio|=numerator÷denominator]13
|
||||
scripts.seagallop.destinations , 146EF0, [bank. map. x. y.]scripts.seagallop.count
|
||||
|
||||
scripts.fromthumb.safari.end , 0A1004, `xse`
|
||||
scripts.text.names , 12DB90, [text<"">]
|
||||
scripts.text.interviews , 12CE90, [text<"">]
|
||||
scripts.text.destinations , 09D8F4, [text<"">]
|
||||
|
||||
// From Soup
|
||||
data.statstages.accuracy , 01E08C, [numerator. divisor. unused:]13
|
||||
data.statstages.critical , 01E4FC, [rate:]!0000
|
||||
|
||||
// From Yogia
|
||||
graphics.trainers.elite4.mugshot.palettes, 0D2AE0, [palette<`ucp4`>]trainerMugshots
|
||||
graphics.trainers.players.mugshot.palettes, 0D2AE4, [palette<`ucp4`>]playerMugshots
|
||||
|
|
@ -506,7 +506,7 @@ graphics.misc.questionnaire.button.sprite, ,,,, 43F948, 43F784, 43F9B8, 43F7F4,
|
|||
|
||||
graphics.misc.stationary, ,,,, 1462E4,,,, , [id:: tileset<`lzt4`> tilemap<`lzm4x30x24|table`> palette<`ucp4`>]8
|
||||
|
||||
graphics.overworld.firstpersonview.sprites, , , , , 0F80FC, 0F80D4, 0F8174, 0F814C, , [id.data.maps.names+88 transition.transitiontype worldmapflag:|h tileset<`lzt4|graphics.firstpersonview.sprites`> tilemap<`lzm4x32x20|graphics.firstpersonview.sprites`> pal<`ucp4:DE`>]graphics.firstpersonview.count
|
||||
graphics.overworld.firstpersonview.sprites, , , , , 0F80FC, 0F80D4, 0F8174, 0F814C, , [id.data.maps.names+88 transition.transitiontype worldmapflag:|h tileset<`lzt4|graphics.overworld.firstpersonview.sprites`> tilemap<`lzm4x32x20|graphics.overworld.firstpersonview.sprites`> pal<`ucp4:DE`>]graphics.firstpersonview.count
|
||||
graphics.battle.background.sprites, , , , , 00F2A0, 00F2A0, 00F2B4, 00F2B4, , [battletiles<`lzt4`> battlemap<`lzm4x32x64|graphics.battle.background.sprites|battletiles`> | introtiles<`lzt4`> intromap<`lzm4x32x14|graphics.battle.background.sprites|introtiles`> pal<`lzp4:234`>]
|
||||
graphics.battle.background.sprites, 00D954, 00D954, 00D954, 00D954, , , , , 035940, [battletiles<`lzt4`> battlemap<`lzm4x32x64|graphics.battle.background.sprites|battletiles`> | introtiles<`lzt4`> intromap<`lzm4x32x32|graphics.battle.background.sprites|introtiles`> pal<`lzp4:234`>]10
|
||||
graphics.battle.background.fighttype, ,,,, 00F24C, 00F24C, 00F260, 00F260, , [id. entry.graphics.battle.background.sprites unused:]8
|
||||
|
|
|
|||
|
|
@ -91,7 +91,11 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
Ruby1_1 = "AXVE1",
|
||||
Sapphire1_1 = "AXPE1",
|
||||
FireRed1_1 = "BPRE1",
|
||||
LeafGreen1_1 = "BPGE1";
|
||||
LeafGreen1_1 = "BPGE1",
|
||||
FireRedFr = "BPRF0",
|
||||
FireRedIt = "BPRI0",
|
||||
EmeraldFr = "BPEF0",
|
||||
EmeraldIt = "BPEI0";
|
||||
|
||||
public const string
|
||||
TmMoves = "data.pokemon.moves.tms",
|
||||
|
|
@ -140,9 +144,10 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
Sapphire1_1,
|
||||
FireRed1_1,
|
||||
LeafGreen1_1,
|
||||
"BPRF0", // french firered
|
||||
"BPEF0", // french emerald
|
||||
"BPEI0", // italian emerald
|
||||
FireRedFr,
|
||||
FireRedIt,
|
||||
EmeraldFr,
|
||||
EmeraldIt,
|
||||
"ABCD0", // for tests
|
||||
};
|
||||
|
||||
|
|
@ -234,8 +239,8 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
"Set Terrain", "Pledge", "Field Effects", "Fling", "Feint", "Attack Blockers", "Type Changes", "Heal Target", "Topsy Turvy Electrify", "Fairy Lock Happy Hour",
|
||||
"Instruct After You Quash", "Sucker Punch", "Ignore Redirection", "Team Effects", "Camouflage", "Flame Burst", "Last Resort Sky Drop", "Damage Set Terrain", "Teatime",
|
||||
"RaiseTarget\'sAttackSpAtk2"});
|
||||
for(var k = 0; k < 7; k++) newList.Add(null); // 246 to 252 are unused.
|
||||
newList.AddRange(new string[] {"Max Move", "Synchronoise"});
|
||||
for (var k = 0; k < 7; k++) newList.Add(null); // 246 to 252 are unused.
|
||||
newList.AddRange(new string[] { "Max Move", "Synchronoise" });
|
||||
SetList(new NoDataChangeDeltaModel(), MoveEffectListName, newList, null, StoredList.GenerateHash(newList));
|
||||
}
|
||||
|
||||
|
|
@ -264,7 +269,7 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
});
|
||||
SetList(new NoDataChangeDeltaModel(), "holdeffects", newList, null, StoredList.GenerateHash(newList));
|
||||
}
|
||||
|
||||
|
||||
// Update the type chart effectivenesses list
|
||||
if (isCFRU && TryGetList("effectiveness", out var multipliers)) {
|
||||
var newMultipliers = new string[21]; // 0 to 20
|
||||
|
|
@ -377,7 +382,7 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
newList.Add("Acc Up 3");
|
||||
newList.Add("Evsn Up 3");
|
||||
SetList(new NoDataChangeDeltaModel(), "zeffects", newList, null, StoredList.GenerateHash(newList));
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static StoredMetadata DecodeConstantsFromReference(IReadOnlyList<byte> model, IMetadataInfo info, StoredMetadata metadata, GameReferenceConstants constants) {
|
||||
|
|
@ -535,7 +540,7 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
|
||||
if (name == ItemEffectsTableName) format = format.Replace("-199", string.Empty); // item effects are as long as the items
|
||||
|
||||
|
||||
|
||||
|
||||
// type chart
|
||||
if (name == "data.pokemon.type.chart") {
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
}
|
||||
public static bool IsFRLG(this IReadOnlyList<byte> model) {
|
||||
var code = model.GetGameCode();
|
||||
return code.StartsWith("BPRE") || code.StartsWith("BPGE");
|
||||
return code.StartsWith("BPR") || code.StartsWith("BPG");
|
||||
}
|
||||
public static bool IsEmerald(this IReadOnlyList<byte> model) => model.GetGameCode() == "BPEE0";
|
||||
|
||||
|
|
@ -631,7 +631,7 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
model.LoadMetadataProperties(metadata);
|
||||
}
|
||||
|
||||
public static void LoadMetadataProperties(this IDataModel model, StoredMetadata metadata){
|
||||
public static void LoadMetadataProperties(this IDataModel model, StoredMetadata metadata) {
|
||||
if (metadata.NextExportID > 0) model.NextExportID = metadata.NextExportID;
|
||||
if (metadata.FreeSpaceSearch >= 0) model.FreeSpaceStart = Math.Min(model.Count - 1, metadata.FreeSpaceSearch);
|
||||
if (metadata.FreeSpaceBuffer >= 0) model.FreeSpaceBuffer = metadata.FreeSpaceBuffer;
|
||||
|
|
|
|||
|
|
@ -158,14 +158,46 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
Task RunBackgroundWork(Action action);
|
||||
|
||||
IDelayWorkTimer CreateDelayTimer();
|
||||
}
|
||||
|
||||
public class ImmediateWorkTimer : IDelayWorkTimer {
|
||||
public bool HasScheduledWork => false;
|
||||
|
||||
public DelayWorkResult DelayCall(TimeSpan delay, Action action) {
|
||||
action.Invoke();
|
||||
return DelayWorkResult.WorkScheduled;
|
||||
}
|
||||
|
||||
public void Reset() { }
|
||||
}
|
||||
|
||||
public class ManualWorkTimer : IDelayWorkTimer {
|
||||
Action? work;
|
||||
|
||||
public bool HasScheduledWork => work != null;
|
||||
|
||||
public DelayWorkResult DelayCall(TimeSpan delay, Action action) {
|
||||
var result = work == null ? DelayWorkResult.WorkScheduled : DelayWorkResult.WorkScheduledAndPreviousWorkCleared;
|
||||
work = action;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Reset() => work = null;
|
||||
|
||||
public void RunWork() {
|
||||
work?.Invoke();
|
||||
work = null;
|
||||
}
|
||||
}
|
||||
public class InstantDispatch : IWorkDispatcher {
|
||||
public static IWorkDispatcher Instance { get; } = new InstantDispatch();
|
||||
public Task WaitForRenderingAsync() => Task.CompletedTask;
|
||||
public void BlockOnUIWork(Action action) => action();
|
||||
public Task DispatchWork(Action action) { action?.Invoke(); return Task.CompletedTask; }
|
||||
public Task RunBackgroundWork(Action action) => DispatchWork(action);
|
||||
public IDelayWorkTimer CreateDelayTimer() => new ImmediateWorkTimer();
|
||||
}
|
||||
|
||||
public class ControlledDispatch : IWorkDispatcher {
|
||||
|
|
@ -196,6 +228,14 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
public void RunAllWorkloads() {
|
||||
while (workloads.Count > 0) RunWorkloadAndContinuations(0);
|
||||
}
|
||||
|
||||
public event EventHandler<ImmediateWorkTimer> DelayTimerRequested;
|
||||
|
||||
public IDelayWorkTimer CreateDelayTimer() {
|
||||
var timer = new ImmediateWorkTimer();
|
||||
DelayTimerRequested?.Invoke(this, timer);
|
||||
return timer;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -57,14 +57,14 @@ namespace HavenSoft.HexManiac.Core.Models.Map {
|
|||
public int NameIndex {
|
||||
get {
|
||||
var code = Element.Model.GetShortGameCode();
|
||||
int offset = code.IsAny(0x45525042, 0x45475042) ? 88 : 0; // BPRE, BPGE
|
||||
int offset = code.IsAny(0x45525042, 0x45475042, 0x49525042) ? 88 : 0; // BPRE, BPGE, BPRI
|
||||
if (!Element.TryGetValue("regionSectionID", out var value)) return -1;
|
||||
return value - offset;
|
||||
}
|
||||
set {
|
||||
if (!Element.HasField("regionSectionID")) return;
|
||||
var code = Element.Model.GetShortGameCode();
|
||||
int offset = code.IsAny(0x45525042, 0x45475042) ? 88 : 0; // BPRE, BPGE
|
||||
int offset = code.IsAny(0x45525042, 0x45475042, 0x49525042) ? 88 : 0; // BPRE, BPGE, BPRI
|
||||
Element.SetValue("regionSectionID", value + offset);
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +177,7 @@ namespace HavenSoft.HexManiac.Core.Models.Map {
|
|||
int Elevation { get; }
|
||||
}
|
||||
|
||||
public record BaseEventModel(ModelArrayElement Element): IEventModel {
|
||||
public record BaseEventModel(ModelArrayElement Element) : IEventModel {
|
||||
public int X => Element.TryGetValue("x", out int x) ? x : 0;
|
||||
public int Y => Element.TryGetValue("y", out int y) ? y : 0;
|
||||
public int Elevation => Element.TryGetValue("elevation", out int elevation) ? elevation : 0;
|
||||
|
|
@ -229,6 +229,8 @@ namespace HavenSoft.HexManiac.Core.Models.Map {
|
|||
public bool HasScript => Kind < 5;
|
||||
public bool IsHiddenItem => Kind.IsAny(5, 6, 7);
|
||||
public int ItemValue => Element.Model.ReadMultiByteValue(Element.Start + 8, 2);
|
||||
public int HiddenItemFlag => Element.Model[Element.Start + 10];
|
||||
public int HiddenItemCount => Element.Model[Element.Start + 11];
|
||||
public int ScriptAddress => Element.Model.ReadPointer(Element.Start + 8);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,12 +64,12 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
public static class PCSString {
|
||||
private const string TextReferenceFileName = "resources/pcsReference.txt";
|
||||
|
||||
public static IReadOnlyList<string> PCS;
|
||||
public static IReadOnlySet<string> ValidInProgressEscapes;
|
||||
public static IReadOnlyList<byte> Newlines;
|
||||
public static IReadOnlyDictionary<byte, byte> ControlCodeLengths;
|
||||
public static IReadOnlyDictionary<string, IReadOnlyDictionary<string, byte[]>> TextMacros;
|
||||
public static IReadOnlyDictionary<string, IReadOnlyList<IReadOnlyDictionary<string, byte[]>>> TextMacrosIndex;
|
||||
public static readonly IReadOnlyList<string> PCS;
|
||||
public static readonly IReadOnlySet<string> ValidInProgressEscapes;
|
||||
public static readonly IReadOnlyList<byte> Newlines;
|
||||
public static readonly IReadOnlyDictionary<byte, byte> ControlCodeLengths;
|
||||
public static readonly IReadOnlyDictionary<string, IReadOnlyDictionary<string, byte[]>> TextMacros;
|
||||
public static readonly IReadOnlyDictionary<string, IReadOnlyList<IReadOnlyDictionary<string, byte[]>>> TextMacrosIndex;
|
||||
|
||||
public static readonly byte DynamicEscape = 0xF7;
|
||||
public static readonly byte FunctionEscape = 0xFC;
|
||||
|
|
@ -581,6 +581,7 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
}
|
||||
|
||||
private static string FindMacro(IReadOnlyList<IReadOnlyDictionary<string, byte[]>> macrosIndex, IReadOnlyList<byte> data, int index) {
|
||||
if (!index.InRange(0, data.Count)) return null;
|
||||
var macros = macrosIndex[data[index]];
|
||||
if (macros == null) return null;
|
||||
foreach (var kvp in macros) {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
|
|||
public class AutocompleteItem {
|
||||
public string Text { get; } // text to show in completion tip
|
||||
public string LineText { get; } // text to use to replace entire line
|
||||
public int CharacterOffset { get; set; } // character-size offset to use for the autocomplete options
|
||||
public AutocompleteItem(string text, string lineText) => (Text, LineText) = (text, lineText);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -154,14 +154,14 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
|
|||
private readonly Dictionary<int, ScriptInfo> cachedScripts = new();
|
||||
|
||||
public ScriptInfo GetScriptInfo(ScriptParser parser, int scriptStart, CodeBody updateBody, ref int existingSectionCount) {
|
||||
if (cachedScripts.TryGetValue(scriptStart, out var scriptInfo)) {
|
||||
if (cachedScripts.TryGetValue(scriptStart, out var scriptInfo) && scriptInfo.Parser == parser) {
|
||||
existingSectionCount = scriptInfo.SectionCount;
|
||||
return scriptInfo;
|
||||
}
|
||||
var destinations = ScriptDestinations(scriptStart);
|
||||
var scriptLength = parser.FindLength(model, scriptStart, destinations);
|
||||
var content = parser.Parse(model, scriptStart, scriptLength, ref existingSectionCount, updateBody);
|
||||
scriptInfo = new ScriptInfo(scriptStart, scriptLength, content, existingSectionCount);
|
||||
scriptInfo = new ScriptInfo(scriptStart, scriptLength, parser, content, existingSectionCount);
|
||||
destinations[scriptStart] = scriptLength;
|
||||
cachedScripts[scriptStart] = scriptInfo;
|
||||
return scriptInfo;
|
||||
|
|
@ -248,5 +248,5 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
|
|||
public void GotoMap() => JumpAction(new(Group, Map));
|
||||
}
|
||||
|
||||
public record ScriptInfo(int Start, int Length, string Content, int SectionCount) : ISearchTreePayload;
|
||||
public record ScriptInfo(int Start, int Length, ScriptParser Parser, string Content, int SectionCount) : ISearchTreePayload;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -275,8 +275,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
|
|||
} else if (segment.Length == 0) {
|
||||
continue;
|
||||
}
|
||||
var extraWhitespace = new string(' ', longestLabel - segment.Name.Length);
|
||||
result.Append($"{segment.Name}:{extraWhitespace} {value}");
|
||||
result.Append($"{segment.Name.PadRight(longestLabel)} : {value}");
|
||||
if (i < ElementContent.Count - 1) result.AppendLine();
|
||||
offset += segment.Length;
|
||||
}
|
||||
|
|
@ -295,7 +294,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
|
|||
for (int j = 0; j < ElementContent.Count; j++) {
|
||||
while (fieldIndex < fields.Length && string.IsNullOrWhiteSpace(fields[fieldIndex])) fieldIndex += 1;
|
||||
if (fieldIndex >= fields.Length) break;
|
||||
var data = j < fields.Length ? fields[fieldIndex].Split(new[] { ':' }, 2).Last() : string.Empty;
|
||||
var data = j < fields.Length ? fields[fieldIndex].Split(new[] { ':' }, 2).Last().Trim() : string.Empty;
|
||||
if (ElementContent[j].Write(this.ElementContent, model, token, Start + segmentOffset, ref data)) {
|
||||
changeAddresses.Add(Start + segmentOffset);
|
||||
changedSegments.Add(j);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
/// </summary>
|
||||
public class Singletons {
|
||||
private const string TableReferenceFileName = "resources/tableReference.txt";
|
||||
private const string TableReferenceFileNameItalian = "resources/tableReference.it.txt";
|
||||
private const string ConstantReferenceFileName = "resources/constantReference.txt";
|
||||
private const string ConstantReferenceFileNameItalian = "resources/constantReference.it.txt";
|
||||
private const string ThumbReferenceFileName = "resources/armReference.txt";
|
||||
private const string ScriptReferenceFileName = "resources/scriptReference.txt";
|
||||
private const string BattleScriptReferenceFileName = "resources/battleScriptReference.txt";
|
||||
|
|
@ -137,6 +139,20 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
|
||||
public static IReadOnlyList<string> ReferenceOrder { get; } = new string[] { "name", Ruby, Sapphire, Ruby1_1, Sapphire1_1, FireRed, LeafGreen, FireRed1_1, LeafGreen1_1, Emerald, "format" };
|
||||
private IReadOnlyDictionary<string, GameReferenceTables> CreateGameReferenceTables() {
|
||||
var tableEn = CreateGameReferenceTablesEnglish();
|
||||
var tableIt = CreateGameReferenceTablesItalian();
|
||||
var readonlyTables = new Dictionary<string, GameReferenceTables>();
|
||||
foreach (var pair in tableEn) {
|
||||
readonlyTables.Add(pair.Key, pair.Value);
|
||||
}
|
||||
foreach (var pair in tableIt) {
|
||||
readonlyTables.Add(pair.Key, pair.Value);
|
||||
}
|
||||
return readonlyTables;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<string> ReferenceOrderItalian { get; } = new string[] { "name", FireRedIt, "format" };
|
||||
private IReadOnlyDictionary<string, GameReferenceTables> CreateGameReferenceTablesEnglish() {
|
||||
if (!File.Exists(TableReferenceFileName)) return new Dictionary<string, GameReferenceTables>();
|
||||
var lines = File.ReadAllLines(TableReferenceFileName);
|
||||
var tables = new Dictionary<string, List<ReferenceTable>>();
|
||||
|
|
@ -172,7 +188,59 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
return readonlyTables;
|
||||
}
|
||||
|
||||
private IReadOnlyDictionary<string, GameReferenceTables> CreateGameReferenceTablesItalian() {
|
||||
if (!File.Exists(TableReferenceFileNameItalian)) {
|
||||
return new Dictionary<string, GameReferenceTables>();
|
||||
}
|
||||
var lines = File.ReadAllLines(TableReferenceFileNameItalian);
|
||||
var tables = new Dictionary<string, List<ReferenceTable>>();
|
||||
for (int i = 0; i < ReferenceOrderItalian.Count - 2; i++) tables[ReferenceOrderItalian[i + 1]] = new List<ReferenceTable>();
|
||||
foreach (var line in lines) {
|
||||
var row = line.Trim();
|
||||
if (row.StartsWith("//")) continue;
|
||||
var segments = row.Split("//")[0].Split(",");
|
||||
if (segments.Length != ReferenceOrderItalian.Count) continue;
|
||||
var name = segments[0].Trim();
|
||||
var offset = 0;
|
||||
if (name.Contains("+")) {
|
||||
var parts = name.Split("+");
|
||||
name = parts[0];
|
||||
int.TryParse(parts[1], NumberStyles.HexNumber, CultureInfo.CurrentCulture, out offset);
|
||||
} else if (name.Contains("-")) {
|
||||
var parts = name.Split("-");
|
||||
name = parts[0];
|
||||
int.TryParse(parts[1], NumberStyles.HexNumber, CultureInfo.CurrentCulture, out offset);
|
||||
offset = -offset;
|
||||
}
|
||||
var format = segments.Last().Trim();
|
||||
for (int i = 0; i < ReferenceOrderItalian.Count - 2; i++) {
|
||||
var addressHex = segments[i + 1].Trim();
|
||||
if (addressHex == string.Empty) continue;
|
||||
if (!int.TryParse(addressHex, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out int address)) continue;
|
||||
tables[ReferenceOrderItalian[i + 1]].Add(new ReferenceTable(name, offset, address, format));
|
||||
}
|
||||
}
|
||||
|
||||
var readonlyTables = new Dictionary<string, GameReferenceTables>();
|
||||
foreach (var pair in tables) readonlyTables.Add(pair.Key, new GameReferenceTables(pair.Value));
|
||||
return readonlyTables;
|
||||
}
|
||||
|
||||
|
||||
private IReadOnlyDictionary<string, GameReferenceConstants> CreateGameReferenceConstants() {
|
||||
var constantsEn = CreateGameReferenceConstantsEnglish();
|
||||
var constantsIt = CreateGameReferenceConstantsItalian();
|
||||
var readonlyConstants = new Dictionary<string, GameReferenceConstants>();
|
||||
foreach (var pair in constantsEn) {
|
||||
readonlyConstants.Add(pair.Key, pair.Value);
|
||||
}
|
||||
foreach (var pair in constantsIt) {
|
||||
readonlyConstants.Add(pair.Key, pair.Value);
|
||||
}
|
||||
return readonlyConstants;
|
||||
}
|
||||
|
||||
private IReadOnlyDictionary<string, GameReferenceConstants> CreateGameReferenceConstantsEnglish() {
|
||||
if (!File.Exists(ConstantReferenceFileName)) return new Dictionary<string, GameReferenceConstants>();
|
||||
var lines = File.ReadAllLines(ConstantReferenceFileName);
|
||||
var constants = new Dictionary<string, List<ReferenceConstant>>();
|
||||
|
|
@ -194,6 +262,28 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
return readonlyConstants;
|
||||
}
|
||||
|
||||
private IReadOnlyDictionary<string, GameReferenceConstants> CreateGameReferenceConstantsItalian() {
|
||||
if (!File.Exists(ConstantReferenceFileNameItalian)) return new Dictionary<string, GameReferenceConstants>();
|
||||
var lines = File.ReadAllLines(ConstantReferenceFileNameItalian);
|
||||
var constants = new Dictionary<string, List<ReferenceConstant>>();
|
||||
foreach (var line in lines) {
|
||||
if (string.IsNullOrWhiteSpace(line)) continue;
|
||||
var cleanLine = line.Trim();
|
||||
if (cleanLine.Length < 6) continue;
|
||||
if (!char.IsLetter(cleanLine[0])) continue;
|
||||
var gameCode = cleanLine.Substring(0, 5).ToUpper();
|
||||
if (!constants.TryGetValue(gameCode, out var collection)) {
|
||||
collection = new List<ReferenceConstant>();
|
||||
constants[gameCode] = collection;
|
||||
}
|
||||
collection.Add(new ReferenceConstant(cleanLine.Substring(5)));
|
||||
}
|
||||
|
||||
var readonlyConstants = new Dictionary<string, GameReferenceConstants>();
|
||||
foreach (var pair in constants) readonlyConstants.Add(pair.Key, new GameReferenceConstants(pair.Value));
|
||||
return readonlyConstants;
|
||||
}
|
||||
|
||||
private (IReadOnlyList<ConditionCode>, IReadOnlyList<IInstruction>) LoadThumbReference() {
|
||||
var conditionalCodes = new List<ConditionCode>();
|
||||
var instructionTemplates = new List<IInstruction>();
|
||||
|
|
@ -209,7 +299,7 @@ namespace HavenSoft.HexManiac.Core.Models {
|
|||
public void ExportReadableScriptReference(EditorViewModel editor) {
|
||||
var specials = new Dictionary<string, StoredList>(
|
||||
new[] { "axve", "axpe", "bpre", "bpge", "bpee" }
|
||||
.Select<string, KeyValuePair<string,StoredList>>(
|
||||
.Select<string, KeyValuePair<string, StoredList>>(
|
||||
code => new(code, BaseModel.GetDefaultMetadatas(code).SelectMany(md => md.Lists).Single(list => list.Name == "specials"))
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -985,123 +985,175 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
var layout = GetLayout();
|
||||
var (width, height) = (layout.GetValue("width"), layout.GetValue("height"));
|
||||
var (xx, yy) = ConvertCoordinates(x, y);
|
||||
xx = xx.LimitToRange(0, width - 1);
|
||||
yy = yy.LimitToRange(0, height - 1);
|
||||
xx = xx.LimitToRange(0, width - 2);
|
||||
yy = yy.LimitToRange(0, height - 2);
|
||||
Draw9Grid(token, grid, xx, yy);
|
||||
}
|
||||
|
||||
public void Draw25Grid(ModelDelta token, int[,] grid, double x, double y) {
|
||||
var (xx, yy) = ConvertCoordinates(x, y);
|
||||
Draw25Grid(token, grid, xx, yy);
|
||||
}
|
||||
public void DiscoverCornersFor9Grid(int[,] grid) {
|
||||
innerCornersFor9Grid = null;
|
||||
var layout = new LayoutModel(GetLayout());
|
||||
|
||||
public void Draw9Grid(ModelDelta token, int[,] grid, int xx, int yy) {
|
||||
var targets = new List<int>();
|
||||
for (int x = 0; x < 3; x++) for (int y = 0; y < 3; y++) targets.Add(grid[x, y] & 0x3FF);
|
||||
// numpad layout for easy reference
|
||||
var np7 = grid[0, 0] & 0x3FF;
|
||||
var np8 = grid[1, 0] & 0x3FF;
|
||||
var np9 = grid[2, 0] & 0x3FF;
|
||||
var np4 = grid[0, 1] & 0x3FF;
|
||||
var np5 = grid[1, 1] & 0x3FF;
|
||||
var np6 = grid[2, 1] & 0x3FF;
|
||||
var np1 = grid[0, 2] & 0x3FF;
|
||||
var np2 = grid[1, 2] & 0x3FF;
|
||||
var np3 = grid[2, 2] & 0x3FF;
|
||||
|
||||
var layout = GetLayout();
|
||||
var (width, height) = (layout.GetValue("width"), layout.GetValue("height"));
|
||||
var start = layout.GetAddress("blockmap");
|
||||
// histograms for inside corners, where the ! is a 'hole':
|
||||
// 7 . 9
|
||||
// . ! .
|
||||
// 1 . 3
|
||||
var corner7 = new AutoDictionary<int, int>(_ => 0);
|
||||
var corner9 = new AutoDictionary<int, int>(_ => 0);
|
||||
var corner1 = new AutoDictionary<int, int>(_ => 0);
|
||||
var corner3 = new AutoDictionary<int, int>(_ => 0);
|
||||
|
||||
int get(Point p) => p.X < 0 || p.Y < 0 || p.X >= width || p.Y >= height ? -1 : model.ReadMultiByteValue(start + (p.Y * width + p.X) * 2, 2) & 0x3FF;
|
||||
void set(Point p, int block) => model.WriteMultiByteValue(start + (p.Y * width + p.X) * 2, 2, token, block);
|
||||
// use maps in the game to figure out the corners
|
||||
foreach (var map in GetAllMaps()) {
|
||||
if (map.Layout.PrimaryBlockset.Start != layout.PrimaryBlockset.Start && map.Layout.SecondaryBlockset.Start != layout.SecondaryBlockset.Start) continue;
|
||||
|
||||
// change all connected blocks based on the grid
|
||||
var todo = new List<Point> { new(xx, yy), new(xx - 1, yy), new(xx + 1, yy), new(xx, yy - 1), new(xx, yy + 1) };
|
||||
lock (pixelWriteLock) {
|
||||
set(todo[0], grid[1, 1]);
|
||||
foreach (var cell in todo) {
|
||||
var cellValue = get(cell);
|
||||
if (!targets.Contains(cellValue)) continue;
|
||||
|
||||
var north = targets.Contains(get(new(cell.X, cell.Y - 1)));
|
||||
var south = targets.Contains(get(new(cell.X, cell.Y + 1)));
|
||||
var west = targets.Contains(get(new(cell.X - 1, cell.Y)));
|
||||
var east = targets.Contains(get(new(cell.X + 1, cell.Y)));
|
||||
var aggregate = (north ? "N" : " ") + (east ? "E" : " ") + (south ? "S" : " ") + (west ? "W" : " ");
|
||||
|
||||
var block = aggregate switch {
|
||||
" ES " => grid[0, 0],
|
||||
" ESW" => grid[1, 0],
|
||||
" SW" => grid[2, 0],
|
||||
"NES " => grid[0, 1],
|
||||
"NESW" => grid[1, 1],
|
||||
"N SW" => grid[2, 1],
|
||||
"NE " => grid[0, 2],
|
||||
"NE W" => grid[1, 2],
|
||||
"N W" => grid[2, 2],
|
||||
_ => grid[1, 1],
|
||||
};
|
||||
set(cell, block);
|
||||
var blockMap = map.Layout.BlockMap;
|
||||
for (int y = 0; y < blockMap.Height; y++) {
|
||||
bool topEdge = y == 0, bottomEdge = y == blockMap.Height - 1;
|
||||
for (int x = 0; x < blockMap.Width; x++) {
|
||||
bool leftEdge = x == 0, rightEdge = x == blockMap.Width - 1;
|
||||
var block = blockMap[x, y].Block; // store tile and collision, but only check that tiles match
|
||||
if (!rightEdge && !bottomEdge && blockMap[x + 1, y].Tile.IsAny(np3, np2) && blockMap[x, y + 1].Tile.IsAny(np3, np6)) {
|
||||
corner7[block] += 1;
|
||||
}
|
||||
if (!leftEdge && !bottomEdge && blockMap[x - 1, y].Tile.IsAny(np1, np2) && blockMap[x, y + 1].Tile.IsAny(np1, np4)) {
|
||||
corner9[block] += 1;
|
||||
}
|
||||
if (!rightEdge && !topEdge && blockMap[x + 1, y].Tile.IsAny(np8, np9) && blockMap[x, y - 1].Tile.IsAny(np6, np9)) {
|
||||
corner1[block] += 1;
|
||||
}
|
||||
if (!leftEdge && !topEdge && blockMap[x - 1, y].Tile.IsAny(np7, np8) && blockMap[x, y - 1].Tile.IsAny(np4, np7)) {
|
||||
corner3[block] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearPixelCache();
|
||||
var corners = new int[2, 2];
|
||||
var defaultBlock = grid[1, 1] & 0x3FF;
|
||||
var key = corner7.MostCommonKey(); corners[0, 0] = key != 0 ? key : defaultBlock;
|
||||
key = corner9.MostCommonKey(); corners[1, 0] = key != 0 ? key : defaultBlock;
|
||||
key = corner1.MostCommonKey(); corners[0, 1] = key != 0 ? key : defaultBlock;
|
||||
key = corner3.MostCommonKey(); corners[1, 1] = key != 0 ? key : defaultBlock;
|
||||
innerCornersFor9Grid = corners;
|
||||
}
|
||||
|
||||
public void Draw25Grid(ModelDelta token, int[,] grid, int xx, int yy) {
|
||||
var targets = new List<int>();
|
||||
for (int x = 0; x < 5; x++) {
|
||||
for (int y = 0; y < 5; y++) {
|
||||
if (x == 0 && y == 0) continue;
|
||||
if (x == 4 && y == 0) continue;
|
||||
if (x == 0 && y == 4) continue;
|
||||
if (x == 4 && y == 4) continue;
|
||||
targets.Add(grid[x, y] & 0x3FF);
|
||||
public void PrepareFor9GridDraw(int[,] grid) {
|
||||
// when we do a 9-grid draw,
|
||||
// we want to track which draws are part of the current interaction only
|
||||
var layout = new LayoutModel(GetLayout());
|
||||
current9gridInteractionMap = new bool[layout.Width, layout.Height];
|
||||
|
||||
// NOTE add this code if we want to make new draws connect to existing draws
|
||||
|
||||
/*
|
||||
var corners = this.innerCornersFor9Grid ?? new int[,] { { grid[1, 1], grid[1, 1] }, { grid[1, 1], grid[1, 1] } };
|
||||
|
||||
var targets = new HashSet<int>();
|
||||
for (int y = 0; y < 3; y++) for (int x = 0; x < 3; x++) targets.Add(grid[x, y]);
|
||||
for (int y = 0; y < 2; y++) for (int x = 0; x < 2; x++) targets.Add(corners[x, y]);
|
||||
|
||||
for (int y = 0; y < layout.Height; y++) {
|
||||
for (int x = 0; x < layout.Width; x++) {
|
||||
current9gridInteractionMap[x, y] = targets.Contains(layout.BlockMap[x, y].Block);
|
||||
}
|
||||
}
|
||||
//*/
|
||||
}
|
||||
|
||||
public static int Get9GridBlock(bool[,] map, Point p, int[,] grid9, int[,] corners) {
|
||||
int neighborhood = 0;
|
||||
for (int y = -1; y < 2; y++) {
|
||||
for (int x = -1; x < 2; x++) {
|
||||
if (!(p.X + x).InRange(0, map.GetLength(0)) || !(p.Y + y).InRange(0, map.GetLength(1))) continue;
|
||||
var bit = 8 - ((y + 1) * 3 + x + 1);
|
||||
neighborhood |= (map[p.X + x, p.Y + y] ? 1 : 0) << bit;
|
||||
}
|
||||
}
|
||||
|
||||
var layout = GetLayout();
|
||||
var (width, height) = (layout.GetValue("width"), layout.GetValue("height"));
|
||||
var start = layout.GetAddress("blockmap");
|
||||
return neighborhood switch {
|
||||
// normal corners - 4 varients of each (2 corners don't matter)
|
||||
0b110_110_000 => grid9[2, 2], // bottom-right
|
||||
0b110_110_100 => grid9[2, 2],
|
||||
0b111_110_100 => grid9[2, 2],
|
||||
0b111_110_000 => grid9[2, 2],
|
||||
0b011_011_000 => grid9[0, 2], // bottom-left
|
||||
0b011_011_001 => grid9[0, 2],
|
||||
0b111_011_001 => grid9[0, 2],
|
||||
0b111_011_000 => grid9[0, 2],
|
||||
0b000_110_110 => grid9[2, 0], // top-right
|
||||
0b100_110_110 => grid9[2, 0],
|
||||
0b100_110_111 => grid9[2, 0],
|
||||
0b000_110_111 => grid9[2, 0],
|
||||
0b000_011_011 => grid9[0, 0], // top-left
|
||||
0b001_011_011 => grid9[0, 0],
|
||||
0b000_011_111 => grid9[0, 0],
|
||||
0b001_011_111 => grid9[0, 0],
|
||||
|
||||
int get(Point p) => p.X < 0 || p.Y < 0 || p.X >= width || p.Y >= height ? -1 : model.ReadMultiByteValue(start + (p.Y * width + p.X) * 2, 2) & 0x3FF;
|
||||
void set(Point p, int block) => model.WriteMultiByteValue(start + (p.Y * width + p.X) * 2, 2, token, block);
|
||||
// edges - 4 variants of each (2 corners don't matter)
|
||||
0b111_111_000 => grid9[1, 2], // bottom
|
||||
0b111_111_100 => grid9[1, 2],
|
||||
0b111_111_001 => grid9[1, 2],
|
||||
0b111_111_101 => grid9[1, 2],
|
||||
0b000_111_111 => grid9[1, 0], // top
|
||||
0b100_111_111 => grid9[1, 0],
|
||||
0b001_111_111 => grid9[1, 0],
|
||||
0b101_111_111 => grid9[1, 0],
|
||||
0b011_011_011 => grid9[0, 1], // left
|
||||
0b111_011_011 => grid9[0, 1],
|
||||
0b011_011_111 => grid9[0, 1],
|
||||
0b111_011_111 => grid9[0, 1],
|
||||
0b110_110_110 => grid9[2, 1], // right
|
||||
0b111_110_110 => grid9[2, 1],
|
||||
0b110_110_111 => grid9[2, 1],
|
||||
0b111_110_111 => grid9[2, 1],
|
||||
|
||||
// change all connected blocks based on the grid
|
||||
var todo = new List<Point> {
|
||||
new(xx, yy),
|
||||
new(xx - 1, yy), new(xx + 1, yy), new(xx, yy - 1), new(xx, yy + 1),
|
||||
new(xx + 1, yy + 1), new(xx + 1, yy - 1), new(xx - 1, yy + 1), new(xx - 1, yy - 1),
|
||||
// inside corners
|
||||
0b111_111_110 => corners[0, 0],
|
||||
0b111_111_011 => corners[1, 0],
|
||||
0b110_111_111 => corners[0, 1],
|
||||
0b011_111_111 => corners[1, 1],
|
||||
|
||||
_ => grid9[1, 1],
|
||||
};
|
||||
}
|
||||
|
||||
private bool[,] current9gridInteractionMap;
|
||||
private int[,]? innerCornersFor9Grid;
|
||||
// 9-grid is always drawn 2x2
|
||||
public void Draw9Grid(ModelDelta token, int[,] grid, int x, int y) {
|
||||
var innerCornersFor9Grid = this.innerCornersFor9Grid ?? new[,] { { grid[1, 1], grid[1, 1] }, { grid[1, 1], grid[1, 1] } }; // copy so that there's no reference changing during the method
|
||||
var layout = GetLayout();
|
||||
var (width, height) = (layout.GetValue("width"), layout.GetValue("height"));
|
||||
|
||||
current9gridInteractionMap[x, y] = true;
|
||||
current9gridInteractionMap[x + 1, y] = true;
|
||||
current9gridInteractionMap[x, y + 1] = true;
|
||||
current9gridInteractionMap[x + 1, y + 1] = true;
|
||||
|
||||
var start = layout.GetAddress("blockmap");
|
||||
|
||||
// need to draw these 4 blocks as well as the 12 neighboring blocks (up/down/left/right/diagonal) if they're set to true
|
||||
lock (pixelWriteLock) {
|
||||
set(todo[0], grid[1, 1]);
|
||||
foreach (var cell in todo) {
|
||||
var cellValue = get(cell);
|
||||
if (!targets.Contains(cellValue)) continue;
|
||||
|
||||
var northwest = targets.Contains(get(new(cell.X - 1, cell.Y - 1)));
|
||||
var northeast = targets.Contains(get(new(cell.X + 1, cell.Y - 1)));
|
||||
var southwest = targets.Contains(get(new(cell.X - 1, cell.Y + 1)));
|
||||
var southeast = targets.Contains(get(new(cell.X + 1, cell.Y + 1)));
|
||||
var north = targets.Contains(get(new(cell.X, cell.Y - 1)));
|
||||
var south = targets.Contains(get(new(cell.X, cell.Y + 1)));
|
||||
var west = targets.Contains(get(new(cell.X - 1, cell.Y)));
|
||||
var east = targets.Contains(get(new(cell.X + 1, cell.Y)));
|
||||
|
||||
var aggregate = (north ? "N" : " ") + (east ? "E" : " ") + (south ? "S" : " ") + (west ? "W" : " ");
|
||||
var corners = (northwest ? "7" : " ") + (northeast ? "9" : " ") + (southeast ? "3" : " ") + (southwest ? "1" : " ");
|
||||
|
||||
// grid[x, y]
|
||||
var block = aggregate switch {
|
||||
" ES " => grid[1, 0],
|
||||
" ESW" => grid[2, 0],
|
||||
" SW" => grid[3, 0],
|
||||
"NES " => grid[0, 2],
|
||||
"NESW" => grid[2, 2],
|
||||
"N SW" => grid[4, 2],
|
||||
"NE " => grid[0, 3],
|
||||
"NE W" => grid[2, 4],
|
||||
"N W" => grid[4, 3],
|
||||
_ => grid[1, 1],
|
||||
};
|
||||
|
||||
if ("NW".All(aggregate.Contains) && !corners.Contains('7')) block = grid[1, 1];
|
||||
if ("NE".All(aggregate.Contains) && !corners.Contains('9')) block = grid[3, 1];
|
||||
if ("SE".All(aggregate.Contains) && !corners.Contains('3')) block = grid[3, 3];
|
||||
if ("SW".All(aggregate.Contains) && !corners.Contains('1')) block = grid[1, 3];
|
||||
|
||||
set(cell, block);
|
||||
for (int dy = -1; dy < 3; dy++) {
|
||||
for (int dx = -1; dx < 3; dx++) {
|
||||
var (xx, yy) = (x + dx, y + dy);
|
||||
if (!xx.InRange(0, width - 1) || !yy.InRange(0, height - 1)) continue;
|
||||
if (!current9gridInteractionMap[xx, yy]) continue;
|
||||
var chosenBlock = Get9GridBlock(current9gridInteractionMap, new(xx, yy), grid, innerCornersFor9Grid);
|
||||
model.WriteMultiByteValue(start + (yy * width + xx) * 2, 2, token, chosenBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1751,7 +1803,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
|
||||
var map = GetMapModel();
|
||||
var connections = GetConnections(map, group, this.map);
|
||||
for (int i = 0; i < connections.Count; i++) {
|
||||
for (int i = 0; i < (connections?.Count ?? 0); i++) {
|
||||
if (connections[i].Direction != direction || connections[i].MapGroup != mapGroup || connections[i].MapNum != mapNum) continue;
|
||||
|
||||
for (int j = i + 1; j < connections.Count; j++) {
|
||||
|
|
|
|||
|
|
@ -60,15 +60,27 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
public static HashSet<int> GetUsedItemFlags(IDataModel model, ScriptParser parser) {
|
||||
var usedFlags = new HashSet<int>();
|
||||
|
||||
var hiddenItemFlagStart = 600;
|
||||
if (model.IsEmerald()) {
|
||||
for (int i = 0x15C; i <= 0x1A9; i++) usedFlags.Add(i); // Emerald Match Call flags
|
||||
for (int i = 0x15C; i <= 0x1A9; i++) usedFlags.Add(i); // Emerald Match Call flags
|
||||
hiddenItemFlagStart = 500;
|
||||
} else if (model.IsFRLG()) {
|
||||
hiddenItemFlagStart = 1000;
|
||||
}
|
||||
|
||||
// object flags
|
||||
foreach (var element in GetAllEvents(model, "objects")) {
|
||||
if (!element.HasField("flag")) continue;
|
||||
usedFlags.Add(element.GetValue("flag"));
|
||||
}
|
||||
|
||||
// hidden item flags
|
||||
foreach (var element in GetAllEvents(model, "signposts")) {
|
||||
var signpost = new SignpostEventModel(element);
|
||||
if (!signpost.IsHiddenItem) continue;
|
||||
usedFlags.Add(hiddenItemFlagStart + signpost.HiddenItemFlag);
|
||||
}
|
||||
|
||||
foreach (var spot in GetAllScriptSpots(model, parser, GetAllTopLevelScripts(model), 0x29, 0x2A, 0x2B)) {
|
||||
usedFlags.Add(model.ReadMultiByteValue(spot.Address + 1, 2));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -643,7 +643,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
public object Hover(double x, double y) {
|
||||
var map = MapUnderCursor(x, y);
|
||||
if (map == null) return EmptyTooltip;
|
||||
if (drawMultipleTiles && tilesToDraw != null) {
|
||||
if (use9Grid && IsValid9GridSelection) {
|
||||
var p = ToBoundedMapTilePosition(map, x, y, 2, 2);
|
||||
UpdateHover(map, p.X, p.Y, 2, 2);
|
||||
} else if (drawMultipleTiles && tilesToDraw != null) {
|
||||
var p = ToBoundedMapTilePosition(map, x, y, tilesToDraw.GetLength(0), tilesToDraw.GetLength(1));
|
||||
if (interactionType == PrimaryInteractionType.Draw) {
|
||||
while (Math.Abs(p.X - drawSource.X) % tilesToDraw.GetLength(0) != 0) p -= new Point(1, 0);
|
||||
|
|
@ -760,7 +763,6 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
if (interactionType == PrimaryInteractionType.Draw) DrawMove(x, y);
|
||||
if (interactionType == PrimaryInteractionType.RectangleDraw) RectangleDrawMove(x, y);
|
||||
if (interactionType == PrimaryInteractionType.Draw9Grid) Draw9Grid(x, y);
|
||||
if (interactionType == PrimaryInteractionType.Draw25Grid) Draw25Grid(x, y);
|
||||
if (interactionType == PrimaryInteractionType.Event) EventMove(x, y);
|
||||
}
|
||||
|
||||
|
|
@ -769,7 +771,6 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
else if (interactionType == PrimaryInteractionType.Draw) DrawUp(x, y);
|
||||
else if (interactionType == PrimaryInteractionType.RectangleDraw) DrawUp(x, y);
|
||||
else if (interactionType == PrimaryInteractionType.Draw9Grid) DrawUp(x, y);
|
||||
else if (interactionType == PrimaryInteractionType.Draw25Grid) DrawUp(x, y);
|
||||
interactionType = PrimaryInteractionType.None;
|
||||
}
|
||||
|
||||
|
|
@ -777,8 +778,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
private void DrawDown(double x, double y, PrimaryInteractionStart click) {
|
||||
interactionType = PrimaryInteractionType.Draw;
|
||||
if ((click & PrimaryInteractionStart.ControlClick) != 0) interactionType = PrimaryInteractionType.RectangleDraw;
|
||||
if (use9Grid && IsValid9GridSelection) interactionType = PrimaryInteractionType.Draw9Grid;
|
||||
if (use25Grid && IsValid25GridSelection) interactionType = PrimaryInteractionType.Draw25Grid;
|
||||
if (use9Grid && IsValid9GridSelection) {
|
||||
interactionType = PrimaryInteractionType.Draw9Grid;
|
||||
primaryMap.PrepareFor9GridDraw(tilesToDraw);
|
||||
}
|
||||
var map = MapUnderCursor(x, y);
|
||||
if ((click & PrimaryInteractionStart.DoubleClick) != 0) {
|
||||
if (drawBlockIndex < 0 && collisionIndex < 0) {
|
||||
|
|
@ -799,9 +802,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
drawSource = ToBoundedMapTilePosition(map, x, y, 1, 1);
|
||||
lastDraw = drawSource;
|
||||
if (click == PrimaryInteractionStart.ControlClick) RectangleDrawMove(x, y);
|
||||
if (click == PrimaryInteractionStart.Click) DrawMove(x, y);
|
||||
if (interactionType == PrimaryInteractionType.Draw9Grid) Draw9Grid(x, y);
|
||||
if (interactionType == PrimaryInteractionType.Draw25Grid) Draw25Grid(x, y);
|
||||
else if (interactionType == PrimaryInteractionType.Draw9Grid) Draw9Grid(x, y);
|
||||
else if (click == PrimaryInteractionStart.Click) DrawMove(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -852,12 +854,6 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
Hover(x, y);
|
||||
}
|
||||
|
||||
private void Draw25Grid(double x, double y) {
|
||||
var map = MapUnderCursor(x, y);
|
||||
map.Draw25Grid(history.CurrentChange, tilesToDraw, x, y);
|
||||
Hover(x, y);
|
||||
}
|
||||
|
||||
private void DrawUp(double x, double y) {
|
||||
rectangleBackup = null;
|
||||
history.ChangeCompleted();
|
||||
|
|
@ -1171,9 +1167,6 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
if (use9Grid && IsValid9GridSelection) {
|
||||
DrawMultipleTiles = false;
|
||||
width = height = 1;
|
||||
} else if (use25Grid && IsValid25GridSelection) {
|
||||
DrawMultipleTiles = false;
|
||||
width = height = 1;
|
||||
} else {
|
||||
DrawMultipleTiles = true;
|
||||
}
|
||||
|
|
@ -1193,9 +1186,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
}
|
||||
|
||||
private void FillMultiTileRender() {
|
||||
NotifyPropertiesChanged(nameof(IsValid9GridSelection), nameof(IsValid25GridSelection));
|
||||
NotifyPropertiesChanged(nameof(IsValid9GridSelection));
|
||||
if (tilesToDraw == null) return;
|
||||
var localCopy = tilesToDraw;
|
||||
if (IsValid9GridSelection && use9Grid) singletons.WorkDispatcher.DispatchWork(() => PrimaryMap.DiscoverCornersFor9Grid(localCopy));
|
||||
var localRenders = primaryMap.BlockRenders;
|
||||
var (width, height) = (localCopy.GetLength(0), localCopy.GetLength(1));
|
||||
var scale = (width < 4 && height < 4) ? 2 : 1;
|
||||
|
|
@ -1697,45 +1691,25 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
private bool selectionFromBlock = false;
|
||||
private Point blockInteractionStart;
|
||||
|
||||
// when in 9-grid mode, show a 2x2 draw area, and draw from blocks in the 9-grid
|
||||
private bool use9Grid;
|
||||
public bool Use9Grid { get => use9Grid; set => Set(ref use9Grid, value, old => {
|
||||
if (use9Grid && IsValid9GridSelection) DrawMultipleTiles = false;
|
||||
else DrawMultipleTiles = tilesToDraw != null && (tilesToDraw.GetLength(0) > 1 || tilesToDraw.GetLength(1) > 1);
|
||||
}); }
|
||||
public bool Use9Grid {
|
||||
get => use9Grid;
|
||||
set => Set(ref use9Grid, value, old => {
|
||||
if (use9Grid && IsValid9GridSelection) {
|
||||
DrawMultipleTiles = true;
|
||||
singletons.WorkDispatcher.DispatchWork(() => PrimaryMap.DiscoverCornersFor9Grid(tilesToDraw));
|
||||
} else {
|
||||
DrawMultipleTiles = tilesToDraw != null && (tilesToDraw.GetLength(0) > 1 || tilesToDraw.GetLength(1) > 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
public bool IsValid9GridSelection {
|
||||
get {
|
||||
return tilesToDraw != null && tilesToDraw.GetLength(0) == 3 && tilesToDraw.GetLength(1) == 3;
|
||||
}
|
||||
}
|
||||
|
||||
private bool use25Grid;
|
||||
public bool Use25Grid { get => use25Grid; set => Set(ref use25Grid, value, old => {
|
||||
if (use25Grid && IsValid25GridSelection) DrawMultipleTiles = false;
|
||||
else DrawMultipleTiles = tilesToDraw != null && (tilesToDraw.GetLength(0) > 1 || tilesToDraw.GetLength(1) > 1);
|
||||
}); }
|
||||
public bool IsValid25GridSelection {
|
||||
get {
|
||||
if (tilesToDraw == null || tilesToDraw.GetLength(0) != 5 || tilesToDraw.GetLength(1) != 5) return false;
|
||||
|
||||
// require like-blocks
|
||||
// 25 blocks - 4 corners - 1 center - 4 partial centers - 4 corners - 4 edges = 8 duplicate blocks
|
||||
|
||||
// 4 corner duplicates
|
||||
if (tilesToDraw[1, 0] != tilesToDraw[0, 1]) return false;
|
||||
if (tilesToDraw[3, 0] != tilesToDraw[4, 1]) return false;
|
||||
if (tilesToDraw[0, 3] != tilesToDraw[1, 4]) return false;
|
||||
if (tilesToDraw[3, 4] != tilesToDraw[4, 3]) return false;
|
||||
|
||||
// 4 center duplicates
|
||||
if (tilesToDraw[2, 2] != tilesToDraw[2, 1]) return false;
|
||||
if (tilesToDraw[2, 2] != tilesToDraw[2, 3]) return false;
|
||||
if (tilesToDraw[2, 2] != tilesToDraw[1, 2]) return false;
|
||||
if (tilesToDraw[2, 2] != tilesToDraw[3, 2]) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectBlock(int x, int y) {
|
||||
while (y * BlockMapViewModel.BlocksPerRow + x > PrimaryMap.BlockRenders.Count) y -= 1;
|
||||
blockInteractionStart = new(x, y);
|
||||
|
|
@ -1793,7 +1767,6 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
PrimaryMap.BlockEditor.ShowTiles = false;
|
||||
FillMultiTileRender();
|
||||
if (use9Grid && IsValid9GridSelection) DrawMultipleTiles = false;
|
||||
else if (use25Grid && IsValid25GridSelection) DrawMultipleTiles = false;
|
||||
else DrawMultipleTiles = true;
|
||||
BlockEditorVisible = false;
|
||||
}
|
||||
|
|
@ -2309,7 +2282,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
|
|||
ControlClick = 4,
|
||||
ShiftClick = 8,
|
||||
}
|
||||
public enum PrimaryInteractionType { None, Draw, Event, RectangleDraw, Draw9Grid, Draw25Grid }
|
||||
public enum PrimaryInteractionType { None, Draw, Event, RectangleDraw, Draw9Grid }
|
||||
|
||||
public record BlocksetCache(ObservableCollection<BlocksetOption> Primary, ObservableCollection<BlocksetOption> Secondary) {
|
||||
public void CalculateBlocksetOptions(IDataModel model) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
|
|||
public TextEditorViewModel() {
|
||||
Keywords.CollectionChanged += (sender, e) => UpdateLayers();
|
||||
Constants.CollectionChanged += (sender, e) => UpdateLayers();
|
||||
SyntaxHighlighting = true;
|
||||
}
|
||||
|
||||
public TextEditorViewModel(bool syntaxHighlighting) {
|
||||
|
|
|
|||
|
|
@ -90,10 +90,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
|
||||
public ObservableCollection<GotoMapButton> MapPreviews { get; } = new();
|
||||
|
||||
public MapOptionsArrayElementViewModel(IWorkDispatcher dispatcher, MapEditorViewModel mapEditor, string tableName, int index) {
|
||||
public MapOptionsArrayElementViewModel(IWorkDispatcher dispatcher, IDelayWorkTimer timer, MapEditorViewModel mapEditor, string tableName, int index) {
|
||||
this.dispatcher = dispatcher;
|
||||
(this.mapEditor, this.tableName, this.index) = (mapEditor, tableName, index);
|
||||
dispatcher.RunBackgroundWork(Load);
|
||||
timer.DelayCall(TimeSpan.FromSeconds(.5), Load);
|
||||
}
|
||||
|
||||
private void Load() {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
private readonly Selection selection;
|
||||
private readonly ChangeHistory<ModelDelta> history;
|
||||
private readonly IRaiseMessageTab messageTab;
|
||||
private readonly IDelayWorkTimer recompileTimer;
|
||||
|
||||
public event EventHandler<ErrorInfo> ModelDataChanged;
|
||||
public event EventHandler AttentionNewContent;
|
||||
|
|
@ -98,6 +99,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
|
||||
public CodeTool(Singletons singletons, ViewPort viewPort, Selection selection, ChangeHistory<ModelDelta> history, IRaiseMessageTab messageTab) {
|
||||
this.singletons = singletons;
|
||||
recompileTimer = singletons.WorkDispatcher.CreateDelayTimer();
|
||||
var gameHash = viewPort.Model.GetShortGameCode();
|
||||
thumb = new ThumbParser(singletons);
|
||||
script = new ScriptParser(gameHash, singletons.ScriptLines, 0x02);
|
||||
|
|
@ -323,7 +325,8 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
if (run != null && run.Start != body.Address) run = null;
|
||||
|
||||
int length = parser.FindLength(model, body.Address);
|
||||
using (ModelCacheScope.CreateScope(model)) {
|
||||
// don't need to run this if they're still typing
|
||||
recompileTimer.DelayCall(TimeSpan.FromSeconds(.5), () => {
|
||||
var initialStart = selection.Scroll.ViewPointToDataIndex(selection.SelectionStart);
|
||||
var initialEnd = selection.Scroll.ViewPointToDataIndex(selection.SelectionEnd);
|
||||
if (initialStart > initialEnd) (initialStart, initialEnd) = (initialEnd, initialStart);
|
||||
|
|
@ -353,7 +356,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
body.Address = start; // in case of the code getting repointed
|
||||
}
|
||||
UpdateContents(start, parser, body.Address, length);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateScriptHelpFromLine(object sender, HelpContext context) {
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
};
|
||||
}
|
||||
|
||||
public class ComboBoxArrayElementViewModel : ViewModelCore, IArrayElementViewModel {
|
||||
public class ComboBoxArrayElementViewModel : ViewModelCore, IMultiEnabledArrayElementViewModel {
|
||||
private string name, enumName;
|
||||
private int start, length;
|
||||
|
||||
|
|
|
|||
|
|
@ -40,9 +40,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
if (selectedIndex < 0 || selectedIndex >= FilteredOptions.Count) return;
|
||||
interactionType = InteractionType.DropDown;
|
||||
selectedIndex = value;
|
||||
dropDownIsOpen = false;
|
||||
if (!ClearFilter()) {
|
||||
displayText = AllOptions[selectedIndex].Text;
|
||||
NotifyPropertiesChanged(nameof(SelectedIndex), nameof(DisplayText), nameof(ModelValue));
|
||||
NotifyPropertiesChanged(nameof(SelectedIndex), nameof(DisplayText), nameof(DropDownIsOpen), nameof(ModelValue));
|
||||
}
|
||||
interactionType = InteractionType.None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@
|
|||
using HavenSoft.HexManiac.Core.Models.Runs;
|
||||
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
||||
public enum ElementContentViewModelType {
|
||||
|
|
@ -20,7 +23,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
string UpdateViewModelFromModel(FieldArrayElementViewModel viewModel);
|
||||
}
|
||||
|
||||
public class FieldArrayElementViewModel : ViewModelCore, IArrayElementViewModel {
|
||||
public class FieldArrayElementViewModel : ViewModelCore, IMultiEnabledArrayElementViewModel {
|
||||
private readonly IFieldArrayElementViewModelStrategy strategy;
|
||||
|
||||
private string name;
|
||||
|
|
@ -46,7 +49,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
private string theme; public string Theme { get => theme; set => Set(ref theme, value); }
|
||||
public bool IsInError => errorText != string.Empty;
|
||||
|
||||
string errorText;
|
||||
private string errorText;
|
||||
public string ErrorText {
|
||||
get => errorText;
|
||||
set {
|
||||
|
|
@ -318,4 +321,103 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
public class MultiFieldArrayElementViewModel : ViewModelCore, IArrayElementViewModel {
|
||||
private string theme, content;
|
||||
private bool visible = true;
|
||||
private List<IMultiEnabledArrayElementViewModel> fields = new();
|
||||
|
||||
public string Theme { get => theme; set => Set(ref theme, value); }
|
||||
public bool Visible { get => visible; set => Set(ref visible, value); }
|
||||
public bool IsInError => false;
|
||||
public string ErrorText => string.Empty;
|
||||
public int ZIndex => 0;
|
||||
|
||||
public event EventHandler DataChanged;
|
||||
public event EventHandler DataSelected;
|
||||
|
||||
public ICommand Undo { get; }
|
||||
public ICommand Redo { get; }
|
||||
|
||||
public MultiFieldArrayElementViewModel(ViewPort port) => (Undo, Redo) = (port.Undo, port.Redo);
|
||||
|
||||
public void Add(IMultiEnabledArrayElementViewModel field) {
|
||||
fields.Add(field);
|
||||
RecalculateBody();
|
||||
}
|
||||
|
||||
// consider hiding fields that don't match the filter
|
||||
public bool Filter(string filter) {
|
||||
foreach (var element in fields) {
|
||||
if (element.Name.MatchesPartial(filter)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public string Content {
|
||||
get => content;
|
||||
set => Set(ref content, value, oldValue => {
|
||||
var lines = content.SplitLines();
|
||||
for (int i = 0; i < fields.Count; i++) {
|
||||
if (i >= lines.Length) break;
|
||||
var parts = lines[i].Split(':', 2);
|
||||
if (parts.Length == 1) continue;
|
||||
var content = parts[1].Trim();
|
||||
if (fields[i] is FieldArrayElementViewModel field) {
|
||||
field.Content = content;
|
||||
} else if (fields[i] is ComboBoxArrayElementViewModel combo) {
|
||||
combo.FilteringComboOptions.DisplayText = content;
|
||||
combo.FilteringComboOptions.SelectConfirm();
|
||||
}
|
||||
}
|
||||
DataChanged?.Invoke(this, EventArgs.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
public bool TryCopy(IArrayElementViewModel other) {
|
||||
if (other is not MultiFieldArrayElementViewModel that) return false;
|
||||
return content == that.content && fields.Count == that.fields.Count && fields.Count.Range().All(i => fields[i].TryCopy(that.fields[i]));
|
||||
}
|
||||
|
||||
private void RecalculateBody() {
|
||||
var longestName = fields.Select(f => f.Name.Length).Max();
|
||||
var content = new StringBuilder();
|
||||
bool first = true;
|
||||
foreach (var field in fields) {
|
||||
if (!first) content.AppendLine();
|
||||
else first = false;
|
||||
|
||||
content.Append(field.Name.PadRight(longestName, ' '));
|
||||
content.Append(" : ");
|
||||
if (field is FieldArrayElementViewModel field1) {
|
||||
content.Append(field1.Content);
|
||||
} else if (field is ComboBoxArrayElementViewModel combo) {
|
||||
content.Append(combo.FilteringComboOptions.DisplayText);
|
||||
}
|
||||
}
|
||||
this.content = content.ToString();
|
||||
NotifyPropertyChanged(nameof(Content));
|
||||
}
|
||||
|
||||
public IReadOnlyList<AutocompleteItem> GetAutoComplete(string line, int caretLineIndex, int caretCharacterIndex) {
|
||||
var parts = line.Split(':', 2);
|
||||
if (parts.Length != 2) return Array.Empty<AutocompleteItem>();
|
||||
var name = parts[0].Trim();
|
||||
var field = fields.FirstOrDefault(f => f.Name == name);
|
||||
if (field == null) return Array.Empty<AutocompleteItem>();
|
||||
|
||||
// right now, the field is always something like free text, free number, or free color
|
||||
// no auto-complete makes sense
|
||||
// but this method is here as a stub in case we add combobox content into the MultiField.
|
||||
if (field is ComboBoxArrayElementViewModel combo) {
|
||||
var content = parts[1].Trim();
|
||||
combo.FilteringComboOptions.DisplayText = content;
|
||||
return combo.FilteringComboOptions.FilteredOptions
|
||||
.Select(option => new AutocompleteItem(option.Text, parts[0] + ": " + option.Text) { CharacterOffset = parts[0].Length })
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
return Array.Empty<AutocompleteItem>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,11 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
bool TryCopy(IArrayElementViewModel other);
|
||||
}
|
||||
|
||||
// shared interface for fields/comboboxes to appear in multi-boxes
|
||||
public interface IMultiEnabledArrayElementViewModel : IArrayElementViewModel {
|
||||
string Name { get; set; }
|
||||
}
|
||||
|
||||
public class SplitterArrayElementViewModel : ViewModelCore, IArrayElementViewModel {
|
||||
event EventHandler IArrayElementViewModel.DataChanged { add { } remove { } }
|
||||
event EventHandler IArrayElementViewModel.DataSelected { add { } remove { } }
|
||||
|
|
@ -101,6 +106,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
|
||||
if (child is FieldArrayElementViewModel faevm) childVisible = filterMatchesGroup || faevm.Name.MatchesPartial(filter);
|
||||
if (child is ComboBoxArrayElementViewModel cbaevm) childVisible = filterMatchesGroup || cbaevm.Name.MatchesPartial(filter);
|
||||
if (child is MultiFieldArrayElementViewModel multi) childVisible = filterMatchesGroup || multi.Filter(filter);
|
||||
if (child is CalculatedElementViewModel cevm) childVisible = filterMatchesGroup || cevm.Name.MatchesPartial(filter);
|
||||
if (child is IStreamArrayElementViewModel saevm) childVisible = lastFieldVisible || (saevm is TextStreamElementViewModel tStream && tStream.Content.MatchesPartial(filter));
|
||||
if (child is BitListArrayElementViewModel blaevm) {
|
||||
|
|
|
|||
|
|
@ -194,6 +194,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
var run = GetRun();
|
||||
Pages = run.Pages;
|
||||
UpdateAvailablePalettes(Start);
|
||||
this.Bind(nameof(Visible), (sender, args) => {
|
||||
if (Visible && needsUpdate) UpdateTiles(Start, CurrentPage, false);
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateAvailablePalettes(int start) {
|
||||
|
|
@ -241,13 +244,18 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
|
||||
protected override void PageChanged() => UpdateTiles(CurrentPage);
|
||||
|
||||
private bool needsUpdate = false;
|
||||
public void UpdateTiles(int? pageOption = null) {
|
||||
// TODO support multiple layers
|
||||
int page = pageOption ?? CurrentPage;
|
||||
|
||||
if (GetRun() is LzTilemapRun mapRun) mapRun.FindMatchingTileset(Model);
|
||||
|
||||
UpdateTiles(Start, page, false);
|
||||
if (Visible) {
|
||||
UpdateTiles(Start, page, false);
|
||||
} else {
|
||||
needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool CanExecuteAddPage() {
|
||||
|
|
@ -266,6 +274,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
private int[,] lastPixels;
|
||||
private IReadOnlyList<short> lastColors;
|
||||
private void UpdateTiles(int start, int page, bool exitPaletteSearchEarly) {
|
||||
needsUpdate = false;
|
||||
var run = GetRun();
|
||||
|
||||
var tableIndex = -1;
|
||||
|
|
|
|||
|
|
@ -36,12 +36,14 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
public bool DisplayHeader => GroupName != DefaultName;
|
||||
public string GroupName { get => groupName; set => Set(ref groupName, value, old => NotifyPropertyChanged(nameof(DisplayHeader))); }
|
||||
|
||||
private readonly ViewPort viewPort;
|
||||
|
||||
public ObservableCollection<IArrayElementViewModel> Members { get; } = new();
|
||||
|
||||
public Action<IStreamArrayElementViewModel> ForwardModelChanged { get; init; }
|
||||
public Action<IStreamArrayElementViewModel> ForwardModelDataMoved { get; init; }
|
||||
|
||||
public TableGroupViewModel() { GroupName = DefaultName; }
|
||||
public TableGroupViewModel(ViewPort viewPort) { GroupName = DefaultName; this.viewPort = viewPort; }
|
||||
|
||||
public bool IsOpen => isOpen;
|
||||
|
||||
|
|
@ -51,29 +53,59 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
isOpen = true;
|
||||
}
|
||||
|
||||
public void Add(IArrayElementViewModel child, string theme = null) {
|
||||
child.Theme = theme;
|
||||
if (currentMember == Members.Count) {
|
||||
private bool useMultiFieldFeature = false;
|
||||
public bool UseMultiFieldFeature { get => useMultiFieldFeature; set => Set(ref useMultiFieldFeature, value); }
|
||||
|
||||
private MultiFieldArrayElementViewModel multiInProgress;
|
||||
|
||||
public void Add(IArrayElementViewModel child) {
|
||||
if (UseMultiFieldFeature && child is IMultiEnabledArrayElementViewModel newField) {
|
||||
if (multiInProgress == null) multiInProgress = new(viewPort);
|
||||
multiInProgress.Add(newField);
|
||||
} else if (currentMember == Members.Count) {
|
||||
if (multiInProgress != null) {
|
||||
Members.Add(multiInProgress);
|
||||
multiInProgress = null;
|
||||
currentMember++;
|
||||
}
|
||||
Members.Add(child);
|
||||
currentMember++;
|
||||
} else {
|
||||
// using var scope = Members[currentMember].SilencePropertyNotifications();
|
||||
if (!Members[currentMember].TryCopy(child)) {
|
||||
Members[currentMember] = child;
|
||||
if (multiInProgress != null) {
|
||||
if (Members[currentMember].TryCopy(multiInProgress)) {
|
||||
// no need to copy
|
||||
} else {
|
||||
Members[currentMember] = multiInProgress;
|
||||
}
|
||||
multiInProgress = null;
|
||||
currentMember++;
|
||||
Add(child); // we're adding a non-multi, and now have dealt with the current multi. Recurse so the child can be added using either "full" or "replace" strategy
|
||||
} else if (Members[currentMember].TryCopy(child)) {
|
||||
currentMember += 1; // copied over successfully
|
||||
} else {
|
||||
Members[currentMember].Theme = child.Theme;
|
||||
// replace existing
|
||||
Members[currentMember] = child;
|
||||
currentMember += 1;
|
||||
}
|
||||
}
|
||||
currentMember += 1;
|
||||
}
|
||||
|
||||
public void Close() {
|
||||
if (!isOpen) return;
|
||||
if (multiInProgress != null) {
|
||||
if (currentMember == Members.Count) {
|
||||
Members.Add(multiInProgress);
|
||||
} else if (!Members[currentMember].TryCopy(multiInProgress)) {
|
||||
Members[currentMember] = multiInProgress;
|
||||
}
|
||||
currentMember += 1;
|
||||
multiInProgress = null;
|
||||
}
|
||||
while (Members.Count > currentMember) Members.RemoveAt(Members.Count - 1);
|
||||
isOpen = false;
|
||||
// Members.RaiseRefresh();
|
||||
}
|
||||
|
||||
public void AddChildrenFromTable(ViewPort viewPort, Selection selection, ITableRun table, int index, string theme, SplitterArrayElementViewModel header, TableGroupViewModel helperGroup, int splitPortion = -1) {
|
||||
public void AddChildrenFromTable(ViewPort viewPort, Selection selection, ITableRun table, int index, SplitterArrayElementViewModel header, TableGroupViewModel helperGroup, int splitPortion = -1) {
|
||||
var itemAddress = table.Start + table.ElementLength * index;
|
||||
var originalItemAddress = itemAddress;
|
||||
var currentPartition = 0;
|
||||
|
|
@ -126,9 +158,9 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
} else {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
if (!item.IsUnused()) {
|
||||
Add(viewModel, theme);
|
||||
helperGroup.AddChildrenFromPointerSegment(viewPort, theme, itemAddress, item, viewModel, header, recursionLevel: 0);
|
||||
if (!item.IsUnused() && viewModel is not null) {
|
||||
Add(viewModel);
|
||||
helperGroup.AddChildrenFromPointerSegment(viewPort, itemAddress, item, viewModel, header, recursionLevel: 0);
|
||||
}
|
||||
itemAddress += item.Length;
|
||||
}
|
||||
|
|
@ -178,7 +210,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
Add(new ButtonArrayElementViewModel("Edit Map", () => viewPort.Goto.Execute(name)));
|
||||
}
|
||||
|
||||
private void AddChildrenFromPointerSegment(ViewPort viewPort, string theme, int itemAddress, ArrayRunElementSegment item, IArrayElementViewModel parent, SplitterArrayElementViewModel header, int recursionLevel) {
|
||||
private void AddChildrenFromPointerSegment(ViewPort viewPort, int itemAddress, ArrayRunElementSegment item, IArrayElementViewModel parent, SplitterArrayElementViewModel header, int recursionLevel) {
|
||||
if (!(item is ArrayRunPointerSegment pointerSegment)) return;
|
||||
if (pointerSegment.InnerFormat == string.Empty) return;
|
||||
var destination = viewPort.Model.ReadPointer(itemAddress);
|
||||
|
|
@ -223,7 +255,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
if (!Members[myIndex].TryCopy(newStream)) Members[myIndex] = newStream;
|
||||
};
|
||||
ForwardModelDataMoved(streamElement);
|
||||
Add(streamElement, theme);
|
||||
Add(streamElement);
|
||||
|
||||
if (streamRun is ITableRun tableRun && recursionLevel < 1) {
|
||||
int segmentOffset = 0;
|
||||
|
|
@ -231,7 +263,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
if (!(tableRun.ElementContent[i] is ArrayRunPointerSegment)) { segmentOffset += tableRun.ElementContent[i].Length; continue; }
|
||||
for (int j = 0; j < tableRun.ElementCount; j++) {
|
||||
itemAddress = tableRun.Start + segmentOffset + j * tableRun.ElementLength;
|
||||
AddChildrenFromPointerSegment(viewPort, theme, itemAddress, tableRun.ElementContent[i], streamElement, header, recursionLevel + 1);
|
||||
AddChildrenFromPointerSegment(viewPort, itemAddress, tableRun.ElementContent[i], streamElement, header, recursionLevel + 1);
|
||||
}
|
||||
segmentOffset += tableRun.ElementContent[i].Length;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
private readonly ViewPort viewPort;
|
||||
private readonly IToolTrayViewModel toolTray;
|
||||
private readonly IWorkDispatcher dispatcher;
|
||||
private readonly IDelayWorkTimer loadMapUsageTimer, dataChangedTimer;
|
||||
|
||||
public string Name => "Table";
|
||||
|
||||
|
|
@ -129,6 +130,15 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
}
|
||||
}
|
||||
|
||||
private bool useMultiFieldFeature = false;
|
||||
public bool UseMultiFieldFeature {
|
||||
get => useMultiFieldFeature;
|
||||
set => Set(ref useMultiFieldFeature, value, old => {
|
||||
foreach (var group in Groups) group.UseMultiFieldFeature = useMultiFieldFeature;
|
||||
DataForCurrentRunChanged();
|
||||
});
|
||||
}
|
||||
|
||||
public ObservableCollection<IArrayElementViewModel> UsageChildren { get; }
|
||||
public ObservableCollection<TableGroupViewModel> Groups { get; }
|
||||
|
||||
|
|
@ -186,8 +196,10 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
this.viewPort = viewPort;
|
||||
this.toolTray = toolTray;
|
||||
this.dispatcher = dispatcher;
|
||||
loadMapUsageTimer = dispatcher.CreateDelayTimer();
|
||||
dataChangedTimer = /* */ new ImmediateWorkTimer(); //*/ dispatcher.CreateDelayTimer();
|
||||
CurrentElementSelector = new FilteringComboOptions();
|
||||
CurrentElementSelector.Bind(nameof(FilteringComboOptions.SelectedIndex), UpdateViewPortSelectionFromTableComboBoxIndex);
|
||||
CurrentElementSelector.Bind(nameof(FilteringComboOptions.ModelValue), UpdateViewPortSelectionFromTableComboBoxIndex);
|
||||
Groups = new();
|
||||
UsageChildren = new();
|
||||
|
||||
|
|
@ -283,29 +295,17 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
public IList<IArrayElementViewModel> Children => Groups.SelectMany(group => group.Members).ToList();
|
||||
|
||||
private void AddGroup() {
|
||||
Groups.Add(new TableGroupViewModel {
|
||||
Groups.Add(new TableGroupViewModel(viewPort) {
|
||||
ForwardModelChanged = element => element.DataChanged += ForwardModelChanged,
|
||||
ForwardModelDataMoved = element => element.DataMoved += ForwardModelDataMoved,
|
||||
});
|
||||
}
|
||||
|
||||
private readonly string[] themes = new string[] {
|
||||
//nameof(Theme.Text1),
|
||||
//nameof(Theme.Text2),
|
||||
//nameof(Theme.Data1),
|
||||
//nameof(Theme.Data2),
|
||||
//nameof(Theme.Accent),
|
||||
//nameof(Theme.Stream1),
|
||||
//nameof(Theme.Stream2),
|
||||
nameof(Theme.Background),
|
||||
};
|
||||
|
||||
private int childIndexGroup = 0, themeIndex = 0;
|
||||
private int childIndexGroup = 0;
|
||||
private void AddChild(IArrayElementViewModel child) {
|
||||
if (child == null) return;
|
||||
if (child is SplitterArrayElementViewModel) themeIndex = (themeIndex + 1) % themes.Length;
|
||||
while (Groups.Count <= childIndexGroup) AddGroup();
|
||||
Groups[childIndexGroup].Add(child, themes[themeIndex]);
|
||||
Groups[childIndexGroup].Add(child);
|
||||
}
|
||||
|
||||
private void MoveToNextGroup() {
|
||||
|
|
@ -337,13 +337,24 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
|
||||
private bool dataForCurrentRunChangeUpdate;
|
||||
public void DataForCurrentRunChanged() {
|
||||
// ignore callbacks while any held comboboxes are open
|
||||
foreach (var group in Groups) {
|
||||
foreach (var member in group.Members) {
|
||||
if (member is ComboBoxArrayElementViewModel box && box.FilteringComboOptions.DropDownIsOpen) return;
|
||||
}
|
||||
}
|
||||
|
||||
// must be longer than initial key-hold delay or app will studder
|
||||
dataChangedTimer.DelayCall(TimeSpan.FromSeconds(.6), DataForCurrentRunChangedCore);
|
||||
}
|
||||
|
||||
private void DataForCurrentRunChangedCore() {
|
||||
foreach (var group in Groups) {
|
||||
foreach (var member in group.Members) ClearHandlers(member);
|
||||
}
|
||||
foreach (var child in UsageChildren) ClearHandlers(child);
|
||||
foreach (var group in Groups) group.Open();
|
||||
childIndexGroup = 0;
|
||||
themeIndex = 0;
|
||||
usageChildInsertionIndex = 0;
|
||||
|
||||
var array = model.GetNextRun(Address) as ITableRun;
|
||||
|
|
@ -389,7 +400,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
streamGroup = Groups[1];
|
||||
streamGroup.Open();
|
||||
} else {
|
||||
streamGroup = new TableGroupViewModel {
|
||||
streamGroup = new TableGroupViewModel(viewPort) {
|
||||
ForwardModelChanged = element => element.DataChanged += ForwardModelChanged,
|
||||
ForwardModelDataMoved = element => element.DataMoved += ForwardModelDataMoved,
|
||||
};
|
||||
|
|
@ -398,7 +409,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
|
||||
var header = new SplitterArrayElementViewModel(viewPort, basename, elementOffset);
|
||||
AddChild(header);
|
||||
Groups[childIndexGroup].AddChildrenFromTable(viewPort, selection, array, index, themes[themeIndex], header, streamGroup);
|
||||
Groups[childIndexGroup].AddChildrenFromTable(viewPort, selection, array, index, header, streamGroup);
|
||||
MoveToNextGroup();
|
||||
Groups[0].GroupName = basename;
|
||||
} else {
|
||||
|
|
@ -414,7 +425,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
streamGroup.GroupName = TableGroupViewModel.DefaultName;
|
||||
streamGroup.Open();
|
||||
} else {
|
||||
streamGroup = new TableGroupViewModel {
|
||||
streamGroup = new TableGroupViewModel(viewPort) {
|
||||
ForwardModelChanged = element => element.DataChanged += ForwardModelChanged,
|
||||
ForwardModelDataMoved = element => element.DataMoved += ForwardModelDataMoved,
|
||||
};
|
||||
|
|
@ -439,7 +450,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
elementOffset = currentArray.Start + currentArray.ElementLength * currentIndex;
|
||||
var header = new SplitterArrayElementViewModel(viewPort, tableName, elementOffset);
|
||||
AddChild(header);
|
||||
Groups[childIndexGroup].AddChildrenFromTable(viewPort, selection, currentArray, currentIndex, themes[themeIndex], header, helperGroup, partition);
|
||||
Groups[childIndexGroup].AddChildrenFromTable(viewPort, selection, currentArray, currentIndex, header, helperGroup, partition);
|
||||
}
|
||||
}
|
||||
while (Groups.Count <= childIndexGroup) AddGroup();
|
||||
|
|
@ -536,6 +547,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
|
||||
private void UpdateViewPortSelectionFromTableComboBoxIndex(object sender = null, EventArgs e = null) {
|
||||
if (selfChange) return;
|
||||
if (CurrentElementSelector.DropDownIsOpen) return;
|
||||
var array = (ITableRun)model.GetNextRun(Address);
|
||||
var address = array.Start + array.ElementLength * CurrentElementSelector.SelectedIndex;
|
||||
selection.SelectionStart = selection.Scroll.DataIndexToViewPoint(address);
|
||||
|
|
@ -624,7 +636,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
|
|||
|
||||
// maps
|
||||
if (viewPort.MapEditor != null && viewPort.MapEditor.IsValidState && !viewPort.SpartanMode) {
|
||||
var mapOptions = new MapOptionsArrayElementViewModel(dispatcher, viewPort.MapEditor, basename, index);
|
||||
var mapOptions = new MapOptionsArrayElementViewModel(dispatcher, loadMapUsageTimer, viewPort.MapEditor, basename, index);
|
||||
mapOptions.MapPreviews.CollectionChanged += (sender, e) => NotifyPropertyChanged(nameof(HasUsageOptions));
|
||||
AddUsageChild(mapOptions); // always add, but invisible when empty
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3314,7 +3314,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels {
|
|||
}
|
||||
|
||||
if (run is ITableRun && sender != Tools.StringTool && Model.GetNextRun(Tools.StringTool.Address).Start == run.Start) Tools.StringTool.DataForCurrentRunChanged();
|
||||
if (run is ITableRun && Model.GetNextRun(Tools.TableTool.Address).Start == run.Start) Tools.TableTool.DataForCurrentRunChanged();
|
||||
if (run is ITableRun && Model.GetNextRun(Tools.TableTool.Address).Start == run.Start) Tools.TableTool.DataForCurrentRunChanged();
|
||||
}
|
||||
|
||||
private void ModelDataMovedByTool(object sender, (int originalLocation, int newLocation) locations) {
|
||||
|
|
|
|||
|
|
@ -851,7 +851,7 @@ namespace HavenSoft.HexManiac.Tests {
|
|||
public void TableTool_FilterTupleFieldName_TupleVisible() {
|
||||
var tool = ViewPort.Tools.TableTool;
|
||||
tool.Groups.Clear();
|
||||
tool.Groups.Add(new());
|
||||
tool.Groups.Add(new(ViewPort));
|
||||
var section = new SplitterArrayElementViewModel(ViewPort, "section", 0);
|
||||
var tuple = new TupleArrayElementViewModel(ViewPort, new ArrayRunTupleSegment("tuples", "|abc:|def:|ijk:|xyz:", 4), 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -340,9 +340,9 @@ namespace HavenSoft.HexManiac.Tests {
|
|||
|
||||
var tokenLines = lines.Select(line => line.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries));
|
||||
lines = tokenLines.Select(tokens => " ".Join(tokens)).ToArray();
|
||||
Assert.Equal("index: (0)", lines[0]);
|
||||
Assert.Equal("unknown: 0x0000", lines[1]);
|
||||
Assert.Equal("unused: 0x0000", lines[2]);
|
||||
Assert.Equal("index : (0)", lines[0]);
|
||||
Assert.Equal("unknown : 0x0000", lines[1]);
|
||||
Assert.Equal("unused : 0x0000", lines[2]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ using HavenSoft.HexManiac.Core.ViewModels.Tools;
|
|||
using HavenSoft.HexManiac.WPF.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace HavenSoft.HexManiac.WPF.Controls {
|
||||
public partial class AutocompleteOverlay {
|
||||
|
|
@ -92,6 +94,8 @@ namespace HavenSoft.HexManiac.WPF.Controls {
|
|||
} else {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
} else if (DataContext is MultiFieldArrayElementViewModel multi) {
|
||||
getAutocomplete = multi.GetAutoComplete;
|
||||
} else if (DataContext is TrainerTeamViewModel team) {
|
||||
getAutocomplete = team.GetTrainerAutocomplete;
|
||||
} else if (DataContext is CodeBody body) {
|
||||
|
|
@ -113,9 +117,13 @@ namespace HavenSoft.HexManiac.WPF.Controls {
|
|||
var verticalOffset = TargetTextBox.VerticalOffset;
|
||||
var lineHeight = TargetTextBox.ExtentHeight / totalLines;
|
||||
var verticalStart = lineHeight * editLineIndex - verticalOffset + 2;
|
||||
double horizontalStart = 0;
|
||||
|
||||
var options = getAutocomplete(lines[lineIndex], lineIndex, index);
|
||||
if (options != null && options.Count > 0) {
|
||||
var offset = options[0].CharacterOffset;
|
||||
var typeFace = new Typeface(TargetTextBox.FontFamily, TargetTextBox.FontStyle, TargetTextBox.FontWeight, TargetTextBox.FontStretch);
|
||||
horizontalStart = new FormattedText(new string('_', offset), CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeFace, Target.FontSize, null, 96).Width;
|
||||
AutocompleteItems.ItemsSource = AutoCompleteSelectionItem.Generate(options, 0).ToList();
|
||||
ShowAutocompleteOptions();
|
||||
} else if (options != null) {
|
||||
|
|
@ -124,7 +132,9 @@ namespace HavenSoft.HexManiac.WPF.Controls {
|
|||
var screenVertical = TargetTextBox.TranslatePoint(new Point(0, verticalStart), Application.Current.MainWindow).Y;
|
||||
ScrollBorder.UpdateLayout();
|
||||
if (Application.Current.MainWindow.ActualHeight - screenVertical < 200) verticalStart -= ScrollBorder.ActualHeight + 12;
|
||||
AutocompleteTransform.X = horizontalStart;
|
||||
AutocompleteTransform.Y = verticalStart;
|
||||
|
||||
Popup.Reposition();
|
||||
|
||||
ignoreNextSelectionChange = true;
|
||||
|
|
|
|||
34
src/HexManiac.WPF/Controls/DelayWorkTimer.cs
Normal file
34
src/HexManiac.WPF/Controls/DelayWorkTimer.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using HavenSoft.HexManiac.Core;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HavenSoft.HexManiac.WPF.Controls;
|
||||
|
||||
public class DelayWorkTimer : IDelayWorkTimer {
|
||||
private CancellationTokenSource? cancel;
|
||||
|
||||
public bool HasScheduledWork => cancel != null;
|
||||
|
||||
public DelayWorkResult DelayCall(TimeSpan delay, Action action) {
|
||||
var result = HasScheduledWork ? DelayWorkResult.WorkScheduledAndPreviousWorkCleared : DelayWorkResult.WorkScheduled;
|
||||
cancel?.Cancel();
|
||||
cancel = new CancellationTokenSource();
|
||||
Run(delay, action, cancel.Token);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Reset() {
|
||||
cancel?.Cancel();
|
||||
cancel = null;
|
||||
}
|
||||
|
||||
private async void Run(TimeSpan delay, Action action, CancellationToken token) {
|
||||
try {
|
||||
await Task.Delay(delay, token);
|
||||
} catch (TaskCanceledException) { }
|
||||
if (token.IsCancellationRequested) return;
|
||||
action();
|
||||
cancel = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -130,9 +130,6 @@
|
|||
<DataTrigger Binding="{Binding IsValid9GridSelection}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsValid25GridSelection}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Decorator.Style>
|
||||
|
|
@ -151,20 +148,6 @@
|
|||
</ToolTip>
|
||||
</CheckBox.ToolTip>
|
||||
</CheckBox>
|
||||
<CheckBox IsChecked="{Binding Use25Grid}" Content="Draw 25-Grid" VerticalAlignment="Top" Grid.Row="1" HorizontalAlignment="Center"
|
||||
Visibility="{Binding IsValid25GridSelection, Converter={StaticResource BoolToVisibility}}">
|
||||
<CheckBox.ToolTip>
|
||||
<ToolTip>
|
||||
<TextBlock>
|
||||
Draw one of the selected 25 blocks based on a grid.
|
||||
<LineBreak />
|
||||
This can make drawing multi-block details like paths,
|
||||
<LineBreak />
|
||||
water, or mountains much faster.
|
||||
</TextBlock>
|
||||
</ToolTip>
|
||||
</CheckBox.ToolTip>
|
||||
</CheckBox>
|
||||
<TextBlock Foreground="{DynamicResource Secondary}" VerticalAlignment="Center" HorizontalAlignment="Center" TextAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
|
||||
|
|
@ -175,7 +158,6 @@
|
|||
<Condition Binding="{Binding BlockEditorVisible}" Value="False" />
|
||||
<Condition Binding="{Binding DrawMultipleTiles}" Value="False" />
|
||||
<Condition Binding="{Binding IsValid9GridSelection}" Value="False" />
|
||||
<Condition Binding="{Binding IsValid25GridSelection}" Value="False" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<MultiDataTrigger.Setters>
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
|
|
|
|||
|
|
@ -268,6 +268,7 @@
|
|||
</Grid>
|
||||
<!-- Add X New -->
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,4">
|
||||
<CheckBox IsChecked="{Binding UseMultiFieldFeature}" Margin="0,0,10,0" Content="Group Similar Fields" />
|
||||
<Border ToolTipService.InitialShowDelay="0">
|
||||
<Border.ToolTip>
|
||||
<ToolTip Background="{DynamicResource Backlight}" BorderBrush="{DynamicResource Accent}" BorderThickness="1">
|
||||
|
|
@ -325,11 +326,6 @@
|
|||
<Grid Background="{DynamicResource Backlight}" Visibility="{Binding DisplayHeader, Converter={StaticResource BoolToVisibility}}">
|
||||
<TextBlock HorizontalAlignment="Center" Text="{Binding GroupName}" FontSize="20" Height="26"/>
|
||||
</Grid>
|
||||
<!-- - ->
|
||||
<hsg3hv:TableGroupPanel Source="{Binding Members}" />
|
||||
<!- - -->
|
||||
<!-- Let's try replacing this entire ItemsControl with a new way to display the Members and see if that's faster... -->
|
||||
<!-- -->
|
||||
<ItemsControl ItemsSource="{Binding Members}">
|
||||
<ItemsControl.ItemContainerStyle>
|
||||
<Style>
|
||||
|
|
@ -393,7 +389,6 @@
|
|||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Width="1" Margin="-2,-2,0,-2" HorizontalAlignment="Left" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<hsg3hv:AngleBorder Grid.Column="1" Direction="Out">
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</hsg3hv:AngleBorder>
|
||||
|
|
@ -419,7 +414,6 @@
|
|||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle HorizontalAlignment="Left" Width="1" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<hsg3hv:AngleBorder Grid.Column="1" Direction="Left">
|
||||
<TextBlock Text="{Binding Name}"/>
|
||||
</hsg3hv:AngleBorder>
|
||||
|
|
@ -438,7 +432,6 @@
|
|||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle HorizontalAlignment="Left" Width="1" Margin="-2,-2,0,-2" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<hsg3hv:AngleBorder Grid.Column="1" Direction="Left">
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</hsg3hv:AngleBorder>
|
||||
|
|
@ -471,7 +464,6 @@
|
|||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:BitListArrayElementViewModel}">
|
||||
<Grid HorizontalAlignment="Stretch" Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<Rectangle HorizontalAlignment="Left" Width="1" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<StackPanel>
|
||||
<Grid HorizontalAlignment="Stretch" Margin="2">
|
||||
<Grid.ColumnDefinitions>
|
||||
|
|
@ -529,29 +521,27 @@
|
|||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:StreamElementViewModel}">
|
||||
<Grid HorizontalAlignment="Stretch" Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<Rectangle Width="1" HorizontalAlignment="Left" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<StackPanel ToolTip="{Binding ParentName}">
|
||||
<hsg3hv:CommonTableStreamControl />
|
||||
<Grid Margin="20,2,2,2" Background="{DynamicResource Backlight}">
|
||||
<TextBox Name="StreamTextBox" UndoLimit="0" Text="{Binding Content, UpdateSourceTrigger=PropertyChanged}"
|
||||
Background="Transparent"
|
||||
VerticalAlignment="Top"
|
||||
CaretBrush="{DynamicResource Secondary}"
|
||||
AcceptsReturn="True" FontFamily="Consolas"
|
||||
Visibility="{Binding ShowContent, Converter={StaticResource BoolToVisibility}}">
|
||||
<TextBox.InputBindings>
|
||||
<KeyBinding Modifiers="Ctrl" Key="Z" Command="{Binding Undo}"/>
|
||||
<KeyBinding Modifiers="Ctrl" Key="Y" Command="{Binding Redo}"/>
|
||||
</TextBox.InputBindings>
|
||||
</TextBox>
|
||||
<hsg3hv:AutocompleteOverlay Target="{Binding ElementName=StreamTextBox}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel ToolTip="{Binding ParentName}">
|
||||
<hsg3hv:CommonTableStreamControl />
|
||||
<Grid Margin="20,2,2,2" Background="{DynamicResource Backlight}">
|
||||
<TextBox Name="StreamTextBox" UndoLimit="0" Text="{Binding Content, UpdateSourceTrigger=PropertyChanged}"
|
||||
Background="Transparent"
|
||||
VerticalAlignment="Top"
|
||||
CaretBrush="{DynamicResource Secondary}"
|
||||
AcceptsReturn="True" FontFamily="Consolas"
|
||||
Visibility="{Binding ShowContent, Converter={StaticResource BoolToVisibility}}">
|
||||
<TextBox.InputBindings>
|
||||
<KeyBinding Modifiers="Ctrl" Key="Z" Command="{Binding Undo}"/>
|
||||
<KeyBinding Modifiers="Ctrl" Key="Y" Command="{Binding Redo}"/>
|
||||
</TextBox.InputBindings>
|
||||
</TextBox>
|
||||
<hsg3hv:AutocompleteOverlay Target="{Binding ElementName=StreamTextBox}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:SpriteElementViewModel}">
|
||||
<Grid Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<Rectangle Width="1" HorizontalAlignment="Left" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<StackPanel>
|
||||
<hsg3hv:CommonTableStreamControl />
|
||||
<hsg3hv:AngleBorder Content="Palette Selection" FontSize="10" Direction="Out" Visibility="{Binding HasMultiplePalettes, Converter={StaticResource BoolToVisibility}}" HorizontalAlignment="Center" />
|
||||
|
|
@ -601,29 +591,24 @@
|
|||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:SpriteIndicatorElementViewModel}">
|
||||
<Grid HorizontalAlignment="Stretch" Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<Rectangle Width="1" HorizontalAlignment="Left" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<hsg3hv:PixelImage SnapsToDevicePixels="True" DataContext="{Binding Image}" />
|
||||
</Grid>
|
||||
<hsg3hv:PixelImage SnapsToDevicePixels="True" DataContext="{Binding Image}" Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}" />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:PaletteElementViewModel}">
|
||||
<Grid HorizontalAlignment="Stretch" Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<Rectangle Width="1" HorizontalAlignment="Left" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<StackPanel PreviewMouseDown="ActivatePalette">
|
||||
<hsg3hv:CommonTableStreamControl />
|
||||
<hsg3hv:PaletteControl DataContext="{Binding Colors}" />
|
||||
<DockPanel Visibility="{Binding ShowPageControls, Converter={StaticResource BoolToVisibility}}" LastChildFill="False">
|
||||
<hsg3hv:AngleButton MinWidth="20" Content="-" Direction="Left" Command="{Binding DeletePage}" HorizontalAlignment="Center" ToolTip="Delete the current page of this sprite and palette" />
|
||||
<hsg3hv:AngleButton MinWidth="20" Content="+" Direction="Right" Command="{Binding AddPage}" DockPanel.Dock="Right" ToolTip="Add a new page to this sprite and palette" />
|
||||
<hsg3hv:AngleButton MinWidth="60" Content="Previous" Direction="Left" DockPanel.Dock="Left" Click="PageMovePrevious" IsEnabled="{Binding CanMovePrevious}" />
|
||||
<hsg3hv:AngleButton MinWidth="60" Content="Next" Direction="Right" DockPanel.Dock="Right" Click="PageMoveNext" IsEnabled="{Binding CanMoveNext}" />
|
||||
</DockPanel>
|
||||
</StackPanel>
|
||||
<StackPanel PreviewMouseDown="ActivatePalette">
|
||||
<hsg3hv:CommonTableStreamControl />
|
||||
<hsg3hv:PaletteControl DataContext="{Binding Colors}" />
|
||||
<DockPanel Visibility="{Binding ShowPageControls, Converter={StaticResource BoolToVisibility}}" LastChildFill="False">
|
||||
<hsg3hv:AngleButton MinWidth="20" Content="-" Direction="Left" Command="{Binding DeletePage}" HorizontalAlignment="Center" ToolTip="Delete the current page of this sprite and palette" />
|
||||
<hsg3hv:AngleButton MinWidth="20" Content="+" Direction="Right" Command="{Binding AddPage}" DockPanel.Dock="Right" ToolTip="Add a new page to this sprite and palette" />
|
||||
<hsg3hv:AngleButton MinWidth="60" Content="Previous" Direction="Left" DockPanel.Dock="Left" Click="PageMovePrevious" IsEnabled="{Binding CanMovePrevious}" />
|
||||
<hsg3hv:AngleButton MinWidth="60" Content="Next" Direction="Right" DockPanel.Dock="Right" Click="PageMoveNext" IsEnabled="{Binding CanMoveNext}" />
|
||||
</DockPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:TrainerPokemonTeamElementViewModel}">
|
||||
<Grid HorizontalAlignment="Stretch" Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<Rectangle Width="1" HorizontalAlignment="Left" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<StackPanel ToolTip="{Binding ParentName}">
|
||||
<hsg3hv:CommonTableStreamControl />
|
||||
<Grid Margin="20,2,2,2" Background="{DynamicResource Backlight}">
|
||||
|
|
@ -672,7 +657,6 @@
|
|||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:TupleArrayElementViewModel}">
|
||||
<Grid Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<Rectangle Width="1" HorizontalAlignment="Left" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<StackPanel>
|
||||
<TextBlock Text="{Binding Name}" Margin="5,0"/>
|
||||
<ItemsControl ItemsSource="{Binding Children}">
|
||||
|
|
@ -716,7 +700,6 @@
|
|||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:CalculatedElementViewModel}">
|
||||
<DockPanel HorizontalAlignment="Stretch" Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<Rectangle Width="1" DockPanel.Dock="Left" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<Grid Margin="1,2,2,2" DockPanel.Dock="Top">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" SharedSizeGroup="LeftColumn"/>
|
||||
|
|
@ -755,18 +738,31 @@
|
|||
</DockPanel>
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:OffsetRenderViewModel}">
|
||||
<Grid HorizontalAlignment="Stretch" Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<Rectangle Width="1" HorizontalAlignment="Left" Fill="{Binding Theme, Converter={StaticResource Theme}}" />
|
||||
<hsg3hv:PixelImage SnapsToDevicePixels="True" Margin="1,0,0,0"
|
||||
MouseDown="OffsetRenderMouseDown" MouseMove="OffsetRenderMouseMove" MouseUp="OffsetRenderMouseUp" />
|
||||
</Grid>
|
||||
<hsg3hv:PixelImage SnapsToDevicePixels="True" Margin="1,0,0,0" Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}"
|
||||
MouseDown="OffsetRenderMouseDown" MouseMove="OffsetRenderMouseMove" MouseUp="OffsetRenderMouseUp" />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:PythonButtonElementViewModel}">
|
||||
<hsg3hv:AngleButton Direction="Out" Content="{Binding Name}" Margin="4" Command="{hsv:MethodCommand Execute}" ToolTip="{Binding Tooltip}" />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type hsg3hvmtr:MultiFieldArrayElementViewModel}">
|
||||
<StackPanel Visibility="{Binding Visible, Converter={StaticResource BoolToVisibility}}">
|
||||
<Grid Margin="20,2,2,2" Background="{DynamicResource Backlight}">
|
||||
<TextBox Name="FieldsTextBox" UndoLimit="0" Text="{Binding Content, UpdateSourceTrigger=PropertyChanged}"
|
||||
Background="Transparent"
|
||||
VerticalAlignment="Top"
|
||||
CaretBrush="{DynamicResource Secondary}"
|
||||
AcceptsReturn="True" FontFamily="Consolas">
|
||||
<TextBox.InputBindings>
|
||||
<KeyBinding Modifiers="Ctrl" Key="Z" Command="{Binding Undo}"/>
|
||||
<KeyBinding Modifiers="Ctrl" Key="Y" Command="{Binding Redo}"/>
|
||||
</TextBox.InputBindings>
|
||||
</TextBox>
|
||||
<hsg3hv:AutocompleteOverlay Target="{Binding ElementName=FieldsTextBox}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.Resources>
|
||||
</ItemsControl>
|
||||
<!-- -->
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
|
|
|
|||
|
|
@ -14,11 +14,32 @@ using System.Collections.Generic;
|
|||
using System.Windows.Input;
|
||||
using System.Linq;
|
||||
using HavenSoft.HexManiac.WPF.Resources;
|
||||
using System.Windows.Threading;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace HavenSoft.HexManiac.WPF.Controls;
|
||||
|
||||
// TODO jump button for enums
|
||||
// TODO jump button for bit arrays
|
||||
// TODO jump button behavior
|
||||
// TODO text selection for textboxes
|
||||
// TODO text editing for textboxes
|
||||
// TODO application commands (cut/copy/paste/selectall) for textboxes
|
||||
// TODO keyboard shortcuts cut/copy/paste/selectall for textboxes
|
||||
// TODO home/end/left/right/increment/decrement for textboxes
|
||||
|
||||
// TODO text editing for enums
|
||||
// TODO showing filtered dropdown for enums
|
||||
|
||||
// additional controls:
|
||||
// tuples
|
||||
// calculated fields
|
||||
|
||||
namespace HavenSoft.HexManiac.WPF.Controls;
|
||||
|
||||
public partial class TableGroupPanel : FrameworkElement {
|
||||
private readonly SpriteCache spriteCache = new();
|
||||
private readonly DispatcherTimer timer;
|
||||
private bool isCursorShowing;
|
||||
|
||||
private IArrayElementViewModel keyboardFocusElement, mouseHoverElement;
|
||||
|
||||
|
|
@ -87,6 +108,21 @@ public partial class TableGroupPanel : FrameworkElement {
|
|||
|
||||
#endregion
|
||||
|
||||
private Point cursorPosition = new(double.NaN, double.NaN);
|
||||
public Point CursorPosition {
|
||||
get => cursorPosition;
|
||||
set {
|
||||
cursorPosition = value;
|
||||
if (double.IsNaN(cursorPosition.X)) {
|
||||
timer.Stop();
|
||||
InvalidateVisual();
|
||||
} else {
|
||||
isCursorShowing = true;
|
||||
timer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int FontSize { get; private set; } = 16; // TODO dependency property?
|
||||
|
||||
public TableGroupPanel() {
|
||||
|
|
@ -94,14 +130,16 @@ public partial class TableGroupPanel : FrameworkElement {
|
|||
VerticalAlignment = VerticalAlignment.Top;
|
||||
ClipToBounds = true;
|
||||
Focusable = true;
|
||||
timer = new DispatcherTimer(TimeSpan.FromSeconds(.6), DispatcherPriority.ApplicationIdle, BlinkCursor, Dispatcher);
|
||||
}
|
||||
|
||||
protected override void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e) {
|
||||
base.OnLostKeyboardFocus(e);
|
||||
if ((keyboardFocusElement != null)) {
|
||||
if (keyboardFocusElement != null) {
|
||||
keyboardFocusElement = null;
|
||||
InvalidateVisual();
|
||||
}
|
||||
CursorPosition = new(double.NaN, double.NaN);
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext dc) {
|
||||
|
|
@ -116,6 +154,11 @@ public partial class TableGroupPanel : FrameworkElement {
|
|||
controls[element].Render(context);
|
||||
offset += controls[element].Height;
|
||||
}
|
||||
|
||||
if (isCursorShowing && !double.IsNaN(CursorPosition.X)) {
|
||||
dc.DrawLine(new Pen(Brush(nameof(Theme.Secondary)), 1), CursorPosition, new(cursorPosition.X, cursorPosition.Y + FontSize));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected override Size MeasureOverride(Size availableSize) {
|
||||
|
|
@ -243,6 +286,12 @@ public partial class TableGroupPanel : FrameworkElement {
|
|||
|
||||
return control;
|
||||
}
|
||||
|
||||
private void BlinkCursor(object sender, EventArgs e) {
|
||||
isCursorShowing = !isCursorShowing;
|
||||
InvalidateVisual();
|
||||
}
|
||||
|
||||
/*
|
||||
#region Helpers
|
||||
|
||||
|
|
@ -733,11 +782,13 @@ public record RenderContext(DrawingContext Api) {
|
|||
}
|
||||
}
|
||||
|
||||
public void DrawTextButton(Rect rect, double fontSize, string text, bool isHover) {
|
||||
public void DrawTextButton(Rect rect, double fontSize, string text, bool isHover, bool isEnabled = true) {
|
||||
var border = isHover ? nameof(Theme.Primary) : nameof(Theme.Secondary);
|
||||
var fill = nameof(Theme.Backlight);
|
||||
var textFill = nameof(Theme.Primary);
|
||||
if (!isEnabled) { fill = nameof(Theme.Background); border = nameof(Theme.Secondary); textFill = nameof(Theme.Secondary); }
|
||||
Api.DrawRectangle(Brush(fill), new Pen(Brush(border), 1), rect);
|
||||
var formattedText = FormattedText(text, fontSize, nameof(Theme.Primary));
|
||||
var formattedText = FormattedText(text, fontSize, textFill);
|
||||
if (formattedText.Width > rect.Width - 2) {
|
||||
fontSize *= (rect.Width - 2) / formattedText.Width;
|
||||
formattedText = FormattedText(text, fontSize, nameof(Theme.Primary));
|
||||
|
|
@ -745,6 +796,24 @@ public record RenderContext(DrawingContext Api) {
|
|||
Api.DrawText(formattedText, new(rect.X + rect.Width / 2 - formattedText.Width / 2, rect.Y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a squarish jump button based on the fontSize.
|
||||
/// </summary>
|
||||
public void DrawJumpButton(Point start, bool enabled, bool hover) {
|
||||
int width = CurrentFontSize, height = CurrentFontSize;
|
||||
Api.PushTransform(new TranslateTransform(start.X, start.Y));
|
||||
var background = enabled ? nameof(Theme.Backlight) : nameof(Theme.Background);
|
||||
var border = (hover && enabled) ? nameof(Theme.Primary) : nameof(Theme.Secondary);
|
||||
|
||||
var content = $"M0,0 L {width-4},0 {width},{height/2} {width-4},{height} 0,{height} 4,{height/2} Z";
|
||||
Api.DrawGeometry(Brush(background), new Pen(Brush(border), 1), Geometry.Parse(content));
|
||||
|
||||
content = $"M4,3 L {width-6},3 {width-4},{height/2} {width-6},{height-3} 4,{height-3} 6,{height/2} Z";
|
||||
Api.DrawGeometry(Brush(border), null, Geometry.Parse(content));
|
||||
|
||||
Api.Pop();
|
||||
}
|
||||
|
||||
public static FormattedText FormattedText(string text, double size, string foreground)
|
||||
=> new FormattedText(text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, Consolas, size, Brush(foreground), 96);
|
||||
|
||||
|
|
@ -811,37 +880,88 @@ public record GroupDefaultControl(IArrayElementViewModel Element) : GroupFixedHe
|
|||
}
|
||||
|
||||
public record GroupTextControl(FieldArrayElementViewModel Element) : GroupFixedHeighteControl(), IGroupControl {
|
||||
enum ControlSegment { None, Label, TextBox, Button }
|
||||
private ControlSegment mouseOver;
|
||||
|
||||
public bool IsFocused { get; private set; }
|
||||
|
||||
public void MouseEnter(TableGroupPanel parent, MouseEventArgs e) { }
|
||||
public void MouseDown(TableGroupPanel parent, MouseButtonEventArgs e) {
|
||||
var text = RenderContext.FormattedText(Element.Content, parent.FontSize, null);
|
||||
var labelAndContentWidth = Width - parent.FontSize - 2;
|
||||
var leftEdge = labelAndContentWidth - text.Width - 2;
|
||||
var characterWidth = text.Width / Element.Content.Length;
|
||||
|
||||
var p = e.GetPosition(parent);
|
||||
var index = (p.X - leftEdge) / characterWidth + .5;
|
||||
index = (int)index.LimitToRange(0, Element.Content.Length);
|
||||
parent.CursorPosition = new((int)(leftEdge + index * characterWidth), YOffset + 1);
|
||||
}
|
||||
public void MouseMove(TableGroupPanel parent, MouseEventArgs e) {
|
||||
var p = e.GetPosition(parent);
|
||||
parent.Cursor = p.X > Width / 2 ? Cursors.IBeam : Cursors.Arrow;
|
||||
|
||||
if (e.LeftButton == MouseButtonState.Pressed) {
|
||||
// TODO mouse drag
|
||||
} else {
|
||||
// mouse hover
|
||||
var newHover = CalculateMouseOver(parent, new(p.X, p.Y - YOffset));
|
||||
if ((newHover == ControlSegment.Button) != (mouseOver == ControlSegment.Button)) {
|
||||
mouseOver = newHover;
|
||||
parent.InvalidateVisual(); // redraw for border
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public void MouseUp(TableGroupPanel parent, MouseButtonEventArgs e) {
|
||||
// TODO end of mouse drag
|
||||
// TODO need the ability to actually respond to these application commands
|
||||
if (e.ChangedButton == MouseButton.Right) {
|
||||
parent.ContextMenu = new ContextMenu {
|
||||
Items = {
|
||||
new MenuItem { Command = ApplicationCommands.Cut },
|
||||
new MenuItem { Command = ApplicationCommands.Copy },
|
||||
new MenuItem { Command = ApplicationCommands.Paste },
|
||||
new Separator(),
|
||||
new MenuItem { Command = ApplicationCommands.SelectAll },
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
public void MouseExit(TableGroupPanel parent, MouseEventArgs e) {
|
||||
parent.Cursor = Cursors.Arrow;
|
||||
if (mouseOver == ControlSegment.Button) {
|
||||
mouseOver = ControlSegment.None;
|
||||
parent.InvalidateVisual();
|
||||
}
|
||||
}
|
||||
private ControlSegment CalculateMouseOver(TableGroupPanel parent, Point internalPoint) {
|
||||
var labelAndContentWidth = Width - parent.FontSize;
|
||||
if (internalPoint.X < labelAndContentWidth / 2) return ControlSegment.Label;
|
||||
if (internalPoint.X > labelAndContentWidth) return ControlSegment.Button;
|
||||
return ControlSegment.TextBox;
|
||||
}
|
||||
|
||||
public void Render(RenderContext context) {
|
||||
var topOfText = YOffset;
|
||||
var labelAndContentWidth = Width - context.CurrentFontSize - 2;
|
||||
|
||||
// label
|
||||
context.DrawText(new(2, topOfText + 2), context.CurrentFontSize - 4, Width / 2, Element.Name, Primary);
|
||||
context.DrawText(new(2, topOfText + 2), context.CurrentFontSize - 4, labelAndContentWidth / 2, Element.Name, Primary);
|
||||
|
||||
// box
|
||||
var pen = IsFocused ? context.AccentPen : null;
|
||||
var background = RenderContext.Brush(Backlight);
|
||||
context.Api.DrawRectangle(background, pen, new Rect(Width / 2, YOffset + 1, Width / 2, Height - 2));
|
||||
context.Api.DrawRectangle(background, pen, new Rect(labelAndContentWidth / 2, YOffset + 1, labelAndContentWidth / 2, Height - 2));
|
||||
|
||||
// content
|
||||
var text = RenderContext.FormattedText(Element.Content, context.CurrentFontSize, Primary);
|
||||
var textWidth = text.Width + 2;
|
||||
if (textWidth > Width / 2) textWidth = Width / 2; // TODO crop the text if this happens
|
||||
context.Api.DrawText(text, new(Width - textWidth, topOfText));
|
||||
if (textWidth > labelAndContentWidth / 2) textWidth = labelAndContentWidth / 2; // TODO crop the text if this happens
|
||||
context.Api.DrawText(text, new(labelAndContentWidth - textWidth, topOfText));
|
||||
|
||||
// TODO render the button
|
||||
}
|
||||
|
||||
public void MouseEnter(TableGroupPanel parent, MouseEventArgs e) { }
|
||||
public void MouseDown(TableGroupPanel parent, MouseButtonEventArgs e) { }
|
||||
public void MouseMove(TableGroupPanel parent, MouseEventArgs e) {
|
||||
var p = e.GetPosition(parent);
|
||||
parent.Cursor = p.X > Width / 2 ? Cursors.IBeam : Cursors.Arrow;
|
||||
}
|
||||
public void MouseUp(TableGroupPanel parent, MouseButtonEventArgs e) { }
|
||||
public void MouseExit(TableGroupPanel parent, MouseEventArgs e) {
|
||||
parent.Cursor = Cursors.Arrow;
|
||||
// goto button
|
||||
context.DrawJumpButton(new(Width - context.CurrentFontSize, topOfText + 2), Element.CanAccept(), mouseOver == ControlSegment.Button);
|
||||
}
|
||||
|
||||
public void KeyInput(TableGroupPanel parent, KeyEventArgs e) { }
|
||||
|
|
@ -1043,7 +1163,7 @@ public record GroupPaletteControl(PaletteElementViewModel Element) : GroupFixedH
|
|||
public void TextInput(TableGroupPanel parent, TextCompositionEventArgs e) { }
|
||||
|
||||
private int GetCell(TableGroupPanel parent, double x, double y) {
|
||||
var unitWidth = parent.FontSize / 2 + 4;
|
||||
var unitWidth = parent.FontSize * 2 / 3 + 4;
|
||||
var cellY = (int)((y - YOffset) / unitWidth).LimitToRange(0, Element.Colors.ColorHeight);
|
||||
var cellX = (int)(x / unitWidth).LimitToRange(0,Element.Colors.ColorWidth);
|
||||
return cellY * Element.Colors.ColorWidth + cellX;
|
||||
|
|
@ -1071,7 +1191,14 @@ public record GroupBitArrayControl(BitListArrayElementViewModel Element) : Group
|
|||
hover = newHover;
|
||||
}
|
||||
public void MouseUp(TableGroupPanel parent, MouseButtonEventArgs e) {
|
||||
if (GetBit(e.GetPosition(parent)) == mouseClickElement && mouseClickElement != null) {
|
||||
if (e.ChangedButton == MouseButton.Right) {
|
||||
parent.ContextMenu = new ContextMenu {
|
||||
Items = {
|
||||
new MenuItem { Header = "Select All", Command = Element.SelectAll },
|
||||
new MenuItem { Header = "Unselect All", Command = Element.UnselectAll },
|
||||
},
|
||||
};
|
||||
} else if (GetBit(e.GetPosition(parent)) == mouseClickElement && mouseClickElement != null) {
|
||||
mouseClickElement.IsChecked = !mouseClickElement.IsChecked;
|
||||
}
|
||||
}
|
||||
|
|
@ -1108,7 +1235,6 @@ public record GroupBitArrayControl(BitListArrayElementViewModel Element) : Group
|
|||
}
|
||||
|
||||
public record GroupButtonControl(ButtonArrayElementViewModel Element) : GroupFixedHeighteControl(), IGroupControl {
|
||||
// TODO CanExecute
|
||||
private bool isHover;
|
||||
public void MouseEnter(TableGroupPanel parent, MouseEventArgs e) { isHover = true; parent.InvalidateVisual(); }
|
||||
public void MouseDown(TableGroupPanel parent, MouseButtonEventArgs e) { }
|
||||
|
|
@ -1117,7 +1243,7 @@ public record GroupButtonControl(ButtonArrayElementViewModel Element) : GroupFix
|
|||
public void MouseExit(TableGroupPanel parent, MouseEventArgs e) { isHover = false; parent.InvalidateVisual(); }
|
||||
|
||||
public void Render(RenderContext context) {
|
||||
context.DrawTextButton(new Rect(2, YOffset + 2, Width - 4, Height - 4), context.CurrentFontSize - 2, Element.Text, isHover);
|
||||
context.DrawTextButton(new Rect(2, YOffset + 2, Width - 4, Height - 4), context.CurrentFontSize - 2, Element.Text, isHover, Element.Command.CanExecute(null));
|
||||
}
|
||||
|
||||
public void KeyInput(TableGroupPanel parent, KeyEventArgs e) { }
|
||||
|
|
@ -1125,7 +1251,6 @@ public record GroupButtonControl(ButtonArrayElementViewModel Element) : GroupFix
|
|||
}
|
||||
|
||||
public record GroupPythonButtonControl(PythonButtonElementViewModel Element) : GroupFixedHeighteControl(), IGroupControl {
|
||||
// TODO CanExecute
|
||||
private bool isHover;
|
||||
public void MouseEnter(TableGroupPanel parent, MouseEventArgs e) { isHover = true; parent.InvalidateVisual(); }
|
||||
public void MouseDown(TableGroupPanel parent, MouseButtonEventArgs e) { }
|
||||
|
|
@ -1134,14 +1259,9 @@ public record GroupPythonButtonControl(PythonButtonElementViewModel Element) : G
|
|||
public void MouseExit(TableGroupPanel parent, MouseEventArgs e) { isHover = false; parent.InvalidateVisual(); }
|
||||
|
||||
public void Render(RenderContext context) {
|
||||
context.DrawTextButton(new Rect(2, YOffset + 2, Width - 4, Height - 4), context.CurrentFontSize - 2, Element.Name, isHover);
|
||||
context.DrawTextButton(new Rect(2, YOffset + 2, Width - 4, Height - 4), context.CurrentFontSize - 2, Element.Name, isHover, Element.CanExecute());
|
||||
}
|
||||
|
||||
public void KeyInput(TableGroupPanel parent, KeyEventArgs e) { }
|
||||
public void TextInput(TableGroupPanel parent, TextCompositionEventArgs e) { }
|
||||
}
|
||||
|
||||
// next most important controls:
|
||||
// SpriteIndicatorElementViewModel
|
||||
// tuples
|
||||
// calculated fields
|
||||
|
|
|
|||
|
|
@ -207,6 +207,8 @@ namespace HavenSoft.HexManiac.WPF.Implementations {
|
|||
|
||||
public Task RunBackgroundWork(Action action) => Task.Run(action);
|
||||
|
||||
public IDelayWorkTimer CreateDelayTimer() => new DelayWorkTimer();
|
||||
|
||||
#endregion
|
||||
|
||||
public string RequestNewName(string currentName, string extensionDescription = null, params string[] extensionOptions) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user