Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
RedToxyl committed Jul 17, 2024
2 parents 325b46a + 7ff8169 commit cb01727
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 30 deletions.
40 changes: 37 additions & 3 deletions services/game_server/backend/game/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
from typing import Dict
from .player import Player
from backend.constants import STATE_UPDATE_INTERVAL, BROADCAST_INTERVAL, DATA_DIR
import random
from .item import DamageItem, SpeedBoostItem, WeaponItem, spawn_randomly

class Game:
def __init__(self, server) -> None:
self.server = server
self.socket_connections: Dict[str, Player] = {}
self.projectiles: Dict[str, Player] = {}
self.projectiles_to_destroy = []
self.items = []
self.reset_events()
with open(os.path.join(DATA_DIR, "weapons.json")) as f:
self.weapons = json.load(f)
Expand All @@ -29,7 +32,8 @@ def register_event(self, event_type, event_data):
def _get_tasks(self):
return [
asyncio.create_task(self.update_game_state()),
asyncio.create_task(self.broadcast_game_state())
asyncio.create_task(self.broadcast_game_state()),
asyncio.create_task(self.manage_items())
]

def get_player_count(self):
Expand Down Expand Up @@ -96,6 +100,13 @@ def check_collisions(self, players) -> None:
if projectile.is_collision(other_projectile):
projectile.destroy()
other_projectile.destroy()
for i in range(len(self.items) - 1, 0, -1):
item = self.items[i]
for _, player in players:
if player.is_collision(item):
item.on_collision(player)
self.items.pop(i)


self.destroy_projectiles()

Expand All @@ -107,7 +118,8 @@ async def update_players(self) -> None:

gameState = {
"players": {pid: {"pos": list(p.pos), "hp": p.hp, "name": p.name} for pid, p in self.socket_connections.items()},
"projectiles": {pid: {"pos": list(p.pos)} for pid, p in self.projectiles.items()}
"items": [ {"pos": list(p.pos), "type": p.item_type} for p in self.items],
"projectiles": {pid: {"pos": list(p.pos), "angle": p.angle} for pid, p in self.projectiles.items()}
}

for player_id, player in connections:
Expand All @@ -128,4 +140,26 @@ async def broadcast_leaderboard(self) -> None:
await self.server.app.sio.emit("current_leaderboard", {
"leaderboard": sorted_leaderboard,
"playerId": player_id
}, room=player_id)
}, room=player_id)

async def manage_items(self):
while True:
self.despawn_items()
self.respawn_items()
await asyncio.sleep(20) # Despawn and respawn every 20 seconds

def despawn_items(self):
self.items.clear()

def respawn_items(self):
# Assuming you have a defined list or method of item types to spawn
for _ in range(random.randint(5, 10)): # Random number of new items
item_type = random.choice([SpeedBoostItem, DamageItem, WeaponItem])
new_item = None
if item_type == WeaponItem:
w = random.choice(list(self.weapons.keys()))
new_item = WeaponItem(w)
else:
new_item = item_type() # Assuming the constructor sets the position
spawn_randomly(new_item)
self.items.append(new_item)
41 changes: 41 additions & 0 deletions services/game_server/backend/game/item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import random

from .player import Player
from .core import Entity, Pos
from backend.constants import X_MAX, Y_MAX

class SpeedBoostItem(Entity):
def __init__(self) -> None:
super().__init__(Pos(0,0))
self.item_type = "speed"
def on_collision(self, player: Player):
player.increase_speed_by(1)
print("Speed: " + str(player._speed))


class DamageItem(Entity):
def __init__(self) -> None:
super().__init__(Pos(0,0))
self.item_type = "damage"

def on_collision(self, player: Player):
player.hp = max(player.hp - 1, 1)

print("HP: " + str(player.hp))


class WeaponItem(Entity):
def __init__(self, weapon_type) -> None:
super().__init__(Pos(0,0))
self.item_type = "weapon"
self.weapon_type = weapon_type

def on_collision(self, player: Player):
player.equipped_weapon = self.weapon_type
print("Weapon: " + str(self.weapon_type))

