Merge pull request #219 from Sakimori/the-storm

Finish brewing the storm
This commit is contained in:
Sakimori 2021-03-13 16:40:06 -05:00 committed by GitHub
commit fd63a2381a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 336 additions and 53 deletions

View file

@ -170,8 +170,11 @@ accepting pull requests, check the issues for to-dos.
- drizzle 🌧: causes each inning to start with the previous inning's final batter on second base. - drizzle 🌧: causes each inning to start with the previous inning's final batter on second base.
- heat wave 🌄: occasionally causes pitchers to be relieved by a random player from the lineup. - heat wave 🌄: occasionally causes pitchers to be relieved by a random player from the lineup.
- breezy 🎐: occasionally swaps letters of a player's name, altering their name for the remainder of the game and changing their stats. - breezy 🎐: occasionally swaps letters of a player's name, altering their name for the remainder of the game and changing their stats.
- starlight 🌃: current patch weather, effects will be revealed the next time weathers are added. - starlight 🌃: the stars are displeased with dingers and will cancel most of them out by pulling them foul.
- meteor shower 🌠: current patch weather, effects will be revealed the next time weathers are added. - meteor shower 🌠: has a chance to warp runners on base to none base causing them to score.
- hurricane 🌀: current patch weather, its effects will be revealed when new weathers are added.
- tornado 🌪: current patch weather, its effects will be revealed when new weathers are added.
- torrential downpour ⛈: current patch weather, its effects will be revealed when new weathers are added.
## patreon! ## patreon!

View file

@ -371,7 +371,7 @@ def add_team_obl(team):
conn.commit() conn.commit()
conn.close() conn.close()
def save_obl_results(winning_team, losing_team): def save_obl_results(winning_team, losing_team, xvi_team = None):
conn = create_connection() conn = create_connection()
if conn is not None: if conn is not None:
c=conn.cursor() c=conn.cursor()
@ -392,7 +392,22 @@ def save_obl_results(winning_team, losing_team):
obl_points += 1 obl_points += 1
c.execute("UPDATE one_big_league SET teams_beaten_list = ?, current_opponent_pool = ?, obl_points = ? WHERE team_name = ?", (list_to_newline_string(beaten_teams), list_to_newline_string(opponent_teams), obl_points, winning_team.name)) c.execute("UPDATE one_big_league SET teams_beaten_list = ?, current_opponent_pool = ?, obl_points = ? WHERE team_name = ?", (list_to_newline_string(beaten_teams), list_to_newline_string(opponent_teams), obl_points, winning_team.name))
conn.commit()
conn.close()
if xvi_team is not None:
add_obl_point(xvi_team)
return
def add_obl_point(team):
conn = create_connection()
if conn is not None:
c=conn.cursor()
c.execute("SELECT obl_points FROM one_big_league WHERE team_name = ?", (team.name,))
xvi_obl_points = c.fetchone()[0]
xvi_obl_points += 1
c.execute("UPDATE one_big_league SET obl_points = ? WHERE team_name = ?", (xvi_obl_points, team.name))
conn.commit() conn.commit()
conn.close() conn.close()
return return
@ -403,15 +418,15 @@ def get_obl_stats(team, full = False):
c=conn.cursor() c=conn.cursor()
opponents_string = None opponents_string = None
while opponents_string is None: while opponents_string is None:
c.execute("SELECT teams_beaten_list, current_opponent_pool, rival_name FROM one_big_league WHERE team_name = ?", (team.name,)) c.execute("SELECT teams_beaten_list, current_opponent_pool, rival_name, obl_points FROM one_big_league WHERE team_name = ?", (team.name,))
try: try:
beaten_string, opponents_string, rival_name = c.fetchone() beaten_string, opponents_string, rival_name, obl_points = c.fetchone()
except TypeError: #add team to OBL except TypeError: #add team to OBL
obl_points = 0
add_team_obl(team) add_team_obl(team)
beaten_teams = newline_string_to_list(beaten_string) beaten_teams = newline_string_to_list(beaten_string)
opponent_teams = opponents_string opponent_teams = opponents_string
obl_points = len(beaten_teams)
teams_list = [name for name, points in obl_leaderboards()] teams_list = [name for name, points in obl_leaderboards()]
rank = teams_list.index(team.name) + 1 rank = teams_list.index(team.name) + 1

