Add abilities, damage number labels, Improve stats, rename lightning bolt to ball of lightning, merge some functions

This commit is contained in:
csd4ni3l
2025-10-12 17:54:50 +02:00
parent 3fd4eee357
commit f7510cfb23
2 changed files with 155 additions and 68 deletions

View File

@@ -1,6 +1,6 @@
import arcade, arcade.gui, random, math, time, json, math 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 BULLET_SPEED, HEALTH_INCREASE_PER_LEVEL, PLAYER_SPEED, IRS_AGENT_TYPES, ATTACK_INTERVAL_DECREASE_PER_LEVEL, ABILITIES
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 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 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
@@ -25,10 +25,11 @@ class IRSAgent(arcade.Sprite):
def __init__(self, x, y): def __init__(self, x, y):
super().__init__(irs_agent_texture, center_x=x, center_y=y, scale=1.25) super().__init__(irs_agent_texture, center_x=x, center_y=y, scale=1.25)
self.speed, self.attack_speed, self.health, self.tax = 0, 0, 0, 0
self.damaged = False self.damaged = False
self.last_damage = time.perf_counter() self.last_damage = time.perf_counter()
self.last_attack = time.perf_counter() self.last_attack = time.perf_counter()
self.health = 0
def update(self): def update(self):
if self.damaged: if self.damaged:
@@ -45,12 +46,26 @@ class Player(arcade.TextureAnimationSprite):
self.direction = arcade.math.Vec2() self.direction = arcade.math.Vec2()
def set_player_animation(self, animation): # this is needed because the animation property will reset to the first frame, so animation doesnt work.
self.animation = animation
class DamageNumberLabel(arcade.gui.UILabel):
def __init__(self, x, y, damage):
super().__init__(x=x, y=y, text=f"-{int(damage)}", text_color=arcade.color.RED)
self.original_y = y
self.finished = False
def update(self):
if self.center_y - self.original_y < 50:
self.rect = self.rect.move(0, 2)
else:
self.finished = True
class Game(arcade.gui.UIView): class Game(arcade.gui.UIView):
def __init__(self, pypresence_client): def __init__(self, pypresence_client):
super().__init__() super().__init__()
arcade.set_background_color(arcade.color.WHITE)
self.pypresence_client = pypresence_client self.pypresence_client = pypresence_client
self.pypresence_client.update(state="Playing the game") self.pypresence_client.update(state="Playing the game")
@@ -71,6 +86,7 @@ class Game(arcade.gui.UIView):
self.spritelist = arcade.SpriteList() self.spritelist = arcade.SpriteList()
self.irs_agents: list[IRSAgent] = [] self.irs_agents: list[IRSAgent] = []
self.damage_numbers: list[arcade.gui.UILabel] = []
self.last_irs_agent_spawn = time.perf_counter() self.last_irs_agent_spawn = time.perf_counter()
self.last_mana = time.perf_counter() self.last_mana = time.perf_counter()
self.last_shoot = time.perf_counter() self.last_shoot = time.perf_counter()
@@ -80,17 +96,22 @@ class Game(arcade.gui.UIView):
self.mana = 0 self.mana = 0
self.tax_evasion_level = TAX_EVASION_NAMES[0] self.tax_evasion_level = TAX_EVASION_NAMES[0]
self.tax_shield = 0
self.immobilize_irs = False
self.last_immobilization = time.perf_counter()
self.last_ability_timers = {}
self.bullets: list[Bullet] = [] 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"]["dark_mode_wizard"])
self.spritelist.append(self.player) 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") self.info_box = self.anchor.add(arcade.gui.UIBoxLayout(space_between=0, align="left"), anchor_x="left", anchor_y="top")
self.evaded_tax_label = self.info_box.add(arcade.gui.UILabel(text="Evaded Tax: 0$", font_size=14, text_color=arcade.color.BLACK)) self.evaded_tax_label = self.info_box.add(arcade.gui.UILabel(text="Evaded Tax: 0$", font_size=14))
self.high_score_label = self.info_box.add(arcade.gui.UILabel(text=f"High Score: {self.high_score}$", font_size=14, text_color=arcade.color.BLACK)) self.high_score_label = self.info_box.add(arcade.gui.UILabel(text=f"High Score: {self.high_score}$", font_size=14))
self.mana_label = self.info_box.add(arcade.gui.UILabel(text="Mana: 0", font_size=14, text_color=arcade.color.BLACK)) self.mana_label = self.info_box.add(arcade.gui.UILabel(text="Mana: 0", font_size=14))
self.tax_evasion_label = self.info_box.add(arcade.gui.UILabel(text=f"Tax Evasion Level: {self.tax_evasion_level}", font_size=14, text_color=arcade.color.BLACK)) self.tax_evasion_label = self.info_box.add(arcade.gui.UILabel(text=f"Tax Evasion Level: {self.tax_evasion_level}", font_size=14))
self.tax_evasion_level_notice = self.anchor.add(arcade.gui.UILabel(text="Tax Evasion Level Increased to example", font_size=28, text_color=arcade.color.BLACK), anchor_x="center", anchor_y="top") self.tax_evasion_level_notice = self.anchor.add(arcade.gui.UILabel(text="Tax Evasion Level Increased to example", font_size=28), anchor_x="center", anchor_y="top")
self.tax_evasion_level_notice.visible = False self.tax_evasion_level_notice.visible = False
self.last_tax_evasion_notice = time.perf_counter() self.last_tax_evasion_notice = time.perf_counter()
@@ -99,11 +120,56 @@ class Game(arcade.gui.UIView):
self.progress_bar._render_thumb = lambda surface: None self.progress_bar._render_thumb = lambda surface: None
self.progress_bar.on_event = lambda event: None 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),
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) 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() self.inventory.pay_tax_button.on_click = lambda event: self.pay_tax()
def dash(self): def damage_irs_agent(self, irs_agent):
self.player.position += self.player.direction * (PLAYER_SPEED * 15 * self.data.get('shop', {}).get('player_speed', 0)) irs_agent.damaged = True
irs_agent.last_damage = time.perf_counter()
item_list = INVENTORY_ITEMS[self.inventory.current_inventory_item]
json_name = item_to_json_name[item_list[0]]
damage = item_list[2] + (item_list[2] / 10 * self.data["shop"][f"{json_name}_dmg"])
irs_agent.health -= damage
self.damage_numbers.append(self.ui.add(DamageNumberLabel(irs_agent.left, irs_agent.top, damage)))
if irs_agent.health <= 0:
self.spritelist.remove(irs_agent)
self.irs_agents.remove(irs_agent)
self.evaded_tax += irs_agent.tax / 2
self.update_evasion_level()
self.camera_shake.start()
def ability(self, ability):
if self.mana >= ABILITIES[ability][1] and time.perf_counter() - self.last_ability_timers.get(ability, 9999999999) <= ABILITIES[ability][0]:
self.mana -= ABILITIES[ability][1]
self.last_ability_timers[ability] = time.perf_counter()
if ability == "dash":
self.player.position += self.player.direction * (PLAYER_SPEED + self.data.get('shop', {}).get('player_speed', 0)) * 30
elif ability == "tax_shield":
self.tax_shield += 750
elif ability == "audit_bomb":
for irs_agent in self.irs_agents:
if arcade.math.Vec2(self.player.center_x, self.player.center_y).distance((irs_agent.center_x, irs_agent.center_y)) <= 250:
for i in range(3):
if irs_agent in self.irs_agents: # if they died the first or second time, they cant be damaged again
self.damage_irs_agent(irs_agent)
elif ability == "freeze_audit":
self.last_immobilization = time.perf_counter()
self.immobilize_irs = True
def spawn_bullet(self, direction): 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(utils.preload, INVENTORY_ITEMS[self.inventory.current_inventory_item][4]), self.player.center_x, self.player.center_y, direction)
@@ -157,35 +223,46 @@ class Game(arcade.gui.UIView):
x = base_x + (math.cos(angle) * amount) x = base_x + (math.cos(angle) * amount)
y = base_y + (math.sin(angle) * amount) y = base_y + (math.sin(angle) * amount)
atk_speed, speed, health, tax = random.choice(IRS_AGENT_TYPES)
irs_agent = IRSAgent(x, y) agent = IRSAgent(x, y)
irs_agent.health = IRS_AGENT_HEALTH + (HEALTH_INCREASE_PER_LEVEL * self.get_current_level_int()) agent.attack_speed = atk_speed - (ATTACK_INTERVAL_DECREASE_PER_LEVEL * self.get_current_level_int())
agent.speed = speed + (SPEED_INCREASE_PER_LEVEL * self.get_current_level_int())
agent.health = health + (HEALTH_INCREASE_PER_LEVEL * self.get_current_level_int())
agent.tax = tax + (TAX_INCREASE_PER_LEVEL * self.get_current_level_int())
self.irs_agents.append(irs_agent) self.irs_agents.append(agent)
self.spritelist.append(irs_agent) self.spritelist.append(agent)
def on_update(self, delta_time): def on_update(self, delta_time):
if self.immobilize_irs and time.perf_counter() - self.last_immobilization >= 4:
self.immobilize_irs = False
for damage_number_label in self.damage_numbers:
damage_number_label.update()
if damage_number_label.finished:
self.damage_numbers.remove(damage_number_label)
self.ui.remove(damage_number_label)
self.camera_shake.update(delta_time)
self.player.update_animation() self.player.update_animation()
if self.window.keyboard[arcade.key.W]: if self.window.keyboard[arcade.key.W]:
self.player.direction = arcade.math.Vec2(self.player.direction.x, 1) 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.set_player_animation(dark_wizard_up_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_up_animation)
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]: elif self.window.keyboard[arcade.key.S]:
self.player.direction = arcade.math.Vec2(self.player.direction.x, -1) 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.set_player_animation(dark_wizard_standing_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_standing_animation)
self.player.animation = (dark_wizard_standing_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_standing_animation)
else: else:
self.player.direction = arcade.math.Vec2(self.player.direction.x, 0) self.player.direction = arcade.math.Vec2(self.player.direction.x, 0)
if self.window.keyboard[arcade.key.D]: if self.window.keyboard[arcade.key.D]:
self.player.direction = arcade.math.Vec2(1, self.player.direction.y) 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.set_player_animation(dark_wizard_right_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_right_animation)
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]: elif self.window.keyboard[arcade.key.A]:
self.player.direction = arcade.math.Vec2(-1, self.player.direction.y) 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.set_player_animation(dark_wizard_left_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_left_animation)
self.player.animation = (dark_wizard_left_animation if self.data["shop"]["dark_mode_wizard"] else light_wizard_left_animation)
else: else:
self.player.direction = arcade.math.Vec2(0, self.player.direction.y) self.player.direction = arcade.math.Vec2(0, self.player.direction.y)
@@ -230,25 +307,34 @@ class Game(arcade.gui.UIView):
self.mana_label.text = f"Mana: {self.mana}" self.mana_label.text = f"Mana: {self.mana}"
self.camera_shake.update(delta_time) if not self.immobilize_irs:
for irs_agent in self.irs_agents:
irs_agent.update()
for irs_agent in self.irs_agents: wizard_pos_vec = arcade.math.Vec2(self.player.center_x, self.player.center_y)
irs_agent.update()
wizard_pos_vec = arcade.math.Vec2(self.player.center_x, self.player.center_y) if wizard_pos_vec.distance(irs_agent.position) <= self.player.width / 2:
if time.perf_counter() - irs_agent.last_attack >= irs_agent.attack_speed:
irs_agent.last_attack = time.perf_counter()
if wizard_pos_vec.distance(irs_agent.position) <= self.player.width / 2: self.camera_shake.start()
if time.perf_counter() - irs_agent.last_attack >= IRS_AGENT_ATTACK_SPEED:
irs_agent.last_attack = time.perf_counter()
self.camera_shake.start() if not self.tax_shield > 0:
self.evaded_tax -= irs_agent.tax
else:
if irs_agent.tax > self.tax_shield:
self.evaded_tax -= irs_agent.tax - self.tax_shield
self.tax_shield = 0
else:
self.tax_shield -= irs_agent.tax
self.evaded_tax -= TAX_PER_IRS_AGENT + (TAX_INCREASE_PER_LEVEL * self.get_current_level_int()) self.damage_numbers.append(self.ui.add(DamageNumberLabel(self.player.left, self.player.top, irs_agent.tax)))
self.update_evasion_level()
else: self.update_evasion_level()
direction = (wizard_pos_vec - irs_agent.position).normalize() else:
irs_agent.angle = -math.degrees(direction.heading()) direction = (wizard_pos_vec - irs_agent.position).normalize()
irs_agent.position += direction * (IRS_AGENT_SPEED + (SPEED_INCREASE_PER_LEVEL * self.get_current_level_int())) irs_agent.angle = -math.degrees(direction.heading())
irs_agent.position += direction * irs_agent.speed
for bullet in self.bullets: for bullet in self.bullets:
bullet.move() bullet.move()
@@ -257,21 +343,9 @@ class Game(arcade.gui.UIView):
for irs_agent in self.irs_agents: for irs_agent in self.irs_agents:
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): 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 self.damage_irs_agent(irs_agent)
irs_agent.last_damage = time.perf_counter()
damage = item_list[2] + (item_list[2] / 10 * self.data["shop"][f"{json_name}_dmg"]) 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.position += bullet.direction * damage * 1.5
irs_agent.health -= damage
if irs_agent.health <= 0:
self.spritelist.remove(irs_agent)
self.irs_agents.remove(irs_agent)
self.evaded_tax += (TAX_PER_IRS_AGENT / 4) + ((TAX_INCREASE_PER_LEVEL / 4) * self.get_current_level_int())
self.update_evasion_level()
self.camera_shake.start()
hit = True hit = True
if hit or bullet.center_x + bullet.radius / 2 > self.window.width or bullet.center_x - bullet.radius / 2 < 0 or bullet.center_y + bullet.radius / 2 > self.window.height or bullet.center_y - bullet.height / 2 < 0: if hit or bullet.center_x + bullet.radius / 2 > self.window.width or bullet.center_x - bullet.radius / 2 < 0 or bullet.center_y + bullet.radius / 2 > self.window.height or bullet.center_y - bullet.height / 2 < 0:
@@ -299,8 +373,6 @@ class Game(arcade.gui.UIView):
with open("data.json", "w") as file: with open("data.json", "w") as file:
file.write(json.dumps(self.data, indent=4)) file.write(json.dumps(self.data, indent=4))
arcade.set_background_color(menu_background_color)
from menus.main import Main from menus.main import Main
self.window.show_view(Main(self.pypresence_client)) self.window.show_view(Main(self.pypresence_client))
elif symbol in INVENTORY_TRIGGER_KEYS: elif symbol in INVENTORY_TRIGGER_KEYS:
@@ -308,7 +380,13 @@ class Game(arcade.gui.UIView):
elif symbol == arcade.key.P: elif symbol == arcade.key.P:
self.pay_tax() self.pay_tax()
elif symbol == arcade.key.TAB: elif symbol == arcade.key.TAB:
self.dash() self.ability("dash")
elif symbol == arcade.key.T:
self.ability("tax_shield")
elif symbol == arcade.key.B:
self.ability("audit_bomb")
elif symbol == arcade.key.F:
self.ability("freeze_audit")
def on_resize(self, width: int, height: int): def on_resize(self, width: int, height: int):
super().on_resize(width, height) super().on_resize(width, height)

