Add working shop with upgrades and the dark mode wizard, and now the white mode is default, high score and evaded tax in main and add more tax evasion levels

This commit is contained in:
csd4ni3l
2025-10-11 22:16:10 +02:00
parent 5798f3ba2a
commit 5358e3b8ff
4 changed files with 222 additions and 50 deletions

View File

@@ -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:
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)
else:
self.data = {
"high_score": 0
}
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()
self.spawn_bullet(direction)
mouse_pos = arcade.math.Vec2(
self.window.mouse.data.get("x", 0),
self.window.mouse.data.get("y", 0)
)
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)

View File

@@ -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))

View File

@@ -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))
self.main_exit()

View File

@@ -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),