View file

@ -635,7 +635,13 @@ class game(object):
if self.inning > self.max_innings and self.teams["home"].score != self.teams["away"].score: #game over if self.inning > self.max_innings and self.teams["home"].score != self.teams["away"].score: #game over
self.over = True self.over = True
if self.max_innings >= 9: if self.max_innings >= 9:
db.save_obl_results(self.teams["home"] if self.teams["home"].score > self.teams["away"].score else self.teams["away"], self.teams["home"] if self.teams["home"].score < self.teams["away"].score else self.teams["away"]) if self.teams["home"].score == 16:
this_xvi_team = self.teams["home"]
elif self.teams["away"].score == 16:
this_xvi_team = self.teams["away"]
else:
this_xvi_team = None
db.save_obl_results(self.teams["home"] if self.teams["home"].score > self.teams["away"].score else self.teams["away"], self.teams["home"] if self.teams["home"].score < self.teams["away"].score else self.teams["away"], xvi_team=this_xvi_team)
def end_of_game_report(self): def end_of_game_report(self):

View file

@ -146,13 +146,15 @@ def save_league(league):
state_dic = { state_dic = {
"season" : league.season, "season" : league.season,
"day" : league.day, "day" : league.day,
"last_weather_event" : league.last_weather_event_day,
"constraints" : league.constraints, "constraints" : league.constraints,
"schedule" : league.schedule,
"game_length" : league.game_length, "game_length" : league.game_length,
"series_length" : league.series_length, "series_length" : league.series_length,
"games_per_hour" : league.games_per_hour, "games_per_hour" : league.games_per_hour,
"owner" : league.owner, "owner" : league.owner,
"champion" : league.champion, "champion" : league.champion,
"schedule" : league.schedule,
"forecasts" : league.weather_forecast,
"historic" : league.historic "historic" : league.historic
} }
with open(os.path.join(data_dir, league_dir, league.name, f"{league.name}.state"), "w") as state_file: with open(os.path.join(data_dir, league_dir, league.name, f"{league.name}.state"), "w") as state_file:

View file

