from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from .objects.Spectator import Spectator from .objects.Player import Player from .objects.Game import Game from .objects.Ball import Ball from .objects.Point import Point from .objects.Vector import Point from .objects.Segment import Segment from .objects.Vector import Vector from . import config import math import asyncio from asgiref.sync import SyncToAsync from time import sleep def get_sign(num: float) -> int: if (num == 0): return 1 if (num > 0): return 1 if (num < 0): return -1 def simplify_angle(angle: float): return (angle + 2 * math.pi) % (2 * math.pi) def exclude_invalid_segments(segments: list[Segment], ball: Ball): cos: float = math.cos(ball.angle) sin: float = math.sin(ball.angle) valid_segments = [] print(ball.angle) angle: float = ball.angle for segment in segments: diff_x: float = segment.start.x - ball.position.x diff_y: float = segment.start.y - ball.position.y start_angle: float = math.atan2(diff_y, diff_x) diff_x: float = segment.stop.x - ball.position.x diff_y: float = segment.stop.y - ball.position.y stop_angle: float = math.atan2(diff_y, diff_x) min_angle: float = min(start_angle, stop_angle) max_angle: float = max(start_angle, stop_angle) print(min_angle, max_angle) if not (min_angle <= angle <= max_angle): continue start_cos: float = math.cos(start_angle) start_sin: float = math.sin(start_angle) stop_cos: float = math.cos(stop_angle) stop_sin: float = math.sin(stop_angle) if (get_sign(start_cos) != get_sign(cos) and get_sign(cos) != get_sign(stop_cos)): continue if (get_sign(start_sin) != get_sign(sin) and get_sign(sin) != get_sign(stop_sin)): continue valid_segments.append(segment) return valid_segments def get_derive(segment: Segment): if (segment.start.x == segment.stop.x): return None return (segment.stop.y - segment.start.y) / (segment.stop.x - segment.stop.y) def get_intercept(derive: float, point: Point): if (derive is None): return None return point.y - (point.x * derive) def get_interception(segment1: Segment, segment2: Segment): #TODO https://chat.openai.com/c/3fd8b80f-86cc-4fb9-8ff1-44c108f5ccae pass def get_impact_point(segments: list[Segment], ball: Ball): valid_segments: list[Segment] = exclude_invalid_segments(segments, ball) point: Point = Point(ball.position.x + math.cos(ball.angle), ball.position.y + math.sin(ball.angle)) closest: Point = None distance: float = None for segment in valid_segments: impact: Point = get_interception(Segment(ball.position, point), segment) distance2: float = impact.distance(ball.position) if (closest is None or distance2 < distance): closest = impact distance = distance2 return closest async def update_ball(game: Game, impact: Point): distance: float = impact.distance(game.ball.position) - game.ball.size / 2 time_before_impact: float = distance / game.ball.speed await asyncio.sleep(time_before_impact) game.ball.angle = game.ball.angle + 180 game.ball.position = impact await SyncToAsync(game.broadcast)("update_ball", game.ball.to_dict()) async def render(game: Game): while True: segments: list[Segment] = [player.rail for player in game.players] + [wall.rail for wall in game.walls] impact = get_impact_point(segments, game.ball) await update_ball(game, impact) def routine(game: Game): asyncio.run(render(game)) while True: for player in game._updated_players: game.broadcast("update_paddle", player.to_dict(), [player]) game._updated_players.clear() sleep(1 / config.SERVER_TPS)