def spawn_randomly(entity: Entity):
x_position = random.randint(0, X_MAX)
y_position = random.randint(0, Y_MAX)
entity.pos.x = x_position
entity.pos.y = y_position
45 changes: 22 additions & 23 deletions services/game_server/backend/game/player.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import datetime
from .core import Entity, Pos, get_random_position
from .projectile import Projectile
import time
import random
import math
from backend.constants import X_MAX, Y_MAX
from unique_names_generator import get_random_name
from unique_names_generator.data import ADJECTIVES, ANIMALS

class Player (Entity):
def __init__(self, game, pos: Pos, sid, hp: int = 100) -> None:
class Player(Entity):
def __init__(self, game, pos: Pos, sid, hp: int = 100) -> None:
super().__init__(pos)
self.game = game
self.hp = hp
Expand All @@ -19,62 +17,63 @@ def __init__(self, game, pos: Pos, sid, hp: int = 100) -> None:
self.equipped_weapon = "Pistol"
self.cooldown = 0
self.currentmove = 0
self.maxmove = 12
self.speed = 3
self._max_moves = 12
self._speed = 3
self.uuid = sid

def increase_speed_by(self, bySpeed: int):
self._max_moves = min(72, self._max_moves + bySpeed * 2)
self._speed = min(9, self._speed + bySpeed)

def move(self, update):
dx, dy = update
dx, dy = dx * self.speed, dy * self.speed
dx, dy = dx * self._speed, dy * self._speed
self.currentmove += math.sqrt(dx ** 2 + dy ** 2)
if self.currentmove > self.maxmove:
if self.currentmove > self._max_moves:
self.currentmove = 0 # Reset move count after limit is reached
return
self.pos.x += dx
self.pos.y += dy
self.pos.x = min(X_MAX, max(0, self.pos.x))
self.pos.y = min(Y_MAX, max(0, self.pos.y))
self.pos.x = min(X_MAX, max(0, self.pos.x + dx))
self.pos.y = min(Y_MAX, max(0, self.pos.y + dy))

def is_in_visual_range_of(self, other) -> bool:
dx, dy = self.pos.x_distance_to(other.pos), self.pos.y_distance_to(other.pos)
return dx <= X_MAX / 2 and dy <= Y_MAX / 2
def respawn_and_get_survival_time(self) -> datetime:

def respawn_and_get_survival_time(self) -> datetime.timedelta:
now = datetime.datetime.now()
delta = now - self.last_respawned_at
self.last_respawned_at = now
self.kills = 0
self.hp = 100
self.pos = get_random_position()
return delta

def take_damage(self, damage, source):
self.hp -= damage
if self.hp <= 0:
if type(source) == Player:
self.killed_by(source)
if isinstance(source, Player):
source.killed(self)
seconds_alive = self.respawn_and_get_survival_time()
self.on_death(round(seconds_alive.total_seconds()))

def killed(self, victim):
if self != victim:
self.kills += 1
if self.kills in [3, 5, 8, 10, 15]:
if self.kills in {3, 5, 8, 10, 15}:
self.game.register_event("killstreak", {"player": self.uuid, "kills": self.kills})
print(f"{self.name} sent {victim.name} to the shadow realm!")

print(f"{self.name} sent {victim.name} to the shadow realm!")

def killed_by(self, killer):
self.game.register_event("kills", {"killer": killer.uuid, "victim": self.uuid})
killer.killed(self)

def on_death(self, seconds_alive):
self.game.server.matchmaking_api.addHighscore(self.name, self.kills, seconds_alive)