@ -1,5 +1,6 @@
import time, asyncio, json, jsonpickle, random, math, os import time, asyncio, json, jsonpickle, random, math, os
import league_storage as league_db import league_storage as league_db
from weather import WeatherChains, all_weathers
from itertools import chain from itertools import chain
from copy import deepcopy from copy import deepcopy
from games import team, game from games import team, game
@ -16,6 +17,10 @@ class league_structure(object):
self.season = 1 self.season = 1
self.autoplay = -1 self.autoplay = -1
self.champion = None self.champion = None
self.weather_forecast = {}
self.weather_override = None #set to a weather for league-wide weather effects
self.last_weather_event_day = 0
self.weather_event_duration = 0
def setup(self, league_dic, division_games = 1, inter_division_games = 1, inter_league_games = 1, games_per_hour = 2): def setup(self, league_dic, division_games = 1, inter_division_games = 1, inter_league_games = 1, games_per_hour = 2):
self.league = league_dic # { subleague name : { division name : [team object] } } self.league = league_dic # { subleague name : { division name : [team object] } }
@ -33,6 +38,9 @@ class league_structure(object):
self.active = False self.active = False
self.games_per_hour = games_per_hour self.games_per_hour = games_per_hour
def season_reset(self): def season_reset(self):
self.season += 1 self.season += 1
self.day = 1 self.day = 1
@ -256,6 +264,33 @@ class league_structure(object):
scheduled = True scheduled = True
day += 1 day += 1
#now do forecasts
for this_team in self.teams_in_league():
start_weather = WeatherChains.starting_weather() #gets a random starting weather class
start_weather_duration = random.randint(start_weather.duration_range[0], start_weather.duration_range[1])
self.weather_forecast[this_team.name] = [start_weather.name] * start_weather_duration
forecasted_days = []
for i in range(start_weather_duration, len(self.schedule.keys())):
if i not in forecasted_days:
prev_weather = self.weather_forecast[this_team.name][i-1] #get last weather name
next_weather = WeatherChains.chain_weather(all_weathers()[prev_weather]) #ask weatherchains for next weather
next_weather_duration = random.randint(next_weather.duration_range[0], next_weather.duration_range[1])
self.weather_forecast[this_team.name] += [next_weather.name] * next_weather_duration
forecasted_days = [n for n in range(i, i + next_weather_duration)]
def new_weathers_midseason(self, team_name): #generate new forecast for specific team
start_weather = WeatherChains.starting_weather() #gets a random starting weather class
start_weather_duration = self.day - 1 if self.day > 1 else random.randint(start_weather.duration_range[0], start_weather.duration_range[1])
self.weather_forecast[team_name] = [start_weather.name] * start_weather_duration
forecasted_days = []
for i in range(start_weather_duration, len(self.schedule.keys())):
if i not in forecasted_days:
prev_weather = self.weather_forecast[team_name][i-1] #get last weather name
next_weather = WeatherChains.chain_weather(all_weathers()[prev_weather]) #ask weatherchains for next weather
next_weather_duration = random.randint(next_weather.duration_range[0], next_weather.duration_range[1])
self.weather_forecast[team_name] += [next_weather.name] * next_weather_duration
forecasted_days = [n for n in range(i, i + next_weather_duration)]
def division_standings(self, division, standings): def division_standings(self, division, standings):
def sorter(team_in_list): def sorter(team_in_list):
if team_in_list[2] == 0 and team_in_list[1] == 0: if team_in_list[2] == 0 and team_in_list[1] == 0:
@ -433,6 +468,32 @@ class league_structure(object):
this_embed.add_field(name=player_name, value=content_string, inline=False) this_embed.add_field(name=player_name, value=content_string, inline=False)
return this_embed return this_embed
def get_weather_now(self, team_name):
if self.weather_override is None or self.weather_event_duration <= 0: #if no override set or if past event expired
if self.day < len(self.weather_forecast[team_name]) and random.random() < 0.08: #8% chance the forcast was wrong
if random.random() < 0.33:
return all_weathers()[self.weather_forecast[team_name][self.day]] #next weather came a day early
elif random.random() < 0.66:
return random.choice(WeatherChains.parent_weathers(all_weathers()[self.weather_forecast[team_name][self.day]])) #pivot to different parent weather to lead in
else:
return WeatherChains.chain_weather(all_weathers()[self.weather_forecast[team_name][self.day - 1]]) #jump to a child weather for a day
return all_weathers()[self.weather_forecast[team_name][self.day - 1]]
else:
if self.weather_event_duration == 1 and random.random() < 0.1: #once per weather event, roll for forecast regen
self.new_weathers_midseason(team_name)
return self.weather_override
def weather_event_check(self): #2 for new event, 1 for continued event, 0 for no event
if self.day - self.last_weather_event_day > 20: #arbitrary cooldown between weather events
if random.random() < 0.05: #5% chance for weather event?
self.weather_override = all_weathers()["Supernova"]
self.last_weather_event_day = self.day
self.weather_event_duration = random.randint(self.weather_override.duration_range[0], self.weather_override.duration_range[1])
return 2
else:
self.weather_event_duration -= 1
return 1 if self.weather_event_duration > 0 else 0
class tournament(object): class tournament(object):
def __init__(self, name, team_dic, series_length = 5, finals_series_length = 7, max_innings = 9, id = None, secs_between_games = 300, secs_between_rounds = 600): def __init__(self, name, team_dic, series_length = 5, finals_series_length = 7, max_innings = 9, id = None, secs_between_games = 300, secs_between_rounds = 600):
@ -579,4 +640,15 @@ def load_league_file(league_name):
this_league.champion = state_dic["champion"] this_league.champion = state_dic["champion"]
except: except:
this_league.champion = None this_league.champion = None
try:
this_league.weather_forecast = state_dic["forecasts"] #handles legacy leagues
except:
this_league.weather_forecast = {}
for this_team in this_league.teams_in_league(): #give them all fresh forecasts starting at current day
this_league.new_weathers_midseason(this_team.name)
save_league(this_league)
try:
this_league.last_weather_event_day = state_dic["last_weather_event"]
except:
this_league.last_weather_event_day = 0
return this_league return this_league

View file

