-
Notifications
You must be signed in to change notification settings - Fork 59
fix(killtracker): v2.12 codex tracking #2336
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,9 +13,15 @@ | |
| contributors: Nisugi | ||
| game: Gemstone | ||
| tags: hunting, combat, tracking, gemstones, jewels, dust, klocks, data | ||
| version: 2.11 | ||
| version: 2.12 | ||
|
|
||
| Change Log: | ||
| v2.12 (2026-05-24) | ||
| - Added separate codex-eligible search counter (weekly_codex_searches) | ||
| - Gem and codex eligibility are gated independently | ||
| - Added codex tracking for onslaught creatures (10/week cap) | ||
| - Added ;kt codex report command and codex section in summary | ||
| - Codex finds submitted to external spreadsheet when enabled | ||
| v2.11 (2026-05-24) | ||
| - Fix for capitalization in dust found messaging | ||
| v2.10 (2025-09-21) | ||
|
|
@@ -122,6 +128,10 @@ module Killtracker | |
| $killtracker[:weekly_counts] ||= {} | ||
| $killtracker[:jewel_found_this_week] ||= false | ||
| $killtracker[:debug_eligibility] ||= false | ||
| $killtracker[:codex_found] ||= {} | ||
| $killtracker[:weekly_codex] ||= 0 | ||
| $killtracker[:weekly_codex_searches] ||= 0 | ||
| $killtracker[:searches_since_codex] ||= 0 | ||
| end | ||
|
|
||
| def self.create_backup(reason = "manual") | ||
|
|
@@ -330,16 +340,22 @@ module Killtracker | |
| total_this_week = $killtracker[:weekly_ascension_searches].to_i | ||
| weekly_dust = $killtracker[:weekly_dust].to_i | ||
| weekly_gemstone = $killtracker[:weekly_gemstone].to_i | ||
| weekly_codex = $killtracker[:weekly_codex].to_i | ||
| weekly_codex_searches = $killtracker[:weekly_codex_searches].to_i | ||
|
|
||
| $killtracker[:weekly_counts][:"week_#{finished_week}_ascension_searches"] = total_this_week | ||
| $killtracker[:weekly_counts][:"week_#{finished_week}_dust"] = weekly_dust | ||
| $killtracker[:weekly_counts][:"week_#{finished_week}_gemstone"] = weekly_gemstone | ||
| $killtracker[:weekly_counts][:"week_#{finished_week}_codex"] = weekly_codex | ||
| $killtracker[:weekly_counts][:"week_#{finished_week}_codex_searches"] = weekly_codex_searches | ||
|
|
||
| $killtracker[:monthly_ascension_searches] += total_this_week | ||
|
|
||
| $killtracker[:weekly_ascension_searches] = 0 | ||
| $killtracker[:weekly_gemstone] = 0 | ||
| $killtracker[:weekly_dust] = 0 | ||
| $killtracker[:weekly_codex] = 0 | ||
| $killtracker[:weekly_codex_searches] = 0 | ||
| $killtracker[:jewel_found_this_week] = false | ||
| $killtracker[:last_week_reset] = $killtracker[:cached_reset_time] | ||
|
|
||
|
|
@@ -406,6 +422,8 @@ module Killtracker | |
| $killtracker[:weekly_gemstone] = 0 | ||
| $killtracker[:monthly_gemstones] = 0 | ||
| $killtracker[:weekly_dust] = 0 | ||
| $killtracker[:weekly_codex] = 0 | ||
| $killtracker[:weekly_codex_searches] = 0 | ||
| $killtracker[:monthly_ascension_searches] = 0 | ||
| $killtracker[:jewel_found_this_week] = false | ||
|
|
||
|
|
@@ -432,6 +450,14 @@ module Killtracker | |
| end | ||
| end | ||
|
|
||
| $killtracker[:codex_found].each do |key, _| | ||
| next unless key.is_a?(Integer) | ||
| local = tz.to_local(Time.at(key)) | ||
| if local.strftime("%U").to_i == current_week && local.year == current_year | ||
| $killtracker[:weekly_codex] += 1 | ||
| end | ||
| end | ||
|
|
||
| $killtracker[:weekly_counts].each do |week_key, searches| | ||
| next unless week_key.to_s.include?('ascension_searches') | ||
| next unless searches.is_a?(Integer) | ||
|
|
@@ -448,6 +474,7 @@ module Killtracker | |
| update_eligibility | ||
| respond("Counters successfully recalculated.") | ||
| respond(" Weekly gems: #{$killtracker[:weekly_gemstone]}, Monthly gems: #{$killtracker[:monthly_gemstones]}") | ||
| respond(" Weekly codex: #{$killtracker[:weekly_codex]}") | ||
| respond(" Jewel found this week: #{$killtracker[:jewel_found_this_week]}") | ||
| rescue => e | ||
| respond("Error during backfill: #{e.message}") | ||
|
|
@@ -465,6 +492,7 @@ module Killtracker | |
| respond(";kt gemstones report - find report broken down by week") | ||
| respond(";kt jewel report - find report of all jewels") | ||
| respond(";kt dust report - find report of all dust") | ||
| respond(";kt codex report - find report of all codex") | ||
| respond(";kt fix find count - fixes monthly/weekly gemstone count in summary") | ||
| respond(";kt save - force search data to be saved to file") | ||
| respond(";kt backup - create a manual backup of current data") | ||
|
|
@@ -480,15 +508,19 @@ module Killtracker | |
| begin | ||
| gems_total = $killtracker[:jewel_found].size | ||
| dust_total = $killtracker[:dust_found].size | ||
| codex_total = $killtracker[:codex_found].size | ||
|
|
||
| weekly_searches = $killtracker[:weekly_ascension_searches].to_i | ||
| monthly_searches = calculate_monthly_eligible_searches | ||
| gems_this_week = $killtracker[:weekly_gemstone].to_i | ||
| dust_this_week = $killtracker[:weekly_dust].to_i | ||
| codex_this_week = $killtracker[:weekly_codex].to_i | ||
| codex_searches_this_week = $killtracker[:weekly_codex_searches].to_i | ||
| gems_this_month = $killtracker[:monthly_gemstones].to_i | ||
|
|
||
| since_last_gem = $killtracker[:searches_since_jewel].to_i | ||
| since_last_dust = $killtracker[:searches_since_dust].to_i | ||
| since_last_codex = $killtracker[:searches_since_codex].to_i | ||
|
|
||
| total_searches_for_gems = $killtracker[:jewel_found].values | ||
| .map { |ev| ev[:searches_week].to_i } | ||
|
|
@@ -498,10 +530,18 @@ module Killtracker | |
| .map { |ev| ev[:searches_week].to_i } | ||
| .sum | ||
|
|
||
| avg_per_gem = gems_total > 0 ? (total_searches_for_gems.to_f / gems_total).round : 0 | ||
| avg_per_dust = dust_total > 0 ? (total_searches_for_dust.to_f / dust_total).round : 0 | ||
| total_searches_for_codex = $killtracker[:codex_found].values | ||
| .map { |ev| ev[:searches_week].to_i } | ||
| .sum | ||
|
|
||
| avg_per_gem = gems_total > 0 ? (total_searches_for_gems.to_f / gems_total).round : 0 | ||
| avg_per_dust = dust_total > 0 ? (total_searches_for_dust.to_f / dust_total).round : 0 | ||
| avg_per_codex = codex_total > 0 ? (total_searches_for_codex.to_f / codex_total).round : 0 | ||
|
|
||
| remaining_gems = [0, 3 - gems_this_month].max | ||
| remaining_gems = [0, 3 - gems_this_month].max | ||
| remaining_codex = [0, CODEX_WEEKLY_CAP - codex_this_week].max | ||
|
|
||
| codex_status = codex_currently_eligible? ? "Eligible (#{codex_this_week}/#{CODEX_WEEKLY_CAP})" : "Ineligible (#{codex_this_week}/#{CODEX_WEEKLY_CAP})" | ||
|
|
||
| # Determine weekly status with gem number | ||
| if currently_eligible? | ||
|
|
@@ -535,8 +575,16 @@ module Killtracker | |
| [" This Week", dust_this_week], | ||
| [" Avg Searches/Dust", avg_per_dust.with_commas], | ||
| [], | ||
| ["Codex Found (all)", codex_total.with_commas], | ||
| [" This Week", codex_this_week], | ||
| [" Codex Searches This Week", codex_searches_this_week.with_commas], | ||
| [" Remaining This Week", remaining_codex], | ||
| [" Status", codex_status], | ||
| [" Avg Searches/Codex", avg_per_codex.with_commas], | ||
| [], | ||
| ["Since Last Gem", since_last_gem.with_commas], | ||
| ["Since Last Dust", since_last_dust.with_commas], | ||
| ["Since Last Codex", since_last_codex.with_commas], | ||
| ] | ||
|
|
||
| table = Terminal::Table.new( | ||
|
|
@@ -607,6 +655,35 @@ module Killtracker | |
| end | ||
| end | ||
|
|
||
| def self.codex_report | ||
| begin | ||
| events = $killtracker[:codex_found].sort_by { |key, _| key.to_i } | ||
|
|
||
| total_searches_for_codex = events.map { |_, ev| ev[:searches_week].to_i }.sum | ||
|
|
||
| rows = events.map do |key, ev| | ||
| time_str = format_time_eastern(key.to_i) | ||
| searches = (ev[:searches_week] || 0).with_commas | ||
| since_last = (ev[:searches_since] || 0).with_commas | ||
| creature = ev[:creature] || "" | ||
| room = ev[:room] || "" | ||
| name = ev[:name] || "" | ||
| [time_str, searches, since_last, creature, room, name] | ||
| end | ||
|
|
||
| title = "Detailed Codex Report: #{$killtracker[:codex_found].size} Codex over #{total_searches_for_codex.with_commas} Eligible Searches" | ||
| table = Terminal::Table.new( | ||
| title: title, | ||
| headings: ["Time", "Week Searches", "Since Last Codex", "Creature", "Room", "Name"], | ||
| rows: rows | ||
| ) | ||
|
|
||
| respond table.to_s | ||
| rescue => e | ||
| respond "Error generating codex report: #{e.message}" | ||
| end | ||
| end | ||
|
|
||
| def self.gemstones_report(weeks_back = nil) | ||
| begin | ||
| tz = get_eastern_tz | ||
|
|
@@ -629,6 +706,14 @@ module Killtracker | |
| since: ev[:searches_since] | ||
| ) | ||
| end | ||
| $killtracker[:codex_found].each do |key, ev| | ||
| next unless key.is_a?(Integer) && ev.is_a?(Hash) | ||
| combined << ev.merge( | ||
| timestamp: key, | ||
| type: "Codex", | ||
| since: ev[:searches_since] | ||
| ) | ||
| end | ||
|
|
||
| events_by_week = combined.group_by do |ev| | ||
| tz.to_local(Time.at(ev[:timestamp])).strftime("%U").to_i | ||
|
|
@@ -751,6 +836,8 @@ module Killtracker | |
| ev = $killtracker[:dust_found][key] | ||
| when "jewel" | ||
| ev = $killtracker[:jewel_found][key] | ||
| when "codex" | ||
| ev = $killtracker[:codex_found][key] | ||
| end | ||
| return unless ev | ||
|
|
||
|
|
@@ -801,6 +888,16 @@ module Killtracker | |
| end | ||
| respond(" Sent #{sent_dust} dust records") | ||
|
|
||
| respond("Sending found codex...") | ||
| sent_codex = 0 | ||
| $killtracker[:codex_found].each do |timestamp, _| | ||
| next unless timestamp.is_a?(Integer) | ||
| if send_to_sheet("codex", timestamp) | ||
| sent_codex += 1 | ||
| end | ||
| end | ||
| respond(" Sent #{sent_codex} codex records") | ||
|
|
||
| respond("Sending complete.") | ||
| respond("View the data at: https://docs.google.com/spreadsheets/d/1IOLs8AGRR45Kr6Y9nz6CXlMVBKYR7cHLaz0jjAbjMv0") | ||
| respond("") | ||
|
|
@@ -902,6 +999,18 @@ module Killtracker | |
| eligible | ||
| end | ||
|
|
||
| def self.codex_currently_eligible? | ||
| eligible = $killtracker[:weekly_codex].to_i < CODEX_WEEKLY_CAP | ||
|
|
||
| if $killtracker[:debug_eligibility] | ||
| echo "Codex Eligibility Check:" | ||
| echo " Weekly codex: #{$killtracker[:weekly_codex]} / #{CODEX_WEEKLY_CAP}" | ||
| echo " Eligible: #{eligible}" | ||
| end | ||
|
|
||
| eligible | ||
| end | ||
|
|
||
| def self.update_eligibility | ||
| begin | ||
| @eligibility_file = File.join(DATA_DIR, XMLData.game, "jewel_eligibility.yaml") | ||
|
|
@@ -967,13 +1076,14 @@ module Killtracker | |
| errors = [] | ||
|
|
||
| [:weekly_ascension_searches, :monthly_ascension_searches, :searches_since_jewel, | ||
| :searches_since_dust, :monthly_gemstones, :weekly_gemstone, :weekly_dust].each do |key| | ||
| :searches_since_dust, :monthly_gemstones, :weekly_gemstone, :weekly_dust, | ||
| :weekly_codex, :weekly_codex_searches, :searches_since_codex].each do |key| | ||
| if $killtracker[key] && $killtracker[key] < 0 | ||
| errors << "#{key} is negative: #{$killtracker[key]}" | ||
| end | ||
| end | ||
|
|
||
| [$killtracker[:jewel_found], $killtracker[:dust_found]].each do |events| | ||
| [$killtracker[:jewel_found], $killtracker[:dust_found], $killtracker[:codex_found]].each do |events| | ||
| next unless events | ||
| events.each do |timestamp, _data| | ||
| unless timestamp.is_a?(Integer) | ||
|
|
@@ -1000,6 +1110,10 @@ module Killtracker | |
| errors << "Weekly gemstone exceeds maximum: #{$killtracker[:weekly_gemstone]}" | ||
| end | ||
|
|
||
| if $killtracker[:weekly_codex].to_i > CODEX_WEEKLY_CAP | ||
| errors << "Weekly codex exceeds maximum: #{$killtracker[:weekly_codex]}" | ||
| end | ||
|
|
||
| errors | ||
| end | ||
|
|
||
|
|
@@ -1016,6 +1130,8 @@ module Killtracker | |
|
|
||
| FOUND_DUST = %r{<pushBold/>You notice a scintillating mote of gemstone dust on the ground and gather it quickly\.}i | ||
| FOUND_GEMSTONE = %r{<pushBold/> \*\* A glint of light catches your eye, and you notice an? <a exist="\d+" noun="\w+">(?<n>[^<]+)</a> at your feet! \*\*} | ||
| FOUND_CODEX = %r{Among the remains, you find an? <a exist="\d+" noun="codex">(?<n>[^<]+)</a>, which falls alongside your feet\.} | ||
| CODEX_WEEKLY_CAP = 10 unless const_defined?(:CODEX_WEEKLY_CAP) | ||
| ASCENSION_CREATURES = Regexp.union( | ||
| %r{armored battle mastodon}, | ||
| %r{black valravn}, | ||
|
|
@@ -1064,6 +1180,16 @@ module Killtracker | |
| %r{merrow oracle} | ||
| ) | ||
|
|
||
| ONSLAUGHT_CREATURES = Regexp.union( | ||
| %r{battle-worn Empyrean captain}, | ||
| %r{branded goliath diviner}, | ||
| %r{burly goliath engineer}, | ||
| %r{haze-shrouded goliath diviner}, | ||
| %r{masked goliath plunderer}, | ||
| %r{radiant-eyed goliath auramancer}, | ||
| %r{tawny armor-clad pegasus} | ||
| ) | ||
|
|
||
| def self.parse_downstream(line) | ||
| case line | ||
| when FOUND_GEMSTONE | ||
|
|
@@ -1089,6 +1215,26 @@ module Killtracker | |
| REPORT_QUEUE.push(report) | ||
| REPORT_QUEUE.push(["send jewel report", key]) if $killtracker[:submit_finds] | ||
|
|
||
| when FOUND_CODEX | ||
| key = Time.now.to_i | ||
| name = Regexp.last_match[:n] | ||
| room = Room.current.id.to_s | ||
|
|
||
| create_backup("pre_codex_find") | ||
|
|
||
| $killtracker[:weekly_codex] += 1 | ||
| $killtracker[:codex_found][key] = { | ||
| searches_since: $killtracker[:searches_since_codex], | ||
| searches_week: $killtracker[:weekly_ascension_searches], | ||
|
Comment on lines
+1246
to
+1249
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Persist the codex search counter here, not the gem search counter. Line 1228 stores Suggested fix $killtracker[:codex_found][key] = {
searches_since: $killtracker[:searches_since_codex],
- searches_week: $killtracker[:weekly_ascension_searches],
+ searches_week: $killtracker[:weekly_codex_searches],
name: name,
room: room,
creature: $killtracker[:creature]
}🤖 Prompt for AI Agents |
||
| name: name, | ||
| room: room, | ||
| creature: $killtracker[:creature] | ||
| } | ||
| report = ['found codex', $killtracker[:creature], $killtracker[:weekly_codex], $killtracker[:searches_since_codex]] | ||
| $killtracker[:searches_since_codex] = 0 | ||
| REPORT_QUEUE.push(report) | ||
| REPORT_QUEUE.push(["send codex report", key]) if $killtracker[:submit_finds] | ||
|
|
||
| when FOUND_DUST | ||
| key = Time.now.to_i | ||
| room = Room.current.id.to_s | ||
|
|
@@ -1110,7 +1256,14 @@ module Killtracker | |
| maybe_reset_monthly_counter | ||
| name = Regexp.last_match[:creature] | ||
| $killtracker[:creature] = name | ||
| if ASCENSION_CREATURES.match?(name) | ||
|
|
||
| is_ascension = ASCENSION_CREATURES.match?(name) | ||
| is_onslaught = ONSLAUGHT_CREATURES.match?(name) | ||
|
|
||
| # Ascension and onslaught creatures both drop gems and dust. | ||
| # Codex only drops from onslaught creatures. | ||
| # Gem and codex eligibility are gated independently. | ||
| if is_ascension || is_onslaught | ||
| # Always count dust searches (dust has no limits) | ||
| $killtracker[:searches_since_dust] += 1 | ||
|
|
||
|
|
@@ -1124,6 +1277,16 @@ module Killtracker | |
| elsif $killtracker[:debug_eligibility] | ||
| echo "Search not counted for gems - currently ineligible (#{name})" | ||
| end | ||
|
|
||
| # Onslaught creatures additionally contribute to codex tracking | ||
| if is_onslaught | ||
| if codex_currently_eligible? | ||
| $killtracker[:weekly_codex_searches] += 1 | ||
| $killtracker[:searches_since_codex] += 1 | ||
| elsif $killtracker[:debug_eligibility] | ||
| echo "Search not counted for codex - currently ineligible (#{name})" | ||
| end | ||
| end | ||
| end | ||
| end | ||
| line | ||
|
|
@@ -1173,10 +1336,16 @@ module Killtracker | |
| Lich::Messaging.stream_window("Found a gemstone in #{week} searches. (#{creature}) - Since last Jewel: (#{jewel})", "speech") | ||
| save | ||
| update_eligibility | ||
| when "found codex" | ||
| _, creature, weekly_codex, since = report | ||
| Lich::Messaging.stream_window("Found a codex after #{since} searches. (#{creature}) - Week Total: (#{weekly_codex}/#{CODEX_WEEKLY_CAP})", "speech") | ||
| save | ||
| when /send dust report/ | ||
| send_to_sheet("dust", report[1]) | ||
| when /send jewel report/ | ||
| send_to_sheet("jewel", report[1]) | ||
| when /send codex report/ | ||
| send_to_sheet("codex", report[1]) | ||
| end | ||
| end | ||
|
|
||
|
|
@@ -1210,6 +1379,8 @@ module Killtracker | |
| Killtracker.jewel_report | ||
| when /dust report/ | ||
| Killtracker.dust_report | ||
| when /codex report/ | ||
| Killtracker.codex_report | ||
| when /gemstones? report(?:\s+(\d+))?$/ | ||
| weeks = $1 ? $1.to_i : nil | ||
| Killtracker.gemstones_report(weeks) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't zero
weekly_codex_searchesunless you also rebuild it.This method recomputes
weekly_codex, but notweekly_codex_searches. After;kt fix find count, the codex summary added in this PR will show0current-week codex searches until more kills arrive, even if the previous value was correct.🤖 Prompt for AI Agents