mirror of
https://github.com/AlienMajik/pwnagotchi_plugins.git
synced 2025-07-01 18:37:27 -04:00
Update age.py
An enhanced plugin with frequent titles, dynamic quotes, progress bars, random events, handshake streaks, personality evolution, and secret achievements. UI is also optimized to avoid clutter.
This commit is contained in:
269
age.py
269
age.py
@ -14,26 +14,35 @@ from pwnagotchi.ui.view import BLACK
|
|||||||
|
|
||||||
class Age(plugins.Plugin):
|
class Age(plugins.Plugin):
|
||||||
__author__ = 'AlienMajik'
|
__author__ = 'AlienMajik'
|
||||||
__version__ = '2.0.3'
|
__version__ = '3.1.0'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
__description__ = ('Enhanced plugin with achievement tiers, configurable titles, decay mechanics, '
|
__description__ = ('An enhanced plugin with frequent titles, dynamic quotes, progress bars, '
|
||||||
'progress tracking, and dynamic status messages.')
|
'random events, handshake streaks, personality evolution, and secret achievements. '
|
||||||
|
'UI is optimized to avoid clutter.')
|
||||||
|
|
||||||
DEFAULT_AGE_TITLES = {
|
DEFAULT_AGE_TITLES = {
|
||||||
|
100: "Baby Steps",
|
||||||
|
500: "Getting the Hang of It",
|
||||||
1000: "Neon Spawn",
|
1000: "Neon Spawn",
|
||||||
2000: "Script Kiddie",
|
2000: "Script Kiddie",
|
||||||
5000: "WiFi Outlaw",
|
5000: "WiFi Outlaw",
|
||||||
10000: "Data Raider",
|
10000: "Data Raider",
|
||||||
25000: "Prophet",
|
25000: "Prophet",
|
||||||
33333: "Off the Grid"
|
33333: "Off the Grid",
|
||||||
|
55555: "Multiversed",
|
||||||
|
111111: "Intergalactic"
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_STRENGTH_TITLES = {
|
DEFAULT_STRENGTH_TITLES = {
|
||||||
|
100: "Sparring Novice",
|
||||||
|
300: "Gear Tickler",
|
||||||
500: "Fleshbag",
|
500: "Fleshbag",
|
||||||
1500: "Lightweight",
|
1500: "Lightweight",
|
||||||
2000: "Deauth King",
|
2000: "Deauth King",
|
||||||
2500: "Handshake Hunter",
|
2500: "Handshake Hunter",
|
||||||
3333: "Unstoppable"
|
3333: "Unstoppable",
|
||||||
|
55555: "Rev-9",
|
||||||
|
111111: "Kuato"
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -42,10 +51,11 @@ class Age(plugins.Plugin):
|
|||||||
'age': (10, 40),
|
'age': (10, 40),
|
||||||
'strength': (80, 40),
|
'strength': (80, 40),
|
||||||
'points': (10, 60),
|
'points': (10, 60),
|
||||||
'stars': (10, 80),
|
'progress': (10, 80),
|
||||||
|
'personality': (10, 100),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize core metrics
|
# Core metrics
|
||||||
self.epochs = 0
|
self.epochs = 0
|
||||||
self.train_epochs = 0
|
self.train_epochs = 0
|
||||||
self.network_points = 0
|
self.network_points = 0
|
||||||
@ -55,20 +65,18 @@ class Age(plugins.Plugin):
|
|||||||
self.log_path = '/root/network_points.log'
|
self.log_path = '/root/network_points.log'
|
||||||
self.handshake_dir = '/home/pi/handshakes'
|
self.handshake_dir = '/home/pi/handshakes'
|
||||||
|
|
||||||
# Configurable settings with defaults
|
# Configurable settings
|
||||||
self.max_stars = 5
|
|
||||||
self.star_interval = 1000
|
|
||||||
self.decay_interval = 50
|
self.decay_interval = 50
|
||||||
self.decay_amount = 10
|
self.decay_amount = 10
|
||||||
self.age_titles = self.DEFAULT_AGE_TITLES
|
self.age_titles = self.DEFAULT_AGE_TITLES
|
||||||
self.strength_titles = self.DEFAULT_STRENGTH_TITLES
|
self.strength_titles = self.DEFAULT_STRENGTH_TITLES
|
||||||
|
self.show_personality = False # Default to False to avoid clutter
|
||||||
|
|
||||||
# Achievement tracking attributes
|
# Achievement tracking
|
||||||
self.prev_age_title = "Unborn"
|
self.prev_age_title = "Unborn"
|
||||||
self.prev_strength_title = "Untrained"
|
self.prev_strength_title = "Untrained"
|
||||||
self.prev_stars = 0
|
|
||||||
|
|
||||||
# Additional configurations
|
# Points and quotes
|
||||||
self.points_map = {
|
self.points_map = {
|
||||||
'wpa3': 10,
|
'wpa3': 10,
|
||||||
'wpa2': 5,
|
'wpa2': 5,
|
||||||
@ -82,18 +90,29 @@ class Age(plugins.Plugin):
|
|||||||
"Don't stop now, you're almost there!",
|
"Don't stop now, you're almost there!",
|
||||||
"Keep evolving, don't let decay catch you!"
|
"Keep evolving, don't let decay catch you!"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# New features
|
||||||
|
self.last_handshake_enc = None
|
||||||
|
self.last_decay_points = 0
|
||||||
|
self.streak = 0
|
||||||
|
self.active_event = None
|
||||||
|
self.event_handshakes_left = 0
|
||||||
|
self.event_multiplier = 1.0
|
||||||
|
self.personality_points = {'aggro': 0, 'stealth': 0, 'scholar': 0}
|
||||||
|
self.night_owl_handshakes = 0
|
||||||
|
self.enc_types_captured = set()
|
||||||
|
|
||||||
self.data_lock = threading.Lock()
|
self.data_lock = threading.Lock()
|
||||||
|
|
||||||
def on_loaded(self):
|
def on_loaded(self):
|
||||||
# Load configuration options with fallbacks
|
# Load configuration options with fallbacks
|
||||||
self.max_stars = self.options.get('max_stars', 5)
|
|
||||||
self.star_interval = self.options.get('star_interval', 1000)
|
|
||||||
self.decay_interval = self.options.get('decay_interval', 50)
|
self.decay_interval = self.options.get('decay_interval', 50)
|
||||||
self.decay_amount = self.options.get('decay_amount', 10)
|
self.decay_amount = self.options.get('decay_amount', 10)
|
||||||
self.age_titles = self.options.get('age_titles', self.DEFAULT_AGE_TITLES)
|
self.age_titles = self.options.get('age_titles', self.DEFAULT_AGE_TITLES)
|
||||||
self.strength_titles = self.options.get('strength_titles', self.DEFAULT_STRENGTH_TITLES)
|
self.strength_titles = self.options.get('strength_titles', self.DEFAULT_STRENGTH_TITLES)
|
||||||
self.points_map = self.options.get('points_map', self.points_map)
|
self.points_map = self.options.get('points_map', self.points_map)
|
||||||
self.motivational_quotes = self.options.get('motivational_quotes', self.motivational_quotes)
|
self.motivational_quotes = self.options.get('motivational_quotes', self.motivational_quotes)
|
||||||
|
self.show_personality = self.options.get('show_personality', False)
|
||||||
|
|
||||||
self.load_data()
|
self.load_data()
|
||||||
self.initialize_handshakes()
|
self.initialize_handshakes()
|
||||||
@ -102,10 +121,9 @@ class Age(plugins.Plugin):
|
|||||||
"""Initialize handshake count based on existing .pcap files."""
|
"""Initialize handshake count based on existing .pcap files."""
|
||||||
if self.handshake_count == 0 and os.path.isdir(self.handshake_dir):
|
if self.handshake_count == 0 and os.path.isdir(self.handshake_dir):
|
||||||
existing = [f for f in os.listdir(self.handshake_dir) if f.endswith('.pcap')]
|
existing = [f for f in os.listdir(self.handshake_dir) if f.endswith('.pcap')]
|
||||||
if existing:
|
self.handshake_count = len(existing)
|
||||||
self.handshake_count = len(existing)
|
logging.info(f"[Age] Initialized with {self.handshake_count} handshakes")
|
||||||
logging.info(f"[Age] Initialized with {self.handshake_count} handshakes")
|
self.save_data()
|
||||||
self.save_data()
|
|
||||||
|
|
||||||
def get_age_title(self):
|
def get_age_title(self):
|
||||||
"""Determine age title based on epochs."""
|
"""Determine age title based on epochs."""
|
||||||
@ -124,19 +142,28 @@ class Age(plugins.Plugin):
|
|||||||
return "Untrained"
|
return "Untrained"
|
||||||
|
|
||||||
def random_motivational_quote(self):
|
def random_motivational_quote(self):
|
||||||
"""Return a random motivational quote from the configurable list."""
|
"""Return a context-aware motivational quote."""
|
||||||
return random.choice(self.motivational_quotes)
|
if self.last_handshake_enc:
|
||||||
|
quote = f"Boom! That {self.last_handshake_enc.upper()} never saw you coming."
|
||||||
|
self.last_handshake_enc = None
|
||||||
|
return quote
|
||||||
|
elif self.last_decay_points > 0:
|
||||||
|
quote = f"Decay stung for {self.last_decay_points}. Time to fight back!"
|
||||||
|
self.last_decay_points = 0
|
||||||
|
return quote
|
||||||
|
else:
|
||||||
|
return random.choice(self.motivational_quotes)
|
||||||
|
|
||||||
def random_inactivity_message(self, points_lost):
|
def random_inactivity_message(self, points_lost):
|
||||||
"""Return a random inactivity message with points lost."""
|
"""Return a random inactivity message with points lost."""
|
||||||
messages = [
|
messages = [
|
||||||
"Time to wake up, you're rusting!",
|
f"Time to wake up, lost {points_lost} to rust!",
|
||||||
"Decayed by {points_lost}, keep it active!",
|
f"Decayed by {points_lost}, keep it active!",
|
||||||
"Stale, but you can still revive!",
|
"Stale, but you can still revive!",
|
||||||
"Don't let inactivity hold you back!",
|
"Don't let inactivity hold you back!",
|
||||||
"Keep moving, no room for decay!"
|
"Keep moving, no room for decay!"
|
||||||
]
|
]
|
||||||
return random.choice(messages).format(points_lost=points_lost)
|
return random.choice(messages)
|
||||||
|
|
||||||
def check_achievements(self, agent):
|
def check_achievements(self, agent):
|
||||||
"""Check and announce new age or strength achievements."""
|
"""Check and announce new age or strength achievements."""
|
||||||
@ -156,7 +183,7 @@ class Age(plugins.Plugin):
|
|||||||
self.prev_strength_title = current_strength
|
self.prev_strength_title = current_strength
|
||||||
|
|
||||||
def apply_decay(self, agent):
|
def apply_decay(self, agent):
|
||||||
"""Apply decay to network points based on inactivity with refined mechanics."""
|
"""Apply decay to network points based on inactivity."""
|
||||||
inactive_epochs = self.epochs - self.last_active_epoch
|
inactive_epochs = self.epochs - self.last_active_epoch
|
||||||
if inactive_epochs >= self.decay_interval:
|
if inactive_epochs >= self.decay_interval:
|
||||||
decay_factor = inactive_epochs / self.decay_interval
|
decay_factor = inactive_epochs / self.decay_interval
|
||||||
@ -164,37 +191,22 @@ class Age(plugins.Plugin):
|
|||||||
self.network_points = max(0, self.network_points - points_lost)
|
self.network_points = max(0, self.network_points - points_lost)
|
||||||
|
|
||||||
if points_lost > 0:
|
if points_lost > 0:
|
||||||
|
self.last_decay_points = points_lost
|
||||||
|
self.streak = 0 # Reset streak on decay
|
||||||
agent.view().set('face', faces.SAD)
|
agent.view().set('face', faces.SAD)
|
||||||
agent.view().set('status', self.random_inactivity_message(points_lost))
|
agent.view().set('status', self.random_inactivity_message(points_lost))
|
||||||
logging.info(f"[Age] Applied decay: lost {points_lost} points due to inactivity")
|
logging.info(f"[Age] Applied decay: lost {points_lost} points")
|
||||||
self.last_active_epoch = self.epochs
|
self.last_active_epoch = self.epochs
|
||||||
self.save_data()
|
self.save_data()
|
||||||
|
|
||||||
def on_ui_setup(self, ui):
|
def on_ui_setup(self, ui):
|
||||||
"""Set up UI elements with configurable positions."""
|
"""Set up UI elements with configurable positions."""
|
||||||
def get_position(element):
|
def get_position(element):
|
||||||
x = self.options.get(
|
x = self.options.get(f"{element}_x", self.default_positions[element][0])
|
||||||
f"{element}_x",
|
y = self.options.get(f"{element}_y", self.default_positions[element][1])
|
||||||
self.options.get(
|
|
||||||
f"{element}_x_coord", # Backwards compatibility
|
|
||||||
self.default_positions[element][0]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
y = self.options.get(
|
|
||||||
f"{element}_y",
|
|
||||||
self.options.get(
|
|
||||||
f"{element}_y_coord", # Backwards compatibility
|
|
||||||
self.default_positions[element][1]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return (int(x), int(y))
|
return (int(x), int(y))
|
||||||
|
|
||||||
positions = {
|
positions = {key: get_position(key) for key in self.default_positions if key != 'stars'}
|
||||||
'age': get_position('age'),
|
|
||||||
'strength': get_position('strength'),
|
|
||||||
'points': get_position('points'),
|
|
||||||
'stars': get_position('stars'),
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.add_element('Age', LabeledValue(
|
ui.add_element('Age', LabeledValue(
|
||||||
color=BLACK, label='Age', value="Newborn",
|
color=BLACK, label='Age', value="Newborn",
|
||||||
@ -205,35 +217,77 @@ class Age(plugins.Plugin):
|
|||||||
position=positions['strength'], label_font=fonts.Bold, text_font=fonts.Medium))
|
position=positions['strength'], label_font=fonts.Bold, text_font=fonts.Medium))
|
||||||
|
|
||||||
ui.add_element('Points', LabeledValue(
|
ui.add_element('Points', LabeledValue(
|
||||||
color=BLACK, label='★ Pts', value="0",
|
color=BLACK, label='Pts', value="0",
|
||||||
position=positions['points'], label_font=fonts.Bold, text_font=fonts.Medium))
|
position=positions['points'], label_font=fonts.Bold, text_font=fonts.Medium))
|
||||||
|
|
||||||
ui.add_element('ReP', LabeledValue(
|
ui.add_element('Progress', LabeledValue(
|
||||||
color=BLACK, label='ReP', value="★",
|
color=BLACK, label='Next Age', value="[ ]",
|
||||||
position=positions['stars'], label_font=fonts.Bold, text_font=fonts.Medium))
|
position=positions['progress'], label_font=fonts.Bold, text_font=fonts.Medium))
|
||||||
|
|
||||||
|
if self.show_personality:
|
||||||
|
ui.add_element('Personality', LabeledValue(
|
||||||
|
color=BLACK, label='Trait', value="Neutral",
|
||||||
|
position=positions['personality'], label_font=fonts.Bold, text_font=fonts.Medium))
|
||||||
|
|
||||||
def on_ui_update(self, ui):
|
def on_ui_update(self, ui):
|
||||||
"""Update UI elements with current values."""
|
"""Update UI elements with current values."""
|
||||||
ui.set('Age', self.get_age_title())
|
ui.set('Age', self.get_age_title())
|
||||||
ui.set('Strength', self.get_strength_title())
|
ui.set('Strength', self.get_strength_title())
|
||||||
ui.set('Points', self.abrev_number(self.network_points))
|
ui.set('Points', self.abrev_number(self.network_points))
|
||||||
ui.set('ReP', self.get_star_string())
|
|
||||||
|
# Update progress bar for next age title
|
||||||
|
next_threshold = self.get_next_age_threshold()
|
||||||
|
if next_threshold:
|
||||||
|
progress = self.epochs / next_threshold
|
||||||
|
bar_length = 5
|
||||||
|
filled = int(progress * bar_length)
|
||||||
|
bar = '[' + '=' * filled + ' ' * (bar_length - filled) + ']'
|
||||||
|
ui.set('Progress', bar)
|
||||||
|
else:
|
||||||
|
ui.set('Progress', '[MAX]')
|
||||||
|
|
||||||
|
if self.show_personality:
|
||||||
|
ui.set('Personality', self.get_dominant_personality())
|
||||||
|
|
||||||
|
def get_next_age_threshold(self):
|
||||||
|
"""Get the next age title threshold."""
|
||||||
|
thresholds = sorted(self.age_titles.keys())
|
||||||
|
for t in thresholds:
|
||||||
|
if self.epochs < t:
|
||||||
|
return t
|
||||||
|
return None # Max level reached
|
||||||
|
|
||||||
def on_epoch(self, agent, epoch, epoch_data):
|
def on_epoch(self, agent, epoch, epoch_data):
|
||||||
"""Handle epoch events: increment counters, apply decay, and check achievements."""
|
"""Handle epoch events."""
|
||||||
self.epochs += 1
|
self.epochs += 1
|
||||||
# Increment train_epochs every 10 epochs to simulate slower training progress
|
|
||||||
self.train_epochs += 1 if self.epochs % 10 == 0 else 0
|
self.train_epochs += 1 if self.epochs % 10 == 0 else 0
|
||||||
|
if self.epochs % 10 == 0:
|
||||||
|
self.personality_points['scholar'] += 1
|
||||||
|
|
||||||
logging.debug(f"[Age] Epoch {self.epochs}, Points: {self.network_points}")
|
logging.debug(f"[Age] Epoch {self.epochs}, Points: {self.network_points}")
|
||||||
|
|
||||||
self.apply_decay(agent)
|
self.apply_decay(agent)
|
||||||
self.check_achievements(agent)
|
self.check_achievements(agent)
|
||||||
|
|
||||||
if self.epochs % 100 == 0:
|
if self.epochs % 100 == 0:
|
||||||
|
self.handle_random_event(agent)
|
||||||
self.age_checkpoint(agent)
|
self.age_checkpoint(agent)
|
||||||
|
|
||||||
self.save_data()
|
self.save_data()
|
||||||
|
|
||||||
|
def handle_random_event(self, agent):
|
||||||
|
"""Trigger a random event with 5% chance every 100 epochs."""
|
||||||
|
if random.random() < 0.05:
|
||||||
|
events = [
|
||||||
|
{"description": "Lucky Break: Double points for next 5 handshakes!", "multiplier": 2.0, "handshakes": 5},
|
||||||
|
{"description": "Signal Noise: Next handshake worth half points.", "multiplier": 0.5, "handshakes": 1},
|
||||||
|
]
|
||||||
|
self.active_event = random.choice(events)
|
||||||
|
self.event_handshakes_left = self.active_event["handshakes"]
|
||||||
|
self.event_multiplier = self.active_event["multiplier"]
|
||||||
|
agent.view().set('status', self.active_event["description"])
|
||||||
|
logging.info(f"[Age] Random event: {self.active_event['description']}")
|
||||||
|
|
||||||
def age_checkpoint(self, agent):
|
def age_checkpoint(self, agent):
|
||||||
"""Display milestone message every 100 epochs."""
|
"""Display milestone message every 100 epochs."""
|
||||||
view = agent.view()
|
view = agent.view()
|
||||||
@ -242,7 +296,7 @@ class Age(plugins.Plugin):
|
|||||||
view.update(force=True)
|
view.update(force=True)
|
||||||
|
|
||||||
def on_handshake(self, agent, *args):
|
def on_handshake(self, agent, *args):
|
||||||
"""Handle handshake events with enhanced error handling and logging."""
|
"""Handle handshake events with streaks and secret achievements."""
|
||||||
try:
|
try:
|
||||||
if len(args) < 3:
|
if len(args) < 3:
|
||||||
logging.warning("[Age] Insufficient arguments in on_handshake")
|
logging.warning("[Age] Insufficient arguments in on_handshake")
|
||||||
@ -253,40 +307,60 @@ class Age(plugins.Plugin):
|
|||||||
enc = ap.get('encryption', '').lower()
|
enc = ap.get('encryption', '').lower()
|
||||||
essid = ap.get('essid', 'unknown')
|
essid = ap.get('essid', 'unknown')
|
||||||
else:
|
else:
|
||||||
logging.warning(f"[Age] AP is a string, not a dictionary: {ap}. Skipping handshake processing.")
|
logging.warning(f"[Age] AP is a string: {ap}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Base points
|
||||||
points = self.points_map.get(enc, 1)
|
points = self.points_map.get(enc, 1)
|
||||||
|
|
||||||
|
# Apply streak bonus
|
||||||
|
self.streak += 1
|
||||||
|
streak_threshold = 5
|
||||||
|
streak_bonus = 1.2
|
||||||
|
if self.streak >= streak_threshold:
|
||||||
|
points *= streak_bonus
|
||||||
|
agent.view().set('status', f"Streak bonus! +{int((streak_bonus - 1) * 100)}% points")
|
||||||
|
|
||||||
|
# Apply random event multiplier
|
||||||
|
if self.active_event and self.event_handshakes_left > 0:
|
||||||
|
points *= self.event_multiplier
|
||||||
|
self.event_handshakes_left -= 1
|
||||||
|
if self.event_handshakes_left == 0:
|
||||||
|
self.active_event = None
|
||||||
|
self.event_multiplier = 1.0
|
||||||
|
|
||||||
|
points = int(points)
|
||||||
self.network_points += points
|
self.network_points += points
|
||||||
self.handshake_count += 1
|
self.handshake_count += 1
|
||||||
self.last_active_epoch = self.epochs
|
self.last_active_epoch = self.epochs
|
||||||
|
self.last_handshake_enc = enc
|
||||||
|
self.personality_points['aggro'] += 1
|
||||||
|
|
||||||
# Log handshake details with enhanced file I/O safety
|
# Secret achievements
|
||||||
try:
|
current_hour = time.localtime().tm_hour
|
||||||
with open(self.log_path, 'a') as f:
|
if 2 <= current_hour < 4:
|
||||||
f.write(f"{time.time()},{essid},{enc},{points}\n")
|
self.night_owl_handshakes += 1
|
||||||
except Exception as e:
|
if self.night_owl_handshakes == 10:
|
||||||
logging.error(f"[Age] Failed to log handshake: {str(e)}")
|
agent.view().set('status', "Achievement Unlocked: Night Owl!")
|
||||||
|
self.network_points += 50 # Bonus
|
||||||
|
|
||||||
logging.info(f"[Age] Captured handshake: {essid}, encryption: {enc}, points gained: {points}")
|
self.enc_types_captured.add(enc)
|
||||||
|
if self.enc_types_captured == set(self.points_map.keys()):
|
||||||
|
agent.view().set('status', "Achievement Unlocked: Crypto King!")
|
||||||
|
self.network_points += 100 # Bonus
|
||||||
|
|
||||||
|
# Log handshake
|
||||||
|
with open(self.log_path, 'a') as f:
|
||||||
|
f.write(f"{time.time()},{essid},{enc},{points}\n")
|
||||||
|
|
||||||
|
logging.info(f"[Age] Handshake: {essid}, enc: {enc}, points: {points}, streak: {self.streak}")
|
||||||
|
|
||||||
self.new_star_checkpoint(agent)
|
|
||||||
self.save_data()
|
self.save_data()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"[Age] Error in handshake processing: {str(e)}")
|
logging.error(f"[Age] Handshake error: {str(e)}")
|
||||||
|
|
||||||
def new_star_checkpoint(self, agent):
|
|
||||||
"""Check and announce new star tier achievements."""
|
|
||||||
stars = self.get_stars_count()
|
|
||||||
if stars > self.prev_stars:
|
|
||||||
symbol = self.get_symbol_for_handshakes()
|
|
||||||
agent.view().set('face', faces.EXCITED)
|
|
||||||
agent.view().set('status', f"New {symbol} Tier Achieved!")
|
|
||||||
self.prev_stars = stars
|
|
||||||
|
|
||||||
def load_data(self):
|
def load_data(self):
|
||||||
"""Load saved data from JSON file with defaults for new installations."""
|
"""Load saved data from JSON file."""
|
||||||
try:
|
try:
|
||||||
if os.path.exists(self.data_path):
|
if os.path.exists(self.data_path):
|
||||||
with open(self.data_path, 'r') as f:
|
with open(self.data_path, 'r') as f:
|
||||||
@ -298,17 +372,11 @@ class Age(plugins.Plugin):
|
|||||||
self.last_active_epoch = data.get('last_active', 0)
|
self.last_active_epoch = data.get('last_active', 0)
|
||||||
self.prev_age_title = data.get('prev_age', self.get_age_title())
|
self.prev_age_title = data.get('prev_age', self.get_age_title())
|
||||||
self.prev_strength_title = data.get('prev_strength', self.get_strength_title())
|
self.prev_strength_title = data.get('prev_strength', self.get_strength_title())
|
||||||
self.prev_stars = data.get('prev_stars', self.get_stars_count())
|
self.streak = data.get('streak', 0)
|
||||||
else:
|
self.night_owl_handshakes = data.get('night_owl_handshakes', 0)
|
||||||
# Set defaults for a new installation
|
self.enc_types_captured = set(data.get('enc_types_captured', []))
|
||||||
self.epochs = 0
|
for trait in ['aggro', 'stealth', 'scholar']:
|
||||||
self.train_epochs = 0
|
self.personality_points[trait] = data.get(f'personality_{trait}', 0)
|
||||||
self.network_points = 0
|
|
||||||
self.handshake_count = 0
|
|
||||||
self.last_active_epoch = 0
|
|
||||||
self.prev_age_title = self.get_age_title()
|
|
||||||
self.prev_strength_title = self.get_strength_title()
|
|
||||||
self.prev_stars = self.get_stars_count()
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"[Age] Load error: {str(e)}")
|
logging.error(f"[Age] Load error: {str(e)}")
|
||||||
|
|
||||||
@ -322,7 +390,12 @@ class Age(plugins.Plugin):
|
|||||||
'last_active': self.last_active_epoch,
|
'last_active': self.last_active_epoch,
|
||||||
'prev_age': self.get_age_title(),
|
'prev_age': self.get_age_title(),
|
||||||
'prev_strength': self.get_strength_title(),
|
'prev_strength': self.get_strength_title(),
|
||||||
'prev_stars': self.get_stars_count(),
|
'streak': self.streak,
|
||||||
|
'night_owl_handshakes': self.night_owl_handshakes,
|
||||||
|
'enc_types_captured': list(self.enc_types_captured),
|
||||||
|
'personality_aggro': self.personality_points['aggro'],
|
||||||
|
'personality_stealth': self.personality_points['stealth'],
|
||||||
|
'personality_scholar': self.personality_points['scholar'],
|
||||||
}
|
}
|
||||||
with self.data_lock:
|
with self.data_lock:
|
||||||
try:
|
try:
|
||||||
@ -331,27 +404,17 @@ class Age(plugins.Plugin):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"[Age] Save error: {str(e)}")
|
logging.error(f"[Age] Save error: {str(e)}")
|
||||||
|
|
||||||
def get_stars_count(self):
|
def get_dominant_personality(self):
|
||||||
"""Calculate current number of stars."""
|
"""Determine dominant personality trait."""
|
||||||
return min(self.handshake_count // self.star_interval, self.max_stars)
|
if not any(self.personality_points.values()):
|
||||||
|
return "Neutral"
|
||||||
def get_symbol_for_handshakes(self):
|
dominant = max(self.personality_points, key=self.personality_points.get)
|
||||||
"""Return symbol based on handshake count."""
|
return dominant.capitalize()
|
||||||
return '♣' if self.handshake_count >= 10000 else '♦' if self.handshake_count >= 5000 else '★'
|
|
||||||
|
|
||||||
def get_star_string(self):
|
|
||||||
"""Return string of star symbols."""
|
|
||||||
return self.get_symbol_for_handshakes() * self.get_stars_count()
|
|
||||||
|
|
||||||
def abrev_number(self, num):
|
def abrev_number(self, num):
|
||||||
"""Abbreviate large numbers (e.g., 1000 -> 1K)."""
|
"""Abbreviate large numbers."""
|
||||||
for unit in ['','K','M','B']:
|
for unit in ['','K','M','B']:
|
||||||
if abs(num) < 1000:
|
if abs(num) < 1000:
|
||||||
return f"{num:.1f}{unit}".rstrip('.0')
|
return f"{num:.1f}{unit}".rstrip('.0')
|
||||||
num /= 1000.0
|
num /= 1000.0
|
||||||
return f"{num:.1f}T"
|
return f"{num:.1f}T"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user