Implement a lot of other functionality. Still very experimental at this stage

rabi_display
wcko87 8 years ago
parent d4fa840616
commit 1f4fd18662
  1. 120
      rabi_splitter_WPF/MainContext.cs
  2. 30
      rabi_splitter_WPF/MainWindow.xaml
  3. 2
      rabi_splitter_WPF/MainWindow.xaml.cs
  4. 156
      rabi_splitter_WPF/MemorySnapshot.cs
  5. 66
      rabi_splitter_WPF/RabiEnums.cs
  6. 18
      rabi_splitter_WPF/RabiGameState.cs
  7. 150
      rabi_splitter_WPF/RabiRibiDisplay.cs
  8. 85
      rabi_splitter_WPF/StaticData.cs

@ -31,6 +31,55 @@ namespace rabi_splitter_WPF
#endregion #endregion
} }
public class EntityStatsData : INotifyPropertyChanged
{
private int _addr;
private int _intval;
private float _floatval;
public int Addr
{
get { return _addr; }
set
{
if (value == _addr) return;
_addr = value;
OnPropertyChanged(nameof(Addr));
}
}
public int IntVal
{
get { return _intval; }
set
{
if (value == _intval) return;
_intval = value;
OnPropertyChanged(nameof(IntVal));
}
}
public float FloatVal
{
get { return _floatval; }
set
{
if (value == _floatval) return;
_floatval = value;
OnPropertyChanged(nameof(FloatVal));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class BossData:INotifyPropertyChanged public class BossData:INotifyPropertyChanged
{ {
private int _bossIdx; private int _bossIdx;
@ -81,14 +130,27 @@ namespace rabi_splitter_WPF
class DebugContext : INotifyPropertyChanged class DebugContext : INotifyPropertyChanged
{ {
private int _entityAnalysisIndex;
private bool _bossEvent; private bool _bossEvent;
private string _debugLog; private string _debugLog;
public ObservableCollection<BossData> BossList = new ObservableCollection<BossData>(); public ObservableCollection<BossData> BossList = new ObservableCollection<BossData>();
public ObservableCollection<EntityStatsData> EntityStatsListData = new ObservableCollection<EntityStatsData>();
public int targetEntityListSize;
public ObservableCollection<EntityStatsData> EntityStatsListView
{
get
{
return EntityStatsListData;
}
}
public DebugContext() public DebugContext()
{ {
BossList=new ObservableCollection<BossData>(); this.EntityAnalysisIndex = 0;
BossList =new ObservableCollection<BossData>();
for (int i = 0; i < 50; i++) for (int i = 0; i < 50; i++)
{ {
BossList.Add(new BossData() BossList.Add(new BossData()
@ -96,6 +158,22 @@ namespace rabi_splitter_WPF
BossIdx = i BossIdx = i
}); });
} }
while (EntityStatsListData.Count < 449)
{
EntityStatsListData.Add(new EntityStatsData() { Addr = EntityStatsListData.Count * 4 });
}
}
public int EntityAnalysisIndex
{
get { return _entityAnalysisIndex; }
set
{
if (value == _entityAnalysisIndex) return;
_entityAnalysisIndex = value;
OnPropertyChanged(nameof(EntityAnalysisIndex));
}
} }
public bool BossEvent public bool BossEvent
@ -138,7 +216,7 @@ namespace rabi_splitter_WPF
{ {
public string oldtitle; public string oldtitle;
public int veridx; public int veridx;
private int _serverPort; private int _serverPort;
private string _gameVer; private string _gameVer;
private string _gameMusic; private string _gameMusic;
@ -159,6 +237,9 @@ namespace rabi_splitter_WPF
private string _text13; private string _text13;
private string _text14; private string _text14;
private string _text15; private string _text15;
private string _text16;
private string _text17;
private string _text18;
public string Text1 public string Text1
{ {
@ -324,7 +405,40 @@ namespace rabi_splitter_WPF
OnPropertyChanged(nameof(Text15)); OnPropertyChanged(nameof(Text15));
} }
} }
public string Text16
{
get { return _text16; }
set
{
if (value == _text16) return;
_text16 = value;
OnPropertyChanged(nameof(Text16));
}
}
public string Text17
{
get { return _text17; }
set
{
if (value == _text17) return;
_text17 = value;
OnPropertyChanged(nameof(Text17));
}
}
public string Text18
{
get { return _text18; }
set
{
if (value == _text18) return;
_text18 = value;
OnPropertyChanged(nameof(Text18));
}
}
public int ServerPort public int ServerPort
{ {
get { return _serverPort; } get { return _serverPort; }

@ -16,7 +16,7 @@
<TextBlock Text="GitHub" MouseUp="TextBlock_MouseUp" Cursor="Hand" Foreground="Blue" TextDecorations="Underline" VerticalAlignment="Top" /> <TextBlock Text="GitHub" MouseUp="TextBlock_MouseUp" Cursor="Hand" Foreground="Blue" TextDecorations="Underline" VerticalAlignment="Top" />
</StackPanel> </StackPanel>
<DockPanel Margin="5" DockPanel.Dock="Top"> <DockPanel Margin="5" DockPanel.Dock="Top">
<StackPanel Margin="0,0,100,0" Width="356"> <StackPanel Margin="0,0,0,0" Width="500">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,0,0,3"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,0,0,3">
<TextBlock TextWrapping="Wrap" Text="LiveSplit Server Port" VerticalAlignment="Center" FontSize="16" Margin="0,0,10,0"/> <TextBlock TextWrapping="Wrap" Text="LiveSplit Server Port" VerticalAlignment="Center" FontSize="16" Margin="0,0,10,0"/>
<TextBox TextWrapping="Wrap" Text="{Binding ServerPort}" VerticalAlignment="Center" HorizontalAlignment="Center" VerticalContentAlignment="Center" FontSize="16" Padding="6,0"/> <TextBox TextWrapping="Wrap" Text="{Binding ServerPort}" VerticalAlignment="Center" HorizontalAlignment="Center" VerticalContentAlignment="Center" FontSize="16" Padding="6,0"/>
@ -26,14 +26,14 @@
<TextBlock TextWrapping="Wrap" Text="Rabi-Ribi Status:" FontSize="16"/> <TextBlock TextWrapping="Wrap" Text="Rabi-Ribi Status:" FontSize="16"/>
<TextBlock TextWrapping="Wrap" Text="{Binding GameVer}" FontSize="16"/> <TextBlock TextWrapping="Wrap" Text="{Binding GameVer}" FontSize="16"/>
</StackPanel> </StackPanel>
<Grid Height="280"> <Grid Height="500">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="3*"/> <RowDefinition Height="3*"/>
<RowDefinition Height="3*"/> <RowDefinition Height="3*"/>
<RowDefinition Height="3*"/> <RowDefinition Height="3*"/>
<RowDefinition Height="3*"/> <RowDefinition Height="3*"/>
<RowDefinition Height="3*"/> <RowDefinition Height="3*"/>
<RowDefinition Height="3*"/> <RowDefinition Height="15*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/>
@ -56,12 +56,30 @@
<TextBlock Grid.Row="3" Grid.Column="1" TextWrapping="Wrap" Text="{Binding Text11}" FontSize="16"/> <TextBlock Grid.Row="3" Grid.Column="1" TextWrapping="Wrap" Text="{Binding Text11}" FontSize="16"/>
<TextBlock Grid.Row="3" Grid.Column="2" TextWrapping="Wrap" Text="{Binding Text12}" FontSize="16"/> <TextBlock Grid.Row="3" Grid.Column="2" TextWrapping="Wrap" Text="{Binding Text12}" FontSize="16"/>
<TextBlock Grid.Row="4" Grid.Column="0" TextWrapping="Wrap" Text="{Binding Text13}" FontSize="16"/> <TextBlock Grid.Row="4" Grid.ColumnSpan="2" Grid.Column="0" TextWrapping="Wrap" Text="{Binding Text13}" FontSize="16"/>
<TextBlock Grid.Row="4" Grid.Column="1" TextWrapping="Wrap" Text="{Binding Text14}" FontSize="16"/> <TextBlock Grid.Row="4" Grid.Column="2" TextWrapping="Wrap" Text="{Binding Text14}" FontSize="16"/>
<TextBlock Grid.Row="4" Grid.Column="2" TextWrapping="Wrap" Text="{Binding Text15}" FontSize="16"/>
<ScrollViewer Grid.Row="5" Grid.ColumnSpan="2" Grid.Column="0" >
<TextBlock TextWrapping="Wrap" Text="{Binding Text16}" FontSize="16"/>
</ScrollViewer>
<TextBlock Grid.Row="5" Grid.Column="2" TextWrapping="Wrap" Text="{Binding Text17}" FontSize="16"/>
</Grid> </Grid>
</StackPanel> </StackPanel>
<DockPanel x:Name="EntityDataPanel" d:DataContext="{d:DesignData local:DebugContext}">
<TextBox Width="100" DockPanel.Dock="Top" TextWrapping="Wrap" Text="{Binding EntityAnalysisIndex}" VerticalAlignment="Center" HorizontalAlignment="Center" VerticalContentAlignment="Center" FontSize="16" Padding="0,0"/>
<DataGrid Width="250" x:Name="EntityStats" ItemsSource="{Binding EntityStatsListView}" CanUserAddRows="False" IsReadOnly="True" AutoGenerateColumns="False" SelectionUnit="FullRow"
d:DataContext="{d:DesignData local:EntityStatsData}">
<DataGrid.Columns>
<DataGridTextColumn Width="2*" Binding="{Binding Addr}" ClipboardContentBinding="{Binding Addr}" Header="Addr"/>
<DataGridTextColumn Width="3*" Binding="{Binding IntVal}" ClipboardContentBinding="{Binding IntVal}" Header="int"/>
<DataGridTextColumn Width="3*" Binding="{Binding FloatVal}" ClipboardContentBinding="{Binding FloatVal}" Header="float"/>
</DataGrid.Columns>
</DataGrid>
<!--<ScrollViewer Width="250">
<TextBlock TextWrapping="Wrap" Text="{Binding Text18}" FontSize="16"/>
</ScrollViewer>-->
</DockPanel>
<DockPanel> <DockPanel>
<TextBlock TextWrapping="Wrap" Text="debug area" Height="15.24" Width="352.251" DockPanel.Dock="Top"/> <TextBlock TextWrapping="Wrap" Text="debug area" Height="15.24" Width="352.251" DockPanel.Dock="Top"/>
<CheckBox x:Name="BossEventDebug" IsEnabled="False" Content="(DEBUG)BOSS event tracked" FontSize="15" VerticalAlignment="Top" HorizontalAlignment="Left" IsChecked="{Binding BossEvent, Mode=TwoWay}" DockPanel.Dock="Top"/> <CheckBox x:Name="BossEventDebug" IsEnabled="False" Content="(DEBUG)BOSS event tracked" FontSize="15" VerticalAlignment="Top" HorizontalAlignment="Left" IsChecked="{Binding BossEvent, Mode=TwoWay}" DockPanel.Dock="Top"/>

@ -113,6 +113,8 @@ namespace rabi_splitter_WPF
this.DataContext = mainContext; this.DataContext = mainContext;
DebugPanel.DataContext = debugContext; DebugPanel.DataContext = debugContext;
this.Grid.ItemsSource = debugContext.BossList; this.Grid.ItemsSource = debugContext.BossList;
EntityDataPanel.DataContext = debugContext;
this.EntityStats.ItemsSource = debugContext.EntityStatsListView;
BossEventDebug.DataContext = debugContext; BossEventDebug.DataContext = debugContext;
rabiRibiDisplay = new RabiRibiDisplay(mainContext, debugContext, this); rabiRibiDisplay = new RabiRibiDisplay(mainContext, debugContext, this);
memoryThread = new Thread(() => memoryThread = new Thread(() =>

@ -6,6 +6,15 @@ using System.Text;
namespace rabi_splitter_WPF namespace rabi_splitter_WPF
{ {
struct BossStats
{
public int entityArrayIndex;
public int id;
public Boss type;
public int hp;
public int maxHp;
}
class MemorySnapshot class MemorySnapshot
{ {
public readonly int t_playtime; public readonly int t_playtime;
@ -17,7 +26,9 @@ namespace rabi_splitter_WPF
public readonly int hp; public readonly int hp;
public readonly int maxhp; public readonly int maxhp;
public readonly int entityArrayPtr; public readonly int currentSprite;
public readonly int actionFrame;
public readonly int animationFrame;
public readonly float amulet; public readonly float amulet;
public readonly int boost; public readonly int boost;
@ -26,7 +37,24 @@ namespace rabi_splitter_WPF
public readonly float px; public readonly float px;
public readonly float py; public readonly float py;
public readonly int entityArrayPtr;
public readonly int entityArraySize;
public readonly int nActiveEntities;
public readonly List<BossStats> bossList;
public readonly int carrotXp;
public readonly int hammerXp;
public readonly int ribbonXp;
public readonly float itemPercent;
public readonly int nAttackUps;
public readonly int nHpUps;
public readonly int nManaUps;
public readonly int nPackUps;
public readonly int nRegenUps;
public MemorySnapshot(Process process, int veridx) public MemorySnapshot(Process process, int veridx)
{ {
t_playtime = MemoryHelper.GetMemoryValue<int>(process, StaticData.IGTAddr[veridx]); t_playtime = MemoryHelper.GetMemoryValue<int>(process, StaticData.IGTAddr[veridx]);
@ -37,11 +65,26 @@ namespace rabi_splitter_WPF
musicid = MemoryHelper.GetMemoryValue<int>(process, StaticData.MusicAddr[veridx]); musicid = MemoryHelper.GetMemoryValue<int>(process, StaticData.MusicAddr[veridx]);
money = MemoryHelper.GetMemoryValue<int>(process, StaticData.MoneyAddress[veridx]); money = MemoryHelper.GetMemoryValue<int>(process, StaticData.MoneyAddress[veridx]);
carrotXp = MemoryHelper.GetMemoryValue<int>(process, 0xD654BC);
hammerXp = MemoryHelper.GetMemoryValue<int>(process, 0xD654B4);
ribbonXp = MemoryHelper.GetMemoryValue<int>(process, 0xD654B8);
itemPercent = MemoryHelper.GetMemoryValue<int>(process, 0xA730E8);
nAttackUps = countItems(process, 0xD6352C, 0xD63628);
nHpUps = countItems(process, 0xD6342C, 0xD63528);
nManaUps = countItems(process, 0xD6362C, 0xD63728);
nPackUps = countItems(process, 0xD6382C, 0xD63928);
nRegenUps = countItems(process, 0xD6372C, 0xD63828);
entityArrayPtr = MemoryHelper.GetMemoryValue<int>(process, StaticData.EnenyPtrAddr[veridx]); entityArrayPtr = MemoryHelper.GetMemoryValue<int>(process, StaticData.EnenyPtrAddr[veridx]);
hp = MemoryHelper.GetMemoryValue<int>(process, entityArrayPtr + 0x4D8, false); hp = MemoryHelper.GetMemoryValue<int>(process, entityArrayPtr + 0x4D8, false);
maxhp = MemoryHelper.GetMemoryValue<int>(process, entityArrayPtr + 0x4E8, false); maxhp = MemoryHelper.GetMemoryValue<int>(process, entityArrayPtr + 0x4E8, false);
currentSprite = MemoryHelper.GetMemoryValue<int>(process, entityArrayPtr + 0x654, false);
actionFrame = MemoryHelper.GetMemoryValue<int>(process, entityArrayPtr + 0x660, false);
animationFrame = MemoryHelper.GetMemoryValue<int>(process, entityArrayPtr + 0x67c, false);
amulet = MemoryHelper.GetMemoryValue<float>(process, entityArrayPtr + 0x52C, false); amulet = MemoryHelper.GetMemoryValue<float>(process, entityArrayPtr + 0x52C, false);
boost = MemoryHelper.GetMemoryValue<int>(process, entityArrayPtr + 0x5DC, false); boost = MemoryHelper.GetMemoryValue<int>(process, entityArrayPtr + 0x5DC, false);
mana = MemoryHelper.GetMemoryValue<float>(process, entityArrayPtr + 0x6B8, false); mana = MemoryHelper.GetMemoryValue<float>(process, entityArrayPtr + 0x6B8, false);
@ -49,6 +92,57 @@ namespace rabi_splitter_WPF
px = MemoryHelper.GetMemoryValue<float>(process, entityArrayPtr + 0xC, false); px = MemoryHelper.GetMemoryValue<float>(process, entityArrayPtr + 0xC, false);
py = MemoryHelper.GetMemoryValue<float>(process, entityArrayPtr + 0x10, false); py = MemoryHelper.GetMemoryValue<float>(process, entityArrayPtr + 0x10, false);
// Read Entity Array and Search for boss data
bossList = new List<BossStats>();
nActiveEntities = 0;
entityArraySize = 4;
int entitySize = StaticData.EnenyEntitySize[veridx];
int currArrayPtr = entityArrayPtr + entitySize * 4;
for (int i=0; i<500; ++i) {
// (Hard limit of reading 500 entries)
int entityId = MemoryHelper.GetMemoryValue<int>(process,
currArrayPtr + StaticData.EnenyEnitiyIDOffset[veridx], false);
int entityMaxHp = MemoryHelper.GetMemoryValue<int>(process,
currArrayPtr + StaticData.EnenyEnitiyMaxHPOffset[veridx], false);
if (entityId == 0 && entityMaxHp == 0) break;
int activeFlag = MemoryHelper.GetMemoryValue<int>(process,
currArrayPtr + StaticData.EnenyEnitiyIsActiveOffset[veridx], false);
int animationState = MemoryHelper.GetMemoryValue<int>(process,
currArrayPtr + StaticData.EnenyEnitiyAnimationOffset[veridx], false);
bool isAlive = activeFlag == 1 && animationState >= 0;
if (isAlive && StaticData.IsBoss(entityId))
{
BossStats boss;
boss.entityArrayIndex = entityArraySize;
boss.id = entityId;
boss.hp = MemoryHelper.GetMemoryValue<int>(process, currArrayPtr + StaticData.EnenyEnitiyHPOffset[veridx], false);
boss.type = StaticData.GetBoss(entityId).Value;
boss.maxHp = entityMaxHp;
bossList.Add(boss);
}
currArrayPtr += entitySize;
if (isAlive) ++nActiveEntities;
++entityArraySize;
}
}
private int countItems(Process process, int addrFirst, int addrLast)
{
int count = 0;
for (int addr = addrFirst; addr <= addrLast; ++addr)
{
count += MemoryHelper.GetMemoryValue<int>(process, addr) == 1 ? 1 : 0;
}
return count;
} }
public bool CurrentMapIs(Map? map) public bool CurrentMapIs(Map? map)
@ -60,5 +154,63 @@ namespace rabi_splitter_WPF
{ {
return StaticData.GetMusic(musicid) == music; return StaticData.GetMusic(musicid) == music;
} }
public string GetCurrentAction()
{
if (actionFrame == 0) return "None";
int actionType = actionFrame / 100;
switch (actionType)
{
case 1: return "Carrot Bomb";
case 2: return "Bunny Whirl";
case 4: return "Hammer Roll";
case 7: return "Hammer Combo 1";
case 8: return "Hammer Combo 2";
case 0: return "Hammer Combo 3";
case 9: return "Hammer Combo 4";
case 10: return "Hammer Drill";
case 13: return "Air Dash";
case 14: return "Down Drill";
case 100: return "Up Drill";
case 110: return "Walking Hammer";
case 120: return "Bunny Strike";
case 150: return "Amulet";
default: return "Unknown";
}
}
public bool IsDeathSprite()
{
return currentSprite == 31;
}
public string GetCurrentSprite()
{
switch (currentSprite)
{
case 0: return "Idle";
case 1: return "Walking";
case 2: return "Jump";
case 3: return "Air Jump";
case 4: return "Falling";
case 5: return "Slide";
case 6: return "Carrot Bomb";
case 7: return "Hurt";
case 9: return "Bunny Whirl";
case 15: return "Hammer Roll";
case 20: return "Hammer Combo 1/4";
case 21: return "Hammer Combo 2";
case 10: return "Hammer Combo 3";
case 22: return "Hammer Drill";
case 23: return "Air Dash";
case 24: return "Down Drill";
case 25: return "Up Drill";
case 26: return "Walking Hammer";
case 27: return "Amulet";
case 31: return "Death";
case 32: return "Bunny Strike";
default: return "Unknown";
}
}
} }
} }

@ -14,6 +14,7 @@ namespace rabi_splitter_WPF
Ribbon, Ribbon,
Cocoa2, Cocoa2,
Cicini, Cicini,
Cicini2,
Saya, Saya,
Syaro, Syaro,
Pandora, Pandora,
@ -25,13 +26,17 @@ namespace rabi_splitter_WPF
Vanilla, Vanilla,
Chocolate, Chocolate,
IllusionAlius, IllusionAlius,
PinkKotri, Kotri,
Noah1, Noah1,
Irisu, Irisu,
Miriam, Miriam,
Miru, Miru,
Noah3, Noah3,
KekeBunny, KekeBunny,
Cats,
BigBox,
RainbowMaid,
TreasureCrystal,
} }
public enum Map public enum Map
@ -130,19 +135,30 @@ namespace rabi_splitter_WPF
} }
} }
// A set of (exp, string)
public class ExpDescriptions : List<Tuple<int, string, string>>
{
public void Add(int exp, string shortDescript, string description)
{
Add(new Tuple<int, string, string>(exp, shortDescript, description));
}
}
public static partial class StaticData { public static partial class StaticData {
public static readonly Dictionary<int, Boss> _getBoss; private static readonly Dictionary<int, Boss> _getBoss;
public static readonly Dictionary<int, string> _getBossName; private static readonly Dictionary<int, string> _getBossName;
public static readonly Dictionary<Boss, string> _getBossFromType; private static readonly Dictionary<Boss, string> _getBossFromType;
public static readonly Map[] _getMap; private static readonly Map[] _getMap;
public static readonly string[] _getMapName; private static readonly string[] _getMapName;
public static readonly Dictionary<Map, string> _getMapFromType; private static readonly Dictionary<Map, string> _getMapFromType;
public static readonly Music[] _getMusic; private static readonly Music[] _getMusic;
public static readonly string[] _getMusicName; private static readonly string[] _getMusicName;
public static readonly Dictionary<Music, string> _getMusicFromType; private static readonly Dictionary<Music, string> _getMusicFromType;
private static readonly bool[] _isBossMusic;
static StaticData() static StaticData()
{ {
@ -157,6 +173,8 @@ namespace rabi_splitter_WPF
_getMusic = MusicList.Select(t => t.Item1).ToArray(); _getMusic = MusicList.Select(t => t.Item1).ToArray();
_getMusicName = MusicList.Select(t => t.Item2).ToArray(); _getMusicName = MusicList.Select(t => t.Item2).ToArray();
_getMusicFromType = MusicList.ToDictionary(t => t.Item1, t => t.Item2); _getMusicFromType = MusicList.ToDictionary(t => t.Item1, t => t.Item2);
_isBossMusic = Enumerable.Range(0, _getMusic.Length).Select(i => BossMusics.Contains(_getMusic[i])).ToArray();
} }
public static Boss? GetBoss(int id) { public static Boss? GetBoss(int id) {
@ -177,6 +195,11 @@ namespace rabi_splitter_WPF
return ""; return "";
} }
public static bool IsBoss(int id)
{
return GetBoss(id).HasValue;
}
public static Map? GetMap(int id) { public static Map? GetMap(int id) {
if (0 <= id && id < _getMap.Length) return _getMap[id]; if (0 <= id && id < _getMap.Length) return _getMap[id];
return null; return null;
@ -208,5 +231,28 @@ namespace rabi_splitter_WPF
if (music.HasValue && _getMusicFromType.TryGetValue(music.Value, out value)) return value; if (music.HasValue && _getMusicFromType.TryGetValue(music.Value, out value)) return value;
return ""; return "";
} }
public static bool IsBossMusic(int musicId)
{
return musicId < _isBossMusic.Length && _isBossMusic[musicId];
}
public static Tuple<int, string, string> GetNextHammerLevel(int exp) {
int index = 0;
while (index < HammerLevels.Count && exp >= HammerLevels[index].Item1) ++index;
return index < HammerLevels.Count ? HammerLevels[index] : null;
}
public static Tuple<int, string, string> GetNextRibbonLevel(int exp) {
int index = 0;
while (index < RibbonLevels.Count && exp >= RibbonLevels[index].Item1) ++index;
return index < RibbonLevels.Count ? RibbonLevels[index] : null;
}
public static Tuple<int, string, string> GetNextCarrotLevel(int exp) {
int index = 0;
while (index < CarrotLevels.Count && exp >= CarrotLevels[index].Item1) ++index;
return index < CarrotLevels.Count ? CarrotLevels[index] : null;
}
} }
} }

@ -7,6 +7,7 @@ namespace rabi_splitter_WPF
{ {
enum GameActivity enum GameActivity
{ {
STARTING,
WALKING, WALKING,
BOSS_BATTLE, BOSS_BATTLE,
} }
@ -16,12 +17,27 @@ namespace rabi_splitter_WPF
public int nRestarts; public int nRestarts;
public int nDeaths; public int nDeaths;
public int nRestartsAlt;
public int nDeathsAlt;
public GameActivity currentActivity; public GameActivity currentActivity;
public int currentBoss; public int currentBoss;
public int lastNonZeroPlayTime = -1;
public RabiGameState() public RabiGameState()
{ {
currentActivity = GameActivity.WALKING; currentActivity = GameActivity.STARTING;
}
public bool CurrentActivityIs(GameActivity gameActivity)
{
return currentActivity == gameActivity;
}
public bool IsGameStarted()
{
return !CurrentActivityIs(GameActivity.STARTING);
} }

@ -23,15 +23,12 @@ namespace rabi_splitter_WPF
private MemorySnapshot prevSnapshot; private MemorySnapshot prevSnapshot;
private MemorySnapshot snapshot; private MemorySnapshot snapshot;
public int previousBlackness = -1; private string[] datastrings;
public int lastplaytime = 0;
public bool bossbattle;
public List<int> lastbosslist;
public DateTime LastTMAddTime;
public RabiRibiDisplay(MainContext mainContext, DebugContext debugContext, MainWindow mainWindow) public RabiRibiDisplay(MainContext mainContext, DebugContext debugContext, MainWindow mainWindow)
{ {
this.datastrings = new string[StaticData.EnenyEntitySize[mainContext.veridx]];
this.mainContext = mainContext; this.mainContext = mainContext;
this.debugContext = debugContext; this.debugContext = debugContext;
this.mainWindow = mainWindow; this.mainWindow = mainWindow;
@ -43,45 +40,67 @@ namespace rabi_splitter_WPF
// Snapshot Game Memory // Snapshot Game Memory
snapshot = new MemorySnapshot(process, mainContext.veridx); snapshot = new MemorySnapshot(process, mainContext.veridx);
mainContext.Text1 = "Music: " + StaticData.GetMusicName(snapshot.musicid); #region Game State Machine
mainContext.Text2 = "Map: " + StaticData.GetMapName(snapshot.mapid);
mainContext.Text3 = gameState == null ? "" : ("Deaths: " + gameState.nDeaths + "\n" + "Resets: " + gameState.nRestarts);
mainContext.Text4 = "HP: " + snapshot.hp;
mainContext.Text5 = "MaxHP: " + snapshot.maxhp;
mainContext.Text6 = "Amulet: " + snapshot.amulet; if (gameState.CurrentActivityIs(GameActivity.STARTING)) {
mainContext.Text7 = "Boost: " + snapshot.boost; // Detect start game
mainContext.Text8 = "Mana: " + snapshot.mana; if (snapshot.playtime < 200)
mainContext.Text9 = "Stamina: " + snapshot.stamina; {
gameState.currentActivity = GameActivity.WALKING;
mainContext.Text10 = "x: " + snapshot.px; DebugLog("IGT start?");
mainContext.Text11 = "y: " + snapshot.py; }
} else {
if (MusicChanged())
{
if (StaticData.IsBossMusic(snapshot.musicid))
{
gameState.currentActivity = GameActivity.BOSS_BATTLE;
}
else
{
gameState.currentActivity = GameActivity.WALKING;
}
}
}
#endregion
#region Detect Reload #region Detect Reload
bool reloaded = false; bool reloaded = (prevSnapshot != null) && (snapshot.playtime < prevSnapshot.playtime);
if (prevSnapshot != null) { if (gameState.IsGameStarted() && snapshot.playtime > 0)
reloaded = snapshot.playtime != 0 && snapshot.playtime < prevSnapshot.playtime; {
if (reloaded) if (snapshot.playtime < gameState.lastNonZeroPlayTime)
{ {
if (InGame()) if (InGame())
{ {
gameState.nRestarts++; gameState.nRestarts++;
} }
DebugLog("Reload Game!"); DebugLog("Reload Game! " + snapshot.playtime + " <- " + gameState.lastNonZeroPlayTime);
}
gameState.lastNonZeroPlayTime = snapshot.playtime;
}
if (gameState.IsGameStarted() && prevSnapshot != null)
{
// Issue: This sometimes detects warps as resets too.
if (snapshot.animationFrame < prevSnapshot.animationFrame)
{
if (InGame())
{
gameState.nRestartsAlt++;
}
DebugLog("Reload Game (Alt)!");
} }
} }
#endregion #endregion
#region Detect Death #region Detect Death
bool died = false;
if (prevSnapshot != null) if (prevSnapshot != null)
{ {
died = snapshot.hp == 0 && prevSnapshot.hp > 0; if (snapshot.hp == 0 && prevSnapshot.hp > 0)
if (died)
{ {
if (InGame()) if (InGame())
{ {
@ -90,11 +109,23 @@ namespace rabi_splitter_WPF
DebugLog("Death!"); DebugLog("Death!");
} }
} }
if (prevSnapshot != null)
{
if (snapshot.IsDeathSprite() && !prevSnapshot.IsDeathSprite())
{
if (InGame())
{
gameState.nDeathsAlt++;
}
DebugLog("Death (Alt)!");
}
}
#endregion #endregion
#region Detect Start Game #region Detect Start Game
if (prevSnapshot != null && (snapshot.CurrentMusicIs(Music.MAIN_MENU) || snapshot.CurrentMusicIs(Music.ARTBOOK_INTRO)) if (prevSnapshot != null && (snapshot.CurrentMusicIs(Music.MAIN_MENU) || snapshot.CurrentMusicIs(Music.ARTBOOK_INTRO))
&& prevSnapshot.blackness == 0 && snapshot.blackness >= 100000) && prevSnapshot.blackness == 0 && snapshot.blackness >= 100000)
{ {
@ -110,14 +141,43 @@ namespace rabi_splitter_WPF
DebugLog("newmap: " + snapshot.mapid + ":" + StaticData.GetMapName(snapshot.mapid)); DebugLog("newmap: " + snapshot.mapid + ":" + StaticData.GetMapName(snapshot.mapid));
} }
mainContext.Text1 = "Music: " + StaticData.GetMusicName(snapshot.musicid);
mainContext.Text2 = "Map: " + StaticData.GetMapName(snapshot.mapid);
mainContext.Text3 = gameState == null ? "" : ("Deaths: " + gameState.nDeaths// + " [" + gameState.nDeathsAlt + "]"
+ "\n" + "Resets: " + gameState.nRestarts);// + " [" + gameState.nRestartsAlt + "]");
#region checkTM mainContext.Text4 = "HP: " + snapshot.hp + " / " + snapshot.maxhp;
mainContext.Text5 = "Amulet: " + snapshot.amulet + "\n" + "Boost: " + snapshot.boost;
mainContext.Text6 = "MP: " + snapshot.mana + "\n" + "SP: " + snapshot.stamina;
var nextHammer = StaticData.GetNextHammerLevel(snapshot.hammerXp);
var nextRibbon = StaticData.GetNextRibbonLevel(snapshot.ribbonXp);
var nextCarrot = StaticData.GetNextCarrotLevel(snapshot.carrotXp);
mainContext.Text7 = "Hammer: " + snapshot.hammerXp + (nextHammer == null ? "" : ("/" + nextHammer.Item1 + "\n" + "NEXT: " + nextHammer.Item2));
mainContext.Text8 = "Ribbon: " + snapshot.ribbonXp + (nextRibbon == null ? "" : ("/" + nextRibbon.Item1 + "\n" + "NEXT: " + nextRibbon.Item2));
mainContext.Text9 = "Carrot: " + snapshot.carrotXp + (nextCarrot == null ? "" : ("/" + nextCarrot.Item1 + "\n" + "NEXT: " + nextCarrot.Item2));
mainContext.Text10 = "x: " + snapshot.px + "\n" + "y: " + snapshot.py;
mainContext.Text11 = "[A/H/M/P/R] ups:\n" + snapshot.nAttackUps + "/" + snapshot.nHpUps + "/" + snapshot.nManaUps + "/" + snapshot.nPackUps + "/" + snapshot.nRegenUps;
#endregion
mainContext.Text12 = "Entities: " + snapshot.entityArraySize + "\n" + "Active: " + snapshot.nActiveEntities;
mainContext.Text13 = "Sprite: " + snapshot.GetCurrentSprite() + "\n" + "Action: " + snapshot.GetCurrentAction();
mainContext.Text14 = "AnimFrame: " + snapshot.animationFrame;
{
string bosstext = "Boss Fight: " + (gameState.currentActivity == GameActivity.BOSS_BATTLE) + "\n";
bosstext += "Bosses: " + snapshot.bossList.Count + "\n";
foreach (var boss in snapshot.bossList)
{
bosstext += "[" + boss.entityArrayIndex + "] " + StaticData.GetBossName(boss.id) + ": " + boss.hp + "/" + boss.maxHp + "\n";
}
mainContext.Text16 = bosstext;
}
UpdateDebugArea(process); UpdateDebugArea(process);
UpdateEntityData(process);
prevSnapshot = snapshot; prevSnapshot = snapshot;
} }
@ -148,6 +208,30 @@ namespace rabi_splitter_WPF
{ {
return MusicChanged() && snapshot.CurrentMusicIs(music); return MusicChanged() && snapshot.CurrentMusicIs(music);
} }
private void UpdateEntityData(Process process)
{
// Read entire entity data for specific entity
{
var entityStatsList = debugContext.EntityStatsListData;
int entitySize = StaticData.EnenyEntitySize[mainContext.veridx];
int baseArrayPtr = snapshot.entityArrayPtr + entitySize * debugContext.EntityAnalysisIndex;
int[] entitydataint = new int[entitySize / 4];
float[] entitydatafloat = new float[entitySize / 4];
debugContext.targetEntityListSize = entitySize / 4;
int length = Math.Min(entitySize, entityStatsList.Count * 4);
for (int i = 0; i < length; i += 4)
{
int index = i / 4;
int value_int = MemoryHelper.GetMemoryValue<int>(process, baseArrayPtr + i, false);
float value_float = MemoryHelper.GetMemoryValue<float>(process, baseArrayPtr + i, false);
entityStatsList[index].IntVal = value_int;
entityStatsList[index].FloatVal = value_float;
}
}
}
private void UpdateDebugArea(Process process) private void UpdateDebugArea(Process process)
{ {
@ -165,7 +249,7 @@ namespace rabi_splitter_WPF
ptr += StaticData.EnenyEntitySize[mainContext.veridx]; ptr += StaticData.EnenyEntitySize[mainContext.veridx];
} }
debugContext.BossEvent = bossbattle; debugContext.BossEvent = gameState.currentActivity == GameActivity.BOSS_BATTLE;
} }
private void DebugLog(string log) private void DebugLog(string log)

@ -11,8 +11,9 @@ namespace rabi_splitter_WPF
{1012, Boss.Ashuri, "Ashuri"}, {1012, Boss.Ashuri, "Ashuri"},
{1013, Boss.Rita, "Rita"}, {1013, Boss.Rita, "Rita"},
{1014, Boss.Ribbon, "Ribbon"}, {1014, Boss.Ribbon, "Ribbon"},
{1015, Boss.Cocoa2, "Cocoa"}, {1015, Boss.Cocoa2, "Cocoa2"},
{1018, Boss.Cicini, "Cicini"}, {1016, Boss.Cicini, "Cicini"},
{1018, Boss.Cicini2, "Cicini2"},
{1020, Boss.Saya, "Saya"}, {1020, Boss.Saya, "Saya"},
{1021, Boss.Syaro, "Syaro"}, {1021, Boss.Syaro, "Syaro"},
{1022, Boss.Pandora, "Pandora"}, {1022, Boss.Pandora, "Pandora"},
@ -24,13 +25,18 @@ namespace rabi_splitter_WPF
{1032, Boss.Vanilla, "Vanilla"}, {1032, Boss.Vanilla, "Vanilla"},
{1033, Boss.Chocolate, "Chocolate"}, {1033, Boss.Chocolate, "Chocolate"},
{1035, Boss.IllusionAlius, "Illusion Alius"}, {1035, Boss.IllusionAlius, "Illusion Alius"},
{1036, Boss.PinkKotri, "Pink Kotri"}, {1036, Boss.Kotri, "Kotri"},
{1037, Boss.Noah1, "Noah 1"}, {1037, Boss.Noah1, "Noah 1"},
{1038, Boss.Irisu, "Irisu"}, {1038, Boss.Irisu, "Irisu"},
{1039, Boss.Miriam, "Miriam"}, {1039, Boss.Miriam, "Miriam"},
{1043, Boss.Miru, "Miru"}, {1043, Boss.Miru, "Miru"},
{1053, Boss.Noah3, "Noah 3"}, {1053, Boss.Noah3, "Noah 3"},
{1054, Boss.KekeBunny, "Keke Bunny"}, {1054, Boss.KekeBunny, "Keke Bunny"},
{1056, Boss.Cats, "Plurkwood Cats"},
{1133, Boss.BigBox, "Big Box"},
{1136, Boss.RainbowMaid, "Robot Maid"},
{1143, Boss.TreasureCrystal, "Treasure Crystal"},
}; };
public static IndexEnumAssociation<Map> MapList = new IndexEnumAssociation<Map> public static IndexEnumAssociation<Map> MapList = new IndexEnumAssociation<Map>
@ -126,29 +132,72 @@ namespace rabi_splitter_WPF
{Music.MISCHIEVOUS_MASQUERADE, "MISCHIEVOUS MASQUERADE"}, {Music.MISCHIEVOUS_MASQUERADE, "MISCHIEVOUS MASQUERADE"},
}; };
public static int[] BossMusics = new[] public static Music[] BossMusics = new[]
{ {
44, Music.BOUNCE_BOUNCE,
38, Music.MIDSTREAM_JAM,
47, Music.THE_TRUTH_NEVER_SPOKEN,
34, Music.BRAWL_BREAKS_VER_2,
51, Music.BRAWL_BREAKS,
43, Music.RFN_III,
52, Music.NO_REMORSE,
37, Music.GET_ON_WITH_IT,
39, Music.FULL_ON_COMBAT,
42, Music.HI_TECH_DUEL,
48, Music.KITTY_ATTACK,
8, Music.M_R_,
54 Music.SUDDEN_DEATH,
Music.MISCHIEVOUS_MASQUERADE,
}; };
public static ExpDescriptions HammerLevels = new ExpDescriptions
{
{600, "+Rang", "Attack Range Increase"},
{1000, "UpDr", "Up-drill"},
{1650, "Quad", "Quad-combo"},
{2050, "Spd1", "Combo Speed Increase (Lv.1)"},
{2500, "DwnDr", "Down-drill"},
{2750, "Dmg1", "Hammer Drill Damage Increase (Lv.1)"},// / Up-drill Exp Increase"},
{3100, "Penta", "Penta-combo (Ground-drill)"},
{3750, "Spd2", "Combo Speed Increase (Lv.2)"},
{5500, "-SP", "SP Usage Reduced"},
{6450, "Spd3", "Combo Speed Increase (Lv.3)"},
{7500, "Dmg2", "Hammer Drill Damage Increase (Lv.2)"},// / Up-drill/Down-drill Exp Increase"},
};
public static ExpDescriptions RibbonLevels = new ExpDescriptions
{
{100, "CSpd1", "Charge Speed Increase (Lv.1)"},
{600, "CSpd2", "Charge Speed Increase (Lv.2)"},
{1100, "BSpd1", "Boost Attack Speed Increase (Lv.1)"},
{1650, "CSpd3", "Charge Speed Increase (Lv.3)"},
{2800, "BSpd2", "Boost Attack Speed Increase (Lv.2)"},
{3300, "CSpd4", "Charge Speed Increase (Lv.4)"},
{3800, "BGag1", "Boost Gauge Charges Faster (Lv.1)"},
{4500, "BSpd3", "Boost Attack Speed Increase (Lv.3)"},
{5800, "BGag2", "Boost Gauge Charges Faster (Lv.2)"},
{7000, "BGag3", "Boost Gauge Charges Faster (Lv.3)"},
};
public static ExpDescriptions CarrotLevels = new ExpDescriptions
{
{7, "Del1", "Shorter Delay (Lv.1)"},
{25, "Dmg1", "Damage Increase (Lv.1)"},
{50, "Del2", "Shorter Delay (Lv.2)"},
{100, "Rad", "Effect Radius Increase"},
{175, "Dmg2", "Damage Increase (Lv.2)"},// / Exp Increase"},
{275, "Del3", "Shorter Delay (Lv.3)"},
{500, "Dmg3", "Damage Increase (Lv.3)"},// / Exp Increase"},
};
public static int[] MapAddress = { 0xA3353C, 0xA57020, 0xA5E0AC, 0xA600AC }; public static int[] MapAddress = { 0xA3353C, 0xA57020, 0xA5E0AC, 0xA600AC };
public static int[] EnenyPtrAddr = { 0x00940EE0, 0x00964A1C, 0x0096BA3C, 0x0096DA3C }; public static int[] EnenyPtrAddr = { 0x00940EE0, 0x00964A1C, 0x0096BA3C, 0x0096DA3C };
public static int[] EnenyEnitiyHPOffset = { 0x4c8, 0x4d0,0x4d8, 0x4d8 }; public static int[] EnenyEnitiyHPOffset = { 0x4c8, 0x4d0, 0x4d8, 0x4d8 };
public static int[] EnenyEnitiyMaxHPOffset = { 0, 0, 0, 0x4e8 };
public static int[] EnenyEnitiyIDOffset = { 0x4e4,0x4ec, 0x4F4, 0x4F4 }; public static int[] EnenyEnitiyIDOffset = { 0x4e4,0x4ec, 0x4F4, 0x4F4 };
public static int[] EnenyEntitySize = {0x6F4, 0x6FC, 0x704, 0x704 }; public static int[] EnenyEntitySize = {0x6F4, 0x6FC, 0x704, 0x704 };
public static int[] EnenyEnitiyIsActiveOffset = { 0, 0, 0, 0x674 };
public static int[] EnenyEnitiyAnimationOffset = { 0, 0, 0, 0x678 };
public static int[] MaxEntityEntry = { 50,50,50 ,50}; public static int[] MaxEntityEntry = { 50,50,50 ,50};
public static int[] MoneyAddress = { 0xD3823C, 0xD5B9FC, 0xD63D2C, 0xD654CC }; public static int[] MoneyAddress = { 0xD3823C, 0xD5B9FC, 0xD63D2C, 0xD654CC };
public static string[] VerNames = {"1.65", "1.70","1.71","1.75"}; public static string[] VerNames = {"1.65", "1.70","1.71","1.75"};

Loading…
Cancel
Save