Panel Manager Improvements (#85)
* Panel Manager Improvements - Now maps instead of slices for easy traverse - Function for easy and safe traverse of paths - Removed constant updates of Panel Manager to reduce usage. It will now be called on demand. - Updated some functions to use the new Panel Manager. * Remove unused struct members * Added IsDismissalModalPresent (error popup) * Correct function return for char select screen and dismissal modal * Add error checking to IsDismissableModalPresent
This commit is contained in:
@@ -39,19 +39,13 @@ type Data struct {
|
|||||||
AdjacentLevels []Level
|
AdjacentLevels []Level
|
||||||
Rooms []Room
|
Rooms []Room
|
||||||
OpenMenus OpenMenus
|
OpenMenus OpenMenus
|
||||||
Widgets map[string]map[string]interface{}
|
|
||||||
Panels []Panel
|
|
||||||
Roster Roster
|
Roster Roster
|
||||||
HoverData HoverData
|
HoverData HoverData
|
||||||
TerrorZones []area.ID
|
TerrorZones []area.ID
|
||||||
Quests quest.Quests
|
Quests quest.Quests
|
||||||
KeyBindings KeyBindings
|
KeyBindings KeyBindings
|
||||||
LegacyGraphics bool
|
LegacyGraphics bool
|
||||||
IsOnline bool
|
|
||||||
IsIngame bool
|
IsIngame bool
|
||||||
IsInCharCreationScreen bool
|
|
||||||
IsInCharSelectionScreen bool
|
|
||||||
IsInLobby bool
|
|
||||||
HasMerc bool
|
HasMerc bool
|
||||||
ActiveWeaponSlot int
|
ActiveWeaponSlot int
|
||||||
}
|
}
|
||||||
@@ -85,7 +79,7 @@ type Panel struct {
|
|||||||
ExtraText2 string
|
ExtraText2 string
|
||||||
ExtraText3 string
|
ExtraText3 string
|
||||||
PanelParent string
|
PanelParent string
|
||||||
PanelChildren []Panel
|
PanelChildren map[string]Panel
|
||||||
Depth int
|
Depth int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package memory
|
package memory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -105,19 +106,19 @@ func (gd *GameReader) GetData() data.Data {
|
|||||||
Objects: objects,
|
Objects: objects,
|
||||||
Entrances: gd.Entrances(pu.Position, hover),
|
Entrances: gd.Entrances(pu.Position, hover),
|
||||||
OpenMenus: openMenus,
|
OpenMenus: openMenus,
|
||||||
Widgets: gd.UpdateWidgets(),
|
|
||||||
Panels: gd.ReadAllPanels(),
|
|
||||||
Roster: roster,
|
Roster: roster,
|
||||||
HoverData: hover,
|
HoverData: hover,
|
||||||
TerrorZones: gd.TerrorZones(),
|
TerrorZones: gd.TerrorZones(),
|
||||||
Quests: gd.getQuests(gameQuestsBytes),
|
Quests: gd.getQuests(gameQuestsBytes),
|
||||||
KeyBindings: gd.GetKeyBindings(),
|
KeyBindings: gd.GetKeyBindings(),
|
||||||
LegacyGraphics: gd.LegacyGraphics(),
|
LegacyGraphics: gd.LegacyGraphics(),
|
||||||
IsOnline: gd.IsOnline(),
|
|
||||||
IsIngame: gd.IsIngame(),
|
IsIngame: gd.IsIngame(),
|
||||||
IsInCharCreationScreen: gd.IsInCharacterCreationScreen(),
|
|
||||||
IsInLobby: gd.IsInLobby(),
|
// These use the Panel Manager which is heavy to read. Use the functions below instead.
|
||||||
IsInCharSelectionScreen: gd.IsInCharacterSelectionScreen(),
|
//IsOnline: gd.IsOnline(),
|
||||||
|
//IsInCharCreationScreen: gd.IsInCharacterCreationScreen(),
|
||||||
|
//IsInLobby: gd.IsInLobby(),
|
||||||
|
//IsInCharSelectionScreen: gd.IsInCharacterSelectionScreen(),
|
||||||
HasMerc: gd.HasMerc(),
|
HasMerc: gd.HasMerc(),
|
||||||
ActiveWeaponSlot: gd.GetActiveWeaponSlot(),
|
ActiveWeaponSlot: gd.GetActiveWeaponSlot(),
|
||||||
}
|
}
|
||||||
@@ -252,12 +253,39 @@ func (gd *GameReader) getStatsList(statListPtr uintptr) stat.Stats {
|
|||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Take a look to better ways to get this data, now it's very flakky, is just a random memory position + not in game
|
// GetPanel returns a Panel object from the specified path (starting from the root panel)
|
||||||
func (gd *GameReader) InCharacterSelectionScreen() bool {
|
func (gd *GameReader) GetPanel(panelPath ...string) data.Panel {
|
||||||
cs_visible, err := gd.IsWidgetVisible("CharacterSelectPanel")
|
if len(panelPath) == 0 {
|
||||||
if err != nil {
|
return data.Panel{}
|
||||||
}
|
}
|
||||||
return cs_visible
|
|
||||||
|
// Get all panels
|
||||||
|
allPanels := gd.ReadAllPanels()
|
||||||
|
|
||||||
|
// Start with the first panel in the path
|
||||||
|
firstPanelName := panelPath[0]
|
||||||
|
currentPanel, exists := allPanels[firstPanelName]
|
||||||
|
if !exists {
|
||||||
|
// Panel not found at top level
|
||||||
|
return data.Panel{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse the path from left to right
|
||||||
|
for i := 1; i < len(panelPath); i++ {
|
||||||
|
childName := panelPath[i]
|
||||||
|
nextPanel, exists := currentPanel.PanelChildren[childName]
|
||||||
|
if !exists {
|
||||||
|
return data.Panel{}
|
||||||
|
}
|
||||||
|
currentPanel = nextPanel
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentPanel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gd *GameReader) InCharacterSelectionScreen() bool {
|
||||||
|
panel := gd.GetPanel("CharacterSelectPanel")
|
||||||
|
return panel.PanelName != "" && panel.PanelEnabled && panel.PanelVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) GetSelectedCharacterName() string {
|
func (gd *GameReader) GetSelectedCharacterName() string {
|
||||||
@@ -269,8 +297,8 @@ func (gd *GameReader) LegacyGraphics() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) IsOnline() bool {
|
func (gd *GameReader) IsOnline() bool {
|
||||||
// This represents which tab (Online/Offline) we're on in the Character Selection Screen
|
panel := gd.GetPanel("MainMenuPanel", "SecondaryContextButton")
|
||||||
return gd.ReadUInt(gd.moduleBaseAddressPtr+0x21864D0, 1) == 1
|
return panel.PanelName != "" && panel.PanelEnabled && panel.PanelVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) IsIngame() bool {
|
func (gd *GameReader) IsIngame() bool {
|
||||||
@@ -278,30 +306,45 @@ func (gd *GameReader) IsIngame() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) IsInLobby() bool {
|
func (gd *GameReader) IsInLobby() bool {
|
||||||
widgets := gd.UpdateWidgets()
|
panel := gd.GetPanel("LobbyBackgroundPanel")
|
||||||
if lobbyWidget, found := widgets["LobbyBackgroundPanel"]; found {
|
return panel.PanelName != "" && panel.PanelEnabled && panel.PanelVisible
|
||||||
return lobbyWidget["WidgetActive"].(bool) && lobbyWidget["WidgetVisible"].(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) IsInCharacterSelectionScreen() bool {
|
func (gd *GameReader) IsInCharacterSelectionScreen() bool {
|
||||||
widgets := gd.UpdateWidgets()
|
panel := gd.GetPanel("CharacterSelectPanel")
|
||||||
if csWidget, found := widgets["CharacterSelectPanel"]; found {
|
return panel.PanelName != "" && panel.PanelEnabled && panel.PanelVisible
|
||||||
return csWidget["WidgetActive"].(bool) && csWidget["WidgetVisible"].(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) IsInCharacterCreationScreen() bool {
|
func (gd *GameReader) IsInCharacterCreationScreen() bool {
|
||||||
widgets := gd.UpdateWidgets()
|
panel := gd.GetPanel("CharacterCreatePanel")
|
||||||
if ccWidget, found := widgets["CharacterCreatePanel"]; found {
|
return panel.PanelName != "" && panel.PanelEnabled && panel.PanelVisible
|
||||||
return ccWidget["WidgetActive"].(bool) && ccWidget["WidgetVisible"].(bool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
func (gd *GameReader) GetCharacterList() []string {
|
||||||
|
containerPanel := gd.GetPanel("CharacterSelectPanel", "Background", "CharacterList", "View", "Container")
|
||||||
|
if containerPanel.PanelName == "" || containerPanel.NumChildren == 0 {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the character names that are in the container children [ListView 0,1,2 (0 indexed)] -> children -> Name -> Extra Text 3
|
||||||
|
characterNames := make([]string, containerPanel.NumChildren)
|
||||||
|
for i := 0; i < containerPanel.NumChildren; i++ {
|
||||||
|
characterNames[i] = containerPanel.PanelChildren[fmt.Sprintf("ListItem%d", i)].PanelChildren["Name"].ExtraText3
|
||||||
|
}
|
||||||
|
|
||||||
|
return characterNames
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDismissableModalPresent checks if there's a error popup present
|
||||||
|
func (gd *GameReader) IsDismissableModalPresent() (bool, string) {
|
||||||
|
panel := gd.GetPanel("DismissableModal")
|
||||||
|
|
||||||
|
if panel.PanelName == "" {
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
modalText := panel.PanelChildren["Frame"].PanelChildren["Prompt"].ExtraText3
|
||||||
|
return (panel.PanelName != "" && panel.PanelEnabled && panel.PanelVisible), modalText
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) LastGameName() string {
|
func (gd *GameReader) LastGameName() string {
|
||||||
@@ -319,44 +362,6 @@ func (gd *GameReader) FPS() int {
|
|||||||
func (gd *GameReader) HasMerc() bool {
|
func (gd *GameReader) HasMerc() bool {
|
||||||
return gd.ReadUInt(gd.moduleBaseAddressPtr+0x22e51d0+0x12, Uint8) != 0
|
return gd.ReadUInt(gd.moduleBaseAddressPtr+0x22e51d0+0x12, Uint8) != 0
|
||||||
}
|
}
|
||||||
func (gd *GameReader) UpdateWidgets() map[string]map[string]interface{} {
|
|
||||||
widgets := map[string]map[string]interface{}{}
|
|
||||||
|
|
||||||
if gd.offset.PanelManagerContainerOffset == 0 {
|
|
||||||
gd.offset = calculateOffsets(gd.Process)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assuming PanelManagerContainer address is known and stored in gd
|
|
||||||
panelManagerContainerPtrAddr := gd.Process.moduleBaseAddressPtr + gd.offset.PanelManagerContainerOffset
|
|
||||||
|
|
||||||
// Read the PanelManagerContainer pointer value
|
|
||||||
panelManagerContainerAddr, err := gd.Process.ReadPointer(panelManagerContainerPtrAddr, 8)
|
|
||||||
if err != nil {
|
|
||||||
return widgets
|
|
||||||
}
|
|
||||||
// Read the Panel Managers WidgetContainer
|
|
||||||
widgetContainer, err := gd.Process.ReadWidgetContainer(panelManagerContainerAddr, true)
|
|
||||||
if err != nil {
|
|
||||||
return widgets
|
|
||||||
}
|
|
||||||
// Read the list of child widgets
|
|
||||||
childWidgets, err := gd.Process.ReadWidgetList(widgetContainer["ChildWidgetsListPointer"].(uintptr), int(widgetContainer["ChildWidgetSize"].(uint)))
|
|
||||||
if err != nil {
|
|
||||||
return widgets
|
|
||||||
}
|
|
||||||
return childWidgets
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsWidgetVisible checks if any child widget on the PanelManager has the same name and has the Active and Visible booleans on.
|
|
||||||
func (gd *GameReader) IsWidgetVisible(widgetName string) (bool, error) {
|
|
||||||
widgets := gd.UpdateWidgets()
|
|
||||||
widget, exists := widgets[widgetName]
|
|
||||||
if !exists {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return widget["WidgetActive"].(bool) && widget["WidgetVisible"].(bool), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWidgetState reference : https://github.com/ResurrectedTrader/ResurrectedTrade/blob/f121ec02dd3fbe1c574f713e5a0c2db92ccca821/ResurrectedTrade.AgentBase/Capture.cs#L618
|
// GetWidgetState reference : https://github.com/ResurrectedTrader/ResurrectedTrade/blob/f121ec02dd3fbe1c574f713e5a0c2db92ccca821/ResurrectedTrade.AgentBase/Capture.cs#L618
|
||||||
func (gd *GameReader) GetWidgetState(stateFlag uint64) (int, error) {
|
func (gd *GameReader) GetWidgetState(stateFlag uint64) (int, error) {
|
||||||
@@ -394,6 +399,7 @@ func (gd *GameReader) GetWidgetState(stateFlag uint64) (int, error) {
|
|||||||
|
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) GetActiveWeaponSlot() int {
|
func (gd *GameReader) GetActiveWeaponSlot() int {
|
||||||
state, err := gd.GetWidgetState(WidgetStateFlags["WeaponSwap"])
|
state, err := gd.GetWidgetState(WidgetStateFlags["WeaponSwap"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func NewPanel(panelPtr uintptr, panelParent string, depth int, gd *GameReader) *
|
|||||||
ExtraText2: gd.Process.ReadStringFromMemory(uintptr(gd.Process.ReadUInt(panelPtr+0x290, Uint64)), 0),
|
ExtraText2: gd.Process.ReadStringFromMemory(uintptr(gd.Process.ReadUInt(panelPtr+0x290, Uint64)), 0),
|
||||||
ExtraText3: gd.Process.ReadStringFromMemory(uintptr(gd.Process.ReadUInt(panelPtr+0x88, Uint64)), 0),
|
ExtraText3: gd.Process.ReadStringFromMemory(uintptr(gd.Process.ReadUInt(panelPtr+0x88, Uint64)), 0),
|
||||||
PanelParent: panelParent,
|
PanelParent: panelParent,
|
||||||
PanelChildren: make([]data.Panel, 0),
|
PanelChildren: make(map[string]data.Panel),
|
||||||
Depth: depth,
|
Depth: depth,
|
||||||
}
|
}
|
||||||
if panel.NumChildren > 0 && panel.NumChildren < 50 {
|
if panel.NumChildren > 0 && panel.NumChildren < 50 {
|
||||||
@@ -55,23 +55,24 @@ func isASCII(s string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) ReadAllPanels() []data.Panel {
|
// ReadAllPanels reads all panels from the game memory
|
||||||
|
func (gd *GameReader) ReadAllPanels() map[string]data.Panel {
|
||||||
base := gd.Process.moduleBaseAddressPtr + gd.offset.PanelManagerContainerOffset
|
base := gd.Process.moduleBaseAddressPtr + gd.offset.PanelManagerContainerOffset
|
||||||
panelStructPtr := uintptr(gd.Process.ReadUInt(base, Uint64))
|
panelStructPtr := uintptr(gd.Process.ReadUInt(base, Uint64))
|
||||||
panelPtr := uintptr(gd.Process.ReadUInt(panelStructPtr+0x58, Uint64))
|
panelPtr := uintptr(gd.Process.ReadUInt(panelStructPtr+0x58, Uint64))
|
||||||
numChildren := int(gd.Process.ReadUInt(panelStructPtr+0x60, Uint8))
|
numChildren := int(gd.Process.ReadUInt(panelStructPtr+0x60, Uint8))
|
||||||
|
|
||||||
panels := make([]data.Panel, 0)
|
panels := make(map[string]data.Panel)
|
||||||
depth := 0
|
depth := 0
|
||||||
// recursively read all panels, starting with the Root panel
|
// recursively read all panels, starting with the Root panel
|
||||||
readPanel(panelPtr, numChildren, &panels, "Root", depth, gd)
|
readPanel(panelPtr, numChildren, &panels, "Root", depth, gd)
|
||||||
return panels
|
return panels
|
||||||
}
|
}
|
||||||
|
|
||||||
func readPanel(panelPtr uintptr, numChildren int, panels *[]data.Panel, panelParent string, depth int, gd *GameReader) {
|
func readPanel(panelPtr uintptr, numChildren int, panels *map[string]data.Panel, panelParent string, depth int, gd *GameReader) {
|
||||||
for i := 0; i < numChildren; i++ {
|
for i := 0; i < numChildren; i++ {
|
||||||
panelStructPtr := uintptr(gd.Process.ReadUInt(uintptr(uint64(panelPtr)+uint64(i*8)), Uint64))
|
panelStructPtr := uintptr(gd.Process.ReadUInt(uintptr(uint64(panelPtr)+uint64(i*8)), Uint64))
|
||||||
thisPanel := NewPanel(panelStructPtr, panelParent, depth, gd)
|
thisPanel := NewPanel(panelStructPtr, panelParent, depth, gd)
|
||||||
*panels = append(*panels, *thisPanel)
|
(*panels)[thisPanel.PanelName] = *thisPanel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user