Add PyPI support, fix a bunch of issues i didnt notice, update README

This commit is contained in:
csd4ni3l
2025-10-12 21:29:55 +02:00
parent 47e7cc48ef
commit 01bec44101
23 changed files with 212 additions and 144 deletions

34
.github/workflows/pypi.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Publish to PyPI
on:
push:
tags:
- "v*"
jobs:
build-and-publish:
name: Build and Publish to PyPI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade build twine
- name: Build the package
run: |
python -m build
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
python -m twine upload dist/*

View File

@@ -1 +1,14 @@
Wizard vs IRS is a game where as a wizard, you have to escape taxes by defending yourself from IRS agents.
Wizard vs IRS is a game where as a wizard, **you have to escape taxes by defending yourself** from IRS agents.
For each IRS agent you defend, you evade some taxes, and after defending enough, you climb to a new level of tax evasion.
Currently, you have Fireball, Ball of Lightning and Ice Blast as spells/weapons you can use.
During fights, you get mana, which you can spend on abilities:
- Dash: Quickly dash away to escape
- Tax Shield: Permanent 1000$ shield until it breaks
- Audit Bomb: Blow up nearby IRS agents
- Freeze Audit: Freeze each IRS agent for 4 seconds
In the Shop, you can upgrade the DMG and Attack Speed of each spell, improve the accuracy of the wizard, upgrade the player and bullet speed and get the best upgrade: **The Dark Mode Wizard**.
**WARNING: This project is a joke, i don't approve of any illegal activities done whatsoever.**

View File

@@ -8,3 +8,16 @@ dependencies = [
"arcade==3.2.0",
"pypresence>=4.3.0",
]
classifiers = [
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Operating System :: OS Independent",
]
[project.scripts]
wizard_vs_irs = "wizard_vs_irs.run:main"
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

View File

@@ -1,7 +1,7 @@
# This file was autogenerated by uv via the following command:
# uv pip compile pyproject.toml -o requirements.txt
arcade==3.2.0
# via wizard_vs_irs (pyproject.toml)
# via wizard-vs-irs (pyproject.toml)
attrs==25.3.0
# via pytiled-parser
cffi==1.17.1
@@ -15,7 +15,7 @@ pyglet==2.1.6
pymunk==6.9.0
# via arcade
pypresence==4.3.0
# via wizard_vs_irs (pyproject.toml)
# via wizard-vs-irs (pyproject.toml)
pytiled-parser==2.2.9
# via arcade
typing-extensions==4.14.1

93
run.py
View File

@@ -1,93 +0,0 @@
import pyglet
pyglet.options.debug_gl = False
import logging, datetime, os, json, sys, arcade
from utils.utils import get_closest_resolution, print_debug_info, on_exception
from utils.constants import log_dir, menu_background_color
from menus.main import Main
sys.excepthook = on_exception
pyglet.resource.path.append(os.getcwd())
pyglet.font.add_directory('./assets/fonts')
if not log_dir in os.listdir():
os.makedirs(log_dir)
while len(os.listdir(log_dir)) >= 5:
files = [(file, os.path.getctime(os.path.join(log_dir, file))) for file in os.listdir(log_dir)]
oldest_file = sorted(files, key=lambda x: x[1])[0][0]
os.remove(os.path.join(log_dir, oldest_file))
timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
log_filename = f"debug_{timestamp}.log"
logging.basicConfig(filename=f'{os.path.join(log_dir, log_filename)}', format='%(asctime)s %(name)s %(levelname)s: %(message)s', level=logging.DEBUG)
for logger_name_to_disable in ['arcade', "numba"]:
logging.getLogger(logger_name_to_disable).propagate = False
logging.getLogger(logger_name_to_disable).disabled = True
if os.path.exists('settings.json'):
with open('settings.json', 'r') as settings_file:
settings = json.load(settings_file)
resolution = list(map(int, settings['resolution'].split('x')))
if not settings.get("anti_aliasing", "4x MSAA") == "None":
antialiasing = int(settings.get("anti_aliasing", "4x MSAA").split('x')[0])
else:
antialiasing = 0
fullscreen = settings['window_mode'] == 'Fullscreen'
style = arcade.Window.WINDOW_STYLE_BORDERLESS if settings['window_mode'] == 'borderless' else arcade.Window.WINDOW_STYLE_DEFAULT
vsync = settings['vsync']
fps_limit = settings['fps_limit']
else:
resolution = get_closest_resolution()
antialiasing = 4
fullscreen = False
style = arcade.Window.WINDOW_STYLE_DEFAULT
vsync = True
fps_limit = 0
settings = {
"resolution": f"{resolution[0]}x{resolution[1]}",
"antialiasing": "4x MSAA",
"window_mode": "Windowed",
"vsync": True,
"fps_limit": 60,
"discord_rpc": True
}
with open("settings.json", "w") as file:
file.write(json.dumps(settings))
window = arcade.Window(width=resolution[0], height=resolution[1], title='Wizard vs IRS', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style)
if vsync:
window.set_vsync(True)
display_mode = window.display.get_default_screen().get_mode()
refresh_rate = display_mode.rate
window.set_update_rate(1 / refresh_rate)
window.set_draw_rate(1 / refresh_rate)
elif not fps_limit == 0:
window.set_update_rate(1 / fps_limit)
window.set_draw_rate(1 / fps_limit)
else:
window.set_update_rate(1 / 99999999)
window.set_draw_rate(1 / 99999999)
arcade.set_background_color(menu_background_color)
print_debug_info()
main = Main()
window.show_view(main)
logging.debug('Game started.')
arcade.run()
logging.info('Exited with error code 0.')

30
uv.lock generated
View File

@@ -71,21 +71,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009, upload_time = "2024-09-04T20:44:45.309Z" },
]
[[package]]
name = "wizard_vs_irs"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "arcade" },
{ name = "pypresence" },
]
[package.metadata]
requires-dist = [
{ name = "arcade", specifier = "==3.2.0" },
{ name = "pypresence", specifier = ">=4.3.0" },
]
[[package]]
name = "pillow"
version = "11.0.0"
@@ -218,3 +203,18 @@ sdist = { url = "https://files.pythonhosted.org/packages/98/5a/da40306b885cc8c09
wheels = [
{ url = "https://files.pythonhosted.org/packages/b5/00/d631e67a838026495268c2f6884f3711a15a9a2a96cd244fdaea53b823fb/typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76", size = 43906, upload_time = "2025-07-04T13:28:32.743Z" },
]
[[package]]
name = "wizard-vs-irs"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "arcade" },
{ name = "pypresence" },
]
[package.metadata]
requires-dist = [
{ name = "arcade", specifier = "==3.2.0" },
{ name = "pypresence", specifier = ">=4.3.0" },
]

View File

@@ -0,0 +1 @@
__version__ = "0.0.1"

View File

Before

Width:  |  Height:  |  Size: 280 B

After

Width:  |  Height:  |  Size: 280 B

View File

Before

Width:  |  Height:  |  Size: 291 B

After

Width:  |  Height:  |  Size: 291 B

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1,6 +1,6 @@
import arcade, arcade.gui
from utils.constants import button_style
from wizard_vs_irs.utils.constants import button_style
class Inventory(arcade.gui.UIBoxLayout):
def __init__(self, items, window_width):

View File

@@ -1,6 +1,6 @@
import arcade, arcade.gui, random, math, time, json, math
from utils.constants import (
from wizard_vs_irs.utils.constants import (
ABILITIES,
ATTACK_INTERVAL_DECREASE_PER_LEVEL,
BULLET_SPEED,
@@ -19,12 +19,12 @@ from utils.constants import (
TAX_INCREASE_PER_LEVEL,
)
import utils.preload
from utils.preload import irs_agent_texture
from utils.preload import light_wizard_left_animation, light_wizard_right_animation, light_wizard_standing_animation, light_wizard_up_animation
from utils.preload import dark_wizard_left_animation, dark_wizard_right_animation, dark_wizard_standing_animation, dark_wizard_up_animation
import wizard_vs_irs.utils.preload
from wizard_vs_irs.utils.preload import irs_agent_texture
from wizard_vs_irs.utils.preload import light_wizard_left_animation, light_wizard_right_animation, light_wizard_standing_animation, light_wizard_up_animation
from wizard_vs_irs.utils.preload import dark_wizard_left_animation, dark_wizard_right_animation, dark_wizard_standing_animation, dark_wizard_up_animation
from game.inventory import Inventory
from wizard_vs_irs.game.inventory import Inventory
class Bullet(arcade.Sprite):
def __init__(self, radius, texture, x, y, direction):
@@ -117,7 +117,7 @@ class Game(arcade.gui.UIView):
self.last_ability_timers = {}
self.bullets: list[Bullet] = []
self.player = Player(self.window.width / 2, self.window.height / 2, self.data["shop"]["dark_mode_wizard"])
self.player = Player(self.window.width / 2, self.window.height / 2, self.data["shop"].get("dark_mode_wizard", 0))
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")
@@ -136,10 +136,10 @@ class Game(arcade.gui.UIView):
self.progress_bar.on_event = lambda event: None
self.ability_info_label = self.anchor.add(arcade.gui.UILabel(text=f"""Abilities:
Dash (tab): {ABILITIES['dash'][1]} Mana
Tax Shield (t): {ABILITIES["tax_shield"][1]} Mana
Audit Bomb (b): {ABILITIES["audit_bomb"][1]} Mana
Freeze Audit (f): {ABILITIES["freeze_audit"][1]} Mana""", font_size=20, multiline=True),
Dash (tab): {ABILITIES['dash']} Mana
Tax Shield (t): {ABILITIES["tax_shield"]} Mana
Audit Bomb (b): {ABILITIES["audit_bomb"]} Mana
Freeze Audit (f): {ABILITIES["freeze_audit"]} Mana""", font_size=20, multiline=True),
anchor_x="right", anchor_y="bottom", align_x=-5)
self.inventory = self.anchor.add(Inventory(INVENTORY_ITEMS, self.window.width), anchor_x="left", anchor_y="bottom", align_x=self.window.width / 20)
@@ -187,7 +187,7 @@ anchor_x="right", anchor_y="bottom", align_x=-5)
self.immobilize_irs = True
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 = Bullet(INVENTORY_ITEMS[self.inventory.current_inventory_item][3], getattr(wizard_vs_irs.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)
@@ -265,19 +265,19 @@ anchor_x="right", anchor_y="bottom", align_x=-5)
if self.window.keyboard[arcade.key.W]:
self.player.direction = arcade.math.Vec2(self.player.direction.x, 1)
self.player.set_player_animation(dark_wizard_up_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_up_animation)
self.player.set_player_animation(dark_wizard_up_animation if self.data["shop"].get("dark_mode_wizard", False) else light_wizard_up_animation)
elif self.window.keyboard[arcade.key.S]:
self.player.direction = arcade.math.Vec2(self.player.direction.x, -1)
self.player.set_player_animation(dark_wizard_standing_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_standing_animation)
self.player.set_player_animation(dark_wizard_standing_animation if self.data["shop"].get("dark_mode_wizard", False) 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.direction = arcade.math.Vec2(1, self.player.direction.y)
self.player.set_player_animation(dark_wizard_right_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_right_animation)
self.player.set_player_animation(dark_wizard_right_animation if self.data["shop"].get("dark_mode_wizard", False) else light_wizard_right_animation)
elif self.window.keyboard[arcade.key.A]:
self.player.direction = arcade.math.Vec2(-1, self.player.direction.y)
self.player.set_player_animation(dark_wizard_left_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_left_animation)
self.player.set_player_animation(dark_wizard_left_animation if self.data["shop"].get("dark_mode_wizard", False) else light_wizard_left_animation)
else:
self.player.direction = arcade.math.Vec2(0, self.player.direction.y)
@@ -309,7 +309,7 @@ anchor_x="right", anchor_y="bottom", align_x=-5)
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"]))
inaccuracy = random.randint(-(PLAYER_INACCURACY_MAX - self.data["shop"].get("inaccuracy_decrease", 0)), (PLAYER_INACCURACY_MAX - self.data["shop"].get("inaccuracy_decrease", 0)))
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:
@@ -388,7 +388,7 @@ anchor_x="right", anchor_y="bottom", align_x=-5)
with open("data.json", "w") as file:
file.write(json.dumps(self.data, indent=4))
from menus.main import Main
from wizard_vs_irs.menus.main import Main
self.window.show_view(Main(self.pypresence_client))
elif symbol in INVENTORY_TRIGGER_KEYS:
self.inventory.select_item(int(chr(symbol)) - 1)

View File

@@ -1,8 +1,8 @@
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
from wizard_vs_irs.utils.preload import button_texture, button_hovered_texture
from wizard_vs_irs.utils.constants import big_button_style, discord_presence_id
from wizard_vs_irs.utils.utils import FakePyPresence
class Main(arcade.gui.UIView):
def __init__(self, pypresence_client=None):
@@ -60,6 +60,9 @@ class Main(arcade.gui.UIView):
if not "evaded_tax" in self.data:
self.data["evaded_tax"] = 0
if not "shop" in self.data:
self.data["shop"] = {}
with open("data.json", "w") as file:
file.write(json.dumps(self.data, indent=4))
@@ -80,13 +83,13 @@ class Main(arcade.gui.UIView):
self.settings_button.on_click = lambda event: self.settings()
def play(self):
from game.play import Game
from wizard_vs_irs.game.play import Game
self.window.show_view(Game(self.pypresence_client))
def shop(self):
from menus.shop import Shop
from wizard_vs_irs.menus.shop import Shop
self.window.show_view(Shop(self.pypresence_client))
def settings(self):
from menus.settings import Settings
from wizard_vs_irs.menus.settings import Settings
self.window.show_view(Settings(self.pypresence_client))

View File

@@ -2,9 +2,9 @@ import copy, pypresence, json
import arcade, arcade.gui
from utils.constants import button_style, dropdown_style, slider_style, settings, discord_presence_id, settings_start_category
from utils.utils import FakePyPresence
from utils.preload import button_texture, button_hovered_texture
from wizard_vs_irs.utils.constants import button_style, dropdown_style, slider_style, settings, discord_presence_id, settings_start_category
from wizard_vs_irs.utils.utils import FakePyPresence
from wizard_vs_irs.utils.preload import button_texture, button_hovered_texture
from arcade.gui import UIBoxLayout, UIAnchorLayout
@@ -276,7 +276,7 @@ class Settings(arcade.gui.UIView):
element.texture = button_texture
def main_exit(self):
from menus.main import Main
from wizard_vs_irs.menus.main import Main
self.window.show_view(Main(self.pypresence_client, *self.args))
def ui_cleanup(self):

View File

@@ -2,8 +2,8 @@ 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
from wizard_vs_irs.utils.constants import button_style, SHOP_ITEMS
from wizard_vs_irs.utils.preload import button_texture, button_hovered_texture
class Shop(arcade.gui.UIView):
def __init__(self, pypresence_client):
@@ -29,7 +29,7 @@ class Shop(arcade.gui.UIView):
with open("data.json", "w") as file:
file.write(json.dumps(self.data, indent=4))
from menus.main import Main
from wizard_vs_irs.menus.main import Main
self.window.show_view(Main(self.pypresence_client))
def on_show_view(self):

97
wizard_vs_irs/run.py Normal file
View File

@@ -0,0 +1,97 @@
import pyglet
pyglet.options.debug_gl = False
import logging, datetime, os, json, sys, arcade
from wizard_vs_irs.utils.utils import get_closest_resolution, print_debug_info, on_exception
from wizard_vs_irs.utils.constants import log_dir, menu_background_color
from wizard_vs_irs.menus.main import Main
def main():
sys.excepthook = on_exception
pyglet.resource.path.append(os.getcwd())
pyglet.font.add_directory('./assets/fonts')
if not log_dir in os.listdir():
os.makedirs(log_dir)
while len(os.listdir(log_dir)) >= 5:
files = [(file, os.path.getctime(os.path.join(log_dir, file))) for file in os.listdir(log_dir)]
oldest_file = sorted(files, key=lambda x: x[1])[0][0]
os.remove(os.path.join(log_dir, oldest_file))
timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
log_filename = f"debug_{timestamp}.log"
logging.basicConfig(filename=f'{os.path.join(log_dir, log_filename)}', format='%(asctime)s %(name)s %(levelname)s: %(message)s', level=logging.DEBUG)
for logger_name_to_disable in ['arcade', "numba"]:
logging.getLogger(logger_name_to_disable).propagate = False
logging.getLogger(logger_name_to_disable).disabled = True
if os.path.exists('settings.json'):
with open('settings.json', 'r') as settings_file:
settings = json.load(settings_file)
resolution = list(map(int, settings['resolution'].split('x')))
if not settings.get("anti_aliasing", "4x MSAA") == "None":
antialiasing = int(settings.get("anti_aliasing", "4x MSAA").split('x')[0])
else:
antialiasing = 0
fullscreen = settings['window_mode'] == 'Fullscreen'
style = arcade.Window.WINDOW_STYLE_BORDERLESS if settings['window_mode'] == 'borderless' else arcade.Window.WINDOW_STYLE_DEFAULT
vsync = settings['vsync']
fps_limit = settings['fps_limit']
else:
resolution = get_closest_resolution()
antialiasing = 4
fullscreen = False
style = arcade.Window.WINDOW_STYLE_DEFAULT
vsync = True
fps_limit = 0
settings = {
"resolution": f"{resolution[0]}x{resolution[1]}",
"antialiasing": "4x MSAA",
"window_mode": "Windowed",
"vsync": True,
"fps_limit": 60,
"discord_rpc": True
}
with open("settings.json", "w") as file:
file.write(json.dumps(settings))
window = arcade.Window(width=resolution[0], height=resolution[1], title='Wizard vs IRS', samples=antialiasing, antialiasing=antialiasing > 0, fullscreen=fullscreen, vsync=vsync, resizable=False, style=style)
if vsync:
window.set_vsync(True)
display_mode = window.display.get_default_screen().get_mode()
refresh_rate = display_mode.rate
window.set_update_rate(1 / refresh_rate)
window.set_draw_rate(1 / refresh_rate)
elif not fps_limit == 0:
window.set_update_rate(1 / fps_limit)
window.set_draw_rate(1 / fps_limit)
else:
window.set_update_rate(1 / 99999999)
window.set_draw_rate(1 / 99999999)
arcade.set_background_color(menu_background_color)
print_debug_info()
main = Main()
window.show_view(main)
logging.debug('Game started.')
arcade.run()
logging.info('Exited with error code 0.')
if __name__ == "__main__":
main()

View File

@@ -1,6 +1,6 @@
import logging, arcade, arcade.gui, sys, traceback
from utils.constants import menu_background_color
from wizard_vs_irs.utils.constants import menu_background_color
import pyglet.info, pyglet.event