@ -148,6 +148,8 @@ def update_loop():
state["display_top_of_inning"] = state["top_of_inning"] state["display_top_of_inning"] = state["top_of_inning"]
this_game.weather.modify_gamestate(this_game, state)
if state["start_delay"] <= 0: if state["start_delay"] <= 0:
if this_game.top_of_inning != state["top_of_inning"]: if this_game.top_of_inning != state["top_of_inning"]:
state["update_pause"] = 2 state["update_pause"] = 2
@ -171,6 +173,8 @@ def update_loop():
state["update_text"] = f"{winning_team} wins!" state["update_text"] = f"{winning_team} wins!"
state["pitcher"] = "-" state["pitcher"] = "-"
state["batter"] = "-" state["batter"] = "-"
this_game.weather.modify_game_end_message(this_game, state)
else: else:
if this_game.top_of_inning: if this_game.top_of_inning:
state["update_text"] = f"Top of {this_game.inning}. {this_game.teams['away'].name} batting!" state["update_text"] = f"Top of {this_game.inning}. {this_game.teams['away'].name} batting!"

View file

@ -1043,7 +1043,10 @@ class LeagueScheduleCommand(Command):
schedule_text = "" schedule_text = ""
teams = league.team_names_in_league() teams = league.team_names_in_league()
for game in league.schedule[str(current_series+day)]: for game in league.schedule[str(current_series+day)]:
schedule_text += f"**{game[0]}** @ **{game[1]}**\n" emojis = ""
for day_offset in range((current_series+day - 1)*league.series_length, (current_series+day)*(league.series_length)):
emojis += weather.all_weathers()[league.weather_forecast[game[1]][day_offset]].emoji + " "
schedule_text += f"**{game[0]}** @ **{game[1]}** {emojis}\n"
teams.pop(teams.index(game[0])) teams.pop(teams.index(game[0]))
teams.pop(teams.index(game[1])) teams.pop(teams.index(game[1]))
if len(teams) > 0: if len(teams) > 0:
@ -1080,9 +1083,14 @@ class LeagueTeamScheduleCommand(Command):
for day in days: for day in days:
if str(current_series+day) in league.schedule.keys(): if str(current_series+day) in league.schedule.keys():
schedule_text = "" schedule_text = ""
for game in league.schedule[str(current_series+day)]: for game in league.schedule[str(current_series+day)]:
if team.name in game: if team.name in game:
schedule_text += f"**{game[0]}** @ **{game[1]}**" emojis = ""
for day_offset in range((current_series+day - 1)*league.series_length, (current_series+day)*(league.series_length)):
emojis += weather.all_weathers()[league.weather_forecast[game[1]][day_offset]].emoji + " "
schedule_text += f"**{game[0]}** @ **{game[1]}** {emojis}"
if schedule_text == "": if schedule_text == "":
schedule_text += "Resting" schedule_text += "Resting"
sched_embed.add_field(name=f"Days {((current_series+day-1)*league.series_length) + 1} - {(current_series+day)*(league.series_length)}", value=schedule_text, inline = False) sched_embed.add_field(name=f"Days {((current_series+day-1)*league.series_length) + 1} - {(current_series+day)*(league.series_length)}", value=schedule_text, inline = False)
@ -2119,7 +2127,9 @@ def game_over_embed(game):
title_string += ".\n" title_string += ".\n"
winning_team = game.teams['home'].name if game.teams['home'].score > game.teams['away'].score else game.teams['away'].name winning_team = game.teams['home'].name if game.teams['home'].score > game.teams['away'].score else game.teams['away'].name
winstring = f"{game.teams['away'].score} to {game.teams['home'].score}\n" homestring = str(game.teams["home"].score) + ("" if game.teams["home"].score == 16 else "")
awaystring = ("" if game.teams["away"].score == 16 else "") + str(game.teams["away"].score)
winstring = f"{awaystring} to {homestring}\n"
if game.victory_lap and winning_team == game.teams['home'].name: if game.victory_lap and winning_team == game.teams['home'].name:
winstring += f"{winning_team} wins with a victory lap!" winstring += f"{winning_team} wins with a victory lap!"
elif winning_team == game.teams['home'].name: elif winning_team == game.teams['home'].name:
@ -2151,6 +2161,8 @@ async def start_league_day(channel, league, partial = False):
else: else:
game_length = league.game_length game_length = league.game_length
weather_check_result = league.weather_event_check()
for pair in games_to_start: for pair in games_to_start:
if pair[0] is not None and pair[1] is not None: if pair[0] is not None and pair[1] is not None:
away = get_team_fuzzy_search(pair[0]) away = get_team_fuzzy_search(pair[0])
@ -2159,6 +2171,7 @@ async def start_league_day(channel, league, partial = False):
home.set_pitcher(rotation_slot=league.day) home.set_pitcher(rotation_slot=league.day)
this_game = games.game(away.finalize(), home.finalize(), length = game_length) this_game = games.game(away.finalize(), home.finalize(), length = game_length)
this_game.weather = league.get_weather_now(home.name)(this_game)
this_game, state_init = prepare_game(this_game) this_game, state_init = prepare_game(this_game)
state_init["is_league"] = True state_init["is_league"] = True
@ -2175,6 +2188,11 @@ async def start_league_day(channel, league, partial = False):
ext = "?league=" + urllib.parse.quote_plus(league.name) ext = "?league=" + urllib.parse.quote_plus(league.name)
if weather_check_result == 2:
await channel.send(f"The entire league is struck by a {league.weather_override.emoji} {league.weather_override.name}! The games must go on.")
elif weather_check_result == 1:
await channel.send(f"The {league.weather_override.emoji} {league.weather_override.name} continues to afflict the league.")
if league.last_series_check(): #if finals if league.last_series_check(): #if finals
await channel.send(f"The final series of the {league.name} regular season is starting now, at {config()['simmadome_url']+ext}") await channel.send(f"The final series of the {league.name} regular season is starting now, at {config()['simmadome_url']+ext}")
last = True last = True
@ -2274,6 +2292,11 @@ async def league_day_watcher(channel, league, games_list, filter_url, last = Fal
leagues.save_league(league) leagues.save_league(league)
active_standings[league] = await channel.send(embed=league.standings_embed()) active_standings[league] = await channel.send(embed=league.standings_embed())
await channel.send(f"The day {league.day} games for the {league.name} will start in {math.ceil(wait_seconds/60)} minutes.") await channel.send(f"The day {league.day} games for the {league.name} will start in {math.ceil(wait_seconds/60)} minutes.")
weather_check_result = league.weather_event_check()
if weather_check_result == 2:
await channel.send(f"The entire league is struck by a {league.weather_override.emoji} {league.weather_override.name}! The games must go on.")
elif weather_check_result == 1:
await channel.send(f"The {league.weather_override.emoji} {league.weather_override.name} continues to afflict the league.")
await asyncio.sleep(wait_seconds) await asyncio.sleep(wait_seconds)
await channel.send(f"A {league.name} series is continuing now at {filter_url}") await channel.send(f"A {league.name} series is continuing now at {filter_url}")
games_list = await continue_league_series(league, queued_games, games_list, series_results, missed) games_list = await continue_league_series(league, queued_games, games_list, series_results, missed)
@ -2361,6 +2384,7 @@ async def continue_league_series(league, queue, games_list, series_results, miss
home_team = games.get_team(oldgame.teams["home"].name) home_team = games.get_team(oldgame.teams["home"].name)
home_team.set_pitcher(rotation_slot=league.day) home_team.set_pitcher(rotation_slot=league.day)
this_game = games.game(away_team.finalize(), home_team.finalize(), length = league.game_length) this_game = games.game(away_team.finalize(), home_team.finalize(), length = league.game_length)
this_game.weather = league.get_weather_now(home_team.name)(this_game)
this_game, state_init = prepare_game(this_game) this_game, state_init = prepare_game(this_game)
state_init["is_league"] = True state_init["is_league"] = True
@ -2454,6 +2478,4 @@ async def league_postseason(channel, league):
season_save(league) season_save(league)
league.season_reset() league.season_reset()
client.run(config()["token"]) client.run(config()["token"])

View file

@ -1,15 +1,20 @@
import random import random, math, roman
import math
from gametext import appearance_outcomes, base_string from gametext import appearance_outcomes, base_string
class Weather: class Weather:
name = "Sunny"
emoji = "🌞"
duration_range = [3,5]
def __init__(self, game): def __init__(self, game):
self.name = "Sunny" pass
self.emoji = "🌞"
def __str__(self): def __str__(self):
return f"{self.emoji} {self.name}" return f"{self.emoji} {self.name}"
def set_duration(self):
pass
def modify_atbat_stats(self, player_rolls): def modify_atbat_stats(self, player_rolls):
# Activates before batting # Activates before batting
pass pass
@ -37,27 +42,33 @@ class Weather:
def modify_atbat_message(self, game, state): def modify_atbat_message(self, game, state):
pass pass
def modify_gamestate(self, game, state):
pass
def modify_game_end_message(self, game, state):
pass
class Supernova(Weather): class Supernova(Weather):
def __init__(self, game): name = "Supernova"
self.name = "Supernova" emoji = "🌟"
self.emoji = "🌟" duration_range = [1,2]
def modify_atbat_stats(self, roll): def modify_atbat_stats(self, roll):
roll["pitch_stat"] *= 0.8 roll["pitch_stat"] *= 0.8
class Midnight(Weather): class Midnight(Weather):
def __init__(self, game): name = "Midnight"
self.name = "Midnight" emoji = "🕶"
self.emoji = "🕶" duration_range = [1,1]
def modify_steal_stats(self, roll): def modify_steal_stats(self, roll):
roll["run_stars"] *= 2 roll["run_stars"] *= 2
class SlightTailwind(Weather): class SlightTailwind(Weather):
def __init__(self, game): name = "Slight Tailwind"
self.name = "Slight Tailwind" emoji = "🏌️‍♀️"
self.emoji = "🏌️‍♀️" duration_range = [1,2]
def activate(self, game, result): def activate(self, game, result):
@ -73,9 +84,9 @@ class SlightTailwind(Weather):
}) })
class Starlight(Weather): class Starlight(Weather):
def __init__(self, game): name = "Starlight"
self.name = "Starlight" emoji = "🌃"
self.emoji = "🌃" duration_range = [2,2]
def activate(self, game, result): def activate(self, game, result):
@ -104,10 +115,12 @@ class Starlight(Weather):
state["update_text"] = f"The stars enjoy watching dragons play baseball, and allow {result['batter']} to hit a dinger! {game.last_update[1]} runs scored!" state["update_text"] = f"The stars enjoy watching dragons play baseball, and allow {result['batter']} to hit a dinger! {game.last_update[1]} runs scored!"
class HeavySnow(Weather): class Blizzard(Weather):
name = "Blizzard"
emoji = ""
duration_range = [2,3]
def __init__(self, game): def __init__(self, game):
self.name = "Heavy Snow"
self.emoji = ""
self.counter_away = random.randint(0,len(game.teams['away'].lineup)-1) self.counter_away = random.randint(0,len(game.teams['away'].lineup)-1)
self.counter_home = random.randint(0,len(game.teams['home'].lineup)-1) self.counter_home = random.randint(0,len(game.teams['home'].lineup)-1)
@ -150,9 +163,9 @@ class HeavySnow(Weather):
game.current_batter = bat_team.pitcher game.current_batter = bat_team.pitcher
class Twilight(Weather): class Twilight(Weather):
def __init__(self,game): name = "Twilight"
self.name = "Twilight" emoji = "👻"
self.emoji = "👻" duration_range = [2,3]
def modify_atbat_roll(self, outcome, roll, defender): def modify_atbat_roll(self, outcome, roll, defender):
error_line = - (math.log(defender.stlats["defense_stars"] + 1)/50) + 1 error_line = - (math.log(defender.stlats["defense_stars"] + 1)/50) + 1
@ -171,9 +184,9 @@ class Twilight(Weather):
state["update_text"] += f" {this_game.last_update[1]} runs scored!" state["update_text"] += f" {this_game.last_update[1]} runs scored!"
class ThinnedVeil(Weather): class ThinnedVeil(Weather):
def __init__(self,game): name = "Thinned Veil"
self.name = "Thinned Veil" emoji = "🌌"
self.emoji = "🌌" duration_range = [1,3]
def activate(self, game, result): def activate(self, game, result):
if result["ishit"]: if result["ishit"]:
@ -186,10 +199,11 @@ class ThinnedVeil(Weather):
state["update_text"] += f" {game.last_update[0]['batter']}'s will manifests on {base_string(game.last_update[1])} base." state["update_text"] += f" {game.last_update[0]['batter']}'s will manifests on {base_string(game.last_update[1])} base."
class HeatWave(Weather): class HeatWave(Weather):
def __init__(self,game): name = "Heat Wave"
self.name = "Heat Wave" emoji = "🌄"
self.emoji = "🌄" duration_range = [2,3]
def __init__(self,game):
self.counter_away = random.randint(2,4) self.counter_away = random.randint(2,4)
self.counter_home = random.randint(2,4) self.counter_home = random.randint(2,4)
@ -228,9 +242,9 @@ class HeatWave(Weather):
class Drizzle(Weather): class Drizzle(Weather):
def __init__(self,game): name = "Drizzle"
self.name = "Drizzle" emoji = "🌧"
self.emoji = "🌧" duration_range = [2,3]
def on_flip_inning(self, game): def on_flip_inning(self, game):
if game.top_of_inning: if game.top_of_inning:
@ -253,9 +267,11 @@ class Drizzle(Weather):
state["update_text"] += f' Due to inclement weather, {placed_player.name} is placed on second base.' state["update_text"] += f' Due to inclement weather, {placed_player.name} is placed on second base.'
class Breezy(Weather): class Breezy(Weather):
name = "Breezy"
emoji = "🎐"
duration_range = [1,3]
def __init__(self, game): def __init__(self, game):
self.name = "Breezy"
self.emoji = "🎐"
self.activation_chance = 0.08 self.activation_chance = 0.08
def activate(self, game, result): def activate(self, game, result):
@ -295,9 +311,11 @@ class Breezy(Weather):
}) })
class MeteorShower(Weather): class MeteorShower(Weather):
name = "Meteor Shower"
emoji = "🌠"
duration_range = [1,3]
def __init__(self, game): def __init__(self, game):
self.name = "Meteor Shower"
self.emoji = "🌠"
self.activation_chance = 0.13 self.activation_chance = 0.13
def activate(self, game, result): def activate(self, game, result):
@ -319,6 +337,89 @@ class MeteorShower(Weather):
"weather_message": True "weather_message": True
}) })
class Hurricane(Weather):
name = "Hurricane"
emoji = "🌀"
duration_range = [1,1]
def __init__(self, game):
self.swaplength = random.randint(2,4)
self.swapped = False
def on_flip_inning(self, game):
if game.top_of_inning and (game.inning % self.swaplength) == 0:
self.swaplength = random.randint(2,4)
self.swapped = True
def modify_top_of_inning_message(self, game, state):
if self.swapped:
game.teams["home"].score, game.teams["away"].score = (game.teams["away"].score, game.teams["home"].score) #swap scores
state["away_score"], state["home_score"] = (game.teams["away"].score, game.teams["home"].score)
state["update_emoji"] = self.emoji
state["update_text"] += " The hurricane rages on, flipping the scoreboard!"
self.swapped = False
class Tornado(Weather):
name = "Tornado"
emoji = "🌪"
duration_range = [1,2]
def __init__(self, game):
self.activation_chance = 0.33
self.counter = 0
def activate(self, game, result):
if self.counter == 0 and random.random() < self.activation_chance and game.occupied_bases() != {}:
runners = list(game.bases.values())
current_runners = runners.copy()
self.counter = 5
while runners == current_runners and self.counter > 0:
random.shuffle(runners)
self.counter -= 1
for index in range(1,4):
game.bases[index] = runners[index-1]
result.clear()
result.update({
"text": f"The tornado sweeps across the field and pushes {'the runners' if len(game.occupied_bases().values())>1 else list(game.occupied_bases().values())[0].name} to a different base!",
"text_only": True,
"weather_message": True
})
self.counter = 2
elif self.counter > 0:
self.counter -= 1
class Downpour(Weather):
name = "Torrential Downpour"
emoji = ''
duration_range = [1,1]
def __init__(self, game):
self.target = game.max_innings
self.name = f"Torrential Downpour: {roman.roman_convert(str(self.target))}"
self.emoji = ''
def on_flip_inning(self, game):
high_score = game.teams["home"].score if game.teams["home"].score > game.teams["away"].score else game.teams["away"].score
if high_score >= self.target and game.teams["home"].score != game.teams["away"].score:
game.max_innings = game.inning
else:
game.max_innings = game.inning + 1
def modify_gamestate(self, game, state):
state["max_innings"] = ""
def modify_top_of_inning_message(self, game, state):
state["update_emoji"] = self.emoji
state["update_text"] = "The gods are not yet pleased. Play continues through the storm."
def modify_game_end_message(self, game, state):
state["update_emoji"] = self.emoji
state["update_text"] = f"{self.target} runs are reached, pleasing the gods. The storm clears."
def all_weathers(): def all_weathers():
@ -326,14 +427,72 @@ def all_weathers():
"Supernova" : Supernova, "Supernova" : Supernova,
"Midnight": Midnight, "Midnight": Midnight,
"Slight Tailwind": SlightTailwind, "Slight Tailwind": SlightTailwind,
"Heavy Snow": HeavySnow, "Blizzard": Blizzard,
"Twilight" : Twilight, "Twilight" : Twilight,
"Thinned Veil" : ThinnedVeil, "Thinned Veil" : ThinnedVeil,
"Heat Wave" : HeatWave, "Heat Wave" : HeatWave,
"Drizzle" : Drizzle, "Drizzle" : Drizzle,
"Breezy": Breezy, "Breezy": Breezy,
"Starlight" : Starlight, "Starlight" : Starlight,
"Meteor Shower" : MeteorShower "Meteor Shower" : MeteorShower,
"Hurricane" : Hurricane,
"Tornado" : Tornado,
"Torrential Downpour" : Downpour
} }
return weathers_dic return weathers_dic
class WeatherChains():
light = [SlightTailwind, Twilight, Breezy, Drizzle] #basic starting points for weather, good comfortable spots to return to
magic = [Twilight, ThinnedVeil, MeteorShower, Starlight] #weathers involving breaking the fabric of spacetime
sudden = [Tornado, Hurricane, Twilight, Starlight, Midnight, Downpour] #weathers that always happen and leave over 1-3 games
disaster = [Hurricane, Tornado, Downpour, Blizzard] #storms
aftermath = [Midnight, Starlight, MeteorShower] #calm epilogues
dictionary = {
#Supernova : (magic + sudden + disaster, None), supernova happens leaguewide and shouldn't need a chain, but here just in case
Midnight : ([SlightTailwind, Breezy, Drizzle, Starlight, MeteorShower, HeatWave],[2,2,2,4,4,1]),
SlightTailwind : ([Breezy, Drizzle, Tornado], [3,3,1]),
Blizzard : ([Midnight, Starlight, MeteorShower, Twilight, Downpour], [2,2,2,2,4]),
Twilight : ([ThinnedVeil, Midnight, MeteorShower, SlightTailwind], [2,4,2,1]),
ThinnedVeil : (light, None),
HeatWave : ([Tornado, Hurricane, SlightTailwind, Breezy],[4,4,1,1]),
Drizzle : ([Hurricane, Downpour, Blizzard],[2,2,1]),
Breezy : ([Drizzle, HeatWave, Blizzard, Tornado], [3,3,1,1]),
Starlight : ([SlightTailwind, Twilight, Breezy, Drizzle, ThinnedVeil, HeatWave], None),
MeteorShower : ([Starlight, ThinnedVeil, HeatWave], None),
Hurricane : ([Midnight, Starlight, MeteorShower, Twilight, Downpour], [2,2,2,2,4]),
Tornado : ([Midnight, Starlight, MeteorShower, Twilight, Downpour],[2,2,2,2,4]),
Downpour : (aftermath, None)
}
chains = [
[Hurricane, Drizzle, Hurricane]
]
def chain_weather(weather_instance):
#weather_type = type(weather_instance)
weather_type = weather_instance
options, weight = WeatherChains.dictionary[weather_type]
return random.choices(options, weights = weight)[0]
def parent_weathers(weather_type):
parents = []
for this_weather, (children, _) in WeatherChains.dictionary.items():
if weather_type in children:
parents.append(this_weather)
return parents
def starting_weather():
return random.choice(WeatherChains.light + WeatherChains.magic)
def debug_weathers():
names = ["a.txt", "b.txt", "c.txt"]
for name in names:
current = random.choice(list(all_weathers().values()))
out = ""
for i in range(0,50):
out += f"{current.name} {current.emoji}\n"
current = WeatherChains.chain_weather(current)
with open("data/"+name, "w", encoding='utf-8') as file:
file.write(out)