2024-06-18 15:21:43 -07:00
|
|
|
class Item
|
|
|
|
module Dyeworks
|
|
|
|
def dyeworks?
|
2024-06-18 16:50:43 -07:00
|
|
|
dyeworks_base_item.present?
|
|
|
|
end
|
2024-06-18 15:21:43 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
# Whether this is a Dyeworks item whose base item can currently be purchased
|
|
|
|
# in the NC Mall, then dyed via Dyeworks. (Owls tracks this last part!)
|
|
|
|
def dyeworks_buyable?
|
|
|
|
dyeworks_base_buyable? && dyeworks_dyeable?
|
|
|
|
end
|
2024-06-18 15:23:39 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
# Whether this is a Dyeworks item whose base item can currently be purchased
|
|
|
|
# in the NC Mall. It may or may not currently be *dyeable* in the NC Mall,
|
|
|
|
# because Dyeworks eligibility is often a limited-time event.
|
|
|
|
def dyeworks_base_buyable?
|
|
|
|
dyeworks_base_item.present? && dyeworks_base_item.currently_in_mall?
|
|
|
|
end
|
2024-06-18 15:21:43 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
# Whether this is a Dyeworks item that can be dyed in the NC Mall ~right now,
|
|
|
|
# either at any time or as a limited-time event. (Owls tracks this, not us!)
|
|
|
|
def dyeworks_dyeable?
|
|
|
|
dyeworks_permanent? || dyeworks_limited_active?
|
|
|
|
end
|
2024-06-18 15:23:39 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
# Whether this is one of the few Dyeworks items that can be dyed in the NC
|
|
|
|
# Mall at any time, rather than as part of a limited-time event. (Owls tracks
|
|
|
|
# this, not us!)
|
|
|
|
DYEWORKS_PERMANENT_PATTERN = /Permanent\s*Dyeworks/i
|
|
|
|
def dyeworks_permanent?
|
|
|
|
return false if nc_trade_value.nil?
|
|
|
|
nc_trade_value.value_text.match?(DYEWORKS_PERMANENT_PATTERN)
|
|
|
|
end
|
2024-06-18 15:21:43 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
# Whether this is a Dyeworks item that can be dyed in the NC Mall ~right
|
|
|
|
# now, as part of a limited-time event. (Owls tracks this, not us!)
|
|
|
|
#
|
|
|
|
# If we aren't sure of the final date, this will still return `true`, on
|
|
|
|
# the assumption it *is* dyeable right now and we just don't understand the
|
|
|
|
# details of what Owls told us.
|
|
|
|
def dyeworks_limited_active?
|
|
|
|
return false unless dyeworks_limited?
|
|
|
|
return true if dyeworks_limited_final_date.nil?
|
2024-06-18 16:47:05 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
# NOTE: The application is configured to NST, so this should be
|
|
|
|
# equivalent to `Date.today`, but this is clearer and more correct imo!
|
|
|
|
today_in_nst = Time.find_zone("Pacific Time (US & Canada)").today
|
2024-06-18 16:47:05 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
today_in_nst <= dyeworks_limited_final_date
|
|
|
|
end
|
2024-06-18 16:47:05 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
# Whether this is a Dyeworks item that can only be dyed as part of a
|
|
|
|
# limited-time event. (This may return true even if the end date has
|
|
|
|
# passed, see `dyeworks_limited_active?`.) (Owls tracks this, not us!)
|
|
|
|
DYEWORKS_LIMITED_PATTERN = /Limited\s*Dyeworks/i
|
|
|
|
def dyeworks_limited?
|
|
|
|
return false if nc_trade_value.nil?
|
|
|
|
nc_trade_value.value_text.match?(DYEWORKS_LIMITED_PATTERN)
|
|
|
|
end
|
2024-06-18 15:21:43 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
# If this is a limited-time Dyeworks item, this is the date we think the
|
|
|
|
# event will end on. Even if `dyeworks_limited?` returns true, this could
|
|
|
|
# still be `nil`, if we fail to parse this. (Owls tracks this, not us!)
|
|
|
|
DYEWORKS_LIMITED_FINAL_DATE_PATTERN =
|
|
|
|
/Dyeable\s*Thru\s*(?<month>[a-z]+)\s*(?<day>[0-9]+)/i
|
|
|
|
def dyeworks_limited_final_date
|
|
|
|
return nil unless dyeworks_limited?
|
2024-06-18 16:47:05 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
match = nc_trade_value.value_text.
|
|
|
|
match(DYEWORKS_LIMITED_FINAL_DATE_PATTERN)
|
|
|
|
return nil if match.nil?
|
2024-06-18 16:47:05 -07:00
|
|
|
|
Oops, fix silly bug in Dyeworks Owls date parsing
Oh right, if I assume "date in the past means it's for next year", then
that means that, when the date *does pass*, we won't realize it!
e.g. if Owls says "Dyeable Thru July 15", then on July 14 we'll parse
that as July 15, 2024; but on July 16 we'll parse it as July 16, 2025,
and so we'll think it's *still* dyeable. Under this logic, it's
actually impossible for a limited Dyeworks date to *ever* be in the
past, I think!
I think 3 months is a good compromise: it gives Owls plenty of time to
update, but allows for events that could last as long as 9 months into
the future, if I'm doing my math right.
2024-06-20 14:04:45 -07:00
|
|
|
# Parse this "<Month> <Day>" date as the *next* such date, with some
|
|
|
|
# wiggle room for the possibility that it recently passed and Owls hasn't
|
|
|
|
# updated yet: parse it as this year at first, then add a year if that
|
|
|
|
# turns out to be more than 3 months ago. (That way, if it's currently
|
|
|
|
# December 2024, then events ending in Jan will be read as Jan 2025, and
|
|
|
|
# events ending in Nov will be read as Nov 2024.)
|
2024-06-18 16:50:43 -07:00
|
|
|
#
|
|
|
|
# NOTE: This could return strange results if the Owls date contains
|
|
|
|
# something surprising! But the heuristic nature helps with e.g.
|
|
|
|
# flexibility if they abbreviate months, so let's lean into `Date.parse`.
|
2024-06-20 14:08:40 -07:00
|
|
|
begin
|
|
|
|
match => {month:, day:}
|
|
|
|
date = Date.parse("#{month} #{day}, #{Date.today.year}")
|
|
|
|
date += 1.year if date < Date.today - 3.months
|
|
|
|
rescue Date::Error
|
|
|
|
Rails.logger.warn "Could not parse Dyeworks final date: " +
|
|
|
|
"#{nc_trade_value.value_text.inspect}"
|
|
|
|
return nil
|
|
|
|
end
|
2024-06-18 16:47:05 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
date
|
|
|
|
end
|
2024-06-18 16:47:05 -07:00
|
|
|
|
2024-06-20 12:54:39 -07:00
|
|
|
# The probability of getting this item when dyeing the base item.
|
|
|
|
def dyeworks_odds
|
|
|
|
return nil unless dyeworks?
|
|
|
|
|
|
|
|
num_variants = dyeworks_base_item.dyeworks_variants.count
|
|
|
|
raise "Item's Dyeworks base has *no* variants??" if num_variants < 1
|
|
|
|
|
|
|
|
Rational(1, num_variants)
|
|
|
|
end
|
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
# Infer what base item this Dyeworks item probably relates to, based on
|
|
|
|
# their names. We only use this when a new item is modeled to initialize
|
|
|
|
# the `dyeworks_base_item` relationship in the database; after that, we
|
|
|
|
# just use whatever the database says. (This allows manual overrides!)
|
|
|
|
DYEWORKS_NAME_PATTERN = %r{
|
2024-06-28 01:32:15 -07:00
|
|
|
\A(
|
2024-06-18 16:50:43 -07:00
|
|
|
# Most Dyeworks items have a colon in the name.
|
|
|
|
Dyeworks\s+(?<color>.+?:)\s*(?<base>.+)
|
|
|
|
|
|
|
|
|
# But sometimes they omit it. If so, assume the first word is the color!
|
|
|
|
Dyeworks\s+(?<color>\S+)\s*(?<base>.+)
|
2024-06-28 01:32:15 -07:00
|
|
|
)\z
|
2024-06-18 16:50:43 -07:00
|
|
|
}x
|
|
|
|
def inferred_dyeworks_base_item
|
2024-10-21 14:24:45 -07:00
|
|
|
name_match = (name || "").match(DYEWORKS_NAME_PATTERN)
|
2024-06-18 16:50:43 -07:00
|
|
|
return nil if name_match.nil?
|
2024-06-18 15:21:43 -07:00
|
|
|
|
2024-06-18 16:50:43 -07:00
|
|
|
Item.find_by_name(name_match["base"])
|
|
|
|
end
|
2024-06-18 15:21:43 -07:00
|
|
|
end
|
|
|
|
end
|