diff --git a/game/play.py b/game/play.py index 78fa7af..cb03b83 100644 --- a/game/play.py +++ b/game/play.py @@ -1,8 +1,8 @@ -import arcade, arcade.gui, random, math, time, json, os +import arcade, arcade.gui, random, math, time, json, math from utils.constants import BULLET_SPEED, IRS_AGENT_HEALTH, HEALTH_INCREASE_PER_LEVEL, PLAYER_SPEED, IRS_AGENT_SPEED, TAX_PER_IRS_AGENT, IRS_AGENT_ATTACK_SPEED -from utils.constants import IRS_AGENT_SPAWN_INTERVAL, SPAWN_INTERVAL_DECREASE_PER_LEVEL, SPEED_INCREASE_PER_LEVEL -from utils.constants import TAX_EVASION_LEVELS, TAX_EVASION_NAMES, TAX_INCREASE_PER_LEVEL, menu_background_color, INVENTORY_ITEMS, INVENTORY_TRIGGER_KEYS +from utils.constants import IRS_AGENT_SPAWN_INTERVAL, SPAWN_INTERVAL_DECREASE_PER_LEVEL, SPEED_INCREASE_PER_LEVEL, item_to_json_name +from utils.constants import TAX_EVASION_LEVELS, TAX_EVASION_NAMES, TAX_INCREASE_PER_LEVEL, menu_background_color, INVENTORY_ITEMS, INVENTORY_TRIGGER_KEYS, PLAYER_INACCURACY_MAX import utils.preload from utils.preload import irs_agent_texture @@ -16,9 +16,10 @@ class Bullet(arcade.Sprite): super().__init__(texture, center_x=x, center_y=y) self.radius = radius self.direction = direction + self.speed = 0 def move(self): - self.position += self.direction * BULLET_SPEED + self.position += self.direction * self.speed class IRSAgent(arcade.Sprite): def __init__(self, x, y): @@ -39,8 +40,10 @@ class IRSAgent(arcade.Sprite): self.color = (255, 255, 255, 255) class Player(arcade.TextureAnimationSprite): - def __init__(self, x, y): # x, y here because we dont know window width and height - super().__init__(animation=dark_wizard_standing_animation, center_x=x, center_y=y, scale=1.5) + def __init__(self, x, y, dark_mode_unlocked=False): # x, y here because we dont know window width and height + super().__init__(animation=dark_wizard_standing_animation if dark_mode_unlocked else light_wizard_standing_animation, center_x=x, center_y=y, scale=1.5) + + self.direction = arcade.math.Vec2() class Game(arcade.gui.UIView): def __init__(self, pypresence_client): @@ -51,13 +54,8 @@ class Game(arcade.gui.UIView): self.pypresence_client = pypresence_client self.pypresence_client.update(state="Playing the game") - if os.path.exists("data.json"): - with open("data.json", "r") as file: - self.data = json.load(file) - else: - self.data = { - "high_score": 0 - } + with open("data.json", "r") as file: # no need for if, since Main already creates the file with default values. + self.data = json.load(file) self.camera = arcade.Camera2D() @@ -83,8 +81,7 @@ class Game(arcade.gui.UIView): self.tax_evasion_level = TAX_EVASION_NAMES[0] self.bullets: list[Bullet] = [] - self.highscore_evaded_tax = self.data["high_score"] - self.player = Player(self.window.width / 2, self.window.height / 2) + self.player = Player(self.window.width / 2, self.window.height / 2, self.data["shop"]["dark_mode_wizard"]) self.spritelist.append(self.player) self.info_box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=0, align="left"), anchor_x="left", anchor_y="top") @@ -102,11 +99,15 @@ class Game(arcade.gui.UIView): self.progress_bar._render_thumb = lambda surface: None self.progress_bar.on_event = lambda event: None - self.inventory = self.anchor.add(Inventory(INVENTORY_ITEMS, self.window.width), anchor_x="center", anchor_y="bottom") + self.inventory = self.anchor.add(Inventory(INVENTORY_ITEMS, self.window.width), anchor_x="left", anchor_y="bottom", align_x=self.window.width / 20) self.inventory.pay_tax_button.on_click = lambda event: self.pay_tax() + def dash(self): + self.player.position += self.player.direction * (PLAYER_SPEED * 15 * self.data.get('shop', {}).get('player_speed', 0)) + def spawn_bullet(self, direction): bullet = Bullet(INVENTORY_ITEMS[self.inventory.current_inventory_item][3], getattr(utils.preload, INVENTORY_ITEMS[self.inventory.current_inventory_item][4]), self.player.center_x, self.player.center_y, direction) + bullet.speed = BULLET_SPEED + self.data.get('shop', {}).get("bullet_speed", 0) self.bullets.append(bullet) self.spritelist.append(bullet) @@ -167,27 +168,57 @@ class Game(arcade.gui.UIView): self.player.update_animation() if self.window.keyboard[arcade.key.W]: - self.player.center_y += PLAYER_SPEED - if not self.player.animation == dark_wizard_up_animation: # this is needed because the animation property will reset to the first frame, so animation doesnt work. - self.player.animation = dark_wizard_up_animation + self.player.direction = arcade.math.Vec2(self.player.direction.x, 1) + if not self.player.animation == (dark_wizard_up_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_up_animation): # this is needed because the animation property will reset to the first frame, so animation doesnt work. + self.player.animation = (dark_wizard_up_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_up_animation) elif self.window.keyboard[arcade.key.S]: - self.player.center_y -= PLAYER_SPEED - if not self.player.animation == dark_wizard_standing_animation: # this is needed because the animation property will reset to the first frame, so animation doesnt work. - self.player.animation = dark_wizard_standing_animation + self.player.direction = arcade.math.Vec2(self.player.direction.x, -1) + if not self.player.animation == (dark_wizard_standing_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_standing_animation): # this is needed because the animation property will reset to the first frame, so animation doesnt work. + self.player.animation = (dark_wizard_standing_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_standing_animation) + else: + self.player.direction = arcade.math.Vec2(self.player.direction.x, 0) + if self.window.keyboard[arcade.key.D]: - self.player.center_x += PLAYER_SPEED - if not self.player.animation == dark_wizard_right_animation: # this is needed because the animation property will reset to the first frame, so animation doesnt work. - self.player.animation = dark_wizard_right_animation + self.player.direction = arcade.math.Vec2(1, self.player.direction.y) + if not self.player.animation == (dark_wizard_right_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_right_animation): # this is needed because the animation property will reset to the first frame, so animation doesnt work. + self.player.animation = (dark_wizard_right_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_right_animation) elif self.window.keyboard[arcade.key.A]: - self.player.center_x -= PLAYER_SPEED - if not self.player.animation == dark_wizard_left_animation: # this is needed because the animation property will reset to the first frame, so animation doesnt work. - self.player.animation = dark_wizard_left_animation + self.player.direction = arcade.math.Vec2(-1, self.player.direction.y) + if not self.player.animation == (dark_wizard_left_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_left_animation): # this is needed because the animation property will reset to the first frame, so animation doesnt work. + self.player.animation = (dark_wizard_left_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_left_animation) + else: + self.player.direction = arcade.math.Vec2(0, self.player.direction.y) - if time.perf_counter() - self.last_shoot >= INVENTORY_ITEMS[self.inventory.current_inventory_item][1]: + self.player.position += self.player.direction * (PLAYER_SPEED + self.data.get('shop', {}).get('player_speed', 0)) + + if self.player.center_x + self.player.width / 2 > self.window.width: + self.player.center_x = self.window.width - self.player.width / 2 + elif self.player.center_x - self.player.width / 2 < 0: + self.player.center_x = self.player.width / 2 + + if self.player.center_y + self.player.height / 2 > self.window.height: + self.player.center_y = self.window.height - self.player.height / 2 + elif self.player.center_y - self.player.height / 2 < 0: + self.player.center_y = self.player.height / 2 + + item_list = INVENTORY_ITEMS[self.inventory.current_inventory_item] + + json_name = item_to_json_name[item_list[0]] + + if time.perf_counter() - self.last_shoot >= item_list[1] - ((item_list[1] / 15) * self.data["shop"][f"{json_name}_atk_speed"]): self.last_shoot = time.perf_counter() - direction = ((self.window.mouse.data.get("x", 0), self.window.mouse.data.get("y", 0)) - arcade.math.Vec2(self.player.center_x, self.player.center_y)).normalize() + + mouse_pos = arcade.math.Vec2( + self.window.mouse.data.get("x", 0), + self.window.mouse.data.get("y", 0) + ) - self.spawn_bullet(direction) + player_pos = arcade.math.Vec2(self.player.center_x, self.player.center_y) + + direction = (mouse_pos - player_pos).normalize() + + inaccuracy = random.randint(-(PLAYER_INACCURACY_MAX - self.data["shop"]["inaccuracy_decrease"]), (PLAYER_INACCURACY_MAX - self.data["shop"]["inaccuracy_decrease"])) + self.spawn_bullet(direction.rotate(math.radians(inaccuracy))) if self.tax_evasion_level_notice.visible and time.perf_counter() - self.last_tax_evasion_notice >= 2.5: self.tax_evasion_level_notice.visible = False @@ -228,8 +259,11 @@ class Game(arcade.gui.UIView): if arcade.math.Vec2(bullet.center_x, bullet.center_y).distance((irs_agent.center_x, irs_agent.center_y)) <= (irs_agent.width / 2 + bullet.radius): irs_agent.damaged = True irs_agent.last_damage = time.perf_counter() - irs_agent.position += bullet.direction * INVENTORY_ITEMS[self.inventory.current_inventory_item][2] * 1.5 - irs_agent.health -= INVENTORY_ITEMS[self.inventory.current_inventory_item][2] + + damage = item_list[2] + (item_list[2] / 10 * self.data["shop"][f"{json_name}_dmg"]) + + irs_agent.position += bullet.direction * damage * 1.5 + irs_agent.health -= damage if irs_agent.health <= 0: self.spritelist.remove(irs_agent) @@ -261,6 +295,7 @@ class Game(arcade.gui.UIView): def on_key_press(self, symbol, modifiers): if symbol == arcade.key.ESCAPE: self.data["high_score"] = int(self.high_score) + self.data["evaded_tax"] += int(self.evaded_tax) with open("data.json", "w") as file: file.write(json.dumps(self.data, indent=4)) @@ -272,6 +307,8 @@ class Game(arcade.gui.UIView): self.inventory.select_item(int(chr(symbol)) - 1) elif symbol == arcade.key.P: self.pay_tax() + elif symbol == arcade.key.TAB: + self.dash() def on_resize(self, width: int, height: int): super().on_resize(width, height) diff --git a/menus/main.py b/menus/main.py index 25e2620..a32f371 100644 --- a/menus/main.py +++ b/menus/main.py @@ -1,4 +1,5 @@ -import arcade, arcade.gui, asyncio, pypresence, time, copy, json +import arcade, arcade.gui, asyncio, pypresence, time, copy, json, os + from utils.preload import button_texture, button_hovered_texture from utils.constants import big_button_style, discord_presence_id from utils.utils import FakePyPresence @@ -47,21 +48,45 @@ class Main(arcade.gui.UIView): self.pypresence_client.update(state='In Menu', details='In Main Menu', start=self.pypresence_client.start_time) + if os.path.exists("data.json"): + with open("data.json", "r") as file: + self.data = json.load(file) + else: + self.data = {} + + if not "high_score" in self.data: + self.data["high_score"] = 0 + + if not "evaded_tax" in self.data: + self.data["evaded_tax"] = 0 + + with open("data.json", "w") as file: + file.write(json.dumps(self.data, indent=4)) + def on_show_view(self): super().on_show_view() self.title_label = self.box.add(arcade.gui.UILabel(text="Wizard vs IRS", font_name="Roboto", font_size=48)) + self.high_score_label = self.box.add(arcade.gui.UILabel(text=f"High Score: {self.data['high_score']}$", font_name="Roboto", font_size=24)) + self.evaded_tax_label = self.box.add(arcade.gui.UILabel(text=f"Total Evaded Tax: {self.data['evaded_tax']}$", font_name="Roboto", font_size=24)) - self.play_button = self.box.add(arcade.gui.UITextureButton(text="Play", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=150, style=big_button_style)) + self.play_button = self.box.add(arcade.gui.UITextureButton(text="Play", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style)) self.play_button.on_click = lambda event: self.play() - self.settings_button = self.box.add(arcade.gui.UITextureButton(text="Settings", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=150, style=big_button_style)) + self.shop_button = self.box.add(arcade.gui.UITextureButton(text="Shop", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style)) + self.shop_button.on_click = lambda event: self.shop() + + self.settings_button = self.box.add(arcade.gui.UITextureButton(text="Settings", texture=button_texture, texture_hovered=button_hovered_texture, width=self.window.width / 2, height=self.window.height / 10, style=big_button_style)) self.settings_button.on_click = lambda event: self.settings() def play(self): from game.play import Game self.window.show_view(Game(self.pypresence_client)) + def shop(self): + from menus.shop import Shop + self.window.show_view(Shop(self.pypresence_client)) + def settings(self): from menus.settings import Settings self.window.show_view(Settings(self.pypresence_client)) diff --git a/menus/shop.py b/menus/shop.py index 683fa40..094c7aa 100644 --- a/menus/shop.py +++ b/menus/shop.py @@ -1,4 +1,6 @@ -import arcade, arcade.gui +import arcade, arcade.gui, os, json + +from math import ceil from utils.constants import button_style, SHOP_ITEMS from utils.preload import button_texture, button_hovered_texture @@ -10,12 +12,86 @@ class Shop(arcade.gui.UIView): self.pypresence_client = pypresence_client self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1))) - self.grid = self.anchor.add(arcade.gui.UIGridLayout(size_hint=(0.75, 0.75), column_count=4, row_count=999), anchor_x="center", anchor_y="center") + self.grid = self.anchor.add(arcade.gui.UIGridLayout(column_count=4, row_count=ceil(len(SHOP_ITEMS) / 4), horizontal_spacing=10, vertical_spacing=10), anchor_x="center", anchor_y="center") + + if os.path.exists("data.json"): + with open("data.json", "r") as file: + self.data = json.load(file) + else: + self.data = {} + + if not "shop" in self.data: + self.data["shop"] = {} + + self.shop_buttons = [] + + def main_exit(self): + with open("data.json", "w") as file: + file.write(json.dumps(self.data, indent=4)) + + from menus.main import Main + self.window.show_view(Main(self.pypresence_client)) def on_show_view(self): super().on_show_view() + self.back_button = arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text='<--', style=button_style, width=100, height=50) + self.back_button.on_click = lambda event: self.main_exit() + self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5) + + self.evaded_tax_label = self.anchor.add(arcade.gui.UILabel(f"Evaded Tax: {self.data['evaded_tax']}$", font_size=24), anchor_x="center", anchor_y="top") + + for n, shop_item in enumerate(SHOP_ITEMS): + row, col = n // 4, n % 4 + + if not shop_item[1] in self.data["shop"]: + self.data["shop"][shop_item[1]] = 0 + + upgrade_cost = (self.data["shop"][shop_item[1]] + 1) * shop_item[3] + + max_count = shop_item[2] + + notice_string = "\n(Also increases knockback)" if "DMG" in shop_item[0] else "" + level_string = self.data["shop"][shop_item[1]] if self.data["shop"][shop_item[1]] < max_count else "Max" + + button = self.grid.add(arcade.gui.UITextureButton( + text=f'{shop_item[0]}{notice_string}\nLevel: {level_string}\nUpgrade Cost: {upgrade_cost}$\n{shop_item[2] - self.data["shop"][shop_item[1]]}', + texture=button_texture, + texture_hovered=button_hovered_texture, + style=button_style, + width=self.window.width / 8, + height=self.window.width / 8, + multiline=True, + align="center" + ), row=row, column=col) + + self.shop_buttons.append(button) + + button.on_click = lambda event, n=n: self.buy_upgrade(n) + + def buy_upgrade(self, n): + item_list = SHOP_ITEMS[n] + json_name = item_list[1] + max_count = item_list[2] + + current_level = self.data["shop"][json_name] + cost = (current_level + 1) * item_list[3] + + if not self.data["evaded_tax"] >= cost: + return + + if current_level >= item_list[2]: + return + + self.data["shop"][json_name] += 1 + self.data["evaded_tax"] -= cost + + notice_string = "\n(Also increases knockback)" if "DMG" in item_list[0] else "" + level_string = self.data["shop"][item_list[1]] if self.data["shop"][item_list[1]] < max_count else "Max" + + self.shop_buttons[n].text = f"{item_list[0]}{notice_string}\nLevel: {level_string}\nUpgrade Cost: {cost + item_list[3]}" + self.evaded_tax_label.text = f"Evaded Tax: {self.data['evaded_tax']}$" + def on_key_press(self, symbol, modifiers): if symbol == arcade.key.ESCAPE: - from menus.main import Main - self.window.show_view(Main(self.pypresence_client)) \ No newline at end of file + self.main_exit() \ No newline at end of file diff --git a/utils/constants.py b/utils/constants.py index 8e38187..bdd96f6 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -13,26 +13,60 @@ IRS_AGENT_SPEED = 1.5 IRS_AGENT_HEALTH = 15 TAX_PER_IRS_AGENT = 100 -TAX_EVASION_LEVELS = {"Compliant Citizen": 0, "Suspicious": 1, "Under Review": 2500, "Flagged": 5000, "Audited": 10000, "Criminal Case": 20000, "Most Wanted": 50000, "Legendary": 100000} -TAX_EVASION_NAMES = list(TAX_EVASION_LEVELS.keys()) - -BULLET_SPEED = 10 SPEED_INCREASE_PER_LEVEL = 0.3 SPAWN_INTERVAL_DECREASE_PER_LEVEL = 0.075 HEALTH_INCREASE_PER_LEVEL = 2 -TAX_INCREASE_PER_LEVEL = 100 +TAX_INCREASE_PER_LEVEL = 50 ATTACK_INTERVAL_DECREASE_PER_LEVEL = .1 -SHOP_ITEMS = [] +TAX_EVASION_LEVELS = { + "Compliant Citizen": 0, + "Minor Mistake": 300, + "Mildly Suspicious": 1250, + "Suspicious": 3000, + "Under Review": 5000, + "Investigated": 10000, + "Flagged": 15000, + "Audited": 30000, + "Seized Assets": 50000, + "Criminal Case": 125000, + "International Watchlist": 250000, + "Most Wanted": 500000, + "Legendary": 1000000, + "Mythic": 5000000 +} +TAX_EVASION_NAMES = list(TAX_EVASION_LEVELS.keys()) +BULLET_SPEED = 8 +PLAYER_INACCURACY_MAX = 10 PLAYER_SPEED = 4 -INVENTORY_ITEMS = [ - ["Fireball", 0.2, 10, 10, "fireball_texture"], - ["Lightning Bolt", 0.4, 20, 20, "lightning_bolt_texture"], - ["Ice Blast", 0.1, 5, 7.5, "ice_blast_texture"], +# name, json_key, max_count, upgrade_cost +SHOP_ITEMS = [ + ["Fireball DMG", "fb_dmg", 999, 10000], + ["Fireball ATK Speed", "fb_atk_speed", 10, 30000], + ["Lightning Bolt DMG", "lb_dmg", 999, 10000], + ["Lightning Bolt ATK Speed", "lb_atk_speed", 10, 30000], + ["Ice Blast DMG", "ib_dmg", 999, 10000], + ["Ice Blast ATK Speed", "ib_atk_speed", 10, 30000], + ["Inaccuracy Decrease", "inaccuracy_decrease", 10, 25000], + ["Player Speed", "player_speed", 999, 20000], + ["Bullet Speed", "bullet_speed", 999, 20000], + ["Dark Mode Wizard", "dark_mode_wizard", 1, 1000000] ] +INVENTORY_ITEMS = [ + ["Fireball", 0.25, 10, 10, "fireball_texture"], + ["Lightning Bolt", 0.45, 20, 20, "lightning_bolt_texture"], + ["Ice Blast", 0.15, 5, 7.5, "ice_blast_texture"], +] + +item_to_json_name = { + "Fireball": "fb", + "Lightning Bolt": "lb", + "Ice Blast": "ib" +} + INVENTORY_TRIGGER_KEYS = [getattr(arcade.key, f"KEY_{n+1}") for n in range(len(INVENTORY_ITEMS))] button_style = {'normal': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK), 'hover': UITextureButtonStyle(font_name="Roboto", font_color=arcade.color.BLACK),