Skip to content

Commit

Permalink
Big update to readme and UI showcase
Browse files Browse the repository at this point in the history
  • Loading branch information
oskros committed Sep 27, 2020
1 parent 76d13f7 commit aef139c
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 54 deletions.
72 changes: 46 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Diablo 2 MF run counter
You can find a video where I describe the application in detail here (NB: Not fully up to date - this video is missing explanation of new features such as "automode", "autocompletion of item names" and "grail tracking"): https://www.youtube.com/watch?v=e4vcg8MdvrY&t=46s
You can find a video where I describe the application in detail here
**(NB: Not fully up to date - this video is missing explanation of new features such as "automode", "autocompletion of item names" and "grail tracking")**:
https://www.youtube.com/watch?v=e4vcg8MdvrY&t=46s


![GUI](https://github.com/oskros/MF_counter_releases/blob/master/media/UI_showcase.png?raw=true)
![GUI](https://github.com/oskros/MF_counter_releases/blob/master/media/UI_showcase2.png?raw=true)

## How to use
NB: Working on Windows 7 and Windows 10 (I have not tested on any other OS, but probably it wouldn't work)
Expand All @@ -17,22 +19,28 @@ NB: Working on Windows 7 and Windows 10 (I have not tested on any other OS, but

## Features
### Automode
You can active automode in Options -> Automode. The automode automatically advance the run counter every time you create a new game by monitoring the 'latest change time' registered by the OS on your local character files - The local files are a little different on single player and multiplayer, hence the need for specifying the game mode when creating a profile (.d2s files in single player, and .map files in multiplayer).
To set-up automode, enter the game path to your save folder and click 'Apply'. Also make sure the character name specified in your profile is exactly the same as your in-game name, otherwise the automode will fail to start.
You can active automode in **Options -> Automode**. There are two types of automode
1) **Advanced:** Uses memory reading of the D2 "*Game.exe*" process to read from your client when you are in-game. When you enter a game, it will start a new run, and when you exit a game it will stop the run - Only recommended on single player, as it could be deemed cheating on multiplayer servers.
Advanced automode is patch specific, and is currently supported for 1.13c, 1.13d and 1.14d. I have not tested if it works in conjunction with D2SE.

2) **Simple:** Monitors the "*latest change time*" of your character save file. Your character is saved every time you exit a game, and thus a new run can be started - Unfortunately, there is no way to stop a run with this method and also it has two drawbacks:
*2.a)* The game autosaves every 5 minutes, thus you cannot have runs longer than 5 min.
*2.b)* The game autosaves every time you die
To set-up automode, enter the game path to your character save folder and click 'Apply'. Also make sure the character name specified in your profile is exactly the same as your in-game name, otherwise the automode will fail to start.

### System wide hotkeys
You are able to customize hotkeys for each of the commands in the application. The hotkeys are system wide, so you do not need to place focus on the application to use them. However, this means that while the application is open, it overrides any other program using this specific hotkey.
It is possible to disable a command from having a hotkey by choosing "NO_BIND"
There are 2 hidden hotkeys: 'control+shift+PgDn' and 'control+shift+PgUp' for switching between tabs in the app (eg. if you wanna confirm the drop you just added is in fact saved)
There are 2 hidden hotkeys: *CTRL+SHIFT+PgDn* and *CTRL+SHIFT+PgUp* for switching between tabs in the app (eg. if you wanna confirm the drop you just added is in fact saved)

### Run counting
Start a new run by pressing the "Start new run" button, or using the assigned hotkey (default 'alt+Q'). In case a run is already active, this button will end it and start a new run.
End the run by pressing the "End this run" button, or using the assigned hotkey (default 'alt+W').
In case of mistakes, the previous run can be deleted using the assigned hotkey (default 'control+delete')
If the user desires, the current run timer can be reset using the "Reset lap" button or using the assigned hotkey (default 'alt+R').
### Optional: Manual run counting
Start a new run by using the assigned hotkey (default *ALT+Q*). In case a run is already active, this button will end it and start a new run.
End the run by using the assigned hotkey (default *ALT+W*).
In case of mistakes, the previous run can be deleted using the assigned hotkey (default *CTRL+DEL*)
If the user desires, the current run timer can be reset using the assigned hotkey (default *ALT+R*).

