Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ dev = [
strict = true
disable_error_code = ["import-untyped"]

["mypy-pygame.*"]
[[tool.mypy.overrides]]
module = "pygame.*"
ignore_missing_imports = true

["mypy-pymunk.*"]
[[tool.mypy.overrides]]
module = "pymunk.*"
ignore_missing_imports = true
77 changes: 77 additions & 0 deletions src/collisions/ball_to_ball_collisions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import random
from typing import Any

import pymunk


BASE_CRIT_CHANCE = 0.05 # 5% minimum
CRIT_SCALE = 0.0001 # 0.1% per unit of impact speed (tune as needed)
CRIT_MULTIPLIER = 2 # Double damage on crit


def _get_ball_damage(speed: float, crit: bool) -> int:
base_damage = int(speed / 20)
return base_damage * CRIT_MULTIPLIER if crit else base_damage


def _crit_roll(impact_speed: float) -> bool:
crit_chance = min(1.0, BASE_CRIT_CHANCE + impact_speed * CRIT_SCALE)
return random.random() < crit_chance


def handle_ball_to_ball_collision(
arbiter: pymunk.Arbiter,
_space: pymunk.Space,
_data: Any,
) -> None:
shape_a, shape_b = arbiter.shapes
assert shape_a.body is not None
assert shape_b.body is not None
ball_a = shape_a.body.user_data
ball_b = shape_b.body.user_data

n = arbiter.contact_point_set.normal

v_a = ball_a.body.velocity
v_b = ball_b.body.velocity

# Alignment-based impact speeds (damage basis)
impact_b_to_a = max(0, v_b.dot(-n))
impact_a_to_b = max(0, v_a.dot(n))

# Crit rolls
crit_a = _crit_roll(impact_b_to_a)
crit_b = _crit_roll(impact_a_to_b)

damage_to_a = _get_ball_damage(impact_b_to_a, crit_b)
damage_to_b = _get_ball_damage(impact_a_to_b, crit_a)

# First-hitter advantage: whoever was going to do the most damage gets first hit
a_health = ball_a.health - damage_to_a
b_health = ball_b.health - damage_to_b

if a_health <= 0 and b_health <= 0:
if a_health > b_health:
damage_to_a = 0
elif b_health > a_health:
damage_to_b = 0

ball_a.deal_damage(damage_to_b, crit_a)
ball_b.deal_damage(damage_to_a, crit_b)

ball_a.receive_damage(damage_to_a, crit_b)
ball_b.receive_damage(damage_to_b, crit_a)


def handle_post_ball_to_ball_collision(
arbiter: pymunk.Arbiter,
_space: pymunk.Space,
_data: Any,
) -> None:
shape_a, shape_b = arbiter.shapes
assert shape_a.body is not None
assert shape_b.body is not None
ball_a = shape_a.body.user_data
ball_b = shape_b.body.user_data
ball_a.remove_if_dead()
ball_b.remove_if_dead()
64 changes: 0 additions & 64 deletions src/collisions/collision_handler.py

This file was deleted.

5 changes: 4 additions & 1 deletion src/entity/ball/ball.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ def receive_damage(self, damage: int, is_crit: bool) -> None:
)

if self.health == 0:
self.space.remove(self.body, self.shape)
if self.faces:
self.visual_effect_manager.add(
FaceImplosionEffect(
Expand All @@ -140,6 +139,10 @@ def receive_damage(self, damage: int, is_crit: bool) -> None:
)
)

def remove_if_dead(self) -> None:
if self.health <= 0 and self.body in self.space.bodies:
self.space.remove(self.body, self.shape)


def get_current_face(self) -> pygame.Surface:
assert self.faces is not None
Expand Down
8 changes: 6 additions & 2 deletions src/game/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import pygame
import pymunk

from src.collisions.collision_handler import CollisionHandler
from src.collisions.ball_to_ball_collisions import (
handle_ball_to_ball_collision,
handle_post_ball_to_ball_collision,
)
from src.configuration.configuration import Configuration
from src.entity.ball.ball import Ball
from src.entity.ball.ball_spawn_config import BallSpawnConfig
Expand Down Expand Up @@ -34,7 +37,8 @@ def _create_space() -> pymunk.Space:
space.on_collision(
Ball.COLLISION_TYPE,
Ball.COLLISION_TYPE,
begin=CollisionHandler.handle_ball_collision,
begin=handle_ball_to_ball_collision,
separate=handle_post_ball_to_ball_collision,
)
return space

Expand Down
Loading
Loading