def shoot(self, angle):
# emit a projectile into the angle, starting next to the player
weapon = self.game.weapons[self.equipped_weapon]
self.cooldown = weapon["cooldown"]
distance = 50
new_pos = Pos(self.pos.x + distance * math.cos(angle), self.pos.y + distance * math.sin(angle))
new_projectile = Projectile(new_pos, angle, weapon["speed"], weapon["damage"], weapon["range"], self)
self.game.add_projectile(new_projectile)
self.game.add_projectile(new_projectile)
8 changes: 7 additions & 1 deletion services/game_server/data/weapons.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@
},
"Rocket Launcher": {
"damage": 80,
"cooldown": 50,
"cooldown": 1,
"range": 2000,
"speed": 500
},
"MP5": {
"damage": 10,
"cooldown": 0.05,
"range": 500,
"speed": 50
}
}
Binary file modified services/game_server/frontend/public/bullet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion services/game_server/frontend/src/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ export class Entity {
container: PIXI.Container;
spritePath: string;
app: PIXI.Application;
rotation: number;

constructor(x: number, y: number, app: PIXI.Application, spritePath: string) {
constructor(x: number, y: number, app: PIXI.Application, spritePath: string, rotation = 0) {
this.x = x;
this.y = y;
this.targetX = x;
this.targetY = y;
this.lastUpdateTime = Date.now();
this.app = app;
this.spritePath = spritePath;
this.rotation = rotation;
this.container = this.initContainer();
}

Expand All @@ -38,6 +40,7 @@ export class Entity {
const sprite: PIXI.Sprite = PIXI.Sprite.from(this.spritePath);
sprite.anchor.set(0.5, 0.5);
sprite.scale.set(0.1, 0.1); //TODO remove magic numbers
sprite.rotation = this.rotation;
return sprite;
}

Expand Down
31 changes: 29 additions & 2 deletions services/game_server/frontend/src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class Game {
leaderboardGraphic: List | undefined;
playerToLeaderboardText: PIXI.Text[];
vfxHandler: popupMessageQueue;
items: Record<string, Entity>;


constructor(app: PIXI.Application, socket: Socket, keys: Record<string, boolean>
Expand All @@ -49,6 +50,7 @@ export class Game {
this.playerToLeaderboardText = [];
this.setupLeaderboard();
this.vfxHandler = new popupMessageQueue(app);
this.items = {};
}

get gameSize(): number[] {
Expand All @@ -66,7 +68,7 @@ export class Game {
}

try_shoot(pos: number[]): void {
if(this.player.canShoot) {
if (this.player.canShoot) {
let angle = Math.atan2(pos[1] - this.gameSize[1] / 2, pos[0] - this.gameSize[0] / 2);
this.socket.emit("player_click", angle);
}
Expand Down Expand Up @@ -99,6 +101,10 @@ export class Game {
projectile.readdToCanvas();
projectile.updateDraw(projectile.relativeToPlayerTranslation(this.player));
});
Object.values(this.items).forEach((item: Entity) => {
item.readdToCanvas();
item.updateDraw(item.relativeToPlayerTranslation(this.player));
});
const t = (x: number, y: number) => this.player.relativeToPlayerTranslation(this.player)(x, y);
this.player.updateDraw(t);
}
Expand All @@ -121,6 +127,11 @@ export class Game {
});
}

clearItems(): void {
Object.values(this.items).forEach((projectile: Entity) => {
projectile.removeFromCanvas();
});
}
joinGame(): void {
this.enemies = {};
this.socket.connect();
Expand Down Expand Up @@ -168,6 +179,7 @@ export class Game {
let playerId = data.playerId;
let playerData = data.gameState["players"]
let projectileData = data.gameState["projectiles"]
let itemData = data.gameState["items"]
let events = data.events;
this.player.updatePosition(playerData[playerId]["pos"][0], playerData[playerId]["pos"][1]);
this.player.hp = playerData[playerId]["hp"];
Expand All @@ -180,6 +192,7 @@ export class Game {

this.clearEnemies();
this.clearProjectiles();
this.clearItems();

Object.keys(playerData).forEach(id => {

Expand All @@ -203,11 +216,25 @@ export class Game {
if (this.projectiles[id]) {
this.projectiles[id].updatePosition(projectileData[id]["pos"][0], projectileData[id]["pos"][1]);
} else {
this.projectiles[id] = new Entity(projectileData[id]["pos"][0], projectileData[id]["pos"][1], this.app, '/dist/bullet.png');
this.projectiles[id] = new Entity(projectileData[id]["pos"][0], projectileData[id]["pos"][1], this.app, '/dist/bullet.png', projectileData[id]["angle"]);
}
newProjectiles[id] = this.projectiles[id];
});

const addedItems: Record<string, Entity> = {};

this.items = addedItems;
Object.keys(itemData).forEach(id => {
if (this.items[id]) {
this.items[id].updatePosition(itemData[id]["pos"][0], itemData[id]["pos"][1]);
} else {
// Assuming you have an Item class to handle different item types
this.items[id] = new Entity(itemData[id]["pos"][0], itemData[id]["pos"][1], this.app, '/dist/bullet.png');
}
addedItems[id] = this.items[id];
});


this.enemies = newPlayers;
this.projectiles = newProjectiles;

Expand Down

0 comments on commit cb01727

Please sign in to comment.