### Drop logging
You add a drop to the current run (or previous run if no run is currently active) by pressing the "Add drop" button or using the assigned hotkey (default 'alt+A'). It will then be registered in the "Drops" tab under the run where it was found.
You add a drop to the current run (or previous run if no run is currently active) by using the assigned hotkey (default *ALT+A*). It will then be listed in the "Drops" list for the run where it was found.
When adding a drop, the application will try and autocomplete the name of the drop using a full library of all available sets, uniques and runes.
In case a drop was added by mistake, it can be deleted by highlighting it and clicking the "Delete selection" button under the "Drops" tab, or by pressing the Delete button on your keyboard while the drop is highlighted (this hotkey is not system wide).

Expand All @@ -42,34 +50,46 @@ In the profile tab it is possible to create a separate run profile for each char
### Grail logging
There is a tab called "Grail" - Here you can track all your progress in completing the d2 holy grail of collecting all items (and runes). Every time you add a new item that hasn't been found before, it can be added to the grail! Then it will appear in your found items with a "(\*)" in front of the name, to indicate it was a grailer. Furthermore, there is support with d2-holy-grail.herokuapp.com - You can both sync your local grail with the already logged progress there, and also upload any progress back to the webpage again!
In order to view your grail progress there is both a "Grail controller" view, which is similar to the one found on herokuapp, but also a "Item table" view, where you see all items in a table with tons of information about each item.
You can also sync your local grail with already found items. Unfortunately, it is not possible to sync with any items added in your profiles prior to version 1.2.0, sorry!
You can also sync your local grail with already found items. Unfortunately, it is not possible to sync with any items added in your profiles prior to MF Run Counter v.1.2.0, sorry!

### Session timer
The session timer will start as soon as you open the program and run as long as the program is open (an not put on pause).
The session timer will start as soon as you open the program and run as long as the program is open.

### Pausing
In case you need to take a break from the computer, the counter can be paused by using the assigned hotkey (default 'control+space'). This will pause both the run timer (if a run is active) and the session timer.
In case you need to take a break from the computer, the counter can be paused by using the assigned hotkey (default *CTRL+Space*). This will pause both the run timer (if a run is active) and the session timer.

### Saving results
The current session is saved automatically every 30 seconds (to prevent data loss in case of crashes) and also saved automatically when you close the app. You can click the "Archive session" button to reset the session and archive the session results to the profile. In the archive browser, it is possible to save a session or the full profile history to a .txt or .csv file, or copy to clipboard - Example of the .txt file is included below.

Total session time: 00:04:30.5
Total run time: 00:04:17.3
Average run time: 00:01:04.3
Fastest run time: 00:00:44.5
Session percentage spent in runs: 95.1%


Run 1: 00:00:57:9
Run 2: 00:00:44:5 --- Shako, Skullders
Run 3: 00:01:11:6
Run 4: 00:01:24:3 --- Nagelring 25%
*Statistics*
Character name: Tsssch
Run type: Andy
Game mode: Single Player

Total session time: 00:24:20:0
Total run time: 00:23:10:0
Average run time: 00:00:34:7
Fastest run time: 00:00:23:1
Number of runs: 40
Time spent in runs: 95.21%

*Collected drops*
Run 2 --- Atma's Scarab
Run 6 --- Raven Frost 151/18

*Run times*
Run 1: 00:00:23:1
Run 2: 00:00:42:0 --- Atma's Scarab
Run 3: 00:00:25:0
Run 4: 00:00:32:1
Run 5: 00:00:32:3
Run 6: 00:00:26:7 --- Raven Frost 151/18

### Dragging
Window can be dragged on the Diablo 2 banner. Window position is saved in the config file, such that it opens where you closed it.

### Automatic check for updates
The program automatically checks if a new version is available on start-up, providing a link to the release pages where you can download it. This features can be disabled under "Options" and then under "General"
The program automatically checks if a new version is available on start-up, providing a link to the release pages where you can download it. This features can be disabled under *Options->General*

### Color themes
Three different themes for the application have been created, which the user can choose between under Options
Expand Down
5 changes: 4 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import json
import queue
import base64
import logging
# import logging
import ctypes
import traceback
import win32api
Expand All @@ -31,10 +31,13 @@
# FIXME: Solve issue with bad synced sound effects (add sound effect when automode is active and starting new run)
# FIXME: Retain order of item table when adding new drops

# FIXME: Drops logged in profile text is not correct
# FIXME: Pause session and XP timer on pause call
# FIXME: Ability to select which run to archive drop on
# FIXME: Advanced automode and 2 diablo instances...?
# FIXME: Archive reset should always use last update time as stamp
# FIXME: Save relative XP gained in the XP tracker
# FIXME: Save all XP stuff under a character name, so multiple characters wont break it. Also save under individual levels
# FIXME: Build a proper logger

# FIXME: Add item by hovering over it in D2 and pressing hotkey (both for items picked up and on ground)
Expand Down
Binary file added media/UI_showcase2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 29 additions & 27 deletions tabs/stats_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,35 +147,37 @@ def reset_session(self):
self.runs_level_sv.set('0')

def save_state(self):
return dict(session_char_xp_start=self.session_char_xp_start,
session_char_xp=self.session_char_xp,
session_char_time_start=time.time() - self.session_char_time_start,
session_char_xp_missing=self.session_char_xp_missing,
session_xp_runs=list(self.session_xp_runs))
return dict()#session_char_xp_start=self.session_char_xp_start,
# session_char_xp=self.session_char_xp,
# session_char_time_start=time.time() - self.session_char_time_start,
# session_char_xp_missing=self.session_char_xp_missing,
# session_xp_runs=list(self.session_xp_runs))

def load_from_state(self, active_state):
self.session_char_xp_start = active_state.get('session_char_xp_start', 0)
self.session_char_xp = active_state.get('session_char_xp', 0)
self.session_char_time_start = time.time() - active_state.get('session_char_time_start', 0)
self.session_char_xp_missing = active_state.get('session_char_xp_missing', 0)
self.session_xp_runs = set(active_state.get('session_xp_runs', set()))

if self.session_char_xp > 0:
self.curr_run_xp = self.session_char_xp

self.name_sv.set('-----')
self.level_sv.set('-----')
self.mf_sv.set('-----')
self.players_x_sv.set('-----')

self.exp_sv.set('0')
self.exp_level_sv.set('{:,.0f}'.format(self.session_char_xp_missing))
self.exp_session_sv.set('{:,.0f}'.format(self.session_char_xp - self.session_char_xp_start))
if len(self.session_xp_runs) > 0:
self.avg_run = sum(self.session_xp_runs) / len(self.session_xp_runs)
self.runs_level_sv.set('{:.0f}'.format(-(-self.session_char_xp_missing / self.avg_run // 1)))
else:
self.runs_level_sv.set('0')
return

# self.session_char_xp_start = active_state.get('session_char_xp_start', 0)
# self.session_char_xp = active_state.get('session_char_xp', 0)
# self.session_char_time_start = time.time() - active_state.get('session_char_time_start', 0)
# self.session_char_xp_missing = active_state.get('session_char_xp_missing', 0)
# self.session_xp_runs = set(active_state.get('session_xp_runs', set()))
#
# if self.session_char_xp > 0:
# self.curr_run_xp = self.session_char_xp
#
# self.name_sv.set('-----')
# self.level_sv.set('-----')
# self.mf_sv.set('-----')
# self.players_x_sv.set('-----')
#
# self.exp_sv.set('0')
# self.exp_level_sv.set('{:,.0f}'.format(self.session_char_xp_missing))
# self.exp_session_sv.set('{:,.0f}'.format(self.session_char_xp - self.session_char_xp_start))
# if len(self.session_xp_runs) > 0:
# self.avg_run = sum(self.session_xp_runs) / len(self.session_xp_runs)
# self.runs_level_sv.set('{:.0f}'.format(-(-self.session_char_xp_missing / self.avg_run // 1)))
# else:
# self.runs_level_sv.set('0')

@staticmethod
def format_time(hours):
Expand Down

0 comments on commit aef139c

Please sign in to comment.