View File

@@ -8,16 +8,18 @@ log_dir = 'logs'
discord_presence_id = 1424784736726945915 discord_presence_id = 1424784736726945915
IRS_AGENT_SPAWN_INTERVAL = 0.8 IRS_AGENT_SPAWN_INTERVAL = 0.8
IRS_AGENT_ATTACK_SPEED = 1
IRS_AGENT_SPEED = 1.5
IRS_AGENT_HEALTH = 15
TAX_PER_IRS_AGENT = 300
SPEED_INCREASE_PER_LEVEL = 0.3 IRS_AGENT_TYPES = [
SPAWN_INTERVAL_DECREASE_PER_LEVEL = 0.05 (1, 2.25, 15, 200), # Normal
HEALTH_INCREASE_PER_LEVEL = 2 (0.9, 3.5, 8, 150), # Speedrunner
TAX_INCREASE_PER_LEVEL = 100 (1.3, 1.5, 35, 350) # Auditor
ATTACK_INTERVAL_DECREASE_PER_LEVEL = .1 ]
SPEED_INCREASE_PER_LEVEL = 1 / 5
SPAWN_INTERVAL_DECREASE_PER_LEVEL = 1 / 20
HEALTH_INCREASE_PER_LEVEL = 1 / 7.5
TAX_INCREASE_PER_LEVEL = 1 / 3
ATTACK_INTERVAL_DECREASE_PER_LEVEL = 1 / 10
TAX_EVASION_LEVELS = { TAX_EVASION_LEVELS = {
"Compliant Citizen": 0, "Compliant Citizen": 0,
@@ -45,8 +47,8 @@ PLAYER_SPEED = 4
SHOP_ITEMS = [ SHOP_ITEMS = [
["Fireball DMG", "fb_dmg", 999, 10000], ["Fireball DMG", "fb_dmg", 999, 10000],
["Fireball ATK Speed", "fb_atk_speed", 10, 30000], ["Fireball ATK Speed", "fb_atk_speed", 10, 30000],
["Lightning Bolt DMG", "lb_dmg", 999, 10000], ["Ball of Lightning DMG", "lb_dmg", 999, 10000],
["Lightning Bolt ATK Speed", "lb_atk_speed", 10, 30000], ["Ball of Lightning ATK Speed", "lb_atk_speed", 10, 30000],
["Ice Blast DMG", "ib_dmg", 999, 10000], ["Ice Blast DMG", "ib_dmg", 999, 10000],
["Ice Blast ATK Speed", "ib_atk_speed", 10, 30000], ["Ice Blast ATK Speed", "ib_atk_speed", 10, 30000],
["Inaccuracy Decrease", "inaccuracy_decrease", 10, 25000], ["Inaccuracy Decrease", "inaccuracy_decrease", 10, 25000],
@@ -57,13 +59,20 @@ SHOP_ITEMS = [
INVENTORY_ITEMS = [ INVENTORY_ITEMS = [
["Fireball", 0.25, 10, 10, "fireball_texture"], ["Fireball", 0.25, 10, 10, "fireball_texture"],
["Lightning Bolt", 0.45, 20, 20, "lightning_bolt_texture"], ["Ball of Lightning", 0.45, 20, 20, "lightning_bolt_texture"],
["Ice Blast", 0.15, 5, 7.5, "ice_blast_texture"], ["Ice Blast", 0.15, 5, 7.5, "ice_blast_texture"],
] ]
ABILITIES = {
"dash": [5, 20],
"tax_shield": [15, 50],
"audit_bomb": [20, 100],
"freeze_audit": [15, 150]
}
item_to_json_name = { item_to_json_name = {
"Fireball": "fb", "Fireball": "fb",
"Lightning Bolt": "lb", "Ball of Lightning": "bl",
"Ice Blast": "ib" "Ice Blast": "ib"
} }