bombsquad-plugin-manager/plugins/minigames/egg_game.py

494 lines
19 KiB
Python
Raw Normal View History

2024-01-24 14:17:05 +03:00
# Ported by brostos to api 8
# Tool used to make porting easier.(https://github.com/bombsquad-community/baport)
# Released under the MIT License. See LICENSE for details.
"""Egg game and support classes."""
# The Egg Game - throw egg as far as you can
2024-01-24 11:26:19 +00:00
# created in BCS (Bombsquad Consultancy Service) - opensource bombsquad mods for all
# discord.gg/ucyaesh join now and give your contribution
2024-01-24 14:17:05 +03:00
# The Egg game by mr.smoothy
# ba_meta require api 8
# (see https://ballistica.net/wiki/meta-tag-system)
from __future__ import annotations
from typing import TYPE_CHECKING
import babase
import bauiv1 as bui
import bascenev1 as bs
from bascenev1lib.actor.playerspaz import PlayerSpaz
from bascenev1lib.actor.scoreboard import Scoreboard
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
from bascenev1lib.gameutils import SharedObjects
from bascenev1lib.actor.flag import Flag
import math
import random
if TYPE_CHECKING:
from typing import Any, Sequence, Dict, Type, List, Optional, Union
class PuckDiedMessage:
"""Inform something that a puck has died."""
def __init__(self, puck: Puck):
self.puck = puck
class Puck(bs.Actor):
"""A lovely giant hockey puck."""
def __init__(self, position: Sequence[float] = (0.0, 1.0, 0.0)):
super().__init__()
shared = SharedObjects.get()
activity = self.getactivity()
# Spawn just above the provided point.
self._spawn_pos = (position[0], position[1] + 1.0, position[2])
2024-01-24 11:26:19 +00:00
self.last_players_to_touch = None
2024-01-24 14:17:05 +03:00
self.scored = False
self.egg_mesh = bs.getmesh('egg')
self.egg_tex_1 = bs.gettexture('eggTex1')
self.egg_tex_2 = bs.gettexture('eggTex2')
self.egg_tex_3 = bs.gettexture('eggTex3')
2024-01-24 11:26:19 +00:00
self.eggtx = [self.egg_tex_1, self.egg_tex_2, self.egg_tex_3]
regg = random.randrange(0, 3)
2024-01-24 14:17:05 +03:00
assert activity is not None
assert isinstance(activity, EggGame)
pmats = [shared.object_material, activity.puck_material]
self.node = bs.newnode('prop',
delegate=self,
attrs={
'mesh': self.egg_mesh,
'color_texture': self.eggtx[regg],
'body': 'capsule',
'reflection': 'soft',
'reflection_scale': [0.2],
'shadow_size': 0.5,
2024-01-24 11:26:19 +00:00
'body_scale': 0.7,
2024-01-24 14:17:05 +03:00
'is_area_of_interest': True,
'position': self._spawn_pos,
'materials': pmats
})
bs.animate(self.node, 'mesh_scale', {0: 0, 0.2: 0.7, 0.26: 0.6})
def handlemessage(self, msg: Any) -> Any:
if isinstance(msg, bs.DieMessage):
assert self.node
self.node.delete()
activity = self._activity()
if activity and not msg.immediate:
activity.handlemessage(PuckDiedMessage(self))
# If we go out of bounds, move back to where we started.
elif isinstance(msg, bs.OutOfBoundsMessage):
assert self.node
self.node.position = self._spawn_pos
elif isinstance(msg, bs.HitMessage):
assert self.node
assert msg.force_direction is not None
self.node.handlemessage(
'impulse', msg.pos[0], msg.pos[1], msg.pos[2], msg.velocity[0],
msg.velocity[1], msg.velocity[2], 1.0 * msg.magnitude,
1.0 * msg.velocity_magnitude, msg.radius, 0,
msg.force_direction[0], msg.force_direction[1],
msg.force_direction[2])
# If this hit came from a player, log them as the last to touch us.
s_player = msg.get_source_player(Player)
if s_player is not None:
activity = self._activity()
if activity:
if s_player in activity.players:
self.last_players_to_touch = s_player
else:
super().handlemessage(msg)
class Player(bs.Player['Team']):
"""Our player type for this game."""
class Team(bs.Team[Player]):
"""Our team type for this game."""
2024-01-24 21:28:29 +03:00
def __init__(self) -> None:
2024-01-24 14:17:05 +03:00
self.score = 0
# ba_meta export bascenev1.GameActivity
class EggGame(bs.TeamGameActivity[Player, Team]):
"""Egg game."""
name = 'Epic Egg Game'
description = 'Score some goals.'
available_settings = [
bs.IntSetting(
'Score to Win',
min_value=1,
default=1,
increment=1,
),
bs.IntChoiceSetting(
'Time Limit',
choices=[
('None', 0),
('40 Seconds', 40),
('1 Minute', 60),
('2 Minutes', 120),
('5 Minutes', 300),
('10 Minutes', 600),
('20 Minutes', 1200),
],
default=0,
),
bs.FloatChoiceSetting(
'Respawn Times',
choices=[
('Shorter', 0.1),
('Short', 0.5),
('Normal', 1.0),
('Long', 2.0),
('Longer', 4.0),
],
default=1.0,
),
]
default_music = bs.MusicType.HOCKEY
@classmethod
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
return issubclass(sessiontype, bs.DualTeamSession)
@classmethod
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
return bs.app.classic.getmaps('football')
def __init__(self, settings: dict):
super().__init__(settings)
shared = SharedObjects.get()
self.slow_motion = True
self._scoreboard = Scoreboard()
self._cheer_sound = bui.getsound('cheer')
self._chant_sound = bui.getsound('crowdChant')
self._foghorn_sound = bui.getsound('foghorn')
self._swipsound = bui.getsound('swip')
self._whistle_sound = bui.getsound('refWhistle')
self.puck_mesh = bs.getmesh('bomb')
self.puck_tex = bs.gettexture('landMine')
self.puck_scored_tex = bs.gettexture('landMineLit')
self._puck_sound = bui.getsound('metalHit')
self.puck_material = bs.Material()
2024-01-24 11:26:19 +00:00
self._fake_wall_material = bs.Material()
self.HIGHEST = 0
2024-01-24 14:17:05 +03:00
self._fake_wall_material.add_actions(
conditions=('they_have_material', shared.player_material),
actions=(
('modify_part_collision', 'collide', True),
('modify_part_collision', 'physical', True)
2024-01-24 11:26:19 +00:00
2024-01-24 14:17:05 +03:00
))
self.puck_material.add_actions(actions=(('modify_part_collision',
'friction', 0.5)))
self.puck_material.add_actions(conditions=('they_have_material',
shared.pickup_material),
actions=('modify_part_collision',
'collide', True))
self.puck_material.add_actions(
conditions=(
('we_are_younger_than', 100),
'and',
('they_have_material', shared.object_material),
),
actions=('modify_node_collision', 'collide', False),
)
# self.puck_material.add_actions(conditions=('they_have_material',
# shared.footing_material),
# actions=('impact_sound',
# self._puck_sound, 0.2, 5))
# Keep track of which player last touched the puck
self.puck_material.add_actions(
conditions=('they_have_material', shared.player_material),
actions=(('call', 'at_connect',
self._handle_puck_player_collide), ))
# We want the puck to kill powerups; not get stopped by them
self.puck_material.add_actions(
conditions=('they_have_material',
PowerupBoxFactory.get().powerup_material),
actions=(('modify_part_collision', 'physical', False),
('message', 'their_node', 'at_connect', bs.DieMessage())))
# self.puck_material.add_actions(
# conditions=('they_have_material',shared.footing_material)
# actions=(('modify_part_collision', 'collide',
# True), ('modify_part_collision', 'physical', True),
# ('call', 'at_connect', self._handle_egg_collision))
# )
self._score_region_material = bs.Material()
self._score_region_material.add_actions(
conditions=('they_have_material', self.puck_material),
actions=(('modify_part_collision', 'collide',
True), ('modify_part_collision', 'physical', False),
('call', 'at_connect', self._handle_score)))
2024-01-24 11:26:19 +00:00
self.main_ground_material = bs.Material()
2024-01-24 14:17:05 +03:00
self.main_ground_material.add_actions(
conditions=('they_have_material', self.puck_material),
actions=(('modify_part_collision', 'collide',
True), ('modify_part_collision', 'physical', False),
('call', 'at_connect', self._handle_egg_collision)))
self._puck_spawn_pos: Optional[Sequence[float]] = None
self._score_regions: Optional[List[bs.NodeActor]] = None
self._puck: Optional[Puck] = None
2024-01-24 11:26:19 +00:00
self._pucks = []
2024-01-24 14:17:05 +03:00
self._score_to_win = int(settings['Score to Win'])
self._time_limit = float(settings['Time Limit'])
def get_instance_description(self) -> Union[str, Sequence]:
return "Throw Egg as far u can"
def get_instance_description_short(self) -> Union[str, Sequence]:
return "Throw Egg as far u can"
def on_begin(self) -> None:
super().on_begin()
2024-01-24 11:26:19 +00:00
if self._time_limit == 0.0:
self._time_limit = 60
2024-01-24 14:17:05 +03:00
self.setup_standard_time_limit(self._time_limit)
# self.setup_standard_powerup_drops()
self._puck_spawn_pos = self.map.get_flag_position(None)
self._spawn_puck()
self._spawn_puck()
self._spawn_puck()
self._spawn_puck()
self._spawn_puck()
# Set up the two score regions.
defs = self.map.defs
self._score_regions = []
2024-01-24 11:26:19 +00:00
pos = (11.88630542755127, 0.3009839951992035, 1.33331298828125)
2024-01-24 14:17:05 +03:00
# mat=bs.Material()
# mat.add_actions(
2024-01-24 11:26:19 +00:00
2024-01-24 14:17:05 +03:00
# actions=( ('modify_part_collision','physical',True),
# ('modify_part_collision','collide',True))
# )
# self._score_regions.append(
# bs.NodeActor(
# bs.newnode('region',
# attrs={
# 'position': pos,
# 'scale': (2,3,5),
# 'type': 'box',
# 'materials': [self._score_region_material]
# })))
# pos=(-11.88630542755127, 0.3009839951992035, 1.33331298828125)
# self._score_regions.append(
# bs.NodeActor(
# bs.newnode('region',
# attrs={
# 'position': pos,
# 'scale': (2,3,5),
# 'type': 'box',
# 'materials': [self._score_region_material]
# })))
self._score_regions.append(
bs.NodeActor(
bs.newnode('region',
attrs={
2024-01-24 11:26:19 +00:00
'position': (-9.21, defs.boxes['goal2'][0:3][1], defs.boxes['goal2'][0:3][2]),
2024-01-24 14:17:05 +03:00
'scale': defs.boxes['goal2'][6:9],
'type': 'box',
'materials': (self._fake_wall_material, )
})))
2024-01-24 11:26:19 +00:00
pos = (0, 0.1, -5)
self.main_ground = bs.newnode('region', attrs={'position': pos, 'scale': (
25, 0.001, 22), 'type': 'box', 'materials': [self.main_ground_material]})
2024-01-24 14:17:05 +03:00
self._update_scoreboard()
self._chant_sound.play()
def on_team_join(self, team: Team) -> None:
self._update_scoreboard()
def _handle_puck_player_collide(self) -> None:
collision = bs.getcollision()
try:
puck = collision.sourcenode.getdelegate(Puck, True)
player = collision.opposingnode.getdelegate(PlayerSpaz,
True).getplayer(
Player, True)
except bs.NotFoundError:
return
puck.last_players_to_touch = player
def _kill_puck(self) -> None:
self._puck = None
2024-01-24 11:26:19 +00:00
2024-01-24 14:17:05 +03:00
def _handle_egg_collision(self) -> None:
2024-01-24 11:26:19 +00:00
no = bs.getcollision().opposingnode
pos = no.position
egg = no.getdelegate(Puck)
source_player = egg.last_players_to_touch
if source_player == None or pos[0] < -8 or not source_player.node.exists():
2024-01-24 14:17:05 +03:00
return
try:
2024-01-24 11:26:19 +00:00
col = source_player.team.color
self.flagg = Flag(pos, touchable=False, color=col).autoretain()
self.flagg.is_area_of_interest = True
player_pos = source_player.node.position
distance = math.sqrt(pow(player_pos[0]-pos[0], 2) + pow(player_pos[2]-pos[2], 2))
dis_mark = bs.newnode('text',
attrs={
'text': str(round(distance, 2))+"m",
'in_world': True,
'scale': 0.02,
'h_align': 'center',
'position': (pos[0], 1.6, pos[2]),
'color': col
})
bs.animate(dis_mark, 'scale', {
0.0: 0, 0.5: 0.01
2024-01-24 14:17:05 +03:00
})
if distance > self.HIGHEST:
2024-01-24 11:26:19 +00:00
self.HIGHEST = distance
2024-01-24 14:17:05 +03:00
self.stats.player_scored(
2024-01-24 11:26:19 +00:00
source_player,
10,
big_message=False)
2024-01-24 14:17:05 +03:00
no.delete()
2024-01-24 11:26:19 +00:00
bs.timer(2, self._spawn_puck)
source_player.team.score = int(distance)
except ():
2024-01-24 14:17:05 +03:00
pass
2024-01-24 11:26:19 +00:00
2024-01-24 14:17:05 +03:00
def spawn_player(self, player: Player) -> bs.Actor:
2024-01-24 11:26:19 +00:00
zoo = random.randrange(-4, 5)
pos = (-11.204887390136719, 0.2998693287372589, zoo)
2024-01-24 14:17:05 +03:00
spaz = self.spawn_player_spaz(
2024-01-24 11:26:19 +00:00
player, position=pos, angle=90)
2024-01-24 14:17:05 +03:00
assert spaz.node
# Prevent controlling of characters before the start of the race.
2024-01-24 11:26:19 +00:00
2024-01-24 14:17:05 +03:00
return spaz
2024-01-24 11:26:19 +00:00
2024-01-24 14:17:05 +03:00
def _handle_score(self) -> None:
"""A point has been scored."""
assert self._puck is not None
assert self._score_regions is not None
# Our puck might stick around for a second or two
# we don't want it to be able to score again.
if self._puck.scored:
return
region = bs.getcollision().sourcenode
index = 0
for index in range(len(self._score_regions)):
if region == self._score_regions[index].node:
break
for team in self.teams:
if team.id == index:
scoring_team = team
team.score += 1
# Tell all players to celebrate.
for player in team.players:
if player.actor:
player.actor.handlemessage(bs.CelebrateMessage(2.0))
# If we've got the player from the scoring team that last
# touched us, give them points.
if (scoring_team.id in self._puck.last_players_to_touch
and self._puck.last_players_to_touch[scoring_team.id]):
self.stats.player_scored(
self._puck.last_players_to_touch[scoring_team.id],
20,
big_message=True)
# End game if we won.
if team.score >= self._score_to_win:
self.end_game()
self._foghorn_sound.play()
self._cheer_sound.play()
# self._puck.scored = True
# Change puck texture to something cool
# self._puck.node.color_texture = self.puck_scored_tex
# Kill the puck (it'll respawn itself shortly).
bs.timer(1.0, self._kill_puck)
# light = bs.newnode('light',
# attrs={
# 'position': bs.getcollision().position,
# 'height_attenuated': False,
# 'color': (1, 0, 0)
# })
# bs.animate(light, 'intensity', {0: 0, 0.5: 1, 1.0: 0}, loop=True)
# bs.timer(1.0, light.delete)
bs.cameraflash(duration=10.0)
self._update_scoreboard()
def end_game(self) -> None:
results = bs.GameResults()
for team in self.teams:
results.set_team_score(team, team.score)
self.end(results=results)
def _update_scoreboard(self) -> None:
winscore = self._score_to_win
# for team in self.teams:
# self._scoreboard.set_team_value(team, team.score, winscore)
def handlemessage(self, msg: Any) -> Any:
# Respawn dead players if they're still in the game.
if isinstance(msg, bs.PlayerDiedMessage):
# Augment standard behavior...
super().handlemessage(msg)
self.respawn_player(msg.getplayer(Player))
# Respawn dead pucks.
elif isinstance(msg, PuckDiedMessage):
if not self.has_ended():
bs.timer(3.0, self._spawn_puck)
else:
super().handlemessage(msg)
def _flash_puck_spawn(self) -> None:
# light = bs.newnode('light',
# attrs={
# 'position': self._puck_spawn_pos,
# 'height_attenuated': False,
# 'color': (1, 0, 0)
# })
# bs.animate(light, 'intensity', {0.0: 0, 0.25: 1, 0.5: 0}, loop=True)
# bs.timer(1.0, light.delete)
pass
2024-01-24 11:26:19 +00:00
2024-01-24 14:17:05 +03:00
def _spawn_puck(self) -> None:
# self._swipsound.play()
# self._whistle_sound.play()
self._flash_puck_spawn()
assert self._puck_spawn_pos is not None
2024-01-24 11:26:19 +00:00
zoo = random.randrange(-5, 6)
pos = (-11.204887390136719, 0.2998693287372589, zoo)
self._pucks.append(Puck(position=pos))