Skip to content

Commit

Permalink
Added all timers tracker
Browse files Browse the repository at this point in the history
  • Loading branch information
Dregu committed Oct 10, 2023
1 parent 64862cd commit 414de0f
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 9 deletions.
12 changes: 12 additions & 0 deletions src/modlunky2/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,24 @@ class PacifistTrackerConfig(CommonTrackerConfig):
show_kill_count: bool = field(default=False, skip_if_default=True)


@serialize(rename_all="kebabcase")
@deserialize(rename_all="kebabcase")
@dataclass
class TimerTrackerConfig(CommonTrackerConfig):
show_total: bool = field(default=True, skip_if_default=True)
show_level: bool = field(default=True, skip_if_default=True)
show_last_level: bool = field(default=True, skip_if_default=True)
show_tutorial: bool = field(default=True, skip_if_default=True)
show_startup: bool = field(default=True, skip_if_default=True)


@serialize(rename_all="kebabcase")
@deserialize(rename_all="kebabcase")
@dataclass
class TrackersConfig:
category: CategoryTrackerConfig = field(default_factory=CategoryTrackerConfig)
pacifist: PacifistTrackerConfig = field(default_factory=PacifistTrackerConfig)
timer: TimerTrackerConfig = field(default_factory=TimerTrackerConfig)


@serialize # Note: these fields aren't renamed for historical reasons
Expand Down
3 changes: 3 additions & 0 deletions src/modlunky2/mem/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,13 @@ class State:
arena_state: ArenaState = struct_field(0x95C, dc_struct, default_factory=ArenaState)
run_recap_flags: RunRecapFlags = struct_field(0xA34, sc_uint32, default=0)
hud_flags: HudFlags = struct_field(0xA50, sc_uint32, default=0)
time_last_level: int = struct_field(0xA40, sc_uint32, default=0)
time_level: int = struct_field(0xA44, sc_uint32, default=0)
time_tutorial: int = struct_field(0xA48, sc_uint32, default=0)
presence_flags: PresenceFlags = struct_field(0xA54, sc_uint32, default=0)
next_entity_uid: int = struct_field(0x12E0, sc_uint32, default=0)
items: Optional[Items] = struct_field(0x12F0, pointer(dc_struct), default=None)
instance_id_to_pointer: UidEntityMap = struct_field(
0x1348, uid_entity_map, default_factory=DictMap
)
time_startup: int = struct_field(0x13A0, sc_uint32, default=0)
2 changes: 2 additions & 0 deletions src/modlunky2/ui/trackers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from .options import OptionsFrame
from .pacifist import PacifistButtons
from .timer import TimerButtons

logger = logging.getLogger(__name__)

Expand All @@ -21,6 +22,7 @@ def __init__(self, parent, ml_config: Config, *args, **kwargs):

self.add_button(PacifistButtons(self, self.ml_config))
self.add_button(CategoryButtons(self, self.ml_config))
self.add_button(TimerButtons(self, self.ml_config))

self.rowconfigure(self.button_index, weight=1)

Expand Down
25 changes: 17 additions & 8 deletions src/modlunky2/ui/trackers/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,16 +218,23 @@ def __init__(

self.text = "Connecting..."
self.label = tk.Label(
self, text=self.text, bg=self.color_key, fg="white", font=font
self,
text=self.text,
bg=self.color_key,
fg="white",
font=font,
justify="right",
)
self.label.columnconfigure(0, weight=1)
self.label.rowconfigure(0, weight=1)
self.label.grid(row=0, column=0, padx=5, pady=5, sticky="nsew")

TRACKERS_DIR.mkdir(parents=True, exist_ok=True)
self.text_file = TRACKERS_DIR / file_name
with self.text_file.open("w", encoding="utf-8") as handle:
handle.write(self.text)
self.text_file = None
if file_name:
self.text_file = TRACKERS_DIR / file_name
with self.text_file.open("w", encoding="utf-8") as handle:
handle.write(self.text)

self.watcher_thread.start()
self.after(self.POLL_INTERVAL, self.after_watcher_thread)
Expand All @@ -252,8 +259,9 @@ def update_text(self, new_text):
return
self.text = new_text
self.label.configure(text=self.text)
with self.text_file.open("w", encoding="utf-8") as handle:
handle.write(new_text)
if self.text_file:
with self.text_file.open("w", encoding="utf-8") as handle:
handle.write(new_text)

def shut_down(self, level, message):
logger.log(level, "%s", message)
Expand Down Expand Up @@ -297,7 +305,8 @@ def destroy(self):
if self.on_close:
self.on_close()

with self.text_file.open("w", encoding="utf-8") as handle:
handle.write("Not running")
if self.text_file:
with self.text_file.open("w", encoding="utf-8") as handle:
handle.write("Not running")

return super().destroy()
2 changes: 1 addition & 1 deletion src/modlunky2/ui/trackers/pacifist.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def __init__(self, parent, modlunky_config: Config, *args, **kwargs):
offvalue=False,
command=self.toggle_show_kill_count,
)
self.show_kill_count_checkbox.grid(row=0, column=1, pady=5, padx=5, sticky="nw")
self.show_kill_count_checkbox.grid(row=0, column=1, pady=5, padx=5, sticky="w")

def toggle_show_kill_count(self):
self.modlunky_config.trackers.pacifist.show_kill_count = (
Expand Down
197 changes: 197 additions & 0 deletions src/modlunky2/ui/trackers/timer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import logging
import tkinter as tk
from tkinter import ttk
import datetime

from modlunky2.config import Config, TimerTrackerConfig
from modlunky2.mem import Spel2Process

from modlunky2.ui.trackers.common import (
Tracker,
TrackerWindow,
WindowData,
)

logger = logging.getLogger(__name__)


class TimerButtons(ttk.Frame):
def __init__(self, parent, modlunky_config: Config, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.modlunky_config = modlunky_config
self.columnconfigure(0, weight=1)
self.rowconfigure(0, minsize=60)
self.window = None
self.modlunky_config.trackers.timer.anchor = "w"

self.timer_button = ttk.Button(
self,
text="Timer",
command=self.launch,
)
self.timer_button.grid(row=0, column=0, pady=5, padx=5, sticky="nswe")

self.show_total = tk.BooleanVar()
self.show_total.set(self.modlunky_config.trackers.timer.show_total)
self.show_total_checkbox = ttk.Checkbutton(
self,
text="Total",
variable=self.show_total,
onvalue=True,
offvalue=False,
command=self.toggle_show_total,
)
self.show_total_checkbox.grid(row=0, column=1, pady=5, padx=5, sticky="w")

self.show_level = tk.BooleanVar()
self.show_level.set(self.modlunky_config.trackers.timer.show_level)
self.show_level_checkbox = ttk.Checkbutton(
self,
text="Level",
variable=self.show_level,
onvalue=True,
offvalue=False,
command=self.toggle_show_level,
)
self.show_level_checkbox.grid(row=0, column=2, pady=5, padx=5, sticky="w")

self.show_last_level = tk.BooleanVar()
self.show_last_level.set(self.modlunky_config.trackers.timer.show_last_level)
self.show_last_level_checkbox = ttk.Checkbutton(
self,
text="Last Level",
variable=self.show_last_level,
onvalue=True,
offvalue=False,
command=self.toggle_show_last_level,
)
self.show_last_level_checkbox.grid(row=0, column=3, pady=5, padx=5, sticky="w")

self.show_tutorial = tk.BooleanVar()
self.show_tutorial.set(self.modlunky_config.trackers.timer.show_tutorial)
self.show_tutorial_checkbox = ttk.Checkbutton(
self,
text="Tutorial",
variable=self.show_tutorial,
onvalue=True,
offvalue=False,
command=self.toggle_show_tutorial,
)
self.show_tutorial_checkbox.grid(row=0, column=4, pady=5, padx=5, sticky="w")

self.show_startup = tk.BooleanVar()
self.show_startup.set(self.modlunky_config.trackers.timer.show_startup)
self.show_startup_checkbox = ttk.Checkbutton(
self,
text="Session",
variable=self.show_startup,
onvalue=True,
offvalue=False,
command=self.toggle_show_startup,
)
self.show_startup_checkbox.grid(row=0, column=5, pady=5, padx=5, sticky="w")

def toggle_show_total(self):
self.modlunky_config.trackers.timer.show_total = self.show_total.get()
self.modlunky_config.save()
if self.window:
self.window.update_config(self.modlunky_config.trackers.timer)

def toggle_show_level(self):
self.modlunky_config.trackers.timer.show_level = self.show_level.get()
self.modlunky_config.save()
if self.window:
self.window.update_config(self.modlunky_config.trackers.timer)

def toggle_show_last_level(self):
self.modlunky_config.trackers.timer.show_last_level = self.show_last_level.get()
self.modlunky_config.save()
if self.window:
self.window.update_config(self.modlunky_config.trackers.timer)

def toggle_show_tutorial(self):
self.modlunky_config.trackers.timer.show_tutorial = self.show_tutorial.get()
self.modlunky_config.save()
if self.window:
self.window.update_config(self.modlunky_config.trackers.timer)

def toggle_show_startup(self):
self.modlunky_config.trackers.timer.show_startup = self.show_startup.get()
self.modlunky_config.save()
if self.window:
self.window.update_config(self.modlunky_config.trackers.timer)

def launch(self):
color_key = self.modlunky_config.tracker_color_key
self.disable_button()
self.window = TrackerWindow(
title="Timer Tracker",
color_key=color_key,
on_close=self.window_closed,
file_name="",
tracker=TimerTracker(),
config=self.modlunky_config.trackers.timer,
)

def window_closed(self):
self.window = None
# If we're in the midst of destroy() the button might not exist
if self.timer_button.winfo_exists():
self.timer_button["state"] = tk.NORMAL

def disable_button(self):
self.timer_button["state"] = tk.DISABLED


class TimerTracker(Tracker[TimerTrackerConfig, WindowData]):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.time_total = 0
self.time_level = 0
self.time_last_level = 0
self.time_tutorial = 0
self.time_startup = 0

def initialize(self):
self.time_total = 0
self.time_level = 0
self.time_last_level = 0
self.time_tutorial = 0
self.time_startup = 0

def poll(self, proc: Spel2Process, config: TimerTrackerConfig) -> WindowData:
game_state = proc.get_state()
if game_state is None:
return None

self.time_total = game_state.time_total
self.time_level = game_state.time_level
self.time_last_level = game_state.time_last_level
self.time_tutorial = game_state.time_tutorial
self.time_startup = game_state.time_startup

label = self.get_text(config)
return WindowData(label)

def format(self, frames):
return datetime.datetime.utcfromtimestamp(frames / 60).strftime("%H:%M:%S.%f")[
:-3
]

def get_text(
self,
config: TimerTrackerConfig,
):
out = []
if config.show_total:
out.append(f"Total: {self.format(self.time_total)}")
if config.show_level:
out.append(f"Level: {self.format(self.time_level)}")
if config.show_last_level:
out.append(f"Last level: {self.format(self.time_last_level)}")
if config.show_tutorial:
out.append(f"Tutorial: {self.format(self.time_tutorial)}")
if config.show_startup:
out.append(f"Session: {self.format(self.time_startup)}")

return "\n".join(out)

0 comments on commit 414de0f

Please sign in to comment.