mirror of
https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server.git
synced 2025-10-20 00:00:39 +00:00
deleting different case folder names
This commit is contained in:
parent
5e1ab245a9
commit
2e6f150bf8
110 changed files with 1 additions and 256369 deletions
2
dist/ba_root/.bsac2
vendored
2
dist/ba_root/.bsac2
vendored
File diff suppressed because one or more lines are too long
0
dist/ba_root/mods/__init__.py
vendored
0
dist/ba_root/mods/__init__.py
vendored
|
|
@ -1,53 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
|
|
||||||
from playersData import pdata
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
|
|
||||||
def clientid_to_accountid(clientid):
|
|
||||||
"""
|
|
||||||
Transform Clientid To Accountid
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
clientid : int
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
None
|
|
||||||
"""
|
|
||||||
for i in bs.get_game_roster():
|
|
||||||
if i['client_id'] == clientid:
|
|
||||||
return i['account_id']
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def check_permissions(accountid, command):
|
|
||||||
"""
|
|
||||||
Checks The Permission To Player To Executive Command
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
accountid : str
|
|
||||||
command : str
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Boolean
|
|
||||||
"""
|
|
||||||
roles = pdata.get_roles()
|
|
||||||
|
|
||||||
if is_server(accountid):
|
|
||||||
return True
|
|
||||||
|
|
||||||
for role in roles:
|
|
||||||
if accountid in roles[role]["ids"] and "ALL" in roles[role]["commands"]:
|
|
||||||
return True
|
|
||||||
|
|
||||||
elif accountid in roles[role]["ids"] and command in roles[role][
|
|
||||||
"commands"]:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def is_server(accid):
|
|
||||||
for i in bs.get_game_roster():
|
|
||||||
if i['account_id'] == accid and i['client_id'] == -1:
|
|
||||||
return True
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
"""Common bits of functionality shared between all efro projects.
|
|
||||||
|
|
||||||
Things in here should be hardened, highly type-safe, and well-covered by unit
|
|
||||||
tests since they are widely used in live client and server code.
|
|
||||||
|
|
||||||
license : MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
@ -1,271 +0,0 @@
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from .handlers import handlemsg, handlemsg_all, clientid_to_myself
|
|
||||||
|
|
||||||
Commands = ['kill', 'heal', 'curse', 'sleep', 'superpunch', 'gloves', 'shield',
|
|
||||||
'freeze', 'unfreeze', 'godmode']
|
|
||||||
CommandAliases = ['die', 'heath', 'cur', 'sp', 'punch', 'protect', 'ice',
|
|
||||||
'thaw', 'gm']
|
|
||||||
|
|
||||||
|
|
||||||
def ExcelCommand(command, arguments, clientid, accountid):
|
|
||||||
"""
|
|
||||||
Checks The Command And Run Function
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
command : str
|
|
||||||
arguments : str
|
|
||||||
clientid : int
|
|
||||||
accountid : int
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
None
|
|
||||||
"""
|
|
||||||
|
|
||||||
if command in ['kill', 'die']:
|
|
||||||
kill(arguments, clientid)
|
|
||||||
|
|
||||||
elif command in ['heal', 'heath']:
|
|
||||||
heal(arguments, clientid)
|
|
||||||
|
|
||||||
elif command in ['curse', 'cur']:
|
|
||||||
curse(arguments, clientid)
|
|
||||||
|
|
||||||
elif command == 'sleep':
|
|
||||||
sleep(arguments, clientid)
|
|
||||||
|
|
||||||
elif command in ['sp', 'superpunch']:
|
|
||||||
super_punch(arguments, clientid)
|
|
||||||
|
|
||||||
elif command in ['gloves', 'punch']:
|
|
||||||
gloves(arguments, clientid)
|
|
||||||
|
|
||||||
elif command in ['shield', 'protect']:
|
|
||||||
shield(arguments, clientid)
|
|
||||||
|
|
||||||
elif command in ['freeze', 'ice']:
|
|
||||||
freeze(arguments, clientid)
|
|
||||||
|
|
||||||
elif command in ['unfreeze', 'thaw']:
|
|
||||||
un_freeze(arguments, clientid)
|
|
||||||
|
|
||||||
elif command in ['gm', 'godmode']:
|
|
||||||
god_mode(arguments, clientid)
|
|
||||||
|
|
||||||
|
|
||||||
def kill(arguments, clientid):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
myself = clientid_to_myself(clientid)
|
|
||||||
handlemsg(myself, bs.DieMessage())
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
handlemsg_all(bs.DieMessage())
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
req_player = int(arguments[0])
|
|
||||||
handlemsg(req_player, bs.DieMessage())
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def heal(arguments, clientid):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
myself = clientid_to_myself(clientid)
|
|
||||||
handlemsg(myself, babase.PowerupMessage(poweruptype='health'))
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
handlemsg_all(babase.PowerupMessage(poweruptype='health'))
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
req_player = int(arguments[0])
|
|
||||||
handlemsg(req_player, babase.PowerupMessage(poweruptype='health'))
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def curse(arguments, clientid):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
myself = clientid_to_myself(clientid)
|
|
||||||
handlemsg(myself, babase.PowerupMessage(poweruptype='curse'))
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
handlemsg_all(babase.PowerupMessage(poweruptype='curse'))
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
req_player = int(arguments[0])
|
|
||||||
handlemsg(req_player, babase.PowerupMessage(poweruptype='curse'))
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def sleep(arguments, clientid):
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
myself = clientid_to_myself(clientid)
|
|
||||||
activity.players[myself].actor.node.handlemessage('knockout', 8000)
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
for i in activity.players:
|
|
||||||
i.actor.node.handlemessage('knockout', 8000)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
req_player = int(arguments[0])
|
|
||||||
activity.players[req_player].actor.node.handlemessage('knockout',
|
|
||||||
8000)
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def super_punch(arguments, clientid):
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
|
|
||||||
myself = clientid_to_myself(clientid)
|
|
||||||
|
|
||||||
if activity.players[myself].actor._punch_power_scale != 15:
|
|
||||||
activity.players[myself].actor._punch_power_scale = 15
|
|
||||||
activity.players[myself].actor._punch_cooldown = 0
|
|
||||||
else:
|
|
||||||
activity.players[myself].actor._punch_power_scale = 1.2
|
|
||||||
activity.players[myself].actor._punch_cooldown = 400
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
for i in activity.players:
|
|
||||||
if i.actor._punch_power_scale != 15:
|
|
||||||
i.actor._punch_power_scale = 15
|
|
||||||
i.actor._punch_cooldown = 0
|
|
||||||
else:
|
|
||||||
i.actor._punch_power_scale = 1.2
|
|
||||||
i.actor._punch_cooldown = 400
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
req_player = int(arguments[0])
|
|
||||||
|
|
||||||
if activity.players[req_player].actor._punch_power_scale != 15:
|
|
||||||
activity.players[req_player].actor._punch_power_scale = 15
|
|
||||||
activity.players[req_player].actor._punch_cooldown = 0
|
|
||||||
else:
|
|
||||||
activity.players[req_player].actor._punch_power_scale = 1.2
|
|
||||||
activity.players[req_player].actor._punch_cooldown = 400
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def gloves(arguments, clientid):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
myself = clientid_to_myself(clientid)
|
|
||||||
handlemsg(myself, babase.PowerupMessage(poweruptype='punch'))
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
handlemsg_all(babase.PowerupMessage(poweruptype='punch'))
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
req_player = int(arguments[0])
|
|
||||||
handlemsg(req_player, babase.PowerupMessage(poweruptype='punch'))
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def shield(arguments, clientid):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
myself = clientid_to_myself(clientid)
|
|
||||||
handlemsg(myself, babase.PowerupMessage(poweruptype='shield'))
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
handlemsg_all(babase.PowerupMessage(poweruptype='shield'))
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
req_player = int(arguments[0])
|
|
||||||
handlemsg(req_player, babase.PowerupMessage(poweruptype='shield'))
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def freeze(arguments, clientid):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
myself = clientid_to_myself(clientid)
|
|
||||||
handlemsg(myself, babase.FreezeMessage())
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
handlemsg_all(babase.FreezeMessage())
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
req_player = int(arguments[0])
|
|
||||||
handlemsg(req_player, babase.FreezeMessage())
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def un_freeze(arguments, clientid):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
myself = clientid_to_myself(clientid)
|
|
||||||
handlemsg(myself, babase.ThawMessage())
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
handlemsg_all(babase.ThawMessage())
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
req_player = int(arguments[0])
|
|
||||||
handlemsg(req_player, babase.ThawMessage())
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def god_mode(arguments, clientid):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
myself = clientid_to_myself(clientid)
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
player = activity.players[myself].actor
|
|
||||||
|
|
||||||
if player._punch_power_scale != 7:
|
|
||||||
player._punch_power_scale = 7
|
|
||||||
player.node.hockey = True
|
|
||||||
player.node.invincible = True
|
|
||||||
|
|
||||||
else:
|
|
||||||
player._punch_power_scale = 1.2
|
|
||||||
player.node.hockey = False
|
|
||||||
player.node.invincible = False
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
for i in activity.players:
|
|
||||||
if i.actor._punch_power_scale != 7:
|
|
||||||
i.actor._punch_power_scale = 7
|
|
||||||
i.actor.node.hockey = True
|
|
||||||
i.actor.node.invincible = True
|
|
||||||
else:
|
|
||||||
i.actor._punch_power_scale = 1.2
|
|
||||||
i.actor.node.hockey = False
|
|
||||||
i.actor.node.invincible = False
|
|
||||||
|
|
||||||
else:
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
req_player = int(arguments[0])
|
|
||||||
player = activity.players[req_player].actor
|
|
||||||
|
|
||||||
if player._punch_power_scale != 7:
|
|
||||||
player._punch_power_scale = 7
|
|
||||||
player.node.hockey = True
|
|
||||||
player.node.invincible = True
|
|
||||||
|
|
||||||
else:
|
|
||||||
player._punch_power_scale = 1.2
|
|
||||||
player.node.hockey = False
|
|
||||||
player.node.invincible = False
|
|
||||||
|
|
@ -1,218 +0,0 @@
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from tools import corelib
|
|
||||||
from .handlers import handlemsg, handlemsg_all
|
|
||||||
|
|
||||||
Commands = ['fly', 'invisible', 'headless', 'creepy', 'celebrate', 'spaz',
|
|
||||||
'speed', 'floater']
|
|
||||||
CommandAliases = ['inv', 'hl', 'creep', 'celeb', 'flo']
|
|
||||||
|
|
||||||
|
|
||||||
def ExcelCommand(command, arguments, clientid, accountid):
|
|
||||||
"""
|
|
||||||
Checks The Command And Run Function
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
command : str
|
|
||||||
arguments : str
|
|
||||||
clientid : int
|
|
||||||
accountid : int
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
None
|
|
||||||
"""
|
|
||||||
|
|
||||||
if command == 'speed':
|
|
||||||
speed(arguments)
|
|
||||||
|
|
||||||
elif command == 'fly':
|
|
||||||
fly(arguments)
|
|
||||||
|
|
||||||
elif command in ['inv', 'invisible']:
|
|
||||||
invi(arguments)
|
|
||||||
|
|
||||||
elif command in ['hl', 'headless']:
|
|
||||||
headless(arguments)
|
|
||||||
|
|
||||||
elif command in ['creepy', 'creep']:
|
|
||||||
creep(arguments)
|
|
||||||
|
|
||||||
elif command in ['celebrate', 'celeb']:
|
|
||||||
celeb(arguments)
|
|
||||||
|
|
||||||
elif command == 'spaz':
|
|
||||||
spaz(arguments)
|
|
||||||
|
|
||||||
elif command in ['floater', 'flo']:
|
|
||||||
floater(arguments, clientid)
|
|
||||||
|
|
||||||
|
|
||||||
def floater(arguments, clientid):
|
|
||||||
try:
|
|
||||||
from .. import floater
|
|
||||||
if arguments == []:
|
|
||||||
floater.assignFloInputs(clientid)
|
|
||||||
else:
|
|
||||||
floater.assignFloInputs(arguments[0])
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def speed(arguments):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
corelib.set_speed(float(arguments[0]))
|
|
||||||
|
|
||||||
|
|
||||||
def fly(arguments):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
for players in activity.players:
|
|
||||||
if players.actor.node.fly != True:
|
|
||||||
players.actor.node.fly = True
|
|
||||||
else:
|
|
||||||
players.actor.node.fly = False
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
player = int(arguments[0])
|
|
||||||
|
|
||||||
if activity.players[player].actor.node.fly != True:
|
|
||||||
activity.players[player].actor.node.fly = True
|
|
||||||
else:
|
|
||||||
activity.players[player].actor.node.fly = False
|
|
||||||
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def invi(arguments):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
return
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
for i in activity.players:
|
|
||||||
if i.actor.exists() and i.actor.node.torso_mesh != None:
|
|
||||||
body = i.actor.node
|
|
||||||
body.head_mesh = None
|
|
||||||
body.torso_mesh = None
|
|
||||||
body.upper_arm_mesh = None
|
|
||||||
body.forearm_mesh = None
|
|
||||||
body.pelvis_mesh = None
|
|
||||||
body.hand_mesh = None
|
|
||||||
body.toes_mesh = None
|
|
||||||
body.upper_leg_mesh = None
|
|
||||||
body.lower_leg_mesh = None
|
|
||||||
body.style = 'cyborg'
|
|
||||||
else:
|
|
||||||
|
|
||||||
player = int(arguments[0])
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
body = activity.players[player].actor.node
|
|
||||||
|
|
||||||
if body.torso_mesh != None:
|
|
||||||
body.head_mesh = None
|
|
||||||
body.torso_mesh = None
|
|
||||||
body.upper_arm_mesh = None
|
|
||||||
body.forearm_mesh = None
|
|
||||||
body.pelvis_mesh = None
|
|
||||||
body.hand_mesh = None
|
|
||||||
body.toes_mesh = None
|
|
||||||
body.upper_leg_mesh = None
|
|
||||||
body.lower_leg_mesh = None
|
|
||||||
body.style = 'cyborg'
|
|
||||||
|
|
||||||
|
|
||||||
def headless(arguments):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
return
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
for players in activity.players:
|
|
||||||
|
|
||||||
node = players.actor.node
|
|
||||||
if node.head_mesh != None:
|
|
||||||
node.head_mesh = None
|
|
||||||
node.style = 'cyborg'
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
player = int(arguments[0])
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
node = activity.players[player].actor.node
|
|
||||||
|
|
||||||
if node.head_mesh != None:
|
|
||||||
node.head_mesh = None
|
|
||||||
node.style = 'cyborg'
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def creep(arguments):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
return
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
for players in activity.players:
|
|
||||||
node = players.actor.node
|
|
||||||
|
|
||||||
if node.head_mesh != None:
|
|
||||||
node.head_mesh = None
|
|
||||||
node.handlemessage(babase.PowerupMessage(poweruptype='punch'))
|
|
||||||
node.handlemessage(babase.PowerupMessage(poweruptype='shield'))
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
player = int(arguments[0])
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
node = activity.players[player].actor.node
|
|
||||||
|
|
||||||
if node.head_mesh != None:
|
|
||||||
node.head_mesh = None
|
|
||||||
node.handlemessage(babase.PowerupMessage(poweruptype='punch'))
|
|
||||||
node.handlemessage(babase.PowerupMessage(poweruptype='shield'))
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def celeb(arguments):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
return
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
handlemsg_all(bs.CelebrateMessage())
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
player = int(arguments[0])
|
|
||||||
handlemsg(player, bs.CelebrateMessage())
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def spaz(arguments):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
return
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
""" Some useful handlers to reduce lot of code """
|
|
||||||
import _babase
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
|
|
||||||
def send(msg, clientid):
|
|
||||||
"""Shortcut To Send Private Msg To Client"""
|
|
||||||
for m in msg.split("\n"):
|
|
||||||
bs.chatmessage(str(m), clients=[clientid])
|
|
||||||
bs.broadcastmessage(str(msg), transient=True, clients=[clientid])
|
|
||||||
|
|
||||||
|
|
||||||
def clientid_to_myself(clientid):
|
|
||||||
"""Return Player Index Of Self Player"""
|
|
||||||
|
|
||||||
for i, player in enumerate(_babase.get_foreground_host_activity().players):
|
|
||||||
if player.sessionplayer.inputdevice.client_id == clientid:
|
|
||||||
return i
|
|
||||||
|
|
||||||
|
|
||||||
def handlemsg(client, msg):
|
|
||||||
"""Handles Spaz Msg For Single Player"""
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
activity.players[client].actor.node.handlemessage(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def handlemsg_all(msg):
|
|
||||||
"""Handle Spaz message for all players in activity"""
|
|
||||||
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
for i in activity.players:
|
|
||||||
i.actor.node.handlemessage(msg)
|
|
||||||
|
|
@ -1,621 +0,0 @@
|
||||||
import random
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
import _bascenev1
|
|
||||||
import setting
|
|
||||||
from playersData import pdata
|
|
||||||
# from tools.whitelist import add_to_white_list, add_commit_to_logs
|
|
||||||
from serverData import serverdata
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from tools import logger
|
|
||||||
from tools import playlist
|
|
||||||
from .handlers import send
|
|
||||||
|
|
||||||
Commands = ['recents', 'info', 'createteam', 'showid', 'hideid', 'lm', 'gp',
|
|
||||||
'party', 'quit', 'kickvote', 'maxplayers', 'playlist', 'ban',
|
|
||||||
'kick', 'remove', 'end', 'quit', 'mute', 'unmute', 'slowmo', 'nv',
|
|
||||||
'dv', 'pause',
|
|
||||||
'cameramode', 'createrole', 'addrole', 'removerole', 'addcommand',
|
|
||||||
'addcmd', 'removecommand', 'getroles', 'removecmd', 'changetag',
|
|
||||||
'customtag', 'customeffect', 'removeeffect', 'removetag' 'add',
|
|
||||||
'spectators', 'lobbytime']
|
|
||||||
CommandAliases = ['max', 'rm', 'next', 'restart', 'mutechat', 'unmutechat',
|
|
||||||
'sm',
|
|
||||||
'slow', 'night', 'day', 'pausegame', 'camera_mode',
|
|
||||||
'rotate_camera', 'effect']
|
|
||||||
|
|
||||||
|
|
||||||
def ExcelCommand(command, arguments, clientid, accountid):
|
|
||||||
"""
|
|
||||||
Checks The Command And Run Function
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
command : str
|
|
||||||
arguments : str
|
|
||||||
clientid : int
|
|
||||||
accountid : int
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
None
|
|
||||||
"""
|
|
||||||
if command in ['recents']:
|
|
||||||
get_recents(clientid)
|
|
||||||
if command in ['info']:
|
|
||||||
get_player_info(arguments, clientid)
|
|
||||||
if command in ['maxplayers', 'max']:
|
|
||||||
changepartysize(arguments)
|
|
||||||
if command in ['createteam']:
|
|
||||||
create_team(arguments)
|
|
||||||
elif command == 'playlist':
|
|
||||||
changeplaylist(arguments)
|
|
||||||
elif command == 'kick':
|
|
||||||
kick(arguments)
|
|
||||||
elif command == 'ban':
|
|
||||||
ban(arguments)
|
|
||||||
elif command in ['end', 'next']:
|
|
||||||
end(arguments)
|
|
||||||
elif command == 'kickvote':
|
|
||||||
kikvote(arguments, clientid)
|
|
||||||
elif command == 'hideid':
|
|
||||||
hide_player_spec()
|
|
||||||
elif command == "showid":
|
|
||||||
show_player_spec()
|
|
||||||
elif command == 'lm':
|
|
||||||
last_msgs(clientid)
|
|
||||||
|
|
||||||
elif command == 'gp':
|
|
||||||
get_profiles(arguments, clientid)
|
|
||||||
|
|
||||||
elif command == 'party':
|
|
||||||
party_toggle(arguments)
|
|
||||||
|
|
||||||
elif command in ['quit', 'restart']:
|
|
||||||
quit(arguments)
|
|
||||||
|
|
||||||
elif command in ['mute', 'mutechat']:
|
|
||||||
mute(arguments)
|
|
||||||
|
|
||||||
elif command in ['unmute', 'unmutechat']:
|
|
||||||
un_mute(arguments)
|
|
||||||
|
|
||||||
elif command in ['remove', 'rm']:
|
|
||||||
remove(arguments)
|
|
||||||
|
|
||||||
elif command in ['sm', 'slow', 'slowmo']:
|
|
||||||
slow_motion()
|
|
||||||
|
|
||||||
elif command in ['nv', 'night']:
|
|
||||||
nv(arguments)
|
|
||||||
|
|
||||||
elif command in ['dv', 'day']:
|
|
||||||
dv(arguments)
|
|
||||||
|
|
||||||
elif command in ['pause', 'pausegame']:
|
|
||||||
pause()
|
|
||||||
|
|
||||||
elif command in ['cameraMode', 'camera_mode', 'rotate_camera']:
|
|
||||||
rotate_camera()
|
|
||||||
|
|
||||||
elif command == 'createrole':
|
|
||||||
create_role(arguments)
|
|
||||||
|
|
||||||
elif command == 'addrole':
|
|
||||||
add_role_to_player(arguments)
|
|
||||||
|
|
||||||
elif command == 'removerole':
|
|
||||||
remove_role_from_player(arguments)
|
|
||||||
|
|
||||||
elif command == 'getroles':
|
|
||||||
get_roles_of_player(arguments, clientid)
|
|
||||||
|
|
||||||
elif command in ['addcommand', 'addcmd']:
|
|
||||||
add_command_to_role(arguments)
|
|
||||||
|
|
||||||
elif command in ['removecommand', 'removecmd']:
|
|
||||||
remove_command_to_role(arguments)
|
|
||||||
|
|
||||||
elif command == 'changetag':
|
|
||||||
change_role_tag(arguments)
|
|
||||||
|
|
||||||
elif command == 'customtag':
|
|
||||||
set_custom_tag(arguments)
|
|
||||||
|
|
||||||
elif command in ['customeffect', 'effect']:
|
|
||||||
set_custom_effect(arguments)
|
|
||||||
|
|
||||||
elif command in ['removetag']:
|
|
||||||
remove_custom_tag(arguments)
|
|
||||||
|
|
||||||
elif command in ['removeeffect']:
|
|
||||||
remove_custom_effect(arguments)
|
|
||||||
|
|
||||||
# elif command in ['add', 'whitelist']:
|
|
||||||
# whitelst_it(accountid, arguments)
|
|
||||||
|
|
||||||
elif command == 'spectators':
|
|
||||||
spectators(arguments)
|
|
||||||
|
|
||||||
elif command == 'lobbytime':
|
|
||||||
change_lobby_check_time(arguments)
|
|
||||||
|
|
||||||
|
|
||||||
def create_team(arguments):
|
|
||||||
if len(arguments) == 0:
|
|
||||||
bs.chatmessage("enter team name")
|
|
||||||
else:
|
|
||||||
from bascenev1._team import SessionTeam
|
|
||||||
bs.get_foreground_host_session().sessionteams.append(SessionTeam(
|
|
||||||
team_id=len(bs.get_foreground_host_session().sessionteams) + 1,
|
|
||||||
name=str(arguments[0]),
|
|
||||||
color=(random.uniform(0, 1.2), random.uniform(
|
|
||||||
0, 1.2), random.uniform(0, 1.2))))
|
|
||||||
from bascenev1._lobby import Lobby
|
|
||||||
bs.get_foreground_host_session().lobby = Lobby()
|
|
||||||
|
|
||||||
|
|
||||||
def hide_player_spec():
|
|
||||||
_babase.hide_player_device_id(True)
|
|
||||||
|
|
||||||
|
|
||||||
def show_player_spec():
|
|
||||||
_babase.hide_player_device_id(False)
|
|
||||||
|
|
||||||
|
|
||||||
def get_player_info(arguments, client_id):
|
|
||||||
if len(arguments) == 0:
|
|
||||||
send("invalid client id", client_id)
|
|
||||||
for account in serverdata.recents:
|
|
||||||
if account['client_id'] == int(arguments[0]):
|
|
||||||
send(pdata.get_detailed_info(account["pbid"]), client_id)
|
|
||||||
|
|
||||||
|
|
||||||
def get_recents(client_id):
|
|
||||||
for players in serverdata.recents:
|
|
||||||
send(
|
|
||||||
f"{players['client_id']} {players['deviceId']} {players['pbid']}",
|
|
||||||
client_id)
|
|
||||||
|
|
||||||
|
|
||||||
def changepartysize(arguments):
|
|
||||||
if len(arguments) == 0:
|
|
||||||
bs.chatmessage("enter number")
|
|
||||||
else:
|
|
||||||
bs.set_public_party_max_size(int(arguments[0]))
|
|
||||||
|
|
||||||
|
|
||||||
def changeplaylist(arguments):
|
|
||||||
if len(arguments) == 0:
|
|
||||||
bs.chatmessage("enter list code or name")
|
|
||||||
else:
|
|
||||||
if arguments[0] == 'coop':
|
|
||||||
serverdata.coopmode = True
|
|
||||||
else:
|
|
||||||
serverdata.coopmode = False
|
|
||||||
playlist.setPlaylist(arguments[0])
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def kick(arguments):
|
|
||||||
cl_id = int(arguments[0])
|
|
||||||
for ros in bs.get_game_roster():
|
|
||||||
if ros["client_id"] == cl_id:
|
|
||||||
logger.log("kicked " + ros["display_string"])
|
|
||||||
bs.disconnect_client(int(arguments[0]))
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def kikvote(arguments, clientid):
|
|
||||||
if arguments == [] or arguments == [''] or len(arguments) < 2:
|
|
||||||
return
|
|
||||||
|
|
||||||
elif arguments[0] == 'enable':
|
|
||||||
if arguments[1] == 'all':
|
|
||||||
_babase.set_enable_default_kick_voting(True)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
cl_id = int(arguments[1])
|
|
||||||
for ros in bs.get_game_roster():
|
|
||||||
if ros["client_id"] == cl_id:
|
|
||||||
pdata.enable_kick_vote(ros["account_id"])
|
|
||||||
logger.log(
|
|
||||||
f'kick vote enabled for {ros["account_id"]} {ros["display_string"]}')
|
|
||||||
send(
|
|
||||||
"Upon server restart, Kick-vote will be enabled for this person",
|
|
||||||
clientid)
|
|
||||||
return
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
elif arguments[0] == 'disable':
|
|
||||||
if arguments[1] == 'all':
|
|
||||||
_babase.set_enable_default_kick_voting(False)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
cl_id = int(arguments[1])
|
|
||||||
for ros in bs.get_game_roster():
|
|
||||||
if ros["client_id"] == cl_id:
|
|
||||||
_bascenev1.disable_kickvote(ros["account_id"])
|
|
||||||
send("Kick-vote disabled for this person", clientid)
|
|
||||||
logger.log(
|
|
||||||
f'kick vote disabled for {ros["account_id"]} {ros["display_string"]}')
|
|
||||||
pdata.disable_kick_vote(
|
|
||||||
ros["account_id"], 2, "by chat command")
|
|
||||||
return
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def last_msgs(clientid):
|
|
||||||
for i in bs.get_chat_messages():
|
|
||||||
send(i, clientid)
|
|
||||||
|
|
||||||
|
|
||||||
def get_profiles(arguments, clientid):
|
|
||||||
try:
|
|
||||||
playerID = int(arguments[0])
|
|
||||||
num = 1
|
|
||||||
for i in bs.get_foreground_host_session().sessionplayers[
|
|
||||||
playerID].inputdevice.get_player_profiles():
|
|
||||||
try:
|
|
||||||
send(f"{num})- {i}", clientid)
|
|
||||||
num += 1
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def party_toggle(arguments):
|
|
||||||
if arguments == ['public']:
|
|
||||||
bs.set_public_party_enabled(True)
|
|
||||||
bs.chatmessage("party is public now")
|
|
||||||
elif arguments == ['private']:
|
|
||||||
bs.set_public_party_enabled(False)
|
|
||||||
bs.chatmessage("party is private now")
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def end(arguments):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
try:
|
|
||||||
with _babase.Context(_babase.get_foreground_host_activity()):
|
|
||||||
_babase.get_foreground_host_activity().end_game()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def ban(arguments):
|
|
||||||
try:
|
|
||||||
cl_id = int(arguments[0])
|
|
||||||
duration = int(arguments[1]) if len(arguments) >= 2 else 0.5
|
|
||||||
for ros in bs.get_game_roster():
|
|
||||||
if ros["client_id"] == cl_id:
|
|
||||||
pdata.ban_player(ros['account_id'], duration,
|
|
||||||
"by chat command")
|
|
||||||
logger.log(f'banned {ros["display_string"]} by chat command')
|
|
||||||
|
|
||||||
for account in serverdata.recents: # backup case if player left the server
|
|
||||||
if account['client_id'] == int(arguments[0]):
|
|
||||||
pdata.ban_player(
|
|
||||||
account["pbid"], duration, "by chat command")
|
|
||||||
logger.log(
|
|
||||||
f'banned {ros["display_string"]} by chat command, recents')
|
|
||||||
kick(arguments)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def quit(arguments):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
babase.quit()
|
|
||||||
|
|
||||||
|
|
||||||
def mute(arguments):
|
|
||||||
if len(arguments) == 0:
|
|
||||||
serverdata.muted = True
|
|
||||||
try:
|
|
||||||
cl_id = int(arguments[0])
|
|
||||||
duration = int(arguments[1]) if len(arguments) >= 2 else 0.5
|
|
||||||
for ros in bs.get_game_roster():
|
|
||||||
if ros["client_id"] == cl_id:
|
|
||||||
ac_id = ros['account_id']
|
|
||||||
logger.log(f'muted {ros["display_string"]}')
|
|
||||||
pdata.mute(ac_id, duration, "muted by chat command")
|
|
||||||
return
|
|
||||||
for account in serverdata.recents: # backup case if player left the server
|
|
||||||
if account['client_id'] == int(arguments[0]):
|
|
||||||
pdata.mute(account["pbid"], duration,
|
|
||||||
"muted by chat command, from recents")
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def un_mute(arguments):
|
|
||||||
if len(arguments) == 0:
|
|
||||||
serverdata.muted = False
|
|
||||||
try:
|
|
||||||
cl_id = int(arguments[0])
|
|
||||||
for ros in bs.get_game_roster():
|
|
||||||
if ros["client_id"] == cl_id:
|
|
||||||
pdata.unmute(ros['account_id'])
|
|
||||||
logger.log(f'unmuted {ros["display_string"]} by chat command')
|
|
||||||
return
|
|
||||||
for account in serverdata.recents: # backup case if player left the server
|
|
||||||
if account['client_id'] == int(arguments[0]):
|
|
||||||
pdata.unmute(account["pbid"])
|
|
||||||
logger.log(
|
|
||||||
f'unmuted {ros["display_string"]} by chat command, recents')
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def remove(arguments):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
return
|
|
||||||
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
for i in session.sessionplayers:
|
|
||||||
i.remove_from_game()
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
for i in session.sessionplayers:
|
|
||||||
if i.inputdevice.client_id == int(arguments[0]):
|
|
||||||
i.remove_from_game()
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def slow_motion():
|
|
||||||
activity = _babase.get_foreground_host_activity()
|
|
||||||
|
|
||||||
if activity.globalsnode.slow_motion != True:
|
|
||||||
activity.globalsnode.slow_motion = True
|
|
||||||
|
|
||||||
else:
|
|
||||||
activity.globalsnode.slow_motion = False
|
|
||||||
|
|
||||||
|
|
||||||
def nv(arguments):
|
|
||||||
activity = _babase.get_foreground_host_activity()
|
|
||||||
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
|
|
||||||
if activity.globalsnode.tint != (0.5, 0.7, 1.0):
|
|
||||||
activity.globalsnode.tint = (0.5, 0.7, 1.0)
|
|
||||||
else:
|
|
||||||
# will fix this soon
|
|
||||||
pass
|
|
||||||
|
|
||||||
elif arguments[0] == 'off':
|
|
||||||
if activity.globalsnode.tint != (0.5, 0.7, 1.0):
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def dv(arguments):
|
|
||||||
activity = _babase.get_foreground_host_activity()
|
|
||||||
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
|
|
||||||
if activity.globalsnode.tint != (1, 1, 1):
|
|
||||||
activity.globalsnode.tint = (1, 1, 1)
|
|
||||||
else:
|
|
||||||
# will fix this soon
|
|
||||||
pass
|
|
||||||
|
|
||||||
elif arguments[0] == 'off':
|
|
||||||
if activity.globalsnode.tint != (1, 1, 1):
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def pause():
|
|
||||||
activity = _babase.get_foreground_host_activity()
|
|
||||||
|
|
||||||
if activity.globalsnode.paused != True:
|
|
||||||
activity.globalsnode.paused = True
|
|
||||||
|
|
||||||
else:
|
|
||||||
activity.globalsnode.paused = False
|
|
||||||
|
|
||||||
|
|
||||||
def rotate_camera():
|
|
||||||
activity = _babase.get_foreground_host_activity()
|
|
||||||
|
|
||||||
if activity.globalsnode.camera_mode != 'rotate':
|
|
||||||
activity.globalsnode.camera_mode = 'rotate'
|
|
||||||
|
|
||||||
else:
|
|
||||||
activity.globalsnode.camera_mode == 'normal'
|
|
||||||
|
|
||||||
|
|
||||||
def create_role(arguments):
|
|
||||||
try:
|
|
||||||
pdata.create_role(arguments[0])
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def add_role_to_player(arguments):
|
|
||||||
try:
|
|
||||||
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
for i in session.sessionplayers:
|
|
||||||
if i.inputdevice.client_id == int(arguments[1]):
|
|
||||||
roles = pdata.add_player_role(
|
|
||||||
arguments[0], i.get_v1_account_id())
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def remove_role_from_player(arguments):
|
|
||||||
try:
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
for i in session.sessionplayers:
|
|
||||||
if i.inputdevice.client_id == int(arguments[1]):
|
|
||||||
roles = pdata.remove_player_role(
|
|
||||||
arguments[0], i.get_v1_account_id())
|
|
||||||
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def get_roles_of_player(arguments, clientid):
|
|
||||||
try:
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
roles = []
|
|
||||||
reply = ""
|
|
||||||
for i in session.sessionplayers:
|
|
||||||
if i.inputdevice.client_id == int(arguments[0]):
|
|
||||||
roles = pdata.get_player_roles(i.get_v1_account_id())
|
|
||||||
print(roles)
|
|
||||||
for role in roles:
|
|
||||||
reply = reply + role + ","
|
|
||||||
send(reply, clientid)
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def change_role_tag(arguments):
|
|
||||||
try:
|
|
||||||
pdata.change_role_tag(arguments[0], arguments[1])
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def set_custom_tag(arguments):
|
|
||||||
try:
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
for i in session.sessionplayers:
|
|
||||||
if i.inputdevice.client_id == int(arguments[1]):
|
|
||||||
roles = pdata.set_tag(arguments[0], i.get_v1_account_id())
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def remove_custom_tag(arguments):
|
|
||||||
try:
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
for i in session.sessionplayers:
|
|
||||||
if i.inputdevice.client_id == int(arguments[0]):
|
|
||||||
pdata.remove_tag(i.get_v1_account_id())
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def remove_custom_effect(arguments):
|
|
||||||
try:
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
for i in session.sessionplayers:
|
|
||||||
if i.inputdevice.client_id == int(arguments[0]):
|
|
||||||
pdata.remove_effect(i.get_v1_account_id())
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def set_custom_effect(arguments):
|
|
||||||
try:
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
for i in session.sessionplayers:
|
|
||||||
if i.inputdevice.client_id == int(arguments[1]):
|
|
||||||
pdata.set_effect(arguments[0], i.get_v1_account_id())
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
all_commands = ["changetag", "createrole", "addrole", "removerole",
|
|
||||||
"addcommand", "addcmd", "removecommand", "removecmd", "kick",
|
|
||||||
"remove", "rm", "end", "next", "quit", "restart", "mute",
|
|
||||||
"mutechat", "unmute", "unmutechat", "sm", "slow", "slowmo",
|
|
||||||
"nv", "night", "dv", "day", "pause", "pausegame", "cameraMode",
|
|
||||||
"camera_mode", "rotate_camera", "kill", "die", "heal", "heath",
|
|
||||||
"curse", "cur", "sleep", "sp", "superpunch", "gloves", "punch",
|
|
||||||
"shield", "protect", "freeze", "ice", "unfreeze", "thaw", "gm",
|
|
||||||
"godmode", "fly", "inv", "invisible", "hl", "headless",
|
|
||||||
"creepy", "creep", "celebrate", "celeb", "spaz"]
|
|
||||||
|
|
||||||
|
|
||||||
def add_command_to_role(arguments):
|
|
||||||
try:
|
|
||||||
if len(arguments) == 2:
|
|
||||||
pdata.add_command_role(arguments[0], arguments[1])
|
|
||||||
else:
|
|
||||||
bs.chatmessage("invalid command arguments")
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def remove_command_to_role(arguments):
|
|
||||||
try:
|
|
||||||
if len(arguments) == 2:
|
|
||||||
pdata.remove_command_role(arguments[0], arguments[1])
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
# def whitelst_it(accountid : str, arguments):
|
|
||||||
# settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
# if arguments[0] == 'on':
|
|
||||||
# if settings["white_list"]["whitelist_on"]:
|
|
||||||
# bs.chatmessage("Already on")
|
|
||||||
# else:
|
|
||||||
# settings["white_list"]["whitelist_on"] = True
|
|
||||||
# setting.commit(settings)
|
|
||||||
# bs.chatmessage("whitelist on")
|
|
||||||
# from tools import whitelist
|
|
||||||
# whitelist.Whitelist()
|
|
||||||
# return
|
|
||||||
|
|
||||||
# elif arguments[0] == 'off':
|
|
||||||
# settings["white_list"]["whitelist_on"] = False
|
|
||||||
# setting.commit(settings)
|
|
||||||
# bs.chatmessage("whitelist off")
|
|
||||||
# return
|
|
||||||
|
|
||||||
# else:
|
|
||||||
# rost = bs.get_game_roster()
|
|
||||||
|
|
||||||
# for i in rost:
|
|
||||||
# if i['client_id'] == int(arguments[0]):
|
|
||||||
# add_to_white_list(i['account_id'], i['display_string'])
|
|
||||||
# bs.chatmessage(str(i['display_string'])+" whitelisted")
|
|
||||||
# add_commit_to_logs(accountid+" added "+i['account_id'])
|
|
||||||
|
|
||||||
|
|
||||||
def spectators(arguments):
|
|
||||||
if arguments[0] in ['on', 'off']:
|
|
||||||
settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
if arguments[0] == 'on':
|
|
||||||
settings["white_list"]["spectators"] = True
|
|
||||||
setting.commit(settings)
|
|
||||||
bs.chatmessage("spectators on")
|
|
||||||
|
|
||||||
elif arguments[0] == 'off':
|
|
||||||
settings["white_list"]["spectators"] = False
|
|
||||||
setting.commit(settings)
|
|
||||||
bs.chatmessage("spectators off")
|
|
||||||
|
|
||||||
|
|
||||||
def change_lobby_check_time(arguments):
|
|
||||||
try:
|
|
||||||
argument = int(arguments[0])
|
|
||||||
except:
|
|
||||||
bs.chatmessage("must type number to change lobby check time")
|
|
||||||
return
|
|
||||||
settings = setting.get_settings_data()
|
|
||||||
settings["white_list"]["lobbychecktime"] = argument
|
|
||||||
setting.commit(settings)
|
|
||||||
bs.chatmessage(f"lobby check time is {argument} now")
|
|
||||||
255
dist/ba_root/mods/chatHandle/ChatCommands/floater.py
vendored
255
dist/ba_root/mods/chatHandle/ChatCommands/floater.py
vendored
|
|
@ -1,255 +0,0 @@
|
||||||
# ba_meta require api 6
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
import math
|
|
||||||
import random
|
|
||||||
from babase._generated.enums import InputType
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.bomb import Bomb
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Floater(bs.Actor):
|
|
||||||
def __init__(self, bounds):
|
|
||||||
super().__init__()
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self.controlled = False
|
|
||||||
self.source_player = None
|
|
||||||
self.floaterMaterial = bs.Material()
|
|
||||||
self.floaterMaterial.add_actions(
|
|
||||||
conditions=('they_have_material',
|
|
||||||
shared.player_material),
|
|
||||||
actions=(('modify_node_collision', 'collide', True),
|
|
||||||
('modify_part_collision', 'physical', True)))
|
|
||||||
self.floaterMaterial.add_actions(
|
|
||||||
conditions=(('they_have_material',
|
|
||||||
shared.object_material), 'or',
|
|
||||||
('they_have_material',
|
|
||||||
shared.footing_material), 'or',
|
|
||||||
('they_have_material',
|
|
||||||
self.floaterMaterial)),
|
|
||||||
actions=('modify_part_collision', 'physical', False))
|
|
||||||
|
|
||||||
self.pos = bounds
|
|
||||||
self.px = "random.uniform(self.pos[0],self.pos[3])"
|
|
||||||
self.py = "random.uniform(self.pos[1],self.pos[4])"
|
|
||||||
self.pz = "random.uniform(self.pos[2],self.pos[5])"
|
|
||||||
|
|
||||||
self.node = bs.newnode(
|
|
||||||
'prop',
|
|
||||||
delegate=self,
|
|
||||||
owner=None,
|
|
||||||
attrs={
|
|
||||||
'position': (eval(self.px), eval(self.py), eval(self.pz)),
|
|
||||||
'mesh':
|
|
||||||
bs.getmesh('landMine'),
|
|
||||||
'light_mesh':
|
|
||||||
bs.getmesh('landMine'),
|
|
||||||
'body':
|
|
||||||
'landMine',
|
|
||||||
'body_scale':
|
|
||||||
3,
|
|
||||||
'mesh_scale':
|
|
||||||
3.1,
|
|
||||||
'shadow_size':
|
|
||||||
0.25,
|
|
||||||
'density':
|
|
||||||
999999,
|
|
||||||
'gravity_scale':
|
|
||||||
0.0,
|
|
||||||
'color_texture':
|
|
||||||
bs.gettexture('achievementFlawlessVictory'),
|
|
||||||
'reflection':
|
|
||||||
'soft',
|
|
||||||
'reflection_scale': [0.25],
|
|
||||||
'materials':
|
|
||||||
[shared.footing_material, self.floaterMaterial]
|
|
||||||
})
|
|
||||||
self.node2 = bs.newnode(
|
|
||||||
'prop',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'position': (0, 0, 0),
|
|
||||||
'body':
|
|
||||||
'sphere',
|
|
||||||
'mesh':
|
|
||||||
None,
|
|
||||||
'color_texture':
|
|
||||||
None,
|
|
||||||
'body_scale':
|
|
||||||
1.0,
|
|
||||||
'reflection':
|
|
||||||
'powerup',
|
|
||||||
'density':
|
|
||||||
999999,
|
|
||||||
'reflection_scale': [1.0],
|
|
||||||
'mesh_scale':
|
|
||||||
1.0,
|
|
||||||
'gravity_scale':
|
|
||||||
0,
|
|
||||||
'shadow_size':
|
|
||||||
0.1,
|
|
||||||
'is_area_of_interest':
|
|
||||||
True,
|
|
||||||
'materials':
|
|
||||||
[shared.object_material, self.floaterMaterial]
|
|
||||||
})
|
|
||||||
self.node.connectattr('position', self.node2, 'position')
|
|
||||||
|
|
||||||
def checkCanControl(self):
|
|
||||||
if not self.node.exists():
|
|
||||||
return False
|
|
||||||
if not self.source_player.is_alive():
|
|
||||||
self.dis()
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def con(self):
|
|
||||||
self.controlled = True
|
|
||||||
self.checkPlayerDie()
|
|
||||||
|
|
||||||
def up(self):
|
|
||||||
if not self.checkCanControl():
|
|
||||||
return
|
|
||||||
v = self.node.velocity
|
|
||||||
self.node.velocity = (v[0], 5, v[2])
|
|
||||||
|
|
||||||
def upR(self):
|
|
||||||
if not self.checkCanControl():
|
|
||||||
return
|
|
||||||
v = self.node.velocity
|
|
||||||
self.node.velocity = (v[0], 0, v[2])
|
|
||||||
|
|
||||||
def down(self):
|
|
||||||
if not self.checkCanControl():
|
|
||||||
return
|
|
||||||
v = self.node.velocity
|
|
||||||
self.node.velocity = (v[0], -5, v[2])
|
|
||||||
|
|
||||||
def downR(self):
|
|
||||||
if not self.checkCanControl():
|
|
||||||
return
|
|
||||||
v = self.node.velocity
|
|
||||||
self.node.velocity = (v[0], 0, v[2])
|
|
||||||
|
|
||||||
def leftright(self, value):
|
|
||||||
if not self.checkCanControl():
|
|
||||||
return
|
|
||||||
v = self.node.velocity
|
|
||||||
self.node.velocity = (5 * value, v[1], v[2])
|
|
||||||
|
|
||||||
def updown(self, value):
|
|
||||||
if not self.checkCanControl():
|
|
||||||
return
|
|
||||||
v = self.node.velocity
|
|
||||||
self.node.velocity = (v[0], v[1], -5 * value)
|
|
||||||
|
|
||||||
def dis(self):
|
|
||||||
if self.node.exists():
|
|
||||||
self.controlled = False
|
|
||||||
self.node.velocity = (0, 0, 0)
|
|
||||||
self.move()
|
|
||||||
|
|
||||||
def checkPlayerDie(self):
|
|
||||||
if not self.controlled:
|
|
||||||
return
|
|
||||||
if self.source_player is None:
|
|
||||||
return
|
|
||||||
if self.source_player.is_alive():
|
|
||||||
bs.timer(1, self.checkPlayerDie)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
self.dis()
|
|
||||||
|
|
||||||
def distance(self, x1, y1, z1, x2, y2, z2):
|
|
||||||
d = math.sqrt(
|
|
||||||
math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2) + math.pow(z2 - z1, 2))
|
|
||||||
return d
|
|
||||||
|
|
||||||
def drop(self):
|
|
||||||
try:
|
|
||||||
np = self.node.position
|
|
||||||
except:
|
|
||||||
np = (0, 0, 0)
|
|
||||||
self.b = Bomb(bomb_type=random.choice(
|
|
||||||
['normal', 'ice', 'sticky', 'impact', 'land_mine', 'tnt']),
|
|
||||||
source_player=self.source_player,
|
|
||||||
position=(np[0], np[1] - 1, np[2]),
|
|
||||||
velocity=(0, -1, 0)).autoretain()
|
|
||||||
if self.b.bomb_type in ['impact', 'land_mine']:
|
|
||||||
self.b.arm()
|
|
||||||
|
|
||||||
def move(self):
|
|
||||||
px = eval(self.px)
|
|
||||||
py = eval(self.py)
|
|
||||||
pz = eval(self.pz)
|
|
||||||
if self.node.exists() and not self.controlled:
|
|
||||||
pn = self.node.position
|
|
||||||
dist = self.distance(pn[0], pn[1], pn[2], px, py, pz)
|
|
||||||
self.node.velocity = (
|
|
||||||
(px - pn[0]) / dist, (py - pn[1]) / dist, (pz - pn[2]) / dist)
|
|
||||||
t = dist - 1 if dist - 1 >= 0 else 0.1
|
|
||||||
bs.timer(t, bs.WeakCall(self.move), suppress_format_warning=True)
|
|
||||||
|
|
||||||
def handlemessage(self, msg):
|
|
||||||
if isinstance(msg, bs.DieMessage):
|
|
||||||
self.node.delete()
|
|
||||||
self.node2.delete()
|
|
||||||
self.controlled = False
|
|
||||||
elif isinstance(msg, bs.OutOfBoundsMessage):
|
|
||||||
self.handlemessage(bs.DieMessage())
|
|
||||||
else:
|
|
||||||
super().handlemessage(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def assignFloInputs(clientID: int):
|
|
||||||
with babase.Context(_babase.get_foreground_host_activity()):
|
|
||||||
activity = bs.getactivity()
|
|
||||||
if not hasattr(activity, 'flo') or not activity.flo.node.exists():
|
|
||||||
try:
|
|
||||||
activity.flo = Floater(
|
|
||||||
activity.map.get_def_bound_box('map_bounds'))
|
|
||||||
except:
|
|
||||||
return # Perhaps using in main-menu/score-screen
|
|
||||||
floater = activity.flo
|
|
||||||
if floater.controlled:
|
|
||||||
bs.broadcastmessage('Floater is already being controlled',
|
|
||||||
color=(1, 0, 0), transient=True,
|
|
||||||
clients=[clientID])
|
|
||||||
return
|
|
||||||
bs.broadcastmessage(
|
|
||||||
'You Gained Control Over The Floater!\n Press Bomb to Throw Bombs and Punch to leave!',
|
|
||||||
clients=[clientID], transient=True, color=(0, 1, 1))
|
|
||||||
|
|
||||||
for i in _babase.get_foreground_host_activity().players:
|
|
||||||
if i.sessionplayer.inputdevice.client_id == clientID:
|
|
||||||
def dis(i, floater):
|
|
||||||
i.actor.node.invincible = False
|
|
||||||
i.resetinput()
|
|
||||||
i.actor.connect_controls_to_player()
|
|
||||||
floater.dis()
|
|
||||||
|
|
||||||
ps = i.actor.node.position
|
|
||||||
i.actor.node.invincible = True
|
|
||||||
floater.node.position = (ps[0], ps[1] + 1.0, ps[2])
|
|
||||||
i.actor.node.hold_node = bs.Node(None)
|
|
||||||
i.actor.node.hold_node = floater.node2
|
|
||||||
i.actor.connect_controls_to_player()
|
|
||||||
i.actor.disconnect_controls_from_player()
|
|
||||||
i.resetinput()
|
|
||||||
floater.source_player = i
|
|
||||||
floater.con()
|
|
||||||
i.assigninput(InputType.PICK_UP_PRESS, floater.up)
|
|
||||||
i.assigninput(InputType.PICK_UP_RELEASE, floater.upR)
|
|
||||||
i.assigninput(InputType.JUMP_PRESS, floater.down)
|
|
||||||
i.assigninput(InputType.BOMB_PRESS, floater.drop)
|
|
||||||
i.assigninput(InputType.PUNCH_PRESS,
|
|
||||||
babase.Call(dis, i, floater))
|
|
||||||
i.assigninput(InputType.UP_DOWN, floater.updown)
|
|
||||||
i.assigninput(InputType.LEFT_RIGHT, floater.leftright)
|
|
||||||
7
dist/ba_root/mods/chatHandle/__init__.py
vendored
7
dist/ba_root/mods/chatHandle/__init__.py
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
"""Common bits of functionality shared between all efro projects.
|
|
||||||
|
|
||||||
Things in here should be hardened, highly type-safe, and well-covered by unit
|
|
||||||
tests since they are widely used in live client and server code.
|
|
||||||
|
|
||||||
license : MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
import _thread
|
|
||||||
import time
|
|
||||||
|
|
||||||
import setting
|
|
||||||
from features import profanity
|
|
||||||
from playersData import pdata
|
|
||||||
from serverData import serverdata
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from tools import logger
|
|
||||||
from tools import servercheck
|
|
||||||
|
|
||||||
settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
def check_permissions(accountid):
|
|
||||||
roles = pdata.get_roles()
|
|
||||||
for role in roles:
|
|
||||||
if accountid in roles[role]["ids"] and (
|
|
||||||
role == "bypass-warn" or role == "owner"):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def filter(msg, pb_id, client_id):
|
|
||||||
new_msg = profanity.censor(msg)
|
|
||||||
if new_msg != msg:
|
|
||||||
bs.broadcastmessage("Don\'t ABUSE!", color=(1, 0, 0), transient=True,
|
|
||||||
clients=[client_id])
|
|
||||||
if not check_permissions(pb_id):
|
|
||||||
addWarn(pb_id, client_id)
|
|
||||||
else:
|
|
||||||
bs.broadcastmessage("Special role found, Warn BYPASSED!",
|
|
||||||
color=(0, 1, 0), transient=True,
|
|
||||||
clients=[client_id])
|
|
||||||
|
|
||||||
now = time.time()
|
|
||||||
if pb_id not in serverdata.clients:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if "lastMsgTime" in serverdata.clients[pb_id]:
|
|
||||||
count = serverdata.clients[pb_id]["cMsgCount"]
|
|
||||||
smsgcount = serverdata.clients[pb_id]['cSameMsg']
|
|
||||||
if now - serverdata.clients[pb_id]["lastMsgTime"] < 8:
|
|
||||||
count += 1
|
|
||||||
if count == 2: # when 3 msgs
|
|
||||||
bs.broadcastmessage("Sending messages too fast, cool down...",
|
|
||||||
color=(1, 0.40, 0.50), transient=True,
|
|
||||||
clients=[client_id])
|
|
||||||
elif count >= 3: # when 4 msgs
|
|
||||||
bs.broadcastmessage("Don\'t SPAM!", color=(1, 0, 0),
|
|
||||||
transient=True, clients=[client_id])
|
|
||||||
if not check_permissions(pb_id):
|
|
||||||
addWarn(pb_id, client_id)
|
|
||||||
else:
|
|
||||||
bs.broadcastmessage("Special role found, Warn BYPASSED!",
|
|
||||||
color=(0, 1, 0), transient=True,
|
|
||||||
clients=[client_id])
|
|
||||||
count = 0
|
|
||||||
elif now - serverdata.clients[pb_id]["lastMsgTime"] < 20:
|
|
||||||
# < 30
|
|
||||||
if serverdata.clients[pb_id]["lastMsg"] == msg:
|
|
||||||
if len(msg) > 5:
|
|
||||||
smsgcount += 1
|
|
||||||
if smsgcount >= 3:
|
|
||||||
logger.log(pb_id + " | kicked for chat spam")
|
|
||||||
bs.disconnect_client(client_id)
|
|
||||||
smsgcount = 0
|
|
||||||
bs.broadcastmessage("Don\'t SPAM!", color=(1, 0, 0),
|
|
||||||
transient=True, clients=[client_id])
|
|
||||||
if not check_permissions(pb_id):
|
|
||||||
addWarn(pb_id, client_id)
|
|
||||||
else:
|
|
||||||
bs.broadcastmessage("Special role found, Warn BYPASSED!",
|
|
||||||
color=(0, 1, 0), transient=True,
|
|
||||||
clients=[client_id])
|
|
||||||
else:
|
|
||||||
smsgcount = 0
|
|
||||||
else:
|
|
||||||
count = 0
|
|
||||||
smsgcount = 0
|
|
||||||
|
|
||||||
serverdata.clients[pb_id]['cMsgCount'] = count
|
|
||||||
serverdata.clients[pb_id]['lastMsgTime'] = now
|
|
||||||
serverdata.clients[pb_id]['lastMsg'] = msg
|
|
||||||
serverdata.clients[pb_id]['cSameMsg'] = smsgcount
|
|
||||||
else:
|
|
||||||
serverdata.clients[pb_id]['cMsgCount'] = 0
|
|
||||||
serverdata.clients[pb_id]['lastMsgTime'] = now
|
|
||||||
serverdata.clients[pb_id]['lastMsg'] = msg
|
|
||||||
serverdata.clients[pb_id]['cSameMsg'] = 0
|
|
||||||
return new_msg
|
|
||||||
|
|
||||||
|
|
||||||
def addWarn(pb_id, client_id):
|
|
||||||
now = time.time()
|
|
||||||
player = serverdata.clients[pb_id]
|
|
||||||
warn = player['warnCount']
|
|
||||||
if now - player['lastWarned'] <= settings["WarnCooldownMinutes"] * 60:
|
|
||||||
warn += 1
|
|
||||||
if warn > settings["maxWarnCount"]:
|
|
||||||
bs.broadcastmessage(settings["afterWarnKickMsg"], color=(1, 0, 0),
|
|
||||||
transient=True, clients=[client_id])
|
|
||||||
logger.log(pb_id + " | kicked for chat spam")
|
|
||||||
bs.disconnect_client(client_id)
|
|
||||||
_thread.start_new_thread(servercheck.reportSpam, (pb_id,))
|
|
||||||
|
|
||||||
else:
|
|
||||||
bs.broadcastmessage(
|
|
||||||
settings["warnMsg"] + f"\n\nWarn Count = {warn}/3!!!",
|
|
||||||
color=(1, 0, 0), transient=True, clients=[client_id])
|
|
||||||
else:
|
|
||||||
warn = 0
|
|
||||||
serverdata.clients[pb_id]["warnCount"] = warn
|
|
||||||
serverdata.clients[pb_id]['lastWarned'] = now
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
"""Common bits of functionality shared between all efro projects.
|
|
||||||
|
|
||||||
Things in here should be hardened, highly type-safe, and well-covered by unit
|
|
||||||
tests since they are widely used in live client and server code.
|
|
||||||
|
|
||||||
license : MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
"""Common bits of functionality shared between all efro projects.
|
|
||||||
|
|
||||||
Things in here should be hardened, highly type-safe, and well-covered by unit
|
|
||||||
tests since they are widely used in live client and server code.
|
|
||||||
|
|
||||||
license : MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
83
dist/ba_root/mods/chatHandle/handlechat.py
vendored
83
dist/ba_root/mods/chatHandle/handlechat.py
vendored
|
|
@ -1,83 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import setting
|
|
||||||
from chatHandle.chatFilter import ChatFilter
|
|
||||||
from chatHandle.chatcommands import executor
|
|
||||||
from features import votingmachine
|
|
||||||
from playersData import pdata
|
|
||||||
from serverData import serverdata
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from tools import logger, servercheck
|
|
||||||
|
|
||||||
settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
def filter_chat_message(msg, client_id):
|
|
||||||
now = datetime.now()
|
|
||||||
if client_id == -1:
|
|
||||||
if msg.startswith("/"):
|
|
||||||
print("message stars with /")
|
|
||||||
executor.execute(msg, client_id)
|
|
||||||
return None
|
|
||||||
logger.log(f"Host msg: | {msg}", "chat")
|
|
||||||
return msg
|
|
||||||
acid = ""
|
|
||||||
displaystring = ""
|
|
||||||
currentname = ""
|
|
||||||
|
|
||||||
for i in bs.get_game_roster():
|
|
||||||
if i['client_id'] == client_id:
|
|
||||||
acid = i['account_id']
|
|
||||||
try:
|
|
||||||
currentname = i['players'][0]['name_full']
|
|
||||||
except:
|
|
||||||
currentname = "<in-lobby>"
|
|
||||||
displaystring = i['display_string']
|
|
||||||
if acid:
|
|
||||||
msg = ChatFilter.filter(msg, acid, client_id)
|
|
||||||
if msg == None:
|
|
||||||
return
|
|
||||||
logger.log(f'{acid} | {displaystring}| {currentname} | {msg}', "chat")
|
|
||||||
if msg.startswith("/"):
|
|
||||||
msg = executor.execute(msg, client_id)
|
|
||||||
if msg == None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if msg in ["end", "dv", "nv", "sm"] and settings["allowVotes"]:
|
|
||||||
votingmachine.vote(acid, client_id, msg)
|
|
||||||
|
|
||||||
if acid in serverdata.clients and serverdata.clients[acid]["verified"]:
|
|
||||||
|
|
||||||
if serverdata.muted:
|
|
||||||
bs.broadcastmessage("Server on mute",
|
|
||||||
transient=True, clients=[client_id])
|
|
||||||
return
|
|
||||||
|
|
||||||
elif acid in pdata.get_blacklist()[
|
|
||||||
"muted-ids"] and now < datetime.strptime(
|
|
||||||
pdata.get_blacklist()["muted-ids"][acid]["till"],
|
|
||||||
"%Y-%m-%d %H:%M:%S"):
|
|
||||||
bs.broadcastmessage(
|
|
||||||
"You are on mute, maybe try after some time", transient=True,
|
|
||||||
clients=[client_id])
|
|
||||||
return None
|
|
||||||
elif servercheck.get_account_age(
|
|
||||||
serverdata.clients[acid]["accountAge"]) < settings[
|
|
||||||
'minAgeToChatInHours']:
|
|
||||||
bs.broadcastmessage("New accounts not allowed to chat here",
|
|
||||||
transient=True, clients=[client_id])
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
if msg.startswith(",") and settings["allowTeamChat"]:
|
|
||||||
return executor.QuickAccess(msg, client_id)
|
|
||||||
if msg.startswith(".") and settings["allowInGameChat"]:
|
|
||||||
return executor.QuickAccess(msg, client_id)
|
|
||||||
return msg
|
|
||||||
|
|
||||||
else:
|
|
||||||
bs.broadcastmessage("Fetching your account info , Wait a minute",
|
|
||||||
transient=True, clients=[client_id])
|
|
||||||
return None
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
import setting
|
|
||||||
from playersData import pdata
|
|
||||||
from serverData import serverdata
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from .handlers import check_permissions
|
|
||||||
from .handlers import clientid_to_accountid
|
|
||||||
|
|
||||||
settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
def command_type(command):
|
|
||||||
"""
|
|
||||||
Checks The Command Type
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
command : str
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
any
|
|
||||||
"""
|
|
||||||
if command in NormalCommands.Commands or command in NormalCommands.CommandAliases:
|
|
||||||
return "Normal"
|
|
||||||
|
|
||||||
if command in Management.Commands or command in Management.CommandAliases:
|
|
||||||
return "Manage"
|
|
||||||
|
|
||||||
if command in Fun.Commands or command in Fun.CommandAliases:
|
|
||||||
return "Fun"
|
|
||||||
|
|
||||||
if command in Cheats.Commands or command in Cheats.CommandAliases:
|
|
||||||
return "Cheats"
|
|
||||||
|
|
||||||
|
|
||||||
def execute(msg, clientid):
|
|
||||||
"""
|
|
||||||
Command Execution
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
msg : str
|
|
||||||
clientid : int
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
any
|
|
||||||
"""
|
|
||||||
command = msg.lower().split(" ")[0].split("/")[1]
|
|
||||||
arguments = msg.lower().split(" ")[1:]
|
|
||||||
accountid = clientid_to_accountid(clientid)
|
|
||||||
print("checking command type")
|
|
||||||
print(command_type(command))
|
|
||||||
if command_type(command) == "Normal":
|
|
||||||
NormalCommands.ExcelCommand(command, arguments, clientid, accountid)
|
|
||||||
|
|
||||||
elif command_type(command) == "Manage":
|
|
||||||
if check_permissions(accountid, command):
|
|
||||||
Management.ExcelCommand(command, arguments, clientid, accountid)
|
|
||||||
bs.broadcastmessage("Executed", transient=True, clients=[clientid])
|
|
||||||
else:
|
|
||||||
bs.broadcastmessage("access denied", transient=True,
|
|
||||||
clients=[clientid])
|
|
||||||
|
|
||||||
elif command_type(command) == "Fun":
|
|
||||||
if check_permissions(accountid, command):
|
|
||||||
Fun.ExcelCommand(command, arguments, clientid, accountid)
|
|
||||||
bs.broadcastmessage("Executed", transient=True, clients=[clientid])
|
|
||||||
else:
|
|
||||||
bs.broadcastmessage("access denied", transient=True,
|
|
||||||
clients=[clientid])
|
|
||||||
|
|
||||||
elif command_type(command) == "Cheats":
|
|
||||||
if check_permissions(accountid, command):
|
|
||||||
Cheats.ExcelCommand(command, arguments, clientid, accountid)
|
|
||||||
bs.broadcastmessage("Executed", transient=True, clients=[clientid])
|
|
||||||
else:
|
|
||||||
bs.broadcastmessage("access denied", transient=True,
|
|
||||||
clients=[clientid])
|
|
||||||
now = datetime.now()
|
|
||||||
if accountid in pdata.get_blacklist()[
|
|
||||||
"muted-ids"] and now < datetime.strptime(
|
|
||||||
pdata.get_blacklist()["muted-ids"][accountid]["till"],
|
|
||||||
"%Y-%m-%d %H:%M:%S"):
|
|
||||||
bs.broadcastmessage("You are on mute", transient=True,
|
|
||||||
clients=[clientid])
|
|
||||||
return None
|
|
||||||
if serverdata.muted:
|
|
||||||
return None
|
|
||||||
if settings["ChatCommands"]["BrodcastCommand"]:
|
|
||||||
return msg
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def QuickAccess(msg, client_id):
|
|
||||||
from bascenev1lib.actor import popuptext
|
|
||||||
if msg.startswith(","):
|
|
||||||
name = ""
|
|
||||||
teamid = 0
|
|
||||||
for i in bs.get_foreground_host_session().sessionplayers:
|
|
||||||
if i.inputdevice.client_id == client_id:
|
|
||||||
teamid = i.sessionteam.id
|
|
||||||
name = i.getname(True)
|
|
||||||
|
|
||||||
for i in bs.get_foreground_host_session().sessionplayers:
|
|
||||||
if hasattr(i,
|
|
||||||
'sessionteam') and i.sessionteam and teamid == i.sessionteam.id and i.inputdevice.client_id != client_id:
|
|
||||||
bs.broadcastmessage(name + ":" + msg[1:],
|
|
||||||
clients=[i.inputdevice.client_id],
|
|
||||||
color=(0.3, 0.6, 0.3), transient=True)
|
|
||||||
|
|
||||||
return None
|
|
||||||
elif msg.startswith("."):
|
|
||||||
msg = msg[1:]
|
|
||||||
msgAr = msg.split(" ")
|
|
||||||
if len(msg) > 25 or int(len(msg) / 5) > len(msgAr):
|
|
||||||
bs.broadcastmessage("msg/word length too long",
|
|
||||||
clients=[client_id], transient=True)
|
|
||||||
return None
|
|
||||||
msgAr.insert(int(len(msgAr) / 2), "\n")
|
|
||||||
for player in _babase.get_foreground_host_activity().players:
|
|
||||||
if player.sessionplayer.inputdevice.client_id == client_id and player.actor.exists() and hasattr(
|
|
||||||
player.actor.node, "position"):
|
|
||||||
pos = player.actor.node.position
|
|
||||||
with bs.get_foreground_host_activity().context:
|
|
||||||
popuptext.PopupText(
|
|
||||||
" ".join(msgAr),
|
|
||||||
(pos[0], pos[1] + 1, pos[2])).autoretain()
|
|
||||||
return None
|
|
||||||
return None
|
|
||||||
|
|
@ -1,126 +0,0 @@
|
||||||
import _thread
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
import _bascenev1
|
|
||||||
from stats import mystats
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from babase._general import Call
|
|
||||||
from .handlers import send
|
|
||||||
|
|
||||||
Commands = ['me', 'list', 'uniqeid', 'ping']
|
|
||||||
CommandAliases = ['stats', 'score', 'rank',
|
|
||||||
'myself', 'l', 'id', 'pb-id', 'pb', 'accountid']
|
|
||||||
|
|
||||||
|
|
||||||
def ExcelCommand(command, arguments, clientid, accountid):
|
|
||||||
"""
|
|
||||||
Checks The Command And Run Function
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
command : str
|
|
||||||
arguments : str
|
|
||||||
clientid : int
|
|
||||||
accountid : int
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
None
|
|
||||||
"""
|
|
||||||
if command in ['me', 'stats', 'score', 'rank', 'myself']:
|
|
||||||
fetch_send_stats(accountid, clientid)
|
|
||||||
|
|
||||||
elif command in ['list', 'l']:
|
|
||||||
list(clientid)
|
|
||||||
|
|
||||||
elif command in ['uniqeid', 'id', 'pb-id', 'pb', 'accountid']:
|
|
||||||
accountid_request(arguments, clientid, accountid)
|
|
||||||
|
|
||||||
elif command in ['ping']:
|
|
||||||
get_ping(arguments, clientid)
|
|
||||||
|
|
||||||
|
|
||||||
def get_ping(arguments, clientid):
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
send(f"Your ping {_bascenev1.get_client_ping(clientid)}ms ", clientid)
|
|
||||||
elif arguments[0] == 'all':
|
|
||||||
pingall(clientid)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
|
|
||||||
for index, player in enumerate(session.sessionplayers):
|
|
||||||
name = player.getname(full=True, icon=False),
|
|
||||||
if player.inputdevice.client_id == int(arguments[0]):
|
|
||||||
ping = _bascenev1.get_client_ping(int(arguments[0]))
|
|
||||||
send(f" {name}'s ping {ping}ms", clientid)
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def stats(ac_id, clientid):
|
|
||||||
stats = mystats.get_stats_by_id(ac_id)
|
|
||||||
if stats:
|
|
||||||
reply = "Score:" + str(stats["scores"]) + "\nGames:" + str(
|
|
||||||
stats["games"]) + "\nKills:" + str(
|
|
||||||
stats["kills"]) + "\nDeaths:" + str(
|
|
||||||
stats["deaths"]) + "\nAvg.:" + str(stats["avg_score"])
|
|
||||||
else:
|
|
||||||
reply = "Not played any match yet."
|
|
||||||
|
|
||||||
_babase.pushcall(Call(send, reply, clientid), from_other_thread=True)
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_send_stats(ac_id, clientid):
|
|
||||||
_thread.start_new_thread(stats, (ac_id, clientid,))
|
|
||||||
|
|
||||||
|
|
||||||
def pingall(clientid):
|
|
||||||
"""Returns The List Of Players Clientid and index"""
|
|
||||||
|
|
||||||
p = u'{0:^16}{1:^34}ms'
|
|
||||||
seprator = '\n______________________________\n'
|
|
||||||
|
|
||||||
list = p.format('Name', 'Ping (ms)') + seprator
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
|
|
||||||
for index, player in enumerate(session.sessionplayers):
|
|
||||||
list += p.format(player.getname(icon=True),
|
|
||||||
_bascenev1.get_client_ping(
|
|
||||||
int(player.inputdevice.client_id))) + "\n"
|
|
||||||
|
|
||||||
send(list, clientid)
|
|
||||||
|
|
||||||
|
|
||||||
def list(clientid):
|
|
||||||
"""Returns The List Of Players Clientid and index"""
|
|
||||||
|
|
||||||
p = u'{0:^16}{1:^15}{2:^10}'
|
|
||||||
seprator = '\n______________________________\n'
|
|
||||||
|
|
||||||
list = p.format('Name', 'Client ID', 'Player ID') + seprator
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
|
|
||||||
for index, player in enumerate(session.sessionplayers):
|
|
||||||
list += p.format(player.getname(icon=False),
|
|
||||||
player.inputdevice.client_id, index) + "\n"
|
|
||||||
|
|
||||||
send(list, clientid)
|
|
||||||
|
|
||||||
|
|
||||||
def accountid_request(arguments, clientid, accountid):
|
|
||||||
"""Returns The Account Id Of Players"""
|
|
||||||
|
|
||||||
if arguments == [] or arguments == ['']:
|
|
||||||
send(f"Your account id is {accountid} ", clientid)
|
|
||||||
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
player = session.sessionplayers[int(arguments[0])]
|
|
||||||
|
|
||||||
name = player.getname(full=True, icon=True)
|
|
||||||
accountid = player.get_v1_account_id()
|
|
||||||
|
|
||||||
send(f" {name}'s account id is '{accountid}' ", clientid)
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
def isspam():
|
|
||||||
return
|
|
||||||
417
dist/ba_root/mods/custom_hooks.py
vendored
417
dist/ba_root/mods/custom_hooks.py
vendored
|
|
@ -1,417 +0,0 @@
|
||||||
"""Custom hooks to pull of the in-game functions."""
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# (see https://ballistica.net/wiki/meta-tag-system)
|
|
||||||
|
|
||||||
# pylint: disable=import-error
|
|
||||||
# pylint: disable=import-outside-toplevel
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import _thread
|
|
||||||
import importlib
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
import bauiv1 as bui
|
|
||||||
import setting
|
|
||||||
from baclassic._servermode import ServerController
|
|
||||||
from bascenev1._activitytypes import ScoreScreenActivity
|
|
||||||
from bascenev1._map import Map
|
|
||||||
from bascenev1._session import Session
|
|
||||||
from bascenev1lib.activity import dualteamscore, multiteamscore, drawscore
|
|
||||||
from bascenev1lib.activity.coopscore import CoopScoreScreen
|
|
||||||
from bascenev1lib.actor import playerspaz
|
|
||||||
from chathandle import handlechat
|
|
||||||
from features import map_fun
|
|
||||||
from features import team_balancer, afk_check, dual_team_score as newdts
|
|
||||||
from features import text_on_map, announcement
|
|
||||||
from features import votingmachine
|
|
||||||
from playersdata import pdata
|
|
||||||
from serverdata import serverdata
|
|
||||||
from spazmod import modifyspaz
|
|
||||||
from stats import mystats
|
|
||||||
from tools import account
|
|
||||||
from tools import notification_manager
|
|
||||||
from tools import servercheck, ServerUpdate, logger, playlist, servercontroller
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
def filter_chat_message(msg: str, client_id: int) -> str | None:
|
|
||||||
"""Returns all in game messages or None (ignore's message)."""
|
|
||||||
return handlechat.filter_chat_message(msg, client_id)
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export plugin
|
|
||||||
|
|
||||||
|
|
||||||
class modSetup(babase.Plugin):
|
|
||||||
def on_app_running(self):
|
|
||||||
"""Runs when app is launched."""
|
|
||||||
plus = bui.app.plus
|
|
||||||
bootstraping()
|
|
||||||
servercheck.checkserver().start()
|
|
||||||
ServerUpdate.check()
|
|
||||||
bs.AppTimer(5, account.updateOwnerIps)
|
|
||||||
if settings["afk_remover"]['enable']:
|
|
||||||
afk_check.checkIdle().start()
|
|
||||||
if (settings["useV2Account"]):
|
|
||||||
|
|
||||||
if (babase.internal.get_v1_account_state() ==
|
|
||||||
'signed_in' and babase.internal.get_v1_account_type() == 'V2'):
|
|
||||||
logging.debug("Account V2 is active")
|
|
||||||
else:
|
|
||||||
logging.warning("Account V2 login require ....stay tuned.")
|
|
||||||
bs.apptimer(3, babase.Call(logging.debug,
|
|
||||||
"Starting Account V2 login process...."))
|
|
||||||
bs.apptimer(6, account.AccountUtil)
|
|
||||||
else:
|
|
||||||
plus.accounts.set_primary_credentials(None)
|
|
||||||
plus.sign_in_v1('Local')
|
|
||||||
bs.apptimer(60, playlist.flush_playlists)
|
|
||||||
|
|
||||||
# it works sometimes , but it blocks shutdown so server raise runtime
|
|
||||||
# exception, also dump server logs
|
|
||||||
def on_app_shutdown(self):
|
|
||||||
print("Server shutting down , lets save cache")
|
|
||||||
# lets try threading here
|
|
||||||
# _thread.start_new_thread(pdata.dump_cache, ())
|
|
||||||
# _thread.start_new_thread(notification_manager.dump_cache, ())
|
|
||||||
# print("Done dumping memory")
|
|
||||||
|
|
||||||
|
|
||||||
def score_screen_on_begin(func) -> None:
|
|
||||||
"""Runs when score screen is displayed."""
|
|
||||||
|
|
||||||
def wrapper(self, *args, **kwargs):
|
|
||||||
result = func(self, *args, **kwargs) # execute the original method
|
|
||||||
team_balancer.balanceTeams()
|
|
||||||
mystats.update(self._stats)
|
|
||||||
announcement.showScoreScreenAnnouncement()
|
|
||||||
return result
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
ScoreScreenActivity.on_begin = score_screen_on_begin(
|
|
||||||
ScoreScreenActivity.on_begin)
|
|
||||||
|
|
||||||
|
|
||||||
def on_map_init(func):
|
|
||||||
def wrapper(self, *args, **kwargs):
|
|
||||||
func(self, *args, **kwargs)
|
|
||||||
text_on_map.textonmap()
|
|
||||||
modifyspaz.setTeamCharacter()
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
Map.__init__ = on_map_init(Map.__init__)
|
|
||||||
|
|
||||||
|
|
||||||
def playerspaz_init(playerspaz: bs.Player, node: bs.Node, player: bs.Player):
|
|
||||||
"""Runs when player is spawned on map."""
|
|
||||||
modifyspaz.main(playerspaz, node, player)
|
|
||||||
|
|
||||||
|
|
||||||
def bootstraping():
|
|
||||||
"""Bootstarps the server."""
|
|
||||||
logging.warning("Bootstraping mods...")
|
|
||||||
# server related
|
|
||||||
# _bascenev1.set_server_name(settings["HostName"])
|
|
||||||
# _bascenev1.set_transparent_kickvote(settings["ShowKickVoteStarterName"])
|
|
||||||
# _bascenev1.set_kickvote_msg_type(settings["KickVoteMsgType"])
|
|
||||||
# bs.hide_player_device_id(settings["Anti-IdRevealer"]) TODO add call in
|
|
||||||
# cpp
|
|
||||||
|
|
||||||
# check for auto update stats
|
|
||||||
_thread.start_new_thread(mystats.refreshStats, ())
|
|
||||||
pdata.load_cache()
|
|
||||||
_thread.start_new_thread(pdata.dump_cache, ())
|
|
||||||
_thread.start_new_thread(notification_manager.dump_cache, ())
|
|
||||||
|
|
||||||
# import plugins
|
|
||||||
if settings["elPatronPowerups"]["enable"]:
|
|
||||||
from plugins import elPatronPowerups
|
|
||||||
elPatronPowerups.enable()
|
|
||||||
if settings["mikirogQuickTurn"]["enable"]:
|
|
||||||
from plugins import wavedash # pylint: disable=unused-import
|
|
||||||
if settings["colorful_explosions"]["enable"]:
|
|
||||||
from plugins import color_explosion
|
|
||||||
color_explosion.enable()
|
|
||||||
if settings["ballistica_web"]["enable"]:
|
|
||||||
from plugins import bcs_plugin
|
|
||||||
bcs_plugin.enable(settings["ballistica_web"]["server_password"])
|
|
||||||
if settings["character_chooser"]["enable"]:
|
|
||||||
from plugins import character_chooser
|
|
||||||
character_chooser.enable()
|
|
||||||
if settings["custom_characters"]["enable"]:
|
|
||||||
from plugins import importcustomcharacters
|
|
||||||
importcustomcharacters.enable()
|
|
||||||
if settings["StumbledScoreScreen"]:
|
|
||||||
pass
|
|
||||||
# from features import StumbledScoreScreen
|
|
||||||
if settings["colorfullMap"]:
|
|
||||||
from plugins import colorfulmaps2
|
|
||||||
try:
|
|
||||||
pass
|
|
||||||
# from tools import healthcheck
|
|
||||||
# healthcheck.main()
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
try:
|
|
||||||
import subprocess
|
|
||||||
# Install psutil package
|
|
||||||
# Download get-pip.py
|
|
||||||
curl_process = subprocess.Popen(
|
|
||||||
["curl", "-sS", "https://bootstrap.pypa.io/get-pip.py"],
|
|
||||||
stdout=subprocess.PIPE)
|
|
||||||
|
|
||||||
# Install pip using python3.10
|
|
||||||
python_process = subprocess.Popen(
|
|
||||||
["python3.10"], stdin=curl_process.stdout)
|
|
||||||
|
|
||||||
# Wait for the processes to finish
|
|
||||||
curl_process.stdout.close()
|
|
||||||
python_process.wait()
|
|
||||||
|
|
||||||
subprocess.check_call(
|
|
||||||
["python3.10", "-m", "pip", "install", "psutil"])
|
|
||||||
# restart after installation
|
|
||||||
print("dependency installed , restarting server")
|
|
||||||
_babase.quit()
|
|
||||||
from tools import healthcheck
|
|
||||||
healthcheck.main()
|
|
||||||
except BaseException:
|
|
||||||
logging.warning("please install psutil to enable system monitor.")
|
|
||||||
|
|
||||||
# import features
|
|
||||||
if settings["whitelist"]:
|
|
||||||
pdata.load_white_list()
|
|
||||||
|
|
||||||
import_discord_bot()
|
|
||||||
import_games()
|
|
||||||
import_dual_team_score()
|
|
||||||
logger.log("Server started")
|
|
||||||
|
|
||||||
|
|
||||||
def import_discord_bot() -> None:
|
|
||||||
"""Imports the discord bot."""
|
|
||||||
if settings["discordbot"]["enable"]:
|
|
||||||
from features import discord_bot
|
|
||||||
discord_bot.token = settings["discordbot"]["token"]
|
|
||||||
discord_bot.liveStatsChannelID = settings["discordbot"][
|
|
||||||
"liveStatsChannelID"]
|
|
||||||
discord_bot.logsChannelID = settings["discordbot"]["logsChannelID"]
|
|
||||||
discord_bot.liveChat = settings["discordbot"]["liveChat"]
|
|
||||||
discord_bot.BsDataThread()
|
|
||||||
discord_bot.init()
|
|
||||||
|
|
||||||
|
|
||||||
def import_games():
|
|
||||||
"""Imports the custom games from games directory."""
|
|
||||||
import sys
|
|
||||||
sys.path.append(_babase.env()['python_directory_user'] + os.sep + "games")
|
|
||||||
games = os.listdir("ba_root/mods/games")
|
|
||||||
for game in games:
|
|
||||||
if game.endswith(".so"):
|
|
||||||
importlib.import_module("games." + game.replace(".so", ""))
|
|
||||||
|
|
||||||
maps = os.listdir("ba_root/mods/maps")
|
|
||||||
for _map in maps:
|
|
||||||
if _map.endswith(".py") or _map.endswith(".so"):
|
|
||||||
importlib.import_module(
|
|
||||||
"maps." + _map.replace(".so", "").replace(".py", ""))
|
|
||||||
|
|
||||||
|
|
||||||
def import_dual_team_score() -> None:
|
|
||||||
"""Imports the dual team score."""
|
|
||||||
if settings["newResultBoard"]:
|
|
||||||
dualteamscore.TeamVictoryScoreScreenActivity = newdts.TeamVictoryScoreScreenActivity
|
|
||||||
multiteamscore.MultiTeamScoreScreenActivity.show_player_scores = newdts.show_player_scores
|
|
||||||
drawscore.DrawScoreScreenActivity = newdts.DrawScoreScreenActivity
|
|
||||||
|
|
||||||
|
|
||||||
org_begin = bs._activity.Activity.on_begin
|
|
||||||
|
|
||||||
|
|
||||||
def new_begin(self):
|
|
||||||
"""Runs when game is began."""
|
|
||||||
org_begin(self)
|
|
||||||
night_mode()
|
|
||||||
if settings["colorfullMap"]:
|
|
||||||
map_fun.decorate_map()
|
|
||||||
votingmachine.reset_votes()
|
|
||||||
votingmachine.game_started_on = time.time()
|
|
||||||
|
|
||||||
|
|
||||||
bs._activity.Activity.on_begin = new_begin
|
|
||||||
|
|
||||||
org_end = bs._activity.Activity.end
|
|
||||||
|
|
||||||
|
|
||||||
def new_end(self, results: Any = None,
|
|
||||||
delay: float = 0.0, force: bool = False):
|
|
||||||
"""Runs when game is ended."""
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
if isinstance(activity, CoopScoreScreen):
|
|
||||||
team_balancer.checkToExitCoop()
|
|
||||||
org_end(self, results, delay, force)
|
|
||||||
|
|
||||||
|
|
||||||
bs._activity.Activity.end = new_end
|
|
||||||
|
|
||||||
org_player_join = bs._activity.Activity.on_player_join
|
|
||||||
|
|
||||||
|
|
||||||
def on_player_join(self, player) -> None:
|
|
||||||
"""Runs when player joins the game."""
|
|
||||||
team_balancer.on_player_join()
|
|
||||||
org_player_join(self, player)
|
|
||||||
|
|
||||||
|
|
||||||
bs._activity.Activity.on_player_join = on_player_join
|
|
||||||
|
|
||||||
|
|
||||||
def night_mode() -> None:
|
|
||||||
"""Checks the time and enables night mode."""
|
|
||||||
|
|
||||||
if settings['autoNightMode']['enable']:
|
|
||||||
|
|
||||||
start = datetime.strptime(
|
|
||||||
settings['autoNightMode']['startTime'], "%H:%M")
|
|
||||||
end = datetime.strptime(settings['autoNightMode']['endTime'], "%H:%M")
|
|
||||||
now = datetime.now()
|
|
||||||
|
|
||||||
if now.time() > start.time() or now.time() < end.time():
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
|
|
||||||
activity.globalsnode.tint = (0.5, 0.7, 1.0)
|
|
||||||
|
|
||||||
if settings['autoNightMode']['fireflies']:
|
|
||||||
activity.fireflies_generator(
|
|
||||||
20, settings['autoNightMode']["fireflies_random_color"])
|
|
||||||
|
|
||||||
|
|
||||||
def kick_vote_started(started_by: str, started_to: str) -> None:
|
|
||||||
"""Logs the kick vote."""
|
|
||||||
logger.log(f"{started_by} started kick vote for {started_to}.")
|
|
||||||
|
|
||||||
|
|
||||||
def on_kicked(account_id: str) -> None:
|
|
||||||
"""Runs when someone is kicked by kickvote."""
|
|
||||||
logger.log(f"{account_id} kicked by kickvotes.")
|
|
||||||
|
|
||||||
|
|
||||||
def on_kick_vote_end():
|
|
||||||
"""Runs when kickvote is ended."""
|
|
||||||
logger.log("Kick vote End")
|
|
||||||
|
|
||||||
|
|
||||||
def on_join_request(ip):
|
|
||||||
servercheck.on_join_request(ip)
|
|
||||||
|
|
||||||
|
|
||||||
def shutdown(func) -> None:
|
|
||||||
"""Set the app to quit either now or at the next clean opportunity."""
|
|
||||||
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
# add screen text and tell players we are going to restart soon.
|
|
||||||
bs.chatmessage(
|
|
||||||
"Server will restart on next opportunity. (series end)")
|
|
||||||
_babase.restart_scheduled = True
|
|
||||||
bs.get_foreground_host_activity().restart_msg = bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'text': "Server going to restart after this series.",
|
|
||||||
'flatness': 1.0,
|
|
||||||
'h_align': 'right',
|
|
||||||
'v_attach': 'bottom',
|
|
||||||
'h_attach': 'right',
|
|
||||||
'scale': 0.5,
|
|
||||||
'position': (
|
|
||||||
-25,
|
|
||||||
54),
|
|
||||||
'color': (
|
|
||||||
1,
|
|
||||||
0.5,
|
|
||||||
0.7)
|
|
||||||
})
|
|
||||||
func(*args, **kwargs)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
ServerController.shutdown = shutdown(ServerController.shutdown)
|
|
||||||
|
|
||||||
|
|
||||||
def on_player_request(func) -> bool:
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
player = args[1]
|
|
||||||
count = 0
|
|
||||||
if not (player.get_v1_account_id(
|
|
||||||
) in serverdata.clients and
|
|
||||||
serverdata.clients[player.get_v1_account_id()]["verified"]):
|
|
||||||
return False
|
|
||||||
for current_player in args[0].sessionplayers:
|
|
||||||
if current_player.get_v1_account_id() == player.get_v1_account_id():
|
|
||||||
count += 1
|
|
||||||
if count >= settings["maxPlayersPerDevice"]:
|
|
||||||
bs.broadcastmessage("Reached max players limit per device",
|
|
||||||
clients=[
|
|
||||||
player.inputdevice.client_id],
|
|
||||||
transient=True, )
|
|
||||||
return False
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
Session.on_player_request = on_player_request(Session.on_player_request)
|
|
||||||
|
|
||||||
ServerController._access_check_response = servercontroller._access_check_response
|
|
||||||
|
|
||||||
|
|
||||||
def wrap_player_spaz_init(original_class):
|
|
||||||
"""
|
|
||||||
Modify the __init__ method of the player_spaz.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class WrappedClass(original_class):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
# Custom code before the original __init__
|
|
||||||
|
|
||||||
# Modify args or kwargs as needed
|
|
||||||
player = args[0] if args else kwargs.get('player')
|
|
||||||
character = args[3] if len(
|
|
||||||
args) > 3 else kwargs.get('character', 'Spaz')
|
|
||||||
|
|
||||||
print(f"Player: {player}, Character: {character}")
|
|
||||||
# Modify the character value
|
|
||||||
modified_character = modifyspaz.getCharacter(player, character)
|
|
||||||
if len(args) > 3:
|
|
||||||
args = args[:3] + (modified_character,) + args[4:]
|
|
||||||
else:
|
|
||||||
kwargs['character'] = modified_character
|
|
||||||
|
|
||||||
# Call the original __init__
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
playerspaz_init(self, self.node, self._player)
|
|
||||||
|
|
||||||
# Return the modified class
|
|
||||||
return WrappedClass
|
|
||||||
|
|
||||||
|
|
||||||
playerspaz.PlayerSpaz = wrap_player_spaz_init(playerspaz.PlayerSpaz)
|
|
||||||
7
dist/ba_root/mods/features/__init__.py
vendored
7
dist/ba_root/mods/features/__init__.py
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
"""Common bits of functionality shared between all efro projects.
|
|
||||||
|
|
||||||
Things in here should be hardened, highly type-safe, and well-covered by unit
|
|
||||||
tests since they are widely used in live client and server code.
|
|
||||||
|
|
||||||
license : MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
73
dist/ba_root/mods/features/afk_check.py
vendored
73
dist/ba_root/mods/features/afk_check.py
vendored
|
|
@ -1,73 +0,0 @@
|
||||||
# Custom kick idle player script by mr.smoothy#5824
|
|
||||||
|
|
||||||
import setting
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
settings = setting.get_settings_data()
|
|
||||||
INGAME_TIME = settings["afk_remover"]["ingame_idle_time_in_secs"]
|
|
||||||
LOBBY_KICK = settings['afk_remover']["kick_idle_from_lobby"]
|
|
||||||
INLOBBY_TIME = settings['afk_remover']["lobby_idle_time_in_secs"]
|
|
||||||
cIdle = 0 # players/player found idle within very short time
|
|
||||||
cLastIdle = 0
|
|
||||||
|
|
||||||
|
|
||||||
class checkIdle(object):
|
|
||||||
def start(self):
|
|
||||||
self.t1 = bs.timer(2, babase.Call(self.check), repeat=True)
|
|
||||||
self.lobbies = {}
|
|
||||||
|
|
||||||
def check(self):
|
|
||||||
global cLastIdle
|
|
||||||
global cIdle
|
|
||||||
current = bs.time(babase.TimeType.REAL,
|
|
||||||
timeformat=babase.TimeFormat.MILLISECONDS)
|
|
||||||
if not bs.get_foreground_host_session():
|
|
||||||
return
|
|
||||||
for player in bs.get_foreground_host_session().sessionplayers:
|
|
||||||
last_input = int(player.inputdevice.get_last_input_time())
|
|
||||||
afk_time = int((current - last_input) / 1000)
|
|
||||||
if afk_time in range(INGAME_TIME,
|
|
||||||
INGAME_TIME + 20) or afk_time > INGAME_TIME + 20:
|
|
||||||
if (current - cLastIdle) / 1000 < 3:
|
|
||||||
cIdle = cIdle + 1
|
|
||||||
if cIdle >= 3:
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
cIdle = 0
|
|
||||||
cLastIdle = current
|
|
||||||
|
|
||||||
if afk_time in range(INGAME_TIME, INGAME_TIME + 20):
|
|
||||||
self.warn_player(player.get_v1_account_id(),
|
|
||||||
"Press any button within " + str(
|
|
||||||
INGAME_TIME + 20 - afk_time) + " secs")
|
|
||||||
if afk_time > INGAME_TIME + 20:
|
|
||||||
player.remove_from_game()
|
|
||||||
if LOBBY_KICK:
|
|
||||||
current_players = []
|
|
||||||
for player in bs.get_game_roster():
|
|
||||||
if player['client_id'] != -1 and len(player['players']) == 0:
|
|
||||||
current_players.append(player['client_id'])
|
|
||||||
if player['client_id'] not in self.lobbies:
|
|
||||||
self.lobbies[player['client_id']] = current
|
|
||||||
lobby_afk = int(
|
|
||||||
(current - self.lobbies[player['client_id']]) / 1000)
|
|
||||||
if lobby_afk in range(INLOBBY_TIME, INLOBBY_TIME + 10):
|
|
||||||
_bs.broadcastmessage("Join game within " + str(
|
|
||||||
INLOBBY_TIME + 10 - lobby_afk) + " secs",
|
|
||||||
color=(1, 0, 0), transient=True,
|
|
||||||
clients=[player['client_id']])
|
|
||||||
if lobby_afk > INLOBBY_TIME + 10:
|
|
||||||
bs.disconnect_client(player['client_id'], 0)
|
|
||||||
# clean the lobbies dict
|
|
||||||
temp = self.lobbies.copy()
|
|
||||||
for clid in temp:
|
|
||||||
if clid not in current_players:
|
|
||||||
del self.lobbies[clid]
|
|
||||||
|
|
||||||
def warn_player(self, pbid, msg):
|
|
||||||
for player in bs.get_game_roster():
|
|
||||||
if player["account_id"] == pbid:
|
|
||||||
bs.broadcastmessage(msg, color=(1, 0, 0), transient=True,
|
|
||||||
clients=[player['client_id']])
|
|
||||||
15
dist/ba_root/mods/features/announcement.py
vendored
15
dist/ba_root/mods/features/announcement.py
vendored
|
|
@ -1,15 +0,0 @@
|
||||||
import random
|
|
||||||
|
|
||||||
import setting
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
setti = setting.get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
def showScoreScreenAnnouncement():
|
|
||||||
if setti["ScoreScreenAnnouncement"]["enable"]:
|
|
||||||
color = ((0 + random.random() * 1.0), (0 + random.random() * 1.0),
|
|
||||||
(0 + random.random() * 1.0))
|
|
||||||
msgs = setti["ScoreScreenAnnouncement"]["msg"]
|
|
||||||
bs.broadcastmessage(random.choice(msgs), color=color)
|
|
||||||
207
dist/ba_root/mods/features/discord_bot.py
vendored
207
dist/ba_root/mods/features/discord_bot.py
vendored
|
|
@ -1,207 +0,0 @@
|
||||||
import asyncio
|
|
||||||
import logging
|
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
import discord
|
|
||||||
from discord.ext.commands import Bot
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from babase._general import Call
|
|
||||||
|
|
||||||
logging.getLogger('asyncio').setLevel(logging.WARNING)
|
|
||||||
intents = discord.Intents().all()
|
|
||||||
|
|
||||||
client = Bot(command_prefix='!', intents=intents)
|
|
||||||
|
|
||||||
# client = discord.Client()
|
|
||||||
|
|
||||||
|
|
||||||
stats = {}
|
|
||||||
livestatsmsgs = []
|
|
||||||
logsChannelID = 859519868838608970
|
|
||||||
liveStatsChannelID = 924697770554687548
|
|
||||||
liveChat = True
|
|
||||||
token = ''
|
|
||||||
logs = []
|
|
||||||
|
|
||||||
|
|
||||||
def push_log(msg):
|
|
||||||
global logs
|
|
||||||
logs.append(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def init():
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
loop.create_task(client.start(token))
|
|
||||||
|
|
||||||
Thread(target=loop.run_forever).start()
|
|
||||||
|
|
||||||
|
|
||||||
channel = None
|
|
||||||
|
|
||||||
|
|
||||||
@client.event
|
|
||||||
async def on_message(message):
|
|
||||||
global channel
|
|
||||||
if message.author == client.user:
|
|
||||||
return
|
|
||||||
channel = message.channel
|
|
||||||
|
|
||||||
if message.channel.id == logsChannelID:
|
|
||||||
_babase.pushcall(Call(babase.internal.chatmessage,
|
|
||||||
message.content), from_other_thread=True)
|
|
||||||
|
|
||||||
|
|
||||||
@client.event
|
|
||||||
async def on_ready():
|
|
||||||
print("Discord bot logged in as: %s, %s" %
|
|
||||||
(client.user.name, client.user.id))
|
|
||||||
|
|
||||||
await verify_channel()
|
|
||||||
|
|
||||||
|
|
||||||
async def verify_channel():
|
|
||||||
global livestatsmsgs
|
|
||||||
channel = client.get_channel(liveStatsChannelID)
|
|
||||||
botmsg_count = 0
|
|
||||||
msgs = await channel.history(limit=5).flatten()
|
|
||||||
for msg in msgs:
|
|
||||||
if msg.author.id == client.user.id:
|
|
||||||
botmsg_count += 1
|
|
||||||
livestatsmsgs.append(msg)
|
|
||||||
|
|
||||||
livestatsmsgs.reverse()
|
|
||||||
while (botmsg_count < 2):
|
|
||||||
new_msg = await channel.send("msg reserved for live stats")
|
|
||||||
livestatsmsgs.append(new_msg)
|
|
||||||
botmsg_count += 1
|
|
||||||
asyncio.run_coroutine_threadsafe(refresh_stats(), client.loop)
|
|
||||||
asyncio.run_coroutine_threadsafe(send_logs(), client.loop)
|
|
||||||
# client.loop.create_task(refresh_stats())
|
|
||||||
# client.loop.create_task(send_logs())
|
|
||||||
|
|
||||||
|
|
||||||
async def refresh_stats():
|
|
||||||
await client.wait_until_ready()
|
|
||||||
|
|
||||||
while not client.is_closed():
|
|
||||||
await livestatsmsgs[0].edit(content=get_live_players_msg())
|
|
||||||
await livestatsmsgs[1].edit(content=get_chats())
|
|
||||||
await asyncio.sleep(10)
|
|
||||||
|
|
||||||
|
|
||||||
async def send_logs():
|
|
||||||
global logs
|
|
||||||
# safely dispatch logs to dc channel , without being rate limited and getting ban from discord
|
|
||||||
# still we sending 2 msg and updating 2 msg within 5 seconds , umm still risky ...nvm not my problem
|
|
||||||
channel = client.get_channel(logsChannelID)
|
|
||||||
await client.wait_until_ready()
|
|
||||||
while not client.is_closed():
|
|
||||||
if logs:
|
|
||||||
msg = ''
|
|
||||||
for msg_ in logs:
|
|
||||||
msg += msg_ + "\n"
|
|
||||||
logs = []
|
|
||||||
if msg:
|
|
||||||
await channel.send(msg)
|
|
||||||
|
|
||||||
await asyncio.sleep(10)
|
|
||||||
|
|
||||||
|
|
||||||
def get_live_players_msg():
|
|
||||||
global stats
|
|
||||||
msg_1 = '***Live Stats :***\n\n ***Players in server***\n\n'
|
|
||||||
msg = ''
|
|
||||||
try:
|
|
||||||
for id in stats['roster']:
|
|
||||||
name = stats['roster'][id]['name']
|
|
||||||
device_id = stats['roster'][id]['device_id']
|
|
||||||
msg += id + " -> " + name + " -> " + device_id + " \n"
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if not msg:
|
|
||||||
msg = "```No one``` \n"
|
|
||||||
msg_2 = "\n\n***Current: *** " + \
|
|
||||||
stats['playlist']['current'] + "\n ***Next: ***" + \
|
|
||||||
stats['playlist']['next'] + "\n\n."
|
|
||||||
return msg_1 + msg + msg_2
|
|
||||||
|
|
||||||
|
|
||||||
def get_chats():
|
|
||||||
msg_1 = '***Live Chat***\n\n'
|
|
||||||
msg = ''
|
|
||||||
try:
|
|
||||||
for msg_ in stats['chats']:
|
|
||||||
msg += msg_ + "\n"
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if not msg:
|
|
||||||
msg = "```Empty```\n"
|
|
||||||
if not liveChat:
|
|
||||||
return '```disabled```'
|
|
||||||
return msg_1 + msg
|
|
||||||
|
|
||||||
|
|
||||||
class BsDataThread(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.refreshStats()
|
|
||||||
self.Timer = bs.Timer(8, babase.Call(self.refreshStats), repeat=True)
|
|
||||||
# self.Timerr = bs.Timer( 10,babase.Call(self.refreshLeaderboard),timetype = babase.TimeType.REAL,repeat = True)
|
|
||||||
|
|
||||||
# def refreshLeaderboard(self):
|
|
||||||
# global leaderboard
|
|
||||||
# global top200
|
|
||||||
# _t200={}
|
|
||||||
# f=open(statsfile)
|
|
||||||
# lboard=json.loads(f.read())
|
|
||||||
# leaderboard=lboard
|
|
||||||
# entries = [(a['scores'], a['kills'], a['deaths'], a['games'], a['name_html'], a['aid'],a['last_seen']) for a in lboard.values()]
|
|
||||||
|
|
||||||
# entries.sort(reverse=True)
|
|
||||||
# rank=0
|
|
||||||
# for entry in entries:
|
|
||||||
# rank+=1
|
|
||||||
# if rank >201:
|
|
||||||
# break
|
|
||||||
# _t200[entry[5]]={"rank":rank,"scores":int(entry[0]),"games":int(entry[3]),"kills":int(entry[1]),"deaths":int(entry[2]),"name_html":entry[4],"last_seen":entry[6]}
|
|
||||||
# top200=_t200
|
|
||||||
|
|
||||||
def refreshStats(self):
|
|
||||||
|
|
||||||
liveplayers = {}
|
|
||||||
nextMap = ''
|
|
||||||
currentMap = ''
|
|
||||||
global stats
|
|
||||||
|
|
||||||
for i in babase.internal.get_game_roster():
|
|
||||||
try:
|
|
||||||
liveplayers[i['account_id']] = {
|
|
||||||
'name': i['players'][0]['name_full'],
|
|
||||||
'client_id': i['client_id'],
|
|
||||||
'device_id': i['display_string']}
|
|
||||||
except:
|
|
||||||
liveplayers[i['account_id']] = {
|
|
||||||
'name': "<in-lobby>", 'clientid': i['client_id'],
|
|
||||||
'device_id': i['display_string']}
|
|
||||||
try:
|
|
||||||
nextMap = babase.internal.get_foreground_host_session(
|
|
||||||
).get_next_game_description().evaluate()
|
|
||||||
|
|
||||||
current_game_spec = babase.internal.get_foreground_host_session()._current_game_spec
|
|
||||||
gametype: Type[GameActivity] = current_game_spec['resolved_type']
|
|
||||||
|
|
||||||
currentMap = gametype.get_settings_display_string(
|
|
||||||
current_game_spec).evaluate()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
minigame = {'current': currentMap, 'next': nextMap}
|
|
||||||
# system={'cpu':p.cpu_percent(),'ram':p.virtual_memory().percent}
|
|
||||||
# system={'cpu':80,'ram':34}
|
|
||||||
# stats['system']=system
|
|
||||||
stats['roster'] = liveplayers
|
|
||||||
stats['chats'] = babase.internal.get_chat_messages()
|
|
||||||
stats['playlist'] = minigame
|
|
||||||
|
|
||||||
# stats['teamInfo']=self.getTeamInfo()
|
|
||||||
418
dist/ba_root/mods/features/dual_team_score.py
vendored
418
dist/ba_root/mods/features/dual_team_score.py
vendored
|
|
@ -1,418 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
#
|
|
||||||
"""Functionality related to the end screen in dual-team mode."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.activity.multiteamscore import MultiTeamScoreScreenActivity
|
|
||||||
from bascenev1lib.actor.image import Image
|
|
||||||
from bascenev1lib.actor.text import Text
|
|
||||||
from bascenev1lib.actor.zoomtext import ZoomText
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TeamVictoryScoreScreenActivity(MultiTeamScoreScreenActivity):
|
|
||||||
"""Scorescreen between rounds of a dual-team session."""
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings=settings)
|
|
||||||
self._winner: bs.SessionTeam = settings['winner']
|
|
||||||
assert isinstance(self._winner, bs.SessionTeam)
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
babase.set_analytics_screen('Teams Score Screen')
|
|
||||||
super().on_begin()
|
|
||||||
|
|
||||||
height = 130
|
|
||||||
active_team_count = len(self.teams)
|
|
||||||
vval = (height * active_team_count) / 2 - height / 2
|
|
||||||
i = 0
|
|
||||||
shift_time = 2.5
|
|
||||||
|
|
||||||
# Usually we say 'Best of 7', but if the language prefers we can say
|
|
||||||
# 'First to 4'.
|
|
||||||
session = self.session
|
|
||||||
assert isinstance(session, bs.MultiTeamSession)
|
|
||||||
if bs.app.lang.get_resource('bestOfUseFirstToInstead'):
|
|
||||||
best_txt = babase.Lstr(resource='firstToSeriesText',
|
|
||||||
subs=[('${COUNT}',
|
|
||||||
str(session.get_series_length() / 2 + 1))
|
|
||||||
])
|
|
||||||
else:
|
|
||||||
best_txt = babase.Lstr(resource='bestOfSeriesText',
|
|
||||||
subs=[('${COUNT}',
|
|
||||||
str(session.get_series_length()))])
|
|
||||||
if len(self.teams) != 2:
|
|
||||||
ZoomText(best_txt,
|
|
||||||
position=(0, 175),
|
|
||||||
shiftposition=(-250, 175),
|
|
||||||
shiftdelay=2.5,
|
|
||||||
flash=False,
|
|
||||||
trail=False,
|
|
||||||
h_align='center',
|
|
||||||
scale=0.25,
|
|
||||||
color=(0.5, 0.5, 0.5, 1.0),
|
|
||||||
jitter=3.0).autoretain()
|
|
||||||
for team in self.session.sessionteams:
|
|
||||||
bs.timer(
|
|
||||||
i * 0.15 + 0.15,
|
|
||||||
bs.WeakCall(self._show_team_name, vval - i * height, team,
|
|
||||||
i * 0.2, shift_time - (i * 0.150 + 0.150)))
|
|
||||||
bs.timer(i * 0.150 + 0.5, self._score_display_sound_small.play)
|
|
||||||
scored = (team is self._winner)
|
|
||||||
delay = 0.2
|
|
||||||
if scored:
|
|
||||||
delay = 1.2
|
|
||||||
bs.timer(
|
|
||||||
i * 0.150 + 0.2,
|
|
||||||
bs.WeakCall(self._show_team_old_score, vval - i * height,
|
|
||||||
team, shift_time - (i * 0.15 + 0.2)))
|
|
||||||
bs.timer(i * 0.15 + 1.5, self._score_display_sound.play)
|
|
||||||
|
|
||||||
bs.timer(
|
|
||||||
i * 0.150 + delay,
|
|
||||||
bs.WeakCall(self._show_team_score, vval - i * height, team,
|
|
||||||
scored, i * 0.2 + 0.1,
|
|
||||||
shift_time - (i * 0.15 + delay)))
|
|
||||||
i += 1
|
|
||||||
self.show_player_scores()
|
|
||||||
|
|
||||||
def _show_team_name(self, pos_v: float, team: bs.SessionTeam,
|
|
||||||
kill_delay: float, shiftdelay: float) -> None:
|
|
||||||
del kill_delay # Unused arg.
|
|
||||||
if len(self.teams) != 2:
|
|
||||||
ZoomText(
|
|
||||||
babase.Lstr(value='${A}:', subs=[('${A}', team.name)]),
|
|
||||||
position=(100, pos_v),
|
|
||||||
shiftposition=(-150, pos_v),
|
|
||||||
shiftdelay=shiftdelay,
|
|
||||||
flash=False,
|
|
||||||
trail=False,
|
|
||||||
h_align='right',
|
|
||||||
maxwidth=300,
|
|
||||||
color=team.color,
|
|
||||||
jitter=1.0,
|
|
||||||
).autoretain()
|
|
||||||
else:
|
|
||||||
ZoomText(babase.Lstr(value='${A}', subs=[('${A}', team.name)]),
|
|
||||||
position=(-250, 260) if pos_v == 65 else (250, 260),
|
|
||||||
shiftposition=(-250, 260) if pos_v == 65 else (250, 260),
|
|
||||||
shiftdelay=shiftdelay,
|
|
||||||
flash=False,
|
|
||||||
trail=False,
|
|
||||||
h_align='center',
|
|
||||||
maxwidth=300,
|
|
||||||
scale=0.45,
|
|
||||||
color=team.color,
|
|
||||||
jitter=1.0).autoretain()
|
|
||||||
|
|
||||||
def _show_team_old_score(self, pos_v: float, sessionteam: bs.SessionTeam,
|
|
||||||
shiftdelay: float) -> None:
|
|
||||||
|
|
||||||
if len(self.teams) != 2:
|
|
||||||
ZoomText(
|
|
||||||
str(sessionteam.customdata['score'] - 1),
|
|
||||||
position=(150, pos_v),
|
|
||||||
maxwidth=100,
|
|
||||||
color=(0.6, 0.6, 0.7),
|
|
||||||
shiftposition=(-100, pos_v),
|
|
||||||
shiftdelay=shiftdelay,
|
|
||||||
flash=False,
|
|
||||||
trail=False,
|
|
||||||
lifespan=1.0,
|
|
||||||
h_align='left',
|
|
||||||
jitter=1.0,
|
|
||||||
).autoretain()
|
|
||||||
else:
|
|
||||||
ZoomText(str(sessionteam.customdata['score'] - 1),
|
|
||||||
position=(-250, 190) if pos_v == 65 else (250, 190),
|
|
||||||
maxwidth=100,
|
|
||||||
color=(0.6, 0.6, 0.7),
|
|
||||||
shiftposition=(-250, 190) if pos_v == 65 else (250, 190),
|
|
||||||
shiftdelay=shiftdelay,
|
|
||||||
flash=False,
|
|
||||||
trail=False,
|
|
||||||
lifespan=1.0,
|
|
||||||
scale=0.56,
|
|
||||||
h_align='center',
|
|
||||||
jitter=1.0).autoretain()
|
|
||||||
|
|
||||||
def _show_team_score(self, pos_v: float, sessionteam: bs.SessionTeam,
|
|
||||||
scored: bool, kill_delay: float,
|
|
||||||
shiftdelay: float) -> None:
|
|
||||||
del kill_delay # Unused arg.
|
|
||||||
if len(self.teams) != 2:
|
|
||||||
ZoomText(
|
|
||||||
str(sessionteam.customdata['score']),
|
|
||||||
position=(150, pos_v),
|
|
||||||
maxwidth=100,
|
|
||||||
color=(1.0, 0.9, 0.5) if scored else (0.6, 0.6, 0.7),
|
|
||||||
shiftposition=(-100, pos_v),
|
|
||||||
shiftdelay=shiftdelay,
|
|
||||||
flash=scored,
|
|
||||||
trail=scored,
|
|
||||||
h_align='left',
|
|
||||||
jitter=1.0,
|
|
||||||
trailcolor=(1, 0.8, 0.0, 0),
|
|
||||||
).autoretain()
|
|
||||||
else:
|
|
||||||
ZoomText(str(sessionteam.customdata['score']),
|
|
||||||
position=(-250, 190) if pos_v == 65 else (250, 190),
|
|
||||||
maxwidth=100,
|
|
||||||
color=(1.0, 0.9, 0.5) if scored else (0.6, 0.6, 0.7),
|
|
||||||
shiftposition=(-250, 190) if pos_v == 65 else (250, 190),
|
|
||||||
shiftdelay=shiftdelay,
|
|
||||||
flash=scored,
|
|
||||||
trail=scored,
|
|
||||||
scale=0.56,
|
|
||||||
h_align='center',
|
|
||||||
jitter=1.0,
|
|
||||||
trailcolor=(1, 0.8, 0.0, 0)).autoretain()
|
|
||||||
|
|
||||||
|
|
||||||
# ===================================================================================================
|
|
||||||
|
|
||||||
# score board
|
|
||||||
# ====================================================================================================
|
|
||||||
|
|
||||||
def show_player_scores(self,
|
|
||||||
delay: float = 2.5,
|
|
||||||
results: bs.GameResults | None = None,
|
|
||||||
scale: float = 1.0,
|
|
||||||
x_offset: float = 0.0,
|
|
||||||
y_offset: float = 0.0) -> None:
|
|
||||||
"""Show scores for individual players."""
|
|
||||||
# pylint: disable=too-many-locals
|
|
||||||
# pylint: disable=too-many-statements
|
|
||||||
|
|
||||||
ts_v_offset = 150.0 + y_offset
|
|
||||||
ts_h_offs = 80.0 + x_offset
|
|
||||||
tdelay = delay
|
|
||||||
spacing = 40
|
|
||||||
|
|
||||||
is_free_for_all = isinstance(self.session, bs.FreeForAllSession)
|
|
||||||
|
|
||||||
is_two_team = True if len(self.session.sessionteams) == 2 else False
|
|
||||||
|
|
||||||
def _get_prec_score(p_rec: bs.PlayerRecord) -> Optional[int]:
|
|
||||||
if is_free_for_all and results is not None:
|
|
||||||
assert isinstance(results, bs.GameResults)
|
|
||||||
assert p_rec.team.activityteam is not None
|
|
||||||
val = results.get_sessionteam_score(p_rec.team)
|
|
||||||
return val
|
|
||||||
return p_rec.accumscore
|
|
||||||
|
|
||||||
def _get_prec_score_str(p_rec: bs.PlayerRecord) -> Union[str, babase.Lstr]:
|
|
||||||
if is_free_for_all and results is not None:
|
|
||||||
assert isinstance(results, bs.GameResults)
|
|
||||||
assert p_rec.team.activityteam is not None
|
|
||||||
val = results.get_sessionteam_score_str(p_rec.team)
|
|
||||||
assert val is not None
|
|
||||||
return val
|
|
||||||
return str(p_rec.accumscore)
|
|
||||||
|
|
||||||
# stats.get_records() can return players that are no longer in
|
|
||||||
# the game.. if we're using results we have to filter those out
|
|
||||||
# (since they're not in results and that's where we pull their
|
|
||||||
# scores from)
|
|
||||||
if results is not None:
|
|
||||||
assert isinstance(results, bs.GameResults)
|
|
||||||
player_records = []
|
|
||||||
assert self.stats
|
|
||||||
valid_players = list(self.stats.get_records().items())
|
|
||||||
|
|
||||||
def _get_player_score_set_entry(
|
|
||||||
player: bs.SessionPlayer) -> Optional[bs.PlayerRecord]:
|
|
||||||
for p_rec in valid_players:
|
|
||||||
if p_rec[1].player is player:
|
|
||||||
return p_rec[1]
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Results is already sorted; just convert it into a list of
|
|
||||||
# score-set-entries.
|
|
||||||
for winnergroup in results.winnergroups:
|
|
||||||
for team in winnergroup.teams:
|
|
||||||
if len(team.players) == 1:
|
|
||||||
player_entry = _get_player_score_set_entry(
|
|
||||||
team.players[0])
|
|
||||||
if player_entry is not None:
|
|
||||||
player_records.append(player_entry)
|
|
||||||
else:
|
|
||||||
player_records = []
|
|
||||||
player_records_scores = [
|
|
||||||
(_get_prec_score(p), name, p)
|
|
||||||
for name, p in list(self.stats.get_records().items())
|
|
||||||
]
|
|
||||||
player_records_scores.sort(reverse=True)
|
|
||||||
|
|
||||||
# Just want living player entries.
|
|
||||||
player_records = [p[2] for p in player_records_scores if p[2]]
|
|
||||||
|
|
||||||
voffs = -140.0 + spacing * 5 * 0.5
|
|
||||||
|
|
||||||
voffs_team0 = voffs
|
|
||||||
tdelay_team0 = tdelay
|
|
||||||
|
|
||||||
def _txt(xoffs: float,
|
|
||||||
yoffs: float,
|
|
||||||
text: babase.Lstr,
|
|
||||||
h_align: Text.HAlign = Text.HAlign.RIGHT,
|
|
||||||
extrascale: float = 1.0,
|
|
||||||
maxwidth: Optional[float] = 120.0) -> None:
|
|
||||||
Text(text,
|
|
||||||
color=(0.5, 0.5, 0.6, 0.5),
|
|
||||||
position=(ts_h_offs + xoffs * scale,
|
|
||||||
ts_v_offset + (voffs + yoffs + 4.0) * scale),
|
|
||||||
h_align=h_align,
|
|
||||||
v_align=Text.VAlign.CENTER,
|
|
||||||
scale=0.8 * scale * extrascale,
|
|
||||||
maxwidth=maxwidth,
|
|
||||||
transition=Text.Transition.IN_LEFT,
|
|
||||||
transition_delay=tdelay).autoretain()
|
|
||||||
|
|
||||||
session = self.session
|
|
||||||
assert isinstance(session, bs.MultiTeamSession)
|
|
||||||
if is_two_team:
|
|
||||||
tval = "Game " + str(session.get_game_number()) + " Results"
|
|
||||||
_txt(-75,
|
|
||||||
160,
|
|
||||||
tval,
|
|
||||||
h_align=Text.HAlign.CENTER,
|
|
||||||
extrascale=1.4,
|
|
||||||
maxwidth=None)
|
|
||||||
else:
|
|
||||||
tval = babase.Lstr(
|
|
||||||
resource='gameLeadersText',
|
|
||||||
subs=[('${COUNT}', str(session.get_game_number()))],
|
|
||||||
)
|
|
||||||
_txt(
|
|
||||||
180,
|
|
||||||
43,
|
|
||||||
tval,
|
|
||||||
h_align=Text.HAlign.CENTER,
|
|
||||||
extrascale=1.4,
|
|
||||||
maxwidth=None,
|
|
||||||
)
|
|
||||||
_txt(-15, 4, babase.Lstr(resource='playerText'), h_align=Text.HAlign.LEFT)
|
|
||||||
_txt(180, 4, babase.Lstr(resource='killsText'))
|
|
||||||
_txt(280, 4, babase.Lstr(resource='deathsText'), maxwidth=100)
|
|
||||||
|
|
||||||
score_label = 'Score' if results is None else results.score_label
|
|
||||||
translated = babase.Lstr(translate=('scoreNames', score_label))
|
|
||||||
|
|
||||||
_txt(390, 0, translated)
|
|
||||||
|
|
||||||
if is_two_team:
|
|
||||||
_txt(-595, 4, babase.Lstr(resource='playerText'),
|
|
||||||
h_align=Text.HAlign.LEFT)
|
|
||||||
_txt(-400, 4, babase.Lstr(resource='killsText'))
|
|
||||||
_txt(-300, 4, babase.Lstr(resource='deathsText'), maxwidth=100)
|
|
||||||
_txt(-190, 0, translated)
|
|
||||||
|
|
||||||
topkillcount = 0
|
|
||||||
topkilledcount = 99999
|
|
||||||
top_score = 0 if not player_records else _get_prec_score(
|
|
||||||
player_records[0])
|
|
||||||
|
|
||||||
for prec in player_records:
|
|
||||||
topkillcount = max(topkillcount, prec.accum_kill_count)
|
|
||||||
topkilledcount = min(topkilledcount, prec.accum_killed_count)
|
|
||||||
|
|
||||||
def _scoretxt(text: Union[str, babase.Lstr],
|
|
||||||
x_offs: float,
|
|
||||||
highlight: bool,
|
|
||||||
delay2: float,
|
|
||||||
maxwidth: float = 70.0, team_id=1) -> None:
|
|
||||||
|
|
||||||
Text(text,
|
|
||||||
position=(ts_h_offs + x_offs * scale,
|
|
||||||
ts_v_offset + (
|
|
||||||
voffs + 15) * scale) if team_id == 1 else (
|
|
||||||
ts_h_offs + x_offs * scale,
|
|
||||||
ts_v_offset + (voffs_team0 + 15) * scale),
|
|
||||||
scale=scale,
|
|
||||||
color=(1.0, 0.9, 0.5, 1.0) if highlight else
|
|
||||||
(0.5, 0.5, 0.6, 0.5),
|
|
||||||
h_align=Text.HAlign.RIGHT,
|
|
||||||
v_align=Text.VAlign.CENTER,
|
|
||||||
maxwidth=maxwidth,
|
|
||||||
transition=Text.Transition.IN_LEFT,
|
|
||||||
transition_delay=(tdelay + delay2) if team_id == 1 else (
|
|
||||||
tdelay_team0 + delay2)).autoretain()
|
|
||||||
|
|
||||||
for playerrec in player_records:
|
|
||||||
if is_two_team and playerrec.team.id == 0:
|
|
||||||
tdelay_team0 += 0.05
|
|
||||||
voffs_team0 -= spacing
|
|
||||||
x_image = 617
|
|
||||||
x_text = -595
|
|
||||||
y = ts_v_offset + (voffs_team0 + 15.0) * scale
|
|
||||||
|
|
||||||
else:
|
|
||||||
tdelay += 0.05
|
|
||||||
voffs -= spacing
|
|
||||||
x_image = 12
|
|
||||||
x_text = 10.0
|
|
||||||
y = ts_v_offset + (voffs + 15.0) * scale
|
|
||||||
|
|
||||||
Image(playerrec.get_icon(),
|
|
||||||
position=(ts_h_offs - x_image * scale,
|
|
||||||
y),
|
|
||||||
scale=(30.0 * scale, 30.0 * scale),
|
|
||||||
transition=Image.Transition.IN_LEFT,
|
|
||||||
transition_delay=tdelay if playerrec.team.id == 1 else tdelay_team0).autoretain()
|
|
||||||
Text(babase.Lstr(value=playerrec.getname(full=True)),
|
|
||||||
maxwidth=160,
|
|
||||||
scale=0.75 * scale,
|
|
||||||
position=(ts_h_offs + x_text * scale,
|
|
||||||
y),
|
|
||||||
h_align=Text.HAlign.LEFT,
|
|
||||||
v_align=Text.VAlign.CENTER,
|
|
||||||
color=babase.safecolor(playerrec.team.color + (1,)),
|
|
||||||
transition=Text.Transition.IN_LEFT,
|
|
||||||
transition_delay=tdelay if playerrec.team.id == 1 else tdelay_team0).autoretain()
|
|
||||||
|
|
||||||
if is_two_team and playerrec.team.id == 0:
|
|
||||||
_scoretxt(str(playerrec.accum_kill_count), -400,
|
|
||||||
playerrec.accum_kill_count == topkillcount, 0.1,
|
|
||||||
team_id=0)
|
|
||||||
_scoretxt(str(playerrec.accum_killed_count), -300,
|
|
||||||
playerrec.accum_killed_count == topkilledcount, 0.1,
|
|
||||||
team_id=0)
|
|
||||||
_scoretxt(_get_prec_score_str(playerrec), -190,
|
|
||||||
_get_prec_score(playerrec) == top_score, 0.2, team_id=0)
|
|
||||||
else:
|
|
||||||
_scoretxt(str(playerrec.accum_kill_count), 180,
|
|
||||||
playerrec.accum_kill_count == topkillcount, 0.1)
|
|
||||||
_scoretxt(str(playerrec.accum_killed_count), 280,
|
|
||||||
playerrec.accum_killed_count == topkilledcount, 0.1)
|
|
||||||
_scoretxt(_get_prec_score_str(playerrec), 390,
|
|
||||||
_get_prec_score(playerrec) == top_score, 0.2)
|
|
||||||
|
|
||||||
|
|
||||||
# ======================== draw screen =============
|
|
||||||
class DrawScoreScreenActivity(MultiTeamScoreScreenActivity):
|
|
||||||
"""Score screen shown after a draw."""
|
|
||||||
|
|
||||||
default_music = None # Awkward silence...
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
babase.set_analytics_screen('Draw Score Screen')
|
|
||||||
super().on_begin()
|
|
||||||
ZoomText(babase.Lstr(resource='drawText'),
|
|
||||||
position=(0, 200),
|
|
||||||
maxwidth=400,
|
|
||||||
shiftposition=(0, 200),
|
|
||||||
shiftdelay=2.0,
|
|
||||||
flash=False,
|
|
||||||
scale=0.7,
|
|
||||||
trail=False,
|
|
||||||
jitter=1.0).autoretain()
|
|
||||||
bs.timer(0.35, self._score_display_sound.play)
|
|
||||||
self.show_player_scores(results=self.settings_raw.get('results', None))
|
|
||||||
163
dist/ba_root/mods/features/fire_flies.py
vendored
163
dist/ba_root/mods/features/fire_flies.py
vendored
|
|
@ -1,163 +0,0 @@
|
||||||
import random
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1._messages import DeathType, OutOfBoundsMessage
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
on_begin_original = bs._activity.Activity.on_begin
|
|
||||||
|
|
||||||
|
|
||||||
def fireflies_generator(activity, count, random_color: False):
|
|
||||||
if random_color:
|
|
||||||
color = (random.uniform(0, 1.2), random.uniform(
|
|
||||||
0, 1.2), random.uniform(0, 1.2))
|
|
||||||
else:
|
|
||||||
color = (0.9, 0.7, 0.0)
|
|
||||||
increment = count - len(activity.fireflies)
|
|
||||||
|
|
||||||
if increment > 0:
|
|
||||||
spawn_areas = _calculate_spawn_areas()
|
|
||||||
if not spawn_areas:
|
|
||||||
return
|
|
||||||
for _ in range(increment):
|
|
||||||
with activity.context:
|
|
||||||
firefly = FireFly(random.choice(spawn_areas), color)
|
|
||||||
activity.fireflies.append(firefly)
|
|
||||||
else:
|
|
||||||
for _ in range(abs(increment)):
|
|
||||||
firefly = activity.fireflies.pop()
|
|
||||||
try:
|
|
||||||
firefly.handlemessage(bs.DieMessage())
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
firefly.timer = None
|
|
||||||
|
|
||||||
|
|
||||||
def _calculate_spawn_areas():
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
if not isinstance(activity, bs.GameActivity):
|
|
||||||
return
|
|
||||||
aoi_bounds = activity.map.get_def_bound_box("area_of_interest_bounds")
|
|
||||||
# aoi_bounds = activity.map.get_def_bound_box("map_bounds")
|
|
||||||
first_half = list(aoi_bounds)
|
|
||||||
second_half = list(aoi_bounds)
|
|
||||||
midpoint_x = (aoi_bounds[0] + aoi_bounds[3]) / 2
|
|
||||||
first_half[3] = midpoint_x
|
|
||||||
second_half[0] = midpoint_x
|
|
||||||
spawn_areas = (first_half, second_half)
|
|
||||||
return spawn_areas
|
|
||||||
|
|
||||||
|
|
||||||
class FireFly(bs.Actor):
|
|
||||||
def __init__(self, area, color, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.area = area
|
|
||||||
self.color = color
|
|
||||||
initial_timer = random.uniform(0.5, 6)
|
|
||||||
self.timer = bs.Timer(initial_timer, self.on)
|
|
||||||
|
|
||||||
def on(self):
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self.mat = bs.Material()
|
|
||||||
self.mat.add_actions(
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', False),
|
|
||||||
('modify_part_collision', 'physical', False),
|
|
||||||
))
|
|
||||||
self.node = bs.newnode('locator',
|
|
||||||
attrs={'shape': 'circle', 'position': (0, .5, 0),
|
|
||||||
'color': self.color, 'opacity': 0.5,
|
|
||||||
'draw_beauty': True, 'additive': False,
|
|
||||||
'size': [0.10]})
|
|
||||||
# bs.animate(
|
|
||||||
# self.node,
|
|
||||||
# 'scale',
|
|
||||||
# {0:0, 1:0.004, 5:0.006, 10:0.0},
|
|
||||||
# loop=True,
|
|
||||||
# )
|
|
||||||
bs.animate_array(
|
|
||||||
self.node,
|
|
||||||
'position',
|
|
||||||
3,
|
|
||||||
self.generate_keys(self.area),
|
|
||||||
loop=True
|
|
||||||
)
|
|
||||||
|
|
||||||
self.light = bs.newnode(
|
|
||||||
'light',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'intensity': 0.6,
|
|
||||||
'height_attenuated': True,
|
|
||||||
'radius': 0.2,
|
|
||||||
'color': self.color
|
|
||||||
})
|
|
||||||
bs.animate(
|
|
||||||
self.light,
|
|
||||||
'radius',
|
|
||||||
{0: 0.0, 20: 0.4, 70: 0.1, 100: 0.3, 150: 0},
|
|
||||||
loop=True
|
|
||||||
)
|
|
||||||
self.node.connectattr('position', self.light, 'position')
|
|
||||||
|
|
||||||
def off(self):
|
|
||||||
death_secs = random.uniform(0.5, 3)
|
|
||||||
with babase.Context(self._activity()):
|
|
||||||
bs.animate(
|
|
||||||
self.node,
|
|
||||||
'mesh_scale',
|
|
||||||
{0: self.node.mesh_scale, death_secs: 0}
|
|
||||||
)
|
|
||||||
bs.animate(
|
|
||||||
self.light,
|
|
||||||
'radius',
|
|
||||||
{0: self.light.radius, death_secs: 0}
|
|
||||||
)
|
|
||||||
bs.timer(death_secs, self.node.delete)
|
|
||||||
|
|
||||||
def handlemessage(self, msg):
|
|
||||||
if isinstance(msg, bs.DieMessage):
|
|
||||||
self.off()
|
|
||||||
return None
|
|
||||||
elif isinstance(msg, OutOfBoundsMessage):
|
|
||||||
return self.handlemessage(
|
|
||||||
bs.DieMessage(how=DeathType.OUT_OF_BOUNDS))
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
|
|
||||||
def generate_keys(self, m):
|
|
||||||
keys = {}
|
|
||||||
t = 0
|
|
||||||
last_x = random.randrange(int(m[0]), int(m[3]))
|
|
||||||
last_y = random.randrange(int(m[1]), int(m[4]))
|
|
||||||
if int(m[2]) == int(m[5]):
|
|
||||||
last_z = int(m[2])
|
|
||||||
else:
|
|
||||||
last_z = random.randrange(int(m[2]), int(m[5]))
|
|
||||||
for i in range(0, 7):
|
|
||||||
x = self.generate_random(int(m[0]), int(m[3]), last_x)
|
|
||||||
last_x = x
|
|
||||||
y = self.generate_random(int(m[1]), int(m[4]), last_y)
|
|
||||||
last_y = y
|
|
||||||
z = self.generate_random(int(m[2]), int(m[5]), last_z)
|
|
||||||
last_z = z
|
|
||||||
keys[t] = (x, abs(y), z)
|
|
||||||
t += 30
|
|
||||||
return keys
|
|
||||||
|
|
||||||
def generate_random(self, a, b, z):
|
|
||||||
if a == b:
|
|
||||||
return a
|
|
||||||
while True:
|
|
||||||
n = random.randrange(a, b)
|
|
||||||
if abs(z - n) < 6:
|
|
||||||
return n
|
|
||||||
|
|
||||||
|
|
||||||
def on_begin(self, *args, **kwargs) -> None:
|
|
||||||
self.fireflies = []
|
|
||||||
return on_begin_original(self, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
bs._activity.Activity.fireflies_generator = fireflies_generator
|
|
||||||
bs._activity.Activity.on_begin = on_begin
|
|
||||||
146
dist/ba_root/mods/features/hearts.py
vendored
146
dist/ba_root/mods/features/hearts.py
vendored
|
|
@ -1,146 +0,0 @@
|
||||||
import random
|
|
||||||
|
|
||||||
from typing import Any, Sequence
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
|
|
||||||
class PopupText(bs.Actor):
|
|
||||||
"""Text that pops up above a position to denote something special.
|
|
||||||
category: Gameplay Classes
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
text: str | babase.Lstr,
|
|
||||||
position: Sequence[float] = (0.0, 0.0, 0.0),
|
|
||||||
color: Sequence[float] = (1.0, 1.0, 1.0, 1.0),
|
|
||||||
random_offset: float = 0.5,
|
|
||||||
offset: Sequence[float] = (0.0, 0.0, 0.0),
|
|
||||||
scale: float = 1.0,
|
|
||||||
):
|
|
||||||
"""Instantiate with given values.
|
|
||||||
random_offset is the amount of random offset from the provided position
|
|
||||||
that will be applied. This can help multiple achievements from
|
|
||||||
overlapping too much.
|
|
||||||
"""
|
|
||||||
super().__init__()
|
|
||||||
if len(color) == 3:
|
|
||||||
color = (color[0], color[1], color[2], 1.0)
|
|
||||||
pos = (
|
|
||||||
position[0] + offset[0] + random_offset * (0.5 - random.random()),
|
|
||||||
position[1] + offset[1] + random_offset * (0.5 - random.random()),
|
|
||||||
position[2] + offset[2] + random_offset * (0.5 - random.random()),
|
|
||||||
)
|
|
||||||
|
|
||||||
self.node = bs.newnode(
|
|
||||||
'text',
|
|
||||||
attrs={
|
|
||||||
'text': text,
|
|
||||||
'in_world': True,
|
|
||||||
'shadow': 1.0,
|
|
||||||
'flatness': 1.0,
|
|
||||||
'h_align': 'center',
|
|
||||||
},
|
|
||||||
delegate=self,
|
|
||||||
)
|
|
||||||
|
|
||||||
lifespan = 10.5
|
|
||||||
|
|
||||||
# scale up
|
|
||||||
bs.animate(
|
|
||||||
self.node,
|
|
||||||
'scale',
|
|
||||||
{
|
|
||||||
0: 0.0,
|
|
||||||
lifespan * 0.11: 0.020 * 0.7 * scale,
|
|
||||||
lifespan * 0.16: 0.013 * 0.7 * scale,
|
|
||||||
lifespan * 0.25: 0.016 * 0.7 * scale,
|
|
||||||
lifespan * 0.45: 0.012 * 0.7 * scale,
|
|
||||||
lifespan * 0.65: 0.014 * 0.7 * scale,
|
|
||||||
lifespan * 0.75: 0.011 * 0.7 * scale,
|
|
||||||
lifespan * 0.85: 0.015 * 0.7 * scale,
|
|
||||||
lifespan * 0.90: 0.012 * 0.7 * scale,
|
|
||||||
lifespan * 0.95: 0.016 * 0.7 * scale,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# translate upward
|
|
||||||
self._tcombine = bs.newnode(
|
|
||||||
'combine',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={'input0': pos[0], 'input2': pos[2], 'size': 3},
|
|
||||||
)
|
|
||||||
bs.animate(
|
|
||||||
self._tcombine, 'input1', {0: pos[1] + 0, lifespan: pos[1] + 8.0}
|
|
||||||
)
|
|
||||||
self._tcombine.connectattr('output', self.node, 'position')
|
|
||||||
|
|
||||||
# fade our opacity in/out
|
|
||||||
self._combine = bs.newnode(
|
|
||||||
'combine',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'input0': color[0],
|
|
||||||
'input1': color[1],
|
|
||||||
'input2': color[2],
|
|
||||||
'size': 4,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
for i in range(4):
|
|
||||||
bs.animate(
|
|
||||||
self._combine,
|
|
||||||
'input' + str(i),
|
|
||||||
{
|
|
||||||
0.13 * lifespan: color[i],
|
|
||||||
0.18 * lifespan: 4.0 * color[i],
|
|
||||||
0.22 * lifespan: color[i],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
bs.animate(
|
|
||||||
self._combine,
|
|
||||||
'input3',
|
|
||||||
{
|
|
||||||
0: 0,
|
|
||||||
0.1 * lifespan: color[3],
|
|
||||||
0.7 * lifespan: color[3],
|
|
||||||
lifespan: 0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self._combine.connectattr('output', self.node, 'color')
|
|
||||||
|
|
||||||
# kill ourself
|
|
||||||
self._die_timer = bs.Timer(
|
|
||||||
lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage())
|
|
||||||
)
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
assert not self.expired
|
|
||||||
if isinstance(msg, bs.DieMessage):
|
|
||||||
if self.node:
|
|
||||||
self.node.delete()
|
|
||||||
else:
|
|
||||||
super().handlemessage(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def spawn_heart():
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
if not hasattr(activity, "heart"):
|
|
||||||
activity.heart = []
|
|
||||||
if hasattr(activity, "map"):
|
|
||||||
bounds = activity.map.get_def_bound_box("area_of_interest_bounds")
|
|
||||||
for i in range(0, 4):
|
|
||||||
position = (random.uniform(bounds[0], bounds[3]), random.uniform(
|
|
||||||
bounds[4] * 1.15, bounds[4] * 1.45) - 8,
|
|
||||||
random.uniform(bounds[2], bounds[5]))
|
|
||||||
with activity.context:
|
|
||||||
k = PopupText(u"\ue047", position)
|
|
||||||
activity.heart.append(k)
|
|
||||||
|
|
||||||
|
|
||||||
def start(activity):
|
|
||||||
bs.timer(random.uniform(7, 8), spawn_heart, repeat=True)
|
|
||||||
|
|
||||||
|
|
||||||
bs._activity.Activity.hearts_generator = start
|
|
||||||
20
dist/ba_root/mods/features/map_fun.py
vendored
20
dist/ba_root/mods/features/map_fun.py
vendored
|
|
@ -1,20 +0,0 @@
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
|
|
||||||
def decorate_map():
|
|
||||||
try:
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
activity.fireflies_generator(20, True)
|
|
||||||
activity.hearts_generator()
|
|
||||||
activity.map.node.reflection = "powerup"
|
|
||||||
activity.map.node.reflection_scale = [4]
|
|
||||||
activity.globalsnode.tint = (0.5, 0.7, 1)
|
|
||||||
# activity.map.node.color = random.choices([(0.8,0.3,0.3),(0.6,0.5,0.7),(0.3,0.8,0.5)])[0]
|
|
||||||
m = 5
|
|
||||||
s = 5000
|
|
||||||
bs.animate_array(activity.globalsnode, 'ambient_color', 3, {0: (
|
|
||||||
1 * m, 0, 0), s: (0, 1 * m, 0), s * 2: (0, 0, 1 * m),
|
|
||||||
s * 3: (1 * m, 0, 0)}, True)
|
|
||||||
activity.map.background.reflection = "soft"
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
61
dist/ba_root/mods/features/profanity.py
vendored
61
dist/ba_root/mods/features/profanity.py
vendored
|
|
@ -1,61 +0,0 @@
|
||||||
# ported from ankit scripts
|
|
||||||
# need to update in future with easy to add custom list and more deep analysis .
|
|
||||||
# working on other features rn, will update this later , for now lets use this
|
|
||||||
import re
|
|
||||||
|
|
||||||
PATTERN = (
|
|
||||||
r"fu+c+k|"
|
|
||||||
r"fu+c+($|)|"
|
|
||||||
r"fu+k+($|)|"
|
|
||||||
r"\w*ph+u*c+k\w*\b|"
|
|
||||||
r"\b\w+ch+o+d|"
|
|
||||||
r"randi+|"
|
|
||||||
r"chu+t\w*\b|"
|
|
||||||
r"chh+a+k+[ae]|"
|
|
||||||
r"hijd\w|"
|
|
||||||
r"lund\b|"
|
|
||||||
r"\bass\b|"
|
|
||||||
r"asshole|"
|
|
||||||
r"bi*tch|"
|
|
||||||
r"cock|"
|
|
||||||
r"\bga+nd\b|"
|
|
||||||
r"ga+ndu|"
|
|
||||||
r"tharki|"
|
|
||||||
r"tatti|"
|
|
||||||
r"lod\w\b|"
|
|
||||||
r"jha+nt|"
|
|
||||||
r"pu+s+y|"
|
|
||||||
r"pu+z+y|"
|
|
||||||
r"di+c+k|"
|
|
||||||
r"\b([mb]+c+)+\b|"
|
|
||||||
r"\b[mb]+[^a-zA-Z]?c+\b|"
|
|
||||||
r"f.u.c.k|"
|
|
||||||
r"b\w*s\w?d\w?k|"
|
|
||||||
r"m.{0,4}d.?a.{0,8}c.?h.?o.?d|"
|
|
||||||
r"b.+n.?c.?h.?o.?d|"
|
|
||||||
r"cunt|"
|
|
||||||
r"my+r+e|"
|
|
||||||
r"th+y+r|"
|
|
||||||
r"th+y+i+r|"
|
|
||||||
r"th+aa+y+o+l+i|"
|
|
||||||
r"th+a+y+o+l+i|"
|
|
||||||
r"ku+nn+a+n|"
|
|
||||||
r"na+y+i+n+t+e|"
|
|
||||||
r"pu+ll+u|"
|
|
||||||
r"la+(u|v)+d+\w\b|"
|
|
||||||
r"chu+d\w*\b|"
|
|
||||||
"sex+($|)|"
|
|
||||||
r"bo+b(s|z)|"
|
|
||||||
r"po+r+n|"
|
|
||||||
r"ni+p+le+"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def censor(message):
|
|
||||||
censored_message = re.sub(
|
|
||||||
PATTERN,
|
|
||||||
lambda match: "*" * len(match.group()),
|
|
||||||
message,
|
|
||||||
flags=re.IGNORECASE
|
|
||||||
)
|
|
||||||
return censored_message
|
|
||||||
85
dist/ba_root/mods/features/team_balancer.py
vendored
85
dist/ba_root/mods/features/team_balancer.py
vendored
|
|
@ -1,85 +0,0 @@
|
||||||
import setting
|
|
||||||
from serverData import serverdata
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1._coopsession import CoopSession
|
|
||||||
from bascenev1._dualteamsession import DualTeamSession
|
|
||||||
from tools import playlist
|
|
||||||
|
|
||||||
settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
def balanceTeams():
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
if settings["coopModeWithLessPlayers"]["enable"] and len(
|
|
||||||
session.sessionplayers) < settings["coopModeWithLessPlayers"][
|
|
||||||
"minPlayerToExitCoop"]:
|
|
||||||
playlist.setPlaylist('coop')
|
|
||||||
return
|
|
||||||
if not isinstance(session, DualTeamSession) or len(
|
|
||||||
session.sessionplayers) < 4 or len(session.sessionteams) != 2:
|
|
||||||
return
|
|
||||||
teamASize = 0
|
|
||||||
teamBSize = 0
|
|
||||||
try:
|
|
||||||
for player in session.sessionplayers:
|
|
||||||
if player.sessionteam.id == 0:
|
|
||||||
teamASize += 1
|
|
||||||
else:
|
|
||||||
teamBSize += 1
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if settings["autoTeamBalance"]:
|
|
||||||
if abs(teamBSize - teamASize) >= 0:
|
|
||||||
if teamBSize > teamASize and teamBSize != 0:
|
|
||||||
movePlayers(1, 0, abs(teamBSize - teamASize) - 1)
|
|
||||||
elif teamASize > teamBSize and teamASize != 0:
|
|
||||||
movePlayers(0, 1, abs(teamBSize - teamASize) - 1)
|
|
||||||
|
|
||||||
|
|
||||||
def movePlayers(fromTeam, toTeam, count):
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
fromTeam = session.sessionteams[fromTeam]
|
|
||||||
toTeam = session.sessionteams[toTeam]
|
|
||||||
for i in range(0, count):
|
|
||||||
player = fromTeam.players.pop()
|
|
||||||
broadCastShiftMsg(player.get_v1_account_id())
|
|
||||||
player.setdata(team=toTeam, character=player.character,
|
|
||||||
color=toTeam.color, highlight=player.highlight)
|
|
||||||
iconinfo = player.get_icon_info()
|
|
||||||
player.set_icon_info(
|
|
||||||
iconinfo['texture'], iconinfo['tint_texture'], toTeam.color,
|
|
||||||
player.highlight)
|
|
||||||
toTeam.players.append(player)
|
|
||||||
player.sessionteam.activityteam.players.append(player.activityplayer)
|
|
||||||
|
|
||||||
|
|
||||||
def broadCastShiftMsg(pb_id):
|
|
||||||
for ros in bs.get_game_roster():
|
|
||||||
if ros['account_id'] == pb_id:
|
|
||||||
bs.broadcastmessage(
|
|
||||||
"Shifted " + ros["display_string"] + " to balance team")
|
|
||||||
|
|
||||||
|
|
||||||
def on_player_join():
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
if len(session.sessionplayers) > 1:
|
|
||||||
return
|
|
||||||
if isinstance(session, DualTeamSession):
|
|
||||||
if settings["coopModeWithLessPlayers"]["enable"] and len(
|
|
||||||
session.sessionplayers) < settings["coopModeWithLessPlayers"][
|
|
||||||
"minPlayerToExitCoop"]:
|
|
||||||
playlist.setPlaylist('coop')
|
|
||||||
|
|
||||||
# this not usefull now ., leave it here for now
|
|
||||||
elif isinstance(session, CoopSession):
|
|
||||||
if len(session.sessionplayers) >= settings["coopModeWithLessPlayers"][
|
|
||||||
"minPlayerToExitCoop"]:
|
|
||||||
playlist.setPlaylist('default')
|
|
||||||
|
|
||||||
|
|
||||||
def checkToExitCoop():
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
if len(session.sessionplayers) >= settings["coopModeWithLessPlayers"][
|
|
||||||
"minPlayerToExitCoop"] and not serverdata.coopmode:
|
|
||||||
playlist.setPlaylist('default')
|
|
||||||
175
dist/ba_root/mods/features/text_on_map.py
vendored
175
dist/ba_root/mods/features/text_on_map.py
vendored
|
|
@ -1,175 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
|
|
||||||
""" TODO need to set coordinates of text node , move timer values to settings.json """
|
|
||||||
|
|
||||||
import random
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
import setting
|
|
||||||
from stats import mystats
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
setti = setting.get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
class textonmap:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
data = setti['textonmap']
|
|
||||||
left = data['bottom left watermark']
|
|
||||||
top = data['top watermark']
|
|
||||||
nextMap = ""
|
|
||||||
try:
|
|
||||||
nextMap = bs.get_foreground_host_session().get_next_game_description().evaluate()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
top = top.replace("@IP", _babase.our_ip).replace("@PORT",
|
|
||||||
str(_babase.our_port))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
self.index = 0
|
|
||||||
self.highlights = data['center highlights']["msg"]
|
|
||||||
self.left_watermark(left)
|
|
||||||
self.top_message(top)
|
|
||||||
self.nextGame(nextMap)
|
|
||||||
self.restart_msg()
|
|
||||||
if hasattr(_babase, "season_ends_in_days"):
|
|
||||||
if _babase.season_ends_in_days < 9:
|
|
||||||
self.season_reset(_babase.season_ends_in_days)
|
|
||||||
if setti["leaderboard"]["enable"]:
|
|
||||||
self.leaderBoard()
|
|
||||||
self.timer = bs.timer(8, babase.Call(self.highlights_), repeat=True)
|
|
||||||
|
|
||||||
def highlights_(self):
|
|
||||||
if setti["textonmap"]['center highlights']["randomColor"]:
|
|
||||||
color = ((0 + random.random() * 1.0), (0 + random.random() * 1.0),
|
|
||||||
(0 + random.random() * 1.0))
|
|
||||||
else:
|
|
||||||
color = tuple(setti["textonmap"]["center highlights"]["color"])
|
|
||||||
node = bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'text': self.highlights[self.index],
|
|
||||||
'flatness': 1.0,
|
|
||||||
'h_align': 'center',
|
|
||||||
'v_attach': 'bottom',
|
|
||||||
'scale': 1,
|
|
||||||
'position': (0, 138),
|
|
||||||
'color': color
|
|
||||||
})
|
|
||||||
|
|
||||||
self.delt = bs.timer(7, node.delete)
|
|
||||||
self.index = int((self.index + 1) % len(self.highlights))
|
|
||||||
|
|
||||||
def left_watermark(self, text):
|
|
||||||
node = bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'text': text,
|
|
||||||
'flatness': 1.0,
|
|
||||||
'h_align': 'left',
|
|
||||||
'v_attach': 'bottom',
|
|
||||||
'h_attach': 'left',
|
|
||||||
'scale': 0.7,
|
|
||||||
'position': (25, 67),
|
|
||||||
'color': (0.7, 0.7, 0.7)
|
|
||||||
})
|
|
||||||
|
|
||||||
def nextGame(self, text):
|
|
||||||
node = bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'text': "Next : " + text,
|
|
||||||
'flatness': 1.0,
|
|
||||||
'h_align': 'right',
|
|
||||||
'v_attach': 'bottom',
|
|
||||||
'h_attach': 'right',
|
|
||||||
'scale': 0.7,
|
|
||||||
'position': (-25, 16),
|
|
||||||
'color': (0.5, 0.5, 0.5)
|
|
||||||
})
|
|
||||||
|
|
||||||
def season_reset(self, text):
|
|
||||||
node = bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'text': "Season ends in: " + str(text) + " days",
|
|
||||||
'flatness': 1.0,
|
|
||||||
'h_align': 'right',
|
|
||||||
'v_attach': 'bottom',
|
|
||||||
'h_attach': 'right',
|
|
||||||
'scale': 0.5,
|
|
||||||
'position': (-25, 34),
|
|
||||||
'color': (0.6, 0.5, 0.7)
|
|
||||||
})
|
|
||||||
|
|
||||||
def restart_msg(self):
|
|
||||||
if hasattr(_babase, 'restart_scheduled'):
|
|
||||||
_babase.get_foreground_host_activity().restart_msg = bs.newnode(
|
|
||||||
'text',
|
|
||||||
attrs={
|
|
||||||
'text': "Server going to restart after this series.",
|
|
||||||
'flatness': 1.0,
|
|
||||||
'h_align': 'right',
|
|
||||||
'v_attach': 'bottom',
|
|
||||||
'h_attach': 'right',
|
|
||||||
'scale': 0.5,
|
|
||||||
'position': (-25, 54),
|
|
||||||
'color': (1, 0.5, 0.7)
|
|
||||||
})
|
|
||||||
|
|
||||||
def top_message(self, text):
|
|
||||||
node = bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'text': text,
|
|
||||||
'flatness': 1.0,
|
|
||||||
'h_align': 'center',
|
|
||||||
'v_attach': 'top',
|
|
||||||
'scale': 0.7,
|
|
||||||
'position': (0, -70),
|
|
||||||
'color': (1, 1, 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
def leaderBoard(self):
|
|
||||||
if len(mystats.top3Name) > 2:
|
|
||||||
if setti["leaderboard"]["barsBehindName"]:
|
|
||||||
self.ss1 = bs.newnode('image', attrs={'scale': (300, 30),
|
|
||||||
'texture': bs.gettexture(
|
|
||||||
'bar'),
|
|
||||||
'position': (0, -80),
|
|
||||||
'attach': 'topRight',
|
|
||||||
'opacity': 0.5,
|
|
||||||
'color': (0.7, 0.1, 0)})
|
|
||||||
self.ss1 = bs.newnode('image', attrs={'scale': (300, 30),
|
|
||||||
'texture': bs.gettexture(
|
|
||||||
'bar'),
|
|
||||||
'position': (0, -115),
|
|
||||||
'attach': 'topRight',
|
|
||||||
'opacity': 0.5,
|
|
||||||
'color': (0.6, 0.6, 0.6)})
|
|
||||||
self.ss1 = bs.newnode('image', attrs={'scale': (300, 30),
|
|
||||||
'texture': bs.gettexture(
|
|
||||||
'bar'),
|
|
||||||
'position': (0, -150),
|
|
||||||
'attach': 'topRight',
|
|
||||||
'opacity': 0.5,
|
|
||||||
'color': (0.1, 0.3, 0.1)})
|
|
||||||
|
|
||||||
self.ss1a = bs.newnode('text', attrs={
|
|
||||||
'text': "#1 " + mystats.top3Name[0][:10] + "...",
|
|
||||||
'flatness': 1.0, 'h_align': 'left', 'h_attach': 'right',
|
|
||||||
'v_attach': 'top', 'v_align': 'center', 'position': (-140, -80),
|
|
||||||
'scale': 0.7, 'color': (0.7, 0.4, 0.3)})
|
|
||||||
|
|
||||||
self.ss1a = bs.newnode('text', attrs={
|
|
||||||
'text': "#2 " + mystats.top3Name[1][:10] + "...",
|
|
||||||
'flatness': 1.0, 'h_align': 'left', 'h_attach': 'right',
|
|
||||||
'v_attach': 'top', 'v_align': 'center',
|
|
||||||
'position': (-140, -115), 'scale': 0.7,
|
|
||||||
'color': (0.8, 0.8, 0.8)})
|
|
||||||
|
|
||||||
self.ss1a = bs.newnode('text', attrs={
|
|
||||||
'text': "#3 " + mystats.top3Name[2][:10] + "...",
|
|
||||||
'flatness': 1.0, 'h_align': 'left', 'h_attach': 'right',
|
|
||||||
'v_attach': 'top', 'v_align': 'center',
|
|
||||||
'position': (-140, -150), 'scale': 0.7,
|
|
||||||
'color': (0.2, 0.6, 0.2)})
|
|
||||||
145
dist/ba_root/mods/features/votingmachine.py
vendored
145
dist/ba_root/mods/features/votingmachine.py
vendored
|
|
@ -1,145 +0,0 @@
|
||||||
# Electronic Voting Machine (EVM) by -mr.smoothy
|
|
||||||
|
|
||||||
import time
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
game_started_on = 0
|
|
||||||
|
|
||||||
vote_machine = {"end": {"last_vote_start_time": 0, "vote_duration": 50,
|
|
||||||
"min_game_duration_to_start_vote": 30, "voters": []},
|
|
||||||
"sm": {"last_vote_start_time": 0, "vote_duration": 50,
|
|
||||||
"min_game_duration_to_start_vote": 1, "voters": []},
|
|
||||||
"nv": {"last_vote_start_time": 0, "vote_duration": 50,
|
|
||||||
"min_game_duration_to_start_vote": 1, "voters": []},
|
|
||||||
"dv": {"last_vote_start_time": 0, "vote_duration": 50,
|
|
||||||
"min_game_duration_to_start_vote": 1, "voters": []}}
|
|
||||||
|
|
||||||
|
|
||||||
def vote(pb_id, client_id, vote_type):
|
|
||||||
global vote_machine
|
|
||||||
voters = vote_machine[vote_type]["voters"]
|
|
||||||
last_vote_start_time = vote_machine[vote_type]["last_vote_start_time"]
|
|
||||||
vote_duration = vote_machine[vote_type]["vote_duration"]
|
|
||||||
min_game_duration_to_start_vote = vote_machine[vote_type][
|
|
||||||
"min_game_duration_to_start_vote"]
|
|
||||||
|
|
||||||
now = time.time()
|
|
||||||
if now > last_vote_start_time + vote_duration:
|
|
||||||
voters = []
|
|
||||||
vote_machine[vote_type]["last_vote_start_time"] = now
|
|
||||||
if now < game_started_on + min_game_duration_to_start_vote:
|
|
||||||
bs.broadcastmessage(
|
|
||||||
"Seems game just started, Try again after some time",
|
|
||||||
transient=True,
|
|
||||||
clients=[client_id])
|
|
||||||
return
|
|
||||||
if len(voters) == 0:
|
|
||||||
bs.chatmessage(f"{vote_type} vote started")
|
|
||||||
|
|
||||||
# clean up voters list
|
|
||||||
active_players = []
|
|
||||||
for player in bs.get_game_roster():
|
|
||||||
active_players.append(player['account_id'])
|
|
||||||
for voter in voters:
|
|
||||||
if voter not in active_players:
|
|
||||||
voters.remove(voter)
|
|
||||||
if pb_id not in voters:
|
|
||||||
voters.append(pb_id)
|
|
||||||
bs.broadcastmessage(
|
|
||||||
f'Thanks for vote , encourage other players to type {vote_type} too.',
|
|
||||||
transient=True,
|
|
||||||
clients=[client_id])
|
|
||||||
if vote_type == 'end':
|
|
||||||
update_vote_text(max_votes_required(
|
|
||||||
len(active_players)) - len(voters))
|
|
||||||
else:
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
if activity is not None:
|
|
||||||
with _babase.Context(activity):
|
|
||||||
bs.broadcastmessage(
|
|
||||||
f"{max_votes_required(len(active_players)) - len(voters)} votes required for {vote_type}",
|
|
||||||
image={"texture": bs.gettexture(
|
|
||||||
"achievementSharingIsCaring"),
|
|
||||||
"tint_texture": bs.gettexture(
|
|
||||||
"achievementSharingIsCaring"),
|
|
||||||
"tint_color": (0.5, 0.5, 0.5),
|
|
||||||
"tint2_color": (0.7, 0.5, 0.9)},
|
|
||||||
top=True)
|
|
||||||
vote_machine[vote_type]["voters"] = voters
|
|
||||||
|
|
||||||
if len(voters) >= max_votes_required(len(active_players)):
|
|
||||||
bs.chatmessage(f"{vote_type} vote succeed")
|
|
||||||
vote_machine[vote_type]["voters"] = []
|
|
||||||
if vote_type == "end":
|
|
||||||
try:
|
|
||||||
with _babase.Context(bs.get_foreground_host_activity()):
|
|
||||||
bs.get_foreground_host_activity().end_game()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif vote_type == "nv":
|
|
||||||
_babase.chatmessage("/nv")
|
|
||||||
elif vote_type == "dv":
|
|
||||||
_babase.chatmessage("/dv")
|
|
||||||
elif vote_type == "sm":
|
|
||||||
_babase.chatmessage("/sm")
|
|
||||||
|
|
||||||
|
|
||||||
def reset_votes():
|
|
||||||
global vote_machine
|
|
||||||
for value in vote_machine.values():
|
|
||||||
value["voters"] = []
|
|
||||||
|
|
||||||
|
|
||||||
def max_votes_required(players):
|
|
||||||
if players == 2:
|
|
||||||
return 1
|
|
||||||
elif players == 3:
|
|
||||||
return 2
|
|
||||||
elif players == 4:
|
|
||||||
return 2
|
|
||||||
elif players == 5:
|
|
||||||
return 3
|
|
||||||
elif players == 6:
|
|
||||||
return 3
|
|
||||||
elif players == 7:
|
|
||||||
return 4
|
|
||||||
elif players == 8:
|
|
||||||
return 4
|
|
||||||
elif players == 10:
|
|
||||||
return 5
|
|
||||||
else:
|
|
||||||
return players - 5
|
|
||||||
|
|
||||||
|
|
||||||
def update_vote_text(votes_needed):
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
try:
|
|
||||||
activity.end_vote_text.node.text = "{} more votes to end this map\ntype 'end' to vote".format(
|
|
||||||
votes_needed)
|
|
||||||
except:
|
|
||||||
with _babase.Context(bs.get_foreground_host_activity()):
|
|
||||||
node = bs.NodeActor(bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'v_attach': 'top',
|
|
||||||
'h_attach': 'center',
|
|
||||||
'h_align': 'center',
|
|
||||||
'color': (1, 1, 0.5, 1),
|
|
||||||
'flatness': 0.5,
|
|
||||||
'shadow': 0.5,
|
|
||||||
'position': (-200, -30),
|
|
||||||
'scale': 0.7,
|
|
||||||
'text': '{} more votes to end this map \n type \'end\' to vote'.format(
|
|
||||||
votes_needed)
|
|
||||||
})).autoretain()
|
|
||||||
activity.end_vote_text = node
|
|
||||||
bs.timer(20, remove_vote_text)
|
|
||||||
|
|
||||||
|
|
||||||
def remove_vote_text():
|
|
||||||
activity = bs.get_foreground_host_activity()
|
|
||||||
if hasattr(activity,
|
|
||||||
"end_vote_text") and activity.end_vote_text.node.exists():
|
|
||||||
activity.end_vote_text.node.delete()
|
|
||||||
513
dist/ba_root/mods/games/alliance_elimination.py
vendored
513
dist/ba_root/mods/games/alliance_elimination.py
vendored
|
|
@ -1,513 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
#
|
|
||||||
"""Elimination mini-game."""
|
|
||||||
|
|
||||||
# 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 bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
|
||||||
from bascenev1lib.actor.spazfactory import SpazFactory
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import (Any, Tuple, Type, List, Sequence, Optional,
|
|
||||||
Union)
|
|
||||||
|
|
||||||
|
|
||||||
class Icon(bs.Actor):
|
|
||||||
"""Creates in in-game icon on screen."""
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
player: Player,
|
|
||||||
position: Tuple[float, float],
|
|
||||||
scale: float,
|
|
||||||
show_lives: bool = True,
|
|
||||||
show_death: bool = True,
|
|
||||||
name_scale: float = 1.0,
|
|
||||||
name_maxwidth: float = 115.0,
|
|
||||||
flatness: float = 1.0,
|
|
||||||
shadow: float = 1.0):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self._player = player
|
|
||||||
self._show_lives = show_lives
|
|
||||||
self._show_death = show_death
|
|
||||||
self._name_scale = name_scale
|
|
||||||
self._outline_tex = bs.gettexture('characterIconMask')
|
|
||||||
|
|
||||||
icon = player.get_icon()
|
|
||||||
self.node = bs.newnode('image',
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
'texture': icon['texture'],
|
|
||||||
'tint_texture': icon['tint_texture'],
|
|
||||||
'tint_color': icon['tint_color'],
|
|
||||||
'vr_depth': 400,
|
|
||||||
'tint2_color': icon['tint2_color'],
|
|
||||||
'mask_texture': self._outline_tex,
|
|
||||||
'opacity': 1.0,
|
|
||||||
'absolute_scale': True,
|
|
||||||
'attach': 'bottomCenter'
|
|
||||||
})
|
|
||||||
self._name_text = bs.newnode(
|
|
||||||
'text',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'text': babase.Lstr(value=player.getname()),
|
|
||||||
'color': babase.safecolor(player.team.color),
|
|
||||||
'h_align': 'center',
|
|
||||||
'v_align': 'center',
|
|
||||||
'vr_depth': 410,
|
|
||||||
'maxwidth': name_maxwidth,
|
|
||||||
'shadow': shadow,
|
|
||||||
'flatness': flatness,
|
|
||||||
'h_attach': 'center',
|
|
||||||
'v_attach': 'bottom'
|
|
||||||
})
|
|
||||||
if self._show_lives:
|
|
||||||
self._lives_text = bs.newnode('text',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'text': 'x0',
|
|
||||||
'color': (1, 1, 0.5),
|
|
||||||
'h_align': 'left',
|
|
||||||
'vr_depth': 430,
|
|
||||||
'shadow': 1.0,
|
|
||||||
'flatness': 1.0,
|
|
||||||
'h_attach': 'center',
|
|
||||||
'v_attach': 'bottom'
|
|
||||||
})
|
|
||||||
self.set_position_and_scale(position, scale)
|
|
||||||
|
|
||||||
def set_position_and_scale(self, position: Tuple[float, float],
|
|
||||||
scale: float) -> None:
|
|
||||||
"""(Re)position the icon."""
|
|
||||||
assert self.node
|
|
||||||
self.node.position = position
|
|
||||||
self.node.scale = [70.0 * scale]
|
|
||||||
self._name_text.position = (position[0], position[1] + scale * 52.0)
|
|
||||||
self._name_text.scale = 1.0 * scale * self._name_scale
|
|
||||||
if self._show_lives:
|
|
||||||
self._lives_text.position = (position[0] + scale * 10.0,
|
|
||||||
position[1] - scale * 43.0)
|
|
||||||
self._lives_text.scale = 1.0 * scale
|
|
||||||
|
|
||||||
def update_for_lives(self) -> None:
|
|
||||||
"""Update for the target player's current lives."""
|
|
||||||
if self._player:
|
|
||||||
lives = self._player.lives
|
|
||||||
else:
|
|
||||||
lives = 0
|
|
||||||
if self._show_lives:
|
|
||||||
if lives > 0:
|
|
||||||
self._lives_text.text = 'x' + str(lives - 1)
|
|
||||||
else:
|
|
||||||
self._lives_text.text = ''
|
|
||||||
if lives == 0:
|
|
||||||
self._name_text.opacity = 0.2
|
|
||||||
assert self.node
|
|
||||||
self.node.color = (0.7, 0.3, 0.3)
|
|
||||||
self.node.opacity = 0.2
|
|
||||||
|
|
||||||
def handle_player_spawned(self) -> None:
|
|
||||||
"""Our player spawned; hooray!"""
|
|
||||||
if not self.node:
|
|
||||||
return
|
|
||||||
self.node.opacity = 1.0
|
|
||||||
self.update_for_lives()
|
|
||||||
|
|
||||||
def handle_player_died(self) -> None:
|
|
||||||
"""Well poo; our player died."""
|
|
||||||
if not self.node:
|
|
||||||
return
|
|
||||||
if self._show_death:
|
|
||||||
bs.animate(
|
|
||||||
self.node, 'opacity', {
|
|
||||||
0.00: 1.0,
|
|
||||||
0.05: 0.0,
|
|
||||||
0.10: 1.0,
|
|
||||||
0.15: 0.0,
|
|
||||||
0.20: 1.0,
|
|
||||||
0.25: 0.0,
|
|
||||||
0.30: 1.0,
|
|
||||||
0.35: 0.0,
|
|
||||||
0.40: 1.0,
|
|
||||||
0.45: 0.0,
|
|
||||||
0.50: 1.0,
|
|
||||||
0.55: 0.2
|
|
||||||
})
|
|
||||||
lives = self._player.lives
|
|
||||||
if lives == 0:
|
|
||||||
bs.timer(0.6, self.update_for_lives)
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
if isinstance(msg, bs.DieMessage):
|
|
||||||
self.node.delete()
|
|
||||||
return None
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class Player(bs.Player['Team']):
|
|
||||||
"""Our player type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.lives = 0
|
|
||||||
self.icons: List[Icon] = []
|
|
||||||
|
|
||||||
|
|
||||||
class Team(bs.Team[Player]):
|
|
||||||
"""Our team type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.survival_seconds: Optional[int] = None
|
|
||||||
self.spawn_order: List[Player] = []
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class AllianceEliminationGame(bs.TeamGameActivity[Player, Team]):
|
|
||||||
"""Game type where last player(s) left alive win."""
|
|
||||||
|
|
||||||
name = 'Alliance Elimination'
|
|
||||||
description = 'Fight in groups of duo, trio, or more.\nLast remaining alive wins.'
|
|
||||||
scoreconfig = bs.ScoreConfig(label='Survived',
|
|
||||||
scoretype=bs.ScoreType.SECONDS,
|
|
||||||
none_is_winner=True)
|
|
||||||
# Show messages when players die since it's meaningful here.
|
|
||||||
announce_player_deaths = True
|
|
||||||
|
|
||||||
allow_mid_activity_joins = False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_available_settings(
|
|
||||||
cls, sessiontype: Type[bs.Session]) -> List[babase.Setting]:
|
|
||||||
settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Lives Per Player',
|
|
||||||
default=1,
|
|
||||||
min_value=1,
|
|
||||||
max_value=10,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntSetting(
|
|
||||||
'Players Per Team In Arena',
|
|
||||||
default=2,
|
|
||||||
min_value=2,
|
|
||||||
max_value=10,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False),
|
|
||||||
]
|
|
||||||
if issubclass(sessiontype, bs.DualTeamSession):
|
|
||||||
settings.append(
|
|
||||||
bs.BoolSetting('Balance Total Lives', default=False))
|
|
||||||
return settings
|
|
||||||
|
|
||||||
@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('melee')
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
self._scoreboard = Scoreboard()
|
|
||||||
self._start_time: Optional[float] = None
|
|
||||||
self._vs_text: Optional[bs.Actor] = None
|
|
||||||
self._round_end_timer: Optional[bs.Timer] = None
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
self._lives_per_player = int(settings['Lives Per Player'])
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
self._balance_total_lives = bool(
|
|
||||||
settings.get('Balance Total Lives', False))
|
|
||||||
self._players_per_team_in_arena = int(
|
|
||||||
settings['Players Per Team In Arena'])
|
|
||||||
|
|
||||||
# Base class overrides:
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (bs.MusicType.EPIC
|
|
||||||
if self._epic_mode else bs.MusicType.SURVIVAL)
|
|
||||||
|
|
||||||
def get_instance_description(self) -> Union[str, Sequence]:
|
|
||||||
return 'Last team standing wins.' if isinstance(
|
|
||||||
self.session, bs.DualTeamSession) else 'Last one standing wins.'
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> Union[str, Sequence]:
|
|
||||||
return 'last team standing wins' if isinstance(
|
|
||||||
self.session, bs.DualTeamSession) else 'last one standing wins'
|
|
||||||
|
|
||||||
def on_player_join(self, player: Player) -> None:
|
|
||||||
|
|
||||||
# No longer allowing mid-game joiners here; too easy to exploit.
|
|
||||||
if self.has_begun():
|
|
||||||
|
|
||||||
# Make sure their team has survival seconds set if they're all dead
|
|
||||||
# (otherwise blocked new ffa players are considered 'still alive'
|
|
||||||
# in score tallying).
|
|
||||||
if (self._get_total_team_lives(player.team) == 0
|
|
||||||
and player.team.survival_seconds is None):
|
|
||||||
player.team.survival_seconds = 0
|
|
||||||
bs.broadcastmessage(
|
|
||||||
babase.Lstr(resource='playerDelayedJoinText',
|
|
||||||
subs=[('${PLAYER}', player.getname(full=True))]),
|
|
||||||
color=(0, 1, 0),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
player.lives = self._lives_per_player
|
|
||||||
|
|
||||||
player.team.spawn_order.append(player)
|
|
||||||
self._update_alliance_mode()
|
|
||||||
|
|
||||||
# Don't waste time doing this until begin.
|
|
||||||
if self.has_begun():
|
|
||||||
self._update_icons()
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
self._start_time = bs.time()
|
|
||||||
self.setup_standard_time_limit(self._time_limit)
|
|
||||||
self.setup_standard_powerup_drops()
|
|
||||||
self._vs_text = bs.NodeActor(
|
|
||||||
bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'position': (0, 92),
|
|
||||||
'h_attach': 'center',
|
|
||||||
'h_align': 'center',
|
|
||||||
'maxwidth': 200,
|
|
||||||
'shadow': 0.5,
|
|
||||||
'vr_depth': 390,
|
|
||||||
'scale': 0.6,
|
|
||||||
'v_attach': 'bottom',
|
|
||||||
'color': (0.8, 0.8, 0.3, 1.0),
|
|
||||||
'text': babase.Lstr(resource='vsText')
|
|
||||||
}))
|
|
||||||
|
|
||||||
# If balance-team-lives is on, add lives to the smaller team until
|
|
||||||
# total lives match.
|
|
||||||
if (isinstance(self.session, bs.DualTeamSession)
|
|
||||||
and self._balance_total_lives and self.teams[0].players
|
|
||||||
and self.teams[1].players):
|
|
||||||
if self._get_total_team_lives(
|
|
||||||
self.teams[0]) < self._get_total_team_lives(self.teams[1]):
|
|
||||||
lesser_team = self.teams[0]
|
|
||||||
greater_team = self.teams[1]
|
|
||||||
else:
|
|
||||||
lesser_team = self.teams[1]
|
|
||||||
greater_team = self.teams[0]
|
|
||||||
add_index = 0
|
|
||||||
while (self._get_total_team_lives(lesser_team) <
|
|
||||||
self._get_total_team_lives(greater_team)):
|
|
||||||
lesser_team.players[add_index].lives += 1
|
|
||||||
add_index = (add_index + 1) % len(lesser_team.players)
|
|
||||||
|
|
||||||
self._update_icons()
|
|
||||||
|
|
||||||
# We could check game-over conditions at explicit trigger points,
|
|
||||||
# but lets just do the simple thing and poll it.
|
|
||||||
bs.timer(1.0, self._update, repeat=True)
|
|
||||||
|
|
||||||
def _update_alliance_mode(self) -> None:
|
|
||||||
# For both teams, find the first player on the spawn order list with
|
|
||||||
# lives remaining and spawn them if they're not alive.
|
|
||||||
for team in self.teams:
|
|
||||||
# Prune dead players from the spawn order.
|
|
||||||
players_spawned = 0
|
|
||||||
team.spawn_order = [p for p in team.spawn_order if p]
|
|
||||||
for player in team.spawn_order:
|
|
||||||
assert isinstance(player, Player)
|
|
||||||
if player.lives > 0:
|
|
||||||
if not player.is_alive():
|
|
||||||
self.spawn_player(player)
|
|
||||||
self._update_icons()
|
|
||||||
players_spawned += 1
|
|
||||||
if players_spawned >= self._players_per_team_in_arena:
|
|
||||||
break
|
|
||||||
|
|
||||||
def _update_icons(self) -> None:
|
|
||||||
# pylint: disable=too-many-branches
|
|
||||||
# First off, clear out all icons.
|
|
||||||
for player in self.players:
|
|
||||||
player.icons = []
|
|
||||||
|
|
||||||
# Now for each team, cycle through our available players
|
|
||||||
# adding icons.
|
|
||||||
for team in self.teams:
|
|
||||||
if team.id == 0:
|
|
||||||
xval = -60
|
|
||||||
x_offs = -78
|
|
||||||
else:
|
|
||||||
xval = 60
|
|
||||||
x_offs = 78
|
|
||||||
nplayers = self._players_per_team_in_arena
|
|
||||||
test_lives = 1
|
|
||||||
while True:
|
|
||||||
players_with_lives = [
|
|
||||||
p for p in team.spawn_order
|
|
||||||
if p and p.lives >= test_lives
|
|
||||||
]
|
|
||||||
if not players_with_lives:
|
|
||||||
break
|
|
||||||
for player in players_with_lives:
|
|
||||||
player.icons.append(
|
|
||||||
Icon(player,
|
|
||||||
position=(xval, (36 if nplayers > 0 else 25)),
|
|
||||||
scale=0.9 if nplayers > 0 else 0.5,
|
|
||||||
name_maxwidth=85 if nplayers > 0 else 75,
|
|
||||||
name_scale=0.8 if nplayers > 0 else 1.0,
|
|
||||||
flatness=0.0 if nplayers > 0 else 1.0,
|
|
||||||
shadow=0.5 if nplayers > 0 else 1.0,
|
|
||||||
show_death=True if nplayers > 0 else False,
|
|
||||||
show_lives=False))
|
|
||||||
xval += x_offs * (0.85 if nplayers > 0 else 0.56)
|
|
||||||
nplayers -= 1
|
|
||||||
test_lives += 1
|
|
||||||
|
|
||||||
def _get_spawn_point(self, player: Player) -> Optional[babase.Vec3]:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def spawn_player(self, player: Player) -> bs.Actor:
|
|
||||||
actor = self.spawn_player_spaz(player, self._get_spawn_point(player))
|
|
||||||
|
|
||||||
# If we have any icons, update their state.
|
|
||||||
for icon in player.icons:
|
|
||||||
icon.handle_player_spawned()
|
|
||||||
return actor
|
|
||||||
|
|
||||||
def _print_lives(self, player: Player) -> None:
|
|
||||||
from bascenev1lib.actor import popuptext
|
|
||||||
|
|
||||||
# We get called in a timer so it's possible our player has left/etc.
|
|
||||||
if not player or not player.is_alive() or not player.node:
|
|
||||||
return
|
|
||||||
|
|
||||||
popuptext.PopupText('x' + str(player.lives - 1),
|
|
||||||
color=(1, 1, 0, 1),
|
|
||||||
offset=(0, -0.8, 0),
|
|
||||||
random_offset=0.0,
|
|
||||||
scale=1.8,
|
|
||||||
position=player.node.position).autoretain()
|
|
||||||
|
|
||||||
def on_player_leave(self, player: Player) -> None:
|
|
||||||
super().on_player_leave(player)
|
|
||||||
player.icons = []
|
|
||||||
|
|
||||||
# Remove us from spawn-order.
|
|
||||||
if player in player.team.spawn_order:
|
|
||||||
player.team.spawn_order.remove(player)
|
|
||||||
|
|
||||||
# Update icons in a moment since our team will be gone from the
|
|
||||||
# list then.
|
|
||||||
bs.timer(0, self._update_icons)
|
|
||||||
|
|
||||||
# If the player to leave was the last in spawn order and had
|
|
||||||
# their final turn currently in-progress, mark the survival time
|
|
||||||
# for their team.
|
|
||||||
if self._get_total_team_lives(player.team) == 0:
|
|
||||||
assert self._start_time is not None
|
|
||||||
player.team.survival_seconds = int(bs.time() - self._start_time)
|
|
||||||
|
|
||||||
def _get_total_team_lives(self, team: Team) -> int:
|
|
||||||
return sum(player.lives for player in team.players)
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
if isinstance(msg, bs.PlayerDiedMessage):
|
|
||||||
|
|
||||||
# Augment standard behavior.
|
|
||||||
super().handlemessage(msg)
|
|
||||||
player: Player = msg.getplayer(Player)
|
|
||||||
|
|
||||||
player.lives -= 1
|
|
||||||
if player.lives < 0:
|
|
||||||
babase.print_error(
|
|
||||||
"Got lives < 0 in Alliance Elimination; this shouldn't happen.")
|
|
||||||
player.lives = 0
|
|
||||||
|
|
||||||
# If we have any icons, update their state.
|
|
||||||
for icon in player.icons:
|
|
||||||
icon.handle_player_died()
|
|
||||||
|
|
||||||
# Play big death sound on our last death
|
|
||||||
# or for every one.
|
|
||||||
if player.lives == 0:
|
|
||||||
SpazFactory.get().single_player_death_sound.play()
|
|
||||||
|
|
||||||
# If we hit zero lives, we're dead (and our team might be too).
|
|
||||||
if player.lives == 0:
|
|
||||||
# If the whole team is now dead, mark their survival time.
|
|
||||||
if self._get_total_team_lives(player.team) == 0:
|
|
||||||
assert self._start_time is not None
|
|
||||||
player.team.survival_seconds = int(bs.time() -
|
|
||||||
self._start_time)
|
|
||||||
|
|
||||||
# Put ourself at the back of the spawn order.
|
|
||||||
player.team.spawn_order.remove(player)
|
|
||||||
player.team.spawn_order.append(player)
|
|
||||||
|
|
||||||
def _update(self) -> None:
|
|
||||||
# For both teams, find the first player on the spawn order
|
|
||||||
# list with lives remaining and spawn them if they're not alive.
|
|
||||||
for team in self.teams:
|
|
||||||
# Prune dead players from the spawn order.
|
|
||||||
team.spawn_order = [p for p in team.spawn_order if p]
|
|
||||||
players_spawned = 0
|
|
||||||
for player in team.spawn_order:
|
|
||||||
assert isinstance(player, Player)
|
|
||||||
if player.lives > 0:
|
|
||||||
if not player.is_alive():
|
|
||||||
self.spawn_player(player)
|
|
||||||
self._update_icons()
|
|
||||||
players_spawned += 1
|
|
||||||
if players_spawned >= self._players_per_team_in_arena:
|
|
||||||
break
|
|
||||||
|
|
||||||
# If we're down to 1 or fewer living teams, start a timer to end
|
|
||||||
# the game (allows the dust to settle and draws to occur if deaths
|
|
||||||
# are close enough).
|
|
||||||
if len(self._get_living_teams()) < 2:
|
|
||||||
self._round_end_timer = bs.Timer(0.5, self.end_game)
|
|
||||||
|
|
||||||
def _get_living_teams(self) -> List[Team]:
|
|
||||||
return [
|
|
||||||
team for team in self.teams
|
|
||||||
if len(team.players) > 0 and any(player.lives > 0
|
|
||||||
for player in team.players)
|
|
||||||
]
|
|
||||||
|
|
||||||
def end_game(self) -> None:
|
|
||||||
if self.has_ended():
|
|
||||||
return
|
|
||||||
results = bs.GameResults()
|
|
||||||
self._vs_text = None # Kill our 'vs' if its there.
|
|
||||||
for team in self.teams:
|
|
||||||
results.set_team_score(team, team.survival_seconds)
|
|
||||||
self.end(results=results)
|
|
||||||
199
dist/ba_root/mods/games/arms_race.py
vendored
199
dist/ba_root/mods/games/arms_race.py
vendored
|
|
@ -1,199 +0,0 @@
|
||||||
# Ported by your friend: Freaku
|
|
||||||
|
|
||||||
# Join BCS:
|
|
||||||
# https://discord.gg/ucyaesh
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Any, Type, List, Union, Sequence
|
|
||||||
|
|
||||||
|
|
||||||
class State:
|
|
||||||
def __init__(self, bomb=None, grab=False, punch=False, curse=False,
|
|
||||||
required=False, final=False, name=''):
|
|
||||||
self.bomb = bomb
|
|
||||||
self.grab = grab
|
|
||||||
self.punch = punch
|
|
||||||
self.pickup = False
|
|
||||||
self.curse = curse
|
|
||||||
self.required = required or final
|
|
||||||
self.final = final
|
|
||||||
self.name = name
|
|
||||||
self.next = None
|
|
||||||
self.index = None
|
|
||||||
|
|
||||||
def apply(self, spaz):
|
|
||||||
spaz.disconnect_controls_from_player()
|
|
||||||
spaz.connect_controls_to_player(enable_punch=self.punch,
|
|
||||||
enable_bomb=self.bomb,
|
|
||||||
enable_pickup=self.grab)
|
|
||||||
if self.curse:
|
|
||||||
spaz.curse_time = -1
|
|
||||||
spaz.curse()
|
|
||||||
if self.bomb:
|
|
||||||
spaz.bomb_type = self.bomb
|
|
||||||
spaz.set_score_text(self.name)
|
|
||||||
|
|
||||||
def get_setting(self):
|
|
||||||
return (self.name)
|
|
||||||
|
|
||||||
|
|
||||||
states = [State(bomb='normal', name='Basic Bombs'),
|
|
||||||
State(bomb='ice', name='Frozen Bombs'),
|
|
||||||
State(bomb='sticky', name='Sticky Bombs'),
|
|
||||||
State(bomb='impact', name='Impact Bombs'),
|
|
||||||
State(grab=True, name='Grabbing only'),
|
|
||||||
State(punch=True, name='Punching only'),
|
|
||||||
State(curse=True, name='Cursed', final=True)]
|
|
||||||
|
|
||||||
|
|
||||||
class Player(bs.Player['Team']):
|
|
||||||
"""Our player type for this game."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.state = None
|
|
||||||
|
|
||||||
|
|
||||||
class Team(bs.Team[Player]):
|
|
||||||
"""Our team type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.score = 0
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class ArmsRaceGame(bs.TeamGameActivity[Player, Team]):
|
|
||||||
"""A game type based on acquiring kills."""
|
|
||||||
|
|
||||||
name = 'Arms Race'
|
|
||||||
description = 'Upgrade your weapon by eliminating enemies.\nWin the match by being the first player\nto get a kill while cursed.'
|
|
||||||
|
|
||||||
# Print messages when players die since it matters here.
|
|
||||||
announce_player_deaths = True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_available_settings(
|
|
||||||
cls, sessiontype: Type[bs.Session]) -> List[babase.Setting]:
|
|
||||||
settings = [
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False)]
|
|
||||||
for state in states:
|
|
||||||
if not state.required:
|
|
||||||
settings.append(
|
|
||||||
bs.BoolSetting(state.get_setting(), default=True))
|
|
||||||
|
|
||||||
return settings
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
|
|
||||||
return (issubclass(sessiontype, bs.DualTeamSession)
|
|
||||||
or issubclass(sessiontype, bs.FreeForAllSession))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
|
|
||||||
return bs.app.classic.getmaps('melee')
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
self.states = [s for s in states if settings.get(s.name, True)]
|
|
||||||
for i, state in enumerate(self.states):
|
|
||||||
if i < len(self.states) and not state.final:
|
|
||||||
state.next = self.states[i + 1]
|
|
||||||
state.index = i
|
|
||||||
self._dingsound = bs.getsound('dingSmall')
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
|
|
||||||
# Base class overrides.
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (bs.MusicType.EPIC if self._epic_mode else
|
|
||||||
bs.MusicType.TO_THE_DEATH)
|
|
||||||
|
|
||||||
def get_instance_description(self) -> Union[str, Sequence]:
|
|
||||||
return 'Upgrade your weapon by eliminating enemies.'
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> Union[str, Sequence]:
|
|
||||||
return 'kill ${ARG1} enemies', len(self.states)
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
self.setup_standard_time_limit(self._time_limit)
|
|
||||||
# self.setup_standard_powerup_drops()
|
|
||||||
|
|
||||||
def on_player_join(self, player):
|
|
||||||
if player.state is None:
|
|
||||||
player.state = self.states[0]
|
|
||||||
self.spawn_player(player)
|
|
||||||
|
|
||||||
# overriding the default character spawning..
|
|
||||||
def spawn_player(self, player):
|
|
||||||
if player.state is None:
|
|
||||||
player.state = self.states[0]
|
|
||||||
super().spawn_player(player)
|
|
||||||
player.state.apply(player.actor)
|
|
||||||
|
|
||||||
def isValidKill(self, m):
|
|
||||||
if m.getkillerplayer(Player) is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if m.getkillerplayer(Player).team is m.getplayer(Player).team:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
|
|
||||||
if isinstance(msg, bs.PlayerDiedMessage):
|
|
||||||
if self.isValidKill(msg):
|
|
||||||
self.stats.player_scored(msg.getkillerplayer(Player), 10,
|
|
||||||
kill=True)
|
|
||||||
if not msg.getkillerplayer(Player).state.final:
|
|
||||||
msg.getkillerplayer(Player).state = msg.getkillerplayer(
|
|
||||||
Player).state.next
|
|
||||||
msg.getkillerplayer(Player).state.apply(
|
|
||||||
msg.getkillerplayer(Player).actor)
|
|
||||||
else:
|
|
||||||
msg.getkillerplayer(Player).team.score += 1
|
|
||||||
self.end_game()
|
|
||||||
self.respawn_player(msg.getplayer(Player))
|
|
||||||
|
|
||||||
else:
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def end_game(self) -> None:
|
|
||||||
results = bs.GameResults()
|
|
||||||
for team in self.teams:
|
|
||||||
results.set_team_score(team, team.score)
|
|
||||||
self.end(results=results)
|
|
||||||
540
dist/ba_root/mods/games/big_ball.py
vendored
540
dist/ba_root/mods/games/big_ball.py
vendored
|
|
@ -1,540 +0,0 @@
|
||||||
# Made by MythB
|
|
||||||
# Ported by: MysteriousBoi
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
|
||||||
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
|
|
||||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
# goalpost
|
|
||||||
|
|
||||||
|
|
||||||
class FlagKale(bs.Actor):
|
|
||||||
def __init__(self, position=(0, 2.5, 0), color=(1, 1, 1)):
|
|
||||||
super().__init__()
|
|
||||||
activity = self.getactivity()
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self.node = bs.newnode('flag',
|
|
||||||
attrs={'position': (
|
|
||||||
position[0], position[1] + 0.75, position[2]),
|
|
||||||
'color_texture': activity._flagKaleTex,
|
|
||||||
'color': color,
|
|
||||||
'materials': [shared.object_material,
|
|
||||||
activity._kaleMaterial],
|
|
||||||
},
|
|
||||||
delegate=self)
|
|
||||||
|
|
||||||
def handleMessage(self, m):
|
|
||||||
if isinstance(m, bs.DieMessage):
|
|
||||||
if self.node.exists():
|
|
||||||
self.node.delete()
|
|
||||||
elif isinstance(m, bs.OutOfBoundsMessage):
|
|
||||||
self.handlemessage(bs.DieMessage())
|
|
||||||
else:
|
|
||||||
super().handlemessage(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class Puck(bs.Actor):
|
|
||||||
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])
|
|
||||||
self.last_players_to_touch: Dict[int, Player] = {}
|
|
||||||
self.scored = False
|
|
||||||
assert activity is not None
|
|
||||||
assert isinstance(activity, BBGame)
|
|
||||||
pmats = [shared.object_material, activity.puck_material]
|
|
||||||
self.node = bs.newnode('prop',
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
'mesh': activity._ballModel,
|
|
||||||
'color_texture': activity._ballTex,
|
|
||||||
'body': 'sphere',
|
|
||||||
'reflection': 'soft',
|
|
||||||
'reflection_scale': [0.2],
|
|
||||||
'shadow_size': 0.8,
|
|
||||||
'is_area_of_interest': True,
|
|
||||||
'position': self._spawn_pos,
|
|
||||||
'materials': pmats,
|
|
||||||
'body_scale': 4,
|
|
||||||
'mesh_scale': 1,
|
|
||||||
'density': 0.02})
|
|
||||||
bs.animate(self.node, 'mesh_scale', {0: 0, 0.2: 1.3, 0.26: 1})
|
|
||||||
|
|
||||||
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.team.id] = s_player
|
|
||||||
else:
|
|
||||||
super().handlemessage(msg)
|
|
||||||
|
|
||||||
|
|
||||||
# for night mode: using a actor with large shadow and little mesh scale. Better then tint i think, players and objects more visible
|
|
||||||
|
|
||||||
|
|
||||||
class NightMod(bs.Actor):
|
|
||||||
def __init__(self, position=(0, 0, 0)):
|
|
||||||
super().__init__()
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
activity = self.getactivity()
|
|
||||||
# spawn just above the provided point
|
|
||||||
self._spawnPos = (position[0], position[1], position[2])
|
|
||||||
self.node = bs.newnode("prop",
|
|
||||||
attrs={'mesh': activity._nightModel,
|
|
||||||
'color_texture': activity._nightTex,
|
|
||||||
'body': 'sphere',
|
|
||||||
'reflection': 'soft',
|
|
||||||
'body_scale': 0.1,
|
|
||||||
'mesh_scale': 0.001,
|
|
||||||
'density': 0.010,
|
|
||||||
'reflection_scale': [0.23],
|
|
||||||
'shadow_size': 999999.0,
|
|
||||||
'is_area_of_interest': True,
|
|
||||||
'position': self._spawnPos,
|
|
||||||
'materials': [activity._nightMaterial]
|
|
||||||
},
|
|
||||||
delegate=self)
|
|
||||||
|
|
||||||
def handlemssage(self, m):
|
|
||||||
super().handlemessage(m)
|
|
||||||
|
|
||||||
|
|
||||||
class Player(bs.Player['Team']):
|
|
||||||
"""Our player type for this game."""
|
|
||||||
|
|
||||||
|
|
||||||
class Team(bs.Team[Player]):
|
|
||||||
"""Our team type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.score = 0
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class BBGame(bs.TeamGameActivity[Player, Team]):
|
|
||||||
name = 'Big Ball'
|
|
||||||
description = 'Score some goals.\nFlags are goalposts.\nScored team players get boxing gloves,\nNon-scored team players getting shield (if Grant Powers on Score).\nYou can also set Night Mode!'
|
|
||||||
available_settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Score to Win',
|
|
||||||
min_value=1,
|
|
||||||
default=1,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Epic Mode', True),
|
|
||||||
bs.BoolSetting('Night Mode', False),
|
|
||||||
bs.BoolSetting('Grant Powers on Score', False)
|
|
||||||
]
|
|
||||||
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 ['Football Stadium']
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self._scoreboard = Scoreboard()
|
|
||||||
self._cheer_sound = bs.getsound('cheer')
|
|
||||||
self._chant_sound = bs.getsound('crowdChant')
|
|
||||||
self._foghorn_sound = bs.getsound('foghorn')
|
|
||||||
self._swipsound = bs.getsound('swip')
|
|
||||||
self._whistle_sound = bs.getsound('refWhistle')
|
|
||||||
self._ballModel = bs.getmesh("shield")
|
|
||||||
self._ballTex = bs.gettexture("eggTex1")
|
|
||||||
self._ballSound = bs.getsound("impactMedium2")
|
|
||||||
self._flagKaleTex = bs.gettexture("star")
|
|
||||||
self._kaleSound = bs.getsound("metalHit")
|
|
||||||
self._nightModel = bs.getmesh("shield")
|
|
||||||
self._nightTex = bs.gettexture("black")
|
|
||||||
self._kaleMaterial = bs.Material()
|
|
||||||
# add friction to flags for standing our position (as far as)
|
|
||||||
self._kaleMaterial.add_actions(
|
|
||||||
conditions=("they_have_material", shared.footing_material),
|
|
||||||
actions=(("modify_part_collision", "friction", 9999.5)))
|
|
||||||
self._kaleMaterial.add_actions(
|
|
||||||
conditions=(("we_are_younger_than", 1), 'and',
|
|
||||||
("they_have_material", shared.object_material)),
|
|
||||||
actions=(("modify_part_collision", "collide", False)))
|
|
||||||
self._kaleMaterial.add_actions(
|
|
||||||
conditions=("they_have_material", shared.pickup_material),
|
|
||||||
actions=(("modify_part_collision", "collide", False)))
|
|
||||||
self._kaleMaterial.add_actions(
|
|
||||||
conditions=('they_have_material', shared.object_material),
|
|
||||||
actions=(('impact_sound', self._kaleSound, 2, 5)))
|
|
||||||
# we dont wanna hit the night so
|
|
||||||
self._nightMaterial = bs.Material()
|
|
||||||
self._nightMaterial.add_actions(
|
|
||||||
conditions=(('they_have_material', shared.pickup_material), 'or',
|
|
||||||
('they_have_material', shared.attack_material)),
|
|
||||||
actions=(('modify_part_collision', 'collide', False)))
|
|
||||||
# we also dont want anything moving it
|
|
||||||
self._nightMaterial.add_actions(
|
|
||||||
conditions=(('they_have_material', shared.object_material), 'or',
|
|
||||||
('they_dont_have_material', shared.footing_material)),
|
|
||||||
actions=(('modify_part_collision', 'collide', False),
|
|
||||||
('modify_part_collision', 'physical', False)))
|
|
||||||
self.puck_material = bs.Material()
|
|
||||||
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', False))
|
|
||||||
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._ballSound, 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._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)))
|
|
||||||
self._puck_spawn_pos: Optional[Sequence[float]] = None
|
|
||||||
self._score_regions: Optional[List[bs.NodeActor]] = None
|
|
||||||
self._puck: Optional[Puck] = None
|
|
||||||
self._score_to_win = int(settings['Score to Win'])
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
self._nm = bool(settings['Night Mode'])
|
|
||||||
self._grant_power = bool(settings['Grant Powers on Score'])
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
# Base class overrides.
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
|
|
||||||
def get_instance_description(self) -> Union[str, Sequence]:
|
|
||||||
if self._score_to_win == 1:
|
|
||||||
return 'Score a goal.'
|
|
||||||
return 'Score ${ARG1} goals.', self._score_to_win
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> Union[str, Sequence]:
|
|
||||||
if self._score_to_win == 1:
|
|
||||||
return 'score a goal'
|
|
||||||
return 'score ${ARG1} goals', self._score_to_win
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
|
|
||||||
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()
|
|
||||||
# for night mode we need night actor. And same goodies for nigh mode
|
|
||||||
if self._nm:
|
|
||||||
self._nightSpawny(), self._flagKaleFlash()
|
|
||||||
|
|
||||||
# Set up the two score regions.
|
|
||||||
defs = self.map.defs
|
|
||||||
self._score_regions = []
|
|
||||||
self._score_regions.append(
|
|
||||||
bs.NodeActor(
|
|
||||||
bs.newnode('region',
|
|
||||||
attrs={
|
|
||||||
'position': (13.75, 0.85744967453, 0.1095578275),
|
|
||||||
'scale': (1.05, 1.1, 3.8),
|
|
||||||
'type': 'box',
|
|
||||||
'materials': [self._score_region_material]
|
|
||||||
})))
|
|
||||||
self._score_regions.append(
|
|
||||||
bs.NodeActor(
|
|
||||||
bs.newnode('region',
|
|
||||||
attrs={
|
|
||||||
'position': (
|
|
||||||
-13.55, 0.85744967453, 0.1095578275),
|
|
||||||
'scale': (1.05, 1.1, 3.8),
|
|
||||||
'type': 'box',
|
|
||||||
'materials': [self._score_region_material]
|
|
||||||
})))
|
|
||||||
self._update_scoreboard()
|
|
||||||
self._chant_sound.play()
|
|
||||||
|
|
||||||
def _nightSpawny(self):
|
|
||||||
self.MythBrk = NightMod(position=(0, 0.05744967453, 0))
|
|
||||||
|
|
||||||
# spawn some goodies on nightmode for pretty visuals
|
|
||||||
def _flagKaleFlash(self):
|
|
||||||
# flags positions
|
|
||||||
kale1 = (-12.45, 0.05744967453, -2.075)
|
|
||||||
kale2 = (-12.45, 0.05744967453, 2.075)
|
|
||||||
kale3 = (12.66, 0.03986567039, 2.075)
|
|
||||||
kale4 = (12.66, 0.03986567039, -2.075)
|
|
||||||
|
|
||||||
flash = bs.newnode("light",
|
|
||||||
attrs={'position': kale1,
|
|
||||||
'radius': 0.15,
|
|
||||||
'color': (1.0, 1.0, 0.7)})
|
|
||||||
|
|
||||||
flash = bs.newnode("light",
|
|
||||||
attrs={'position': kale2,
|
|
||||||
'radius': 0.15,
|
|
||||||
'color': (1.0, 1.0, 0.7)})
|
|
||||||
|
|
||||||
flash = bs.newnode("light",
|
|
||||||
attrs={'position': kale3,
|
|
||||||
'radius': 0.15,
|
|
||||||
'color': (0.7, 1.0, 1.0)})
|
|
||||||
|
|
||||||
flash = bs.newnode("light",
|
|
||||||
attrs={'position': kale4,
|
|
||||||
'radius': 0.15,
|
|
||||||
'color': (0.7, 1.0, 1.0)})
|
|
||||||
|
|
||||||
# flags positions
|
|
||||||
|
|
||||||
def _flagKalesSpawn(self):
|
|
||||||
for team in self.teams:
|
|
||||||
if team.id == 0:
|
|
||||||
_colorTeam0 = team.color
|
|
||||||
if team.id == 1:
|
|
||||||
_colorTeam1 = team.color
|
|
||||||
|
|
||||||
self._MythB = FlagKale(position=(-12.45, 0.05744967453, -2.075),
|
|
||||||
color=_colorTeam0)
|
|
||||||
self._MythB2 = FlagKale(position=(-12.45, 0.05744967453, 2.075),
|
|
||||||
color=_colorTeam0)
|
|
||||||
self._MythB3 = FlagKale(position=(12.66, 0.03986567039, 2.075),
|
|
||||||
color=_colorTeam1)
|
|
||||||
self._MythB4 = FlagKale(position=(12.66, 0.03986567039, -2.075),
|
|
||||||
color=_colorTeam1)
|
|
||||||
|
|
||||||
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.team.id] = player
|
|
||||||
|
|
||||||
def _kill_puck(self) -> None:
|
|
||||||
self._puck = None
|
|
||||||
|
|
||||||
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 scored team players to celebrate and give them to boxing gloves
|
|
||||||
if self._grant_power:
|
|
||||||
for player in team.players:
|
|
||||||
try:
|
|
||||||
player.actor.node.handlemessage(
|
|
||||||
bs.PowerupMessage('punch'))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 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],
|
|
||||||
100,
|
|
||||||
big_message=True)
|
|
||||||
|
|
||||||
# End game if we won.
|
|
||||||
if team.score >= self._score_to_win:
|
|
||||||
self.end_game()
|
|
||||||
else:
|
|
||||||
if self._grant_power:
|
|
||||||
for player in team.players:
|
|
||||||
try:
|
|
||||||
player.actor.node.handlemessage(
|
|
||||||
bs.PowerupMessage('shield'))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self._foghorn_sound.play()
|
|
||||||
self._cheer_sound.play()
|
|
||||||
|
|
||||||
self._puck.scored = True
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
def _spawn_puck(self) -> None:
|
|
||||||
self._swipsound.play()
|
|
||||||
self._whistle_sound.play()
|
|
||||||
self._flagKalesSpawn()
|
|
||||||
self._flash_puck_spawn()
|
|
||||||
assert self._puck_spawn_pos is not None
|
|
||||||
self._puck = Puck(position=self._puck_spawn_pos)
|
|
||||||
self._puck.light = bs.newnode('light',
|
|
||||||
owner=self._puck.node,
|
|
||||||
attrs={'intensity': 0.3,
|
|
||||||
'height_attenuated': False,
|
|
||||||
'radius': 0.2,
|
|
||||||
'color': (0.9, 0.2, 0.9)})
|
|
||||||
self._puck.node.connectattr('position', self._puck.light, 'position')
|
|
||||||
239
dist/ba_root/mods/games/boxing.py
vendored
239
dist/ba_root/mods/games/boxing.py
vendored
|
|
@ -1,239 +0,0 @@
|
||||||
# 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 bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
|
||||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
|
||||||
from bascenev1lib.game.deathmatch import DeathMatchGame
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Sequence
|
|
||||||
|
|
||||||
lang = bs.app.lang.language
|
|
||||||
|
|
||||||
if lang == 'Spanish':
|
|
||||||
name = 'Super Boxeo'
|
|
||||||
description = ('¡Sin bombas!\n'
|
|
||||||
'¡Noquea a los enemigos con tus propias manos!\n')
|
|
||||||
super_jump_text = 'Super Salto'
|
|
||||||
enable_powerups = 'Habilitar Potenciadores'
|
|
||||||
else:
|
|
||||||
name = 'Super Boxing'
|
|
||||||
description = ('No bombs!\n'
|
|
||||||
'Knock out your enemies using your bare hands!\n')
|
|
||||||
super_jump_text = 'Super Jump'
|
|
||||||
enable_powerups = 'Enable Powerups'
|
|
||||||
|
|
||||||
|
|
||||||
class NewPlayerSpaz(PlayerSpaz):
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
player: bs.Player,
|
|
||||||
color: Sequence[float] = (1.0, 1.0, 1.0),
|
|
||||||
highlight: Sequence[float] = (0.5, 0.5, 0.5),
|
|
||||||
character: str = 'Spaz',
|
|
||||||
powerups_expire: bool = True,
|
|
||||||
super_jump: bool = False):
|
|
||||||
super().__init__(player=player,
|
|
||||||
color=color,
|
|
||||||
highlight=highlight,
|
|
||||||
character=character,
|
|
||||||
powerups_expire=powerups_expire)
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self._super_jump = super_jump
|
|
||||||
self.jump_mode = False
|
|
||||||
self.super_jump_material = bs.Material()
|
|
||||||
self.super_jump_material.add_actions(
|
|
||||||
conditions=('they_have_material', shared.footing_material),
|
|
||||||
actions=(
|
|
||||||
('call', 'at_connect', babase.Call(self.jump_state, True)),
|
|
||||||
('call', 'at_disconnect', babase.Call(self.jump_state, False))
|
|
||||||
),
|
|
||||||
)
|
|
||||||
self.node.roller_materials += (self.super_jump_material,)
|
|
||||||
|
|
||||||
def jump_state(self, mode: bool) -> None:
|
|
||||||
self.jump_mode = mode
|
|
||||||
|
|
||||||
def on_jump_press(self) -> None:
|
|
||||||
"""
|
|
||||||
Called to 'press jump' on this spaz;
|
|
||||||
used by player or AI connections.
|
|
||||||
"""
|
|
||||||
if not self.node:
|
|
||||||
return
|
|
||||||
t_ms = int(bs.time() * 1000.0)
|
|
||||||
assert isinstance(t_ms, int)
|
|
||||||
if t_ms - self.last_jump_time_ms >= self._jump_cooldown:
|
|
||||||
self.node.jump_pressed = True
|
|
||||||
self.last_jump_time_ms = t_ms
|
|
||||||
if self._player.is_alive() and self.jump_mode and (
|
|
||||||
self._super_jump):
|
|
||||||
def do_jump():
|
|
||||||
self.node.handlemessage(
|
|
||||||
'impulse',
|
|
||||||
self.node.position[0],
|
|
||||||
self.node.position[1],
|
|
||||||
self.node.position[2],
|
|
||||||
0, 0, 0, 95, 95, 0, 0, 0, 1, 0
|
|
||||||
)
|
|
||||||
|
|
||||||
bs.timer(0.0, do_jump)
|
|
||||||
bs.timer(0.1, do_jump)
|
|
||||||
bs.timer(0.2, do_jump)
|
|
||||||
self._turbo_filter_add_press('jump')
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class BoxingGame(DeathMatchGame):
|
|
||||||
name = name
|
|
||||||
description = description
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_available_settings(
|
|
||||||
cls, sessiontype: type[bs.Session]
|
|
||||||
) -> list[babase.Setting]:
|
|
||||||
settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Kills to Win Per Player',
|
|
||||||
min_value=1,
|
|
||||||
default=5,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting(super_jump_text, default=False),
|
|
||||||
bs.BoolSetting(enable_powerups, default=False),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False),
|
|
||||||
]
|
|
||||||
|
|
||||||
# In teams mode, a suicide gives a point to the other team, but in
|
|
||||||
# free-for-all it subtracts from your own score. By default we clamp
|
|
||||||
# this at zero to benefit new players, but pro players might like to
|
|
||||||
# be able to go negative. (to avoid a strategy of just
|
|
||||||
# suiciding until you get a good drop)
|
|
||||||
if issubclass(sessiontype, bs.FreeForAllSession):
|
|
||||||
settings.append(
|
|
||||||
bs.BoolSetting('Allow Negative Scores', default=False)
|
|
||||||
)
|
|
||||||
|
|
||||||
return settings
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
self._scoreboard = Scoreboard()
|
|
||||||
self._score_to_win: int | None = None
|
|
||||||
self._dingsound = bs.getsound('dingSmall')
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
self._kills_to_win_per_player = int(settings['Kills to Win Per Player'])
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
self._allow_negative_scores = bool(
|
|
||||||
settings.get('Allow Negative Scores', False)
|
|
||||||
)
|
|
||||||
self._super_jump = bool(settings[super_jump_text])
|
|
||||||
self._enable_powerups = bool(settings[enable_powerups])
|
|
||||||
|
|
||||||
# Base class overrides.
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (
|
|
||||||
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.TO_THE_DEATH
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
bs.TeamGameActivity.on_begin(self)
|
|
||||||
self.setup_standard_time_limit(self._time_limit)
|
|
||||||
if self._enable_powerups:
|
|
||||||
self.setup_standard_powerup_drops()
|
|
||||||
|
|
||||||
# Base kills needed to win on the size of the largest team.
|
|
||||||
self._score_to_win = self._kills_to_win_per_player * max(
|
|
||||||
1, max(len(t.players) for t in self.teams)
|
|
||||||
)
|
|
||||||
self._update_scoreboard()
|
|
||||||
|
|
||||||
def _standard_drop_powerup(self, index: int, expire: bool = True) -> None:
|
|
||||||
# pylint: disable=cyclic-import
|
|
||||||
from bascenev1lib.actor.powerupbox import PowerupBox, PowerupBoxFactory
|
|
||||||
|
|
||||||
PowerupBox(
|
|
||||||
position=self.map.powerup_spawn_points[index],
|
|
||||||
poweruptype=PowerupBoxFactory.get().get_random_powerup_type(
|
|
||||||
excludetypes=['triple_bombs', 'ice_bombs', 'impact_bombs',
|
|
||||||
'land_mines', 'sticky_bombs', 'punch']
|
|
||||||
),
|
|
||||||
expire=expire,
|
|
||||||
).autoretain()
|
|
||||||
|
|
||||||
def spawn_player(self, player: Player) -> bs.Actor:
|
|
||||||
import random
|
|
||||||
from babase import _math
|
|
||||||
from bascenev1._gameutils import animate
|
|
||||||
|
|
||||||
if isinstance(self.session, bs.DualTeamSession):
|
|
||||||
position = self.map.get_start_position(player.team.id)
|
|
||||||
else:
|
|
||||||
# otherwise do free-for-all spawn locations
|
|
||||||
position = self.map.get_ffa_start_position(self.players)
|
|
||||||
angle = None
|
|
||||||
name = player.getname()
|
|
||||||
color = player.color
|
|
||||||
highlight = player.highlight
|
|
||||||
|
|
||||||
light_color = _math.normalized_color(color)
|
|
||||||
display_color = babase.safecolor(color, target_intensity=0.75)
|
|
||||||
|
|
||||||
spaz = NewPlayerSpaz(color=color,
|
|
||||||
highlight=highlight,
|
|
||||||
character=player.character,
|
|
||||||
player=player,
|
|
||||||
super_jump=self._super_jump)
|
|
||||||
|
|
||||||
player.actor = spaz
|
|
||||||
assert spaz.node
|
|
||||||
|
|
||||||
spaz.node.name = name
|
|
||||||
spaz.node.name_color = display_color
|
|
||||||
|
|
||||||
# Move to the stand position and add a flash of light.
|
|
||||||
spaz.handlemessage(
|
|
||||||
bs.StandMessage(
|
|
||||||
position,
|
|
||||||
angle if angle is not None else random.uniform(0, 360)))
|
|
||||||
self._spawn_sound.play(1, position=spaz.node.position)
|
|
||||||
light = bs.newnode('light', attrs={'color': light_color})
|
|
||||||
spaz.node.connectattr('position', light, 'position')
|
|
||||||
animate(light, 'intensity', {0: 0, 0.25: 1, 0.5: 0})
|
|
||||||
bs.timer(0.5, light.delete)
|
|
||||||
|
|
||||||
# custom
|
|
||||||
spaz.connect_controls_to_player(enable_bomb=False)
|
|
||||||
spaz.equip_boxing_gloves()
|
|
||||||
|
|
||||||
return spaz
|
|
||||||
636
dist/ba_root/mods/games/collector.py
vendored
636
dist/ba_root/mods/games/collector.py
vendored
|
|
@ -1,636 +0,0 @@
|
||||||
# ba_meta require api 8
|
|
||||||
# (see https://ballistica.net/wiki/meta-tag-system)
|
|
||||||
|
|
||||||
'''
|
|
||||||
Gamemode: Collector
|
|
||||||
Creator: TheMikirog
|
|
||||||
Website: https://bombsquadjoyride.blogspot.com/
|
|
||||||
|
|
||||||
This is a gamemode purely made by me just to spite unchallenged modders
|
|
||||||
out there that put out crap to the market.
|
|
||||||
We don't want gamemodes that are just the existing ones
|
|
||||||
with some novelties! Gamers deserve more!
|
|
||||||
|
|
||||||
In this gamemode you have to kill others in order to get their Capsules.
|
|
||||||
Capsules can be collected and staked in your inventory,
|
|
||||||
how many as you please.
|
|
||||||
After you kill an enemy that carries some of them,
|
|
||||||
they drop a respective amount of Capsules they carried + two more.
|
|
||||||
Your task is to collect these Capsules,
|
|
||||||
get to the flag and score them KOTH style.
|
|
||||||
You can't score if you don't have any Capsules with you.
|
|
||||||
The first player or team to get to the required ammount wins.
|
|
||||||
This is a gamemode all about trying to stay alive
|
|
||||||
and picking your battles in order to win.
|
|
||||||
A rare skill in BombSquad, where everyone is overly aggressive.
|
|
||||||
'''
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import random
|
|
||||||
import weakref
|
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.flag import Flag
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
|
||||||
from bascenev1lib.actor.popuptext import PopupText
|
|
||||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Any, Sequence
|
|
||||||
|
|
||||||
lang = bs.app.lang.language
|
|
||||||
if lang == 'Spanish':
|
|
||||||
name = 'Coleccionista'
|
|
||||||
description = ('Elimina a tus oponentes para robar sus cápsulas.\n'
|
|
||||||
'¡Recolecta y anota en el punto de depósito!')
|
|
||||||
description_ingame = 'Obtén ${ARG1} cápsulas de tus enemigos.'
|
|
||||||
description_short = 'colecciona ${ARG1} cápsulas'
|
|
||||||
tips = [(
|
|
||||||
'¡Si tu oponente cae fuera del mapa, sus cápsulas desapareceran!\n'
|
|
||||||
'No intestes matar a tus enemigos arrojándolos al vacio.'),
|
|
||||||
'No te apresures. ¡Puedes perder tus cápsulas rápidamente!',
|
|
||||||
('¡No dejes que el jugador con más cápsulas anote!\n'
|
|
||||||
'¡Intenta atraparlo si puedes!'),
|
|
||||||
('¡Las Capsulas de la Suerte te dan 4 cápsulas en lugar de 2'
|
|
||||||
'y tienen un 8% de probabilidad de aparecer después de matar'),
|
|
||||||
('¡No te quedes en un solo lugar! Muevete más rapido que tu enemigo, '
|
|
||||||
'¡con suerte conseguirás algunas cápsulas!'),
|
|
||||||
]
|
|
||||||
capsules_to_win = 'Cápsulas para Ganar'
|
|
||||||
capsules_death = 'Cápsulas al Morir'
|
|
||||||
lucky_capsules = 'Cápsulas de la Suerte'
|
|
||||||
bonus = '¡BONUS!'
|
|
||||||
full_capacity = '¡Capacidad Completa!'
|
|
||||||
else:
|
|
||||||
name = 'Collector'
|
|
||||||
description = ('Kill your opponents to steal their Capsules.\n'
|
|
||||||
'Collect them and score at the Deposit point!')
|
|
||||||
description_ingame = 'Score ${ARG1} capsules from your enemies.'
|
|
||||||
description_short = 'collect ${ARG1} capsules'
|
|
||||||
tips = [(
|
|
||||||
'Making you opponent fall down the pit makes his Capsules wasted!\n'
|
|
||||||
'Try not to kill enemies by throwing them off the cliff.'),
|
|
||||||
'Don\'t be too reckless. You can lose your loot quite quickly!',
|
|
||||||
('Don\'t let the leading player score his Capsules '
|
|
||||||
'at the Deposit Point!\nTry to catch him if you can!'),
|
|
||||||
('Lucky Capsules give 4 to your inventory and they have 8% chance '
|
|
||||||
'of spawning after kill!'),
|
|
||||||
('Don\'t camp in one place! Make your move first, '
|
|
||||||
'so hopefully you get some dough!'),
|
|
||||||
]
|
|
||||||
capsules_to_win = 'Capsules to Win'
|
|
||||||
capsules_death = 'Capsules on Death'
|
|
||||||
lucky_capsules = 'Allow Lucky Capsules'
|
|
||||||
bonus = 'BONUS!'
|
|
||||||
full_capacity = 'Full Capacity!'
|
|
||||||
|
|
||||||
|
|
||||||
class FlagState(Enum):
|
|
||||||
"""States our single flag can be in."""
|
|
||||||
|
|
||||||
NEW = 0
|
|
||||||
UNCONTESTED = 1
|
|
||||||
CONTESTED = 2
|
|
||||||
HELD = 3
|
|
||||||
|
|
||||||
|
|
||||||
class Player(bs.Player['Team']):
|
|
||||||
"""Our player type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.time_at_flag = 0
|
|
||||||
self.capsules = 0
|
|
||||||
self.light = None
|
|
||||||
|
|
||||||
|
|
||||||
class Team(bs.Team[Player]):
|
|
||||||
"""Our team type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.score = 0
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class CollectorGame(bs.TeamGameActivity[Player, Team]):
|
|
||||||
name = name
|
|
||||||
description = description
|
|
||||||
tips = tips
|
|
||||||
|
|
||||||
# Print messages when players die since it matters here.
|
|
||||||
announce_player_deaths = True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_available_settings(
|
|
||||||
cls, sessiontype: type[bs.Session]
|
|
||||||
) -> list[babase.Setting]:
|
|
||||||
settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
capsules_to_win,
|
|
||||||
min_value=1,
|
|
||||||
default=10,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntSetting(
|
|
||||||
capsules_death,
|
|
||||||
min_value=1,
|
|
||||||
max_value=10,
|
|
||||||
default=2,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting(lucky_capsules, default=True),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False),
|
|
||||||
]
|
|
||||||
return settings
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
|
|
||||||
return issubclass(sessiontype, bs.DualTeamSession) or issubclass(
|
|
||||||
sessiontype, bs.FreeForAllSession
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
|
|
||||||
return bs.app.classic.getmaps('keep_away')
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self._scoreboard = Scoreboard()
|
|
||||||
self._score_to_win: int | None = None
|
|
||||||
self._swipsound = bs.getsound('swip')
|
|
||||||
self._lucky_sound = bs.getsound('ding')
|
|
||||||
|
|
||||||
self._flag_pos: Sequence[float] | None = None
|
|
||||||
self._flag_state: FlagState | None = None
|
|
||||||
self._flag: Flag | None = None
|
|
||||||
self._flag_light: bs.Node | None = None
|
|
||||||
self._scoring_team: weakref.ref[Team] | None = None
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
|
|
||||||
self._capsules_to_win = int(settings[capsules_to_win])
|
|
||||||
self._capsules_death = int(settings[capsules_death])
|
|
||||||
self._lucky_capsules = bool(settings[lucky_capsules])
|
|
||||||
self._capsules: list[Any] = []
|
|
||||||
|
|
||||||
self._capsule_mesh = bs.getmesh('bomb')
|
|
||||||
self._capsule_tex = bs.gettexture('bombColor')
|
|
||||||
self._capsule_lucky_tex = bs.gettexture('bombStickyColor')
|
|
||||||
self._collect_sound = bs.getsound('powerup01')
|
|
||||||
self._lucky_collect_sound = bs.getsound('cashRegister2')
|
|
||||||
|
|
||||||
self._capsule_material = bs.Material()
|
|
||||||
self._capsule_material.add_actions(
|
|
||||||
conditions=('they_have_material', shared.player_material),
|
|
||||||
actions=('call', 'at_connect', self._on_capsule_player_collide),
|
|
||||||
)
|
|
||||||
|
|
||||||
self._flag_region_material = bs.Material()
|
|
||||||
self._flag_region_material.add_actions(
|
|
||||||
conditions=('they_have_material', shared.player_material),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', True),
|
|
||||||
('modify_part_collision', 'physical', False),
|
|
||||||
(
|
|
||||||
'call',
|
|
||||||
'at_connect',
|
|
||||||
babase.Call(self._handle_player_flag_region_collide, True),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
'call',
|
|
||||||
'at_disconnect',
|
|
||||||
babase.Call(self._handle_player_flag_region_collide, False),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Base class overrides.
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (
|
|
||||||
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.SCARY
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_instance_description(self) -> str | Sequence:
|
|
||||||
return description_ingame, self._score_to_win
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> str | Sequence:
|
|
||||||
return description_short, self._score_to_win
|
|
||||||
|
|
||||||
def create_team(self, sessionteam: bs.SessionTeam) -> Team:
|
|
||||||
return Team()
|
|
||||||
|
|
||||||
def on_team_join(self, team: Team) -> None:
|
|
||||||
self._update_scoreboard()
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self.setup_standard_time_limit(self._time_limit)
|
|
||||||
self.setup_standard_powerup_drops()
|
|
||||||
|
|
||||||
# Base kills needed to win on the size of the largest team.
|
|
||||||
self._score_to_win = self._capsules_to_win * max(
|
|
||||||
1, max(len(t.players) for t in self.teams)
|
|
||||||
)
|
|
||||||
self._update_scoreboard()
|
|
||||||
|
|
||||||
if isinstance(self.session, bs.FreeForAllSession):
|
|
||||||
self._flag_pos = self.map.get_flag_position(random.randint(0, 1))
|
|
||||||
else:
|
|
||||||
self._flag_pos = self.map.get_flag_position(None)
|
|
||||||
|
|
||||||
bs.timer(1.0, self._tick, repeat=True)
|
|
||||||
self._flag_state = FlagState.NEW
|
|
||||||
Flag.project_stand(self._flag_pos)
|
|
||||||
self._flag = Flag(
|
|
||||||
position=self._flag_pos, touchable=False, color=(1, 1, 1)
|
|
||||||
)
|
|
||||||
self._flag_light = bs.newnode(
|
|
||||||
'light',
|
|
||||||
attrs={
|
|
||||||
'position': self._flag_pos,
|
|
||||||
'intensity': 0.2,
|
|
||||||
'height_attenuated': False,
|
|
||||||
'radius': 0.4,
|
|
||||||
'color': (0.2, 0.2, 0.2),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
# Flag region.
|
|
||||||
flagmats = [self._flag_region_material, shared.region_material]
|
|
||||||
bs.newnode(
|
|
||||||
'region',
|
|
||||||
attrs={
|
|
||||||
'position': self._flag_pos,
|
|
||||||
'scale': (1.8, 1.8, 1.8),
|
|
||||||
'type': 'sphere',
|
|
||||||
'materials': flagmats,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self._update_flag_state()
|
|
||||||
|
|
||||||
def _tick(self) -> None:
|
|
||||||
self._update_flag_state()
|
|
||||||
|
|
||||||
if self._scoring_team is None:
|
|
||||||
scoring_team = None
|
|
||||||
else:
|
|
||||||
scoring_team = self._scoring_team()
|
|
||||||
|
|
||||||
if not scoring_team:
|
|
||||||
return
|
|
||||||
|
|
||||||
if isinstance(self.session, bs.FreeForAllSession):
|
|
||||||
players = self.players
|
|
||||||
else:
|
|
||||||
players = scoring_team.players
|
|
||||||
|
|
||||||
for player in players:
|
|
||||||
if player.time_at_flag > 0:
|
|
||||||
self.stats.player_scored(
|
|
||||||
player, 3, screenmessage=False, display=False
|
|
||||||
)
|
|
||||||
if player.capsules > 0:
|
|
||||||
if self._flag_state != FlagState.HELD:
|
|
||||||
return
|
|
||||||
if scoring_team.score >= self._score_to_win:
|
|
||||||
return
|
|
||||||
|
|
||||||
player.capsules -= 1
|
|
||||||
scoring_team.score += 1
|
|
||||||
self._handle_capsule_storage((
|
|
||||||
self._flag_pos[0],
|
|
||||||
self._flag_pos[1] + 1,
|
|
||||||
self._flag_pos[2]
|
|
||||||
), player)
|
|
||||||
self._collect_sound.play(0.8, position=self._flag_pos)
|
|
||||||
|
|
||||||
self._update_scoreboard()
|
|
||||||
if player.capsules > 0:
|
|
||||||
assert self._flag is not None
|
|
||||||
self._flag.set_score_text(
|
|
||||||
str(self._score_to_win - scoring_team.score))
|
|
||||||
|
|
||||||
# winner
|
|
||||||
if scoring_team.score >= self._score_to_win:
|
|
||||||
self.end_game()
|
|
||||||
|
|
||||||
def end_game(self) -> None:
|
|
||||||
results = bs.GameResults()
|
|
||||||
for team in self.teams:
|
|
||||||
results.set_team_score(team, team.score)
|
|
||||||
self.end(results=results, announce_delay=0)
|
|
||||||
|
|
||||||
def _update_flag_state(self) -> None:
|
|
||||||
holding_teams = set(
|
|
||||||
player.team for player in self.players if player.time_at_flag
|
|
||||||
)
|
|
||||||
prev_state = self._flag_state
|
|
||||||
assert self._flag_light
|
|
||||||
assert self._flag is not None
|
|
||||||
assert self._flag.node
|
|
||||||
if len(holding_teams) > 1:
|
|
||||||
self._flag_state = FlagState.CONTESTED
|
|
||||||
self._scoring_team = None
|
|
||||||
self._flag_light.color = (0.6, 0.6, 0.1)
|
|
||||||
self._flag.node.color = (1.0, 1.0, 0.4)
|
|
||||||
elif len(holding_teams) == 1:
|
|
||||||
holding_team = list(holding_teams)[0]
|
|
||||||
self._flag_state = FlagState.HELD
|
|
||||||
self._scoring_team = weakref.ref(holding_team)
|
|
||||||
self._flag_light.color = babase.normalized_color(holding_team.color)
|
|
||||||
self._flag.node.color = holding_team.color
|
|
||||||
else:
|
|
||||||
self._flag_state = FlagState.UNCONTESTED
|
|
||||||
self._scoring_team = None
|
|
||||||
self._flag_light.color = (0.2, 0.2, 0.2)
|
|
||||||
self._flag.node.color = (1, 1, 1)
|
|
||||||
if self._flag_state != prev_state:
|
|
||||||
self._swipsound.play()
|
|
||||||
|
|
||||||
def _handle_player_flag_region_collide(self, colliding: bool) -> None:
|
|
||||||
try:
|
|
||||||
spaz = bs.getcollision().opposingnode.getdelegate(PlayerSpaz, True)
|
|
||||||
except bs.NotFoundError:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not spaz.is_alive():
|
|
||||||
return
|
|
||||||
|
|
||||||
player = spaz.getplayer(Player, True)
|
|
||||||
|
|
||||||
# Different parts of us can collide so a single value isn't enough
|
|
||||||
# also don't count it if we're dead (flying heads shouldn't be able to
|
|
||||||
# win the game :-)
|
|
||||||
if colliding and player.is_alive():
|
|
||||||
player.time_at_flag += 1
|
|
||||||
else:
|
|
||||||
player.time_at_flag = max(0, player.time_at_flag - 1)
|
|
||||||
|
|
||||||
self._update_flag_state()
|
|
||||||
|
|
||||||
def _update_scoreboard(self) -> None:
|
|
||||||
for team in self.teams:
|
|
||||||
self._scoreboard.set_team_value(
|
|
||||||
team, team.score, self._score_to_win
|
|
||||||
)
|
|
||||||
|
|
||||||
def _drop_capsule(self, player: Player) -> None:
|
|
||||||
pt = player.node.position
|
|
||||||
|
|
||||||
# Throw out capsules that the victim has + 2 more to keep the game running
|
|
||||||
for i in range(player.capsules + self._capsules_death):
|
|
||||||
# How far from each other these capsules should spawn
|
|
||||||
w = 0.6
|
|
||||||
# How much these capsules should fly after spawning
|
|
||||||
s = 0.005 - (player.capsules * 0.01)
|
|
||||||
self._capsules.append(
|
|
||||||
Capsule(
|
|
||||||
position=(pt[0] + random.uniform(-w, w),
|
|
||||||
pt[1] + 0.75 + random.uniform(-w, w),
|
|
||||||
pt[2]),
|
|
||||||
velocity=(random.uniform(-s, s),
|
|
||||||
random.uniform(-s, s),
|
|
||||||
random.uniform(-s, s)),
|
|
||||||
lucky=False))
|
|
||||||
if random.randint(1, 12) == 1 and self._lucky_capsules:
|
|
||||||
# How far from each other these capsules should spawn
|
|
||||||
w = 0.6
|
|
||||||
# How much these capsules should fly after spawning
|
|
||||||
s = 0.005
|
|
||||||
self._capsules.append(
|
|
||||||
Capsule(
|
|
||||||
position=(pt[0] + random.uniform(-w, w),
|
|
||||||
pt[1] + 0.75 + random.uniform(-w, w),
|
|
||||||
pt[2]),
|
|
||||||
velocity=(random.uniform(-s, s),
|
|
||||||
random.uniform(-s, s),
|
|
||||||
random.uniform(-s, s)),
|
|
||||||
lucky=True))
|
|
||||||
|
|
||||||
def _on_capsule_player_collide(self) -> None:
|
|
||||||
if self.has_ended():
|
|
||||||
return
|
|
||||||
collision = bs.getcollision()
|
|
||||||
|
|
||||||
# Be defensive here; we could be hitting the corpse of a player
|
|
||||||
# who just left/etc.
|
|
||||||
try:
|
|
||||||
capsule = collision.sourcenode.getdelegate(Capsule, True)
|
|
||||||
player = collision.opposingnode.getdelegate(
|
|
||||||
PlayerSpaz, True
|
|
||||||
).getplayer(Player, True)
|
|
||||||
except bs.NotFoundError:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not player.is_alive():
|
|
||||||
return
|
|
||||||
|
|
||||||
if capsule.node.color_texture == self._capsule_lucky_tex:
|
|
||||||
player.capsules += 4
|
|
||||||
PopupText(
|
|
||||||
bonus,
|
|
||||||
color=(1, 1, 0),
|
|
||||||
scale=1.5,
|
|
||||||
position=capsule.node.position
|
|
||||||
).autoretain()
|
|
||||||
self._lucky_collect_sound.play(1.0, position=capsule.node.position)
|
|
||||||
bs.emitfx(
|
|
||||||
position=capsule.node.position,
|
|
||||||
velocity=(0, 0, 0),
|
|
||||||
count=int(6.4 + random.random() * 24),
|
|
||||||
scale=1.2,
|
|
||||||
spread=2.0,
|
|
||||||
chunk_type='spark')
|
|
||||||
bs.emitfx(
|
|
||||||
position=capsule.node.position,
|
|
||||||
velocity=(0, 0, 0),
|
|
||||||
count=int(4.0 + random.random() * 6),
|
|
||||||
emit_type='tendrils')
|
|
||||||
else:
|
|
||||||
player.capsules += 1
|
|
||||||
self._collect_sound.play(0.6, position=capsule.node.position)
|
|
||||||
# create a flash
|
|
||||||
light = bs.newnode(
|
|
||||||
'light',
|
|
||||||
attrs={
|
|
||||||
'position': capsule.node.position,
|
|
||||||
'height_attenuated': False,
|
|
||||||
'radius': 0.1,
|
|
||||||
'color': (1, 1, 0)})
|
|
||||||
|
|
||||||
# Create a short text informing about your inventory
|
|
||||||
self._handle_capsule_storage(player.position, player)
|
|
||||||
|
|
||||||
bs.animate(light, 'intensity', {
|
|
||||||
0: 0,
|
|
||||||
0.1: 0.5,
|
|
||||||
0.2: 0
|
|
||||||
}, loop=False)
|
|
||||||
bs.timer(0.2, light.delete)
|
|
||||||
capsule.handlemessage(bs.DieMessage())
|
|
||||||
|
|
||||||
def _update_player_light(self, player: Player, capsules: int) -> None:
|
|
||||||
if player.light:
|
|
||||||
intensity = 0.04 * capsules
|
|
||||||
bs.animate(player.light, 'intensity', {
|
|
||||||
0.0: player.light.intensity,
|
|
||||||
0.1: intensity
|
|
||||||
})
|
|
||||||
|
|
||||||
def newintensity():
|
|
||||||
player.light.intensity = intensity
|
|
||||||
|
|
||||||
bs.timer(0.1, newintensity)
|
|
||||||
else:
|
|
||||||
player.light = bs.newnode(
|
|
||||||
'light',
|
|
||||||
attrs={
|
|
||||||
'height_attenuated': False,
|
|
||||||
'radius': 0.2,
|
|
||||||
'intensity': 0.0,
|
|
||||||
'color': (0.2, 1, 0.2)
|
|
||||||
})
|
|
||||||
player.node.connectattr('position', player.light, 'position')
|
|
||||||
|
|
||||||
def _handle_capsule_storage(self, pos: float, player: Player) -> None:
|
|
||||||
capsules = player.capsules
|
|
||||||
text = str(capsules)
|
|
||||||
scale = 1.75 + (0.02 * capsules)
|
|
||||||
if capsules > 10:
|
|
||||||
player.capsules = 10
|
|
||||||
text = full_capacity
|
|
||||||
color = (1, 0.85, 0)
|
|
||||||
elif capsules > 7:
|
|
||||||
color = (1, 0, 0)
|
|
||||||
scale = 2.4
|
|
||||||
elif capsules > 5:
|
|
||||||
color = (1, 0.4, 0.4)
|
|
||||||
scale = 2.1
|
|
||||||
elif capsules > 3:
|
|
||||||
color = (1, 1, 0.4)
|
|
||||||
scale = 2.0
|
|
||||||
else:
|
|
||||||
color = (1, 1, 1)
|
|
||||||
scale = 1.9
|
|
||||||
PopupText(
|
|
||||||
text,
|
|
||||||
color=color,
|
|
||||||
scale=scale,
|
|
||||||
position=(pos[0], pos[1] - 1, pos[2])
|
|
||||||
).autoretain()
|
|
||||||
self._update_player_light(player, capsules)
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
if isinstance(msg, bs.PlayerDiedMessage):
|
|
||||||
super().handlemessage(msg) # Augment default.
|
|
||||||
# No longer can count as time_at_flag once dead.
|
|
||||||
player = msg.getplayer(Player)
|
|
||||||
player.time_at_flag = 0
|
|
||||||
self._update_flag_state()
|
|
||||||
self._drop_capsule(player)
|
|
||||||
player.capsules = 0
|
|
||||||
self._update_player_light(player, 0)
|
|
||||||
self.respawn_player(player)
|
|
||||||
else:
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class Capsule(bs.Actor):
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
position: Sequence[float] = (0.0, 1.0, 0.0),
|
|
||||||
velocity: Sequence[float] = (0.0, 0.5, 0.0),
|
|
||||||
lucky: bool = False):
|
|
||||||
super().__init__()
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
activity = self.getactivity()
|
|
||||||
|
|
||||||
# spawn just above the provided point
|
|
||||||
self._spawn_pos = (position[0], position[1], position[2])
|
|
||||||
|
|
||||||
if lucky:
|
|
||||||
activity._lucky_sound.play(1.0, self._spawn_pos)
|
|
||||||
|
|
||||||
self.node = bs.newnode(
|
|
||||||
'prop',
|
|
||||||
attrs={
|
|
||||||
'mesh': activity._capsule_mesh,
|
|
||||||
'color_texture': activity._capsule_lucky_tex if lucky else (
|
|
||||||
activity._capsule_tex),
|
|
||||||
'body': 'crate' if lucky else 'capsule',
|
|
||||||
'reflection': 'powerup' if lucky else 'soft',
|
|
||||||
'body_scale': 0.65 if lucky else 0.3,
|
|
||||||
'density': 6.0 if lucky else 4.0,
|
|
||||||
'reflection_scale': [0.15],
|
|
||||||
'shadow_size': 0.65 if lucky else 0.6,
|
|
||||||
'position': self._spawn_pos,
|
|
||||||
'velocity': velocity,
|
|
||||||
'materials': [
|
|
||||||
shared.object_material, activity._capsule_material]
|
|
||||||
},
|
|
||||||
delegate=self)
|
|
||||||
bs.animate(self.node, 'mesh_scale', {
|
|
||||||
0.0: 0.0,
|
|
||||||
0.1: 0.9 if lucky else 0.6,
|
|
||||||
0.16: 0.8 if lucky else 0.5
|
|
||||||
})
|
|
||||||
self._light_capsule = bs.newnode(
|
|
||||||
'light',
|
|
||||||
attrs={
|
|
||||||
'position': self._spawn_pos,
|
|
||||||
'height_attenuated': False,
|
|
||||||
'radius': 0.5 if lucky else 0.1,
|
|
||||||
'color': (0.2, 0.2, 0) if lucky else (0.2, 1, 0.2)
|
|
||||||
})
|
|
||||||
self.node.connectattr('position', self._light_capsule, 'position')
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any):
|
|
||||||
if isinstance(msg, bs.DieMessage):
|
|
||||||
self.node.delete()
|
|
||||||
bs.animate(self._light_capsule, 'intensity', {
|
|
||||||
0: 1.0,
|
|
||||||
0.05: 0.0
|
|
||||||
}, loop=False)
|
|
||||||
bs.timer(0.05, self._light_capsule.delete)
|
|
||||||
elif isinstance(msg, bs.OutOfBoundsMessage):
|
|
||||||
self.handlemessage(bs.DieMessage())
|
|
||||||
elif isinstance(msg, bs.HitMessage):
|
|
||||||
self.node.handlemessage(
|
|
||||||
'impulse',
|
|
||||||
msg.pos[0], msg.pos[1], msg.pos[2],
|
|
||||||
msg.velocity[0] / 8, msg.velocity[1] / 8, msg.velocity[2] / 8,
|
|
||||||
1.0 * msg.magnitude, 1.0 * msg.velocity_magnitude, msg.radius,
|
|
||||||
0,
|
|
||||||
msg.force_direction[0], msg.force_direction[1],
|
|
||||||
msg.force_direction[2])
|
|
||||||
else:
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
314
dist/ba_root/mods/games/demolition_war.py
vendored
314
dist/ba_root/mods/games/demolition_war.py
vendored
|
|
@ -1,314 +0,0 @@
|
||||||
# ba_meta require api 8
|
|
||||||
"""
|
|
||||||
DemolitionWar - BombFight on wooden floor flying in air.
|
|
||||||
Author: Mr.Smoothy
|
|
||||||
Discord: https://discord.gg/ucyaesh
|
|
||||||
Youtube: https://www.youtube.com/c/HeySmoothy
|
|
||||||
Website: https://bombsquad-community.web.app
|
|
||||||
Github: https://github.com/bombsquad-community
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import random
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.bomb import BombFactory
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
|
||||||
from bascenev1lib.game.elimination import EliminationGame, Player
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
|
|
||||||
|
|
||||||
class DemolitionWar(EliminationGame):
|
|
||||||
name = 'DemolitionWar'
|
|
||||||
description = 'Last remaining alive wins.'
|
|
||||||
scoreconfig = bs.ScoreConfig(
|
|
||||||
label='Survived', scoretype=bs.ScoreType.SECONDS, none_is_winner=True
|
|
||||||
)
|
|
||||||
# Show messages when players die since it's meaningful here.
|
|
||||||
announce_player_deaths = True
|
|
||||||
|
|
||||||
allow_mid_activity_joins = False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_available_settings(
|
|
||||||
cls, sessiontype: type[bs.Session]
|
|
||||||
) -> list[babase.Setting]:
|
|
||||||
settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Lives Per Player',
|
|
||||||
default=1,
|
|
||||||
min_value=1,
|
|
||||||
max_value=10,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False),
|
|
||||||
]
|
|
||||||
if issubclass(sessiontype, bs.DualTeamSession):
|
|
||||||
settings.append(bs.BoolSetting('Solo Mode', default=False))
|
|
||||||
settings.append(
|
|
||||||
bs.BoolSetting('Balance Total Lives', default=False)
|
|
||||||
)
|
|
||||||
return settings
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
|
|
||||||
return issubclass(sessiontype, bs.DualTeamSession) or issubclass(
|
|
||||||
sessiontype, bs.FreeForAllSession
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
|
|
||||||
return ['Wooden Floor']
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
self._lives_per_player = 1
|
|
||||||
self._solo_mode = False
|
|
||||||
self._balance_total_lives = False
|
|
||||||
|
|
||||||
def spawn_player(self, player: Player) -> bs.Actor:
|
|
||||||
p = [-6, -4.3, -2.6, -0.9, 0.8, 2.5, 4.2, 5.9]
|
|
||||||
q = [-4, -2.3, -0.6, 1.1, 2.8, 4.5]
|
|
||||||
|
|
||||||
x = random.randrange(0, len(p))
|
|
||||||
y = random.randrange(0, len(q))
|
|
||||||
spaz = self.spawn_player_spaz(player, position=(p[x], 1.8, q[y]))
|
|
||||||
spaz.bomb_type = 'impact'
|
|
||||||
# Let's reconnect this player's controls to this
|
|
||||||
# spaz but *without* the ability to attack or pick stuff up.
|
|
||||||
spaz.connect_controls_to_player(enable_punch=False,
|
|
||||||
enable_bomb=True,
|
|
||||||
enable_pickup=True)
|
|
||||||
|
|
||||||
# Also lets have them make some noise when they die.
|
|
||||||
spaz.play_big_death_sound = True
|
|
||||||
return spaz
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
self.map_extend()
|
|
||||||
|
|
||||||
def on_blast(self):
|
|
||||||
node = bs.getcollision().sourcenode
|
|
||||||
bs.emitfx((node.position[0], 0.9, node.position[2]),
|
|
||||||
(0, 2, 0), 30, 1, spread=1, chunk_type='splinter')
|
|
||||||
bs.timer(0.1, babase.Call(node.delete))
|
|
||||||
|
|
||||||
def map_extend(self):
|
|
||||||
# TODO need to improve here , so we can increase size of map easily with settings
|
|
||||||
p = [-6, -4.3, -2.6, -0.9, 0.8, 2.5, 4.2, 5.9]
|
|
||||||
q = [-4, -2.3, -0.6, 1.1, 2.8, 4.5]
|
|
||||||
factory = BombFactory.get()
|
|
||||||
self.ramp_bomb = bs.Material()
|
|
||||||
self.ramp_bomb.add_actions(
|
|
||||||
conditions=('they_have_material', factory.bomb_material),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', True),
|
|
||||||
('modify_part_collision', 'physical', True),
|
|
||||||
('call', 'at_connect', babase.Call(self.on_blast))
|
|
||||||
))
|
|
||||||
self.ramps = []
|
|
||||||
for i in p:
|
|
||||||
for j in q:
|
|
||||||
self.ramps.append(self.create_ramp(i, j))
|
|
||||||
|
|
||||||
def create_ramp(self, x, z):
|
|
||||||
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self._real_collied_material = bs.Material()
|
|
||||||
|
|
||||||
self._real_collied_material.add_actions(
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', True),
|
|
||||||
('modify_part_collision', 'physical', True)
|
|
||||||
|
|
||||||
))
|
|
||||||
self.mat = bs.Material()
|
|
||||||
self.mat.add_actions(
|
|
||||||
actions=(('modify_part_collision', 'physical', False),
|
|
||||||
('modify_part_collision', 'collide', False))
|
|
||||||
)
|
|
||||||
pos = (x, 0, z)
|
|
||||||
ud_1_r = bs.newnode('region',
|
|
||||||
attrs={'position': pos, 'scale': (1.5, 1, 1.5),
|
|
||||||
'type': 'box', 'materials': [
|
|
||||||
shared.footing_material,
|
|
||||||
self._real_collied_material,
|
|
||||||
self.ramp_bomb]})
|
|
||||||
|
|
||||||
node = bs.newnode('prop',
|
|
||||||
owner=ud_1_r,
|
|
||||||
attrs={
|
|
||||||
'mesh': bs.getmesh('image1x1'),
|
|
||||||
'light_mesh': bs.getmesh('powerupSimple'),
|
|
||||||
'position': (2, 7, 2),
|
|
||||||
'body': 'puck',
|
|
||||||
'shadow_size': 0.0,
|
|
||||||
'velocity': (0, 0, 0),
|
|
||||||
'color_texture': bs.gettexture('tnt'),
|
|
||||||
'mesh_scale': 1.5,
|
|
||||||
'reflection_scale': [1.5],
|
|
||||||
'materials': [self.mat, shared.object_material,
|
|
||||||
shared.footing_material],
|
|
||||||
'density': 9000000000
|
|
||||||
})
|
|
||||||
# node.changerotation(1, 0, 0)
|
|
||||||
mnode = bs.newnode('math',
|
|
||||||
owner=ud_1_r,
|
|
||||||
attrs={
|
|
||||||
'input1': (0, 0.6, 0),
|
|
||||||
'operation': 'add'
|
|
||||||
})
|
|
||||||
ud_1_r.connectattr('position', mnode, 'input2')
|
|
||||||
mnode.connectattr('output', node, 'position')
|
|
||||||
return ud_1_r
|
|
||||||
|
|
||||||
|
|
||||||
class mapdefs:
|
|
||||||
points = {}
|
|
||||||
# noinspection PyDictCreation
|
|
||||||
boxes = {}
|
|
||||||
boxes['area_of_interest_bounds'] = (0.0, 1.185751251, 0.4326226188) + (
|
|
||||||
0.0, 0.0, 0.0) + (29.8180273, 11.57249038, 18.89134176)
|
|
||||||
boxes['edge_box'] = (-0.103873591, 0.4133341891, 0.4294651013) + (
|
|
||||||
0.0, 0.0, 0.0) + (22.48295719, 1.290242794, 8.990252454)
|
|
||||||
points['ffa_spawn1'] = (-0.08015551329, 0.02275111462,
|
|
||||||
-4.373674593) + (8.895057015, 1.0, 0.444350722)
|
|
||||||
points['ffa_spawn2'] = (-0.08015551329, 0.02275111462,
|
|
||||||
4.076288941) + (8.895057015, 1.0, 0.444350722)
|
|
||||||
points['flag1'] = (-10.99027878, 0.05744967453, 0.1095578275)
|
|
||||||
points['flag2'] = (11.01486398, 0.03986567039, 0.1095578275)
|
|
||||||
points['flag_default'] = (-0.1001374046, 0.04180340146, 0.1095578275)
|
|
||||||
boxes['goal1'] = (12.22454533, 1.0,
|
|
||||||
0.1087926362) + (0.0, 0.0, 0.0) + (2.0, 2.0, 12.97466313)
|
|
||||||
boxes['goal2'] = (-12.15961605, 1.0,
|
|
||||||
0.1097860203) + (0.0, 0.0, 0.0) + (2.0, 2.0, 13.11856424)
|
|
||||||
boxes['map_bounds'] = (0.0, 1.185751251, 0.4326226188) + (0.0, 0.0, 0.0) + (
|
|
||||||
42.09506485, 22.81173179, 29.76723155)
|
|
||||||
points['powerup_spawn1'] = (5.414681236, 0.9515026107, -5.037912441)
|
|
||||||
points['powerup_spawn2'] = (-5.555402285, 0.9515026107, -5.037912441)
|
|
||||||
points['powerup_spawn3'] = (5.414681236, 0.9515026107, 5.148223181)
|
|
||||||
points['powerup_spawn4'] = (-5.737266365, 0.9515026107, 5.148223181)
|
|
||||||
points['spawn1'] = (-10.03866341, 0.02275111462, 0.0) + (0.5, 1.0, 4.0)
|
|
||||||
points['spawn2'] = (9.823107149, 0.01092306765, 0.0) + (0.5, 1.0, 4.0)
|
|
||||||
points['tnt1'] = (-0.08421587483, 0.9515026107, -0.7762602271)
|
|
||||||
|
|
||||||
|
|
||||||
class WoodenFloor(
|
|
||||||
bs._map.Map): # ahdunno if this is correct way, change if u find better way
|
|
||||||
"""Stadium map for football games."""
|
|
||||||
defs = mapdefs
|
|
||||||
defs.points['spawn1'] = (-12.03866341, 0.02275111462, 0.0) + (0.5, 1.0, 4.0)
|
|
||||||
defs.points['spawn2'] = (12.823107149, 0.01092306765, 0.0) + (0.5, 1.0, 4.0)
|
|
||||||
name = 'Wooden Floor'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_play_types(cls) -> list[str]:
|
|
||||||
"""Return valid play types for this map."""
|
|
||||||
return ['melee', 'football', 'team_flag', 'keep_away']
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_preview_texture_name(cls) -> str:
|
|
||||||
return 'footballStadiumPreview'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def on_preload(cls) -> Any:
|
|
||||||
data: dict[str, Any] = {
|
|
||||||
|
|
||||||
'mesh_bg': bs.getmesh('doomShroomBG'),
|
|
||||||
'bg_vr_fill_mesh': bs.getmesh('natureBackgroundVRFill'),
|
|
||||||
'collide_mesh': bs.getcollisionmesh('bridgitLevelCollide'),
|
|
||||||
'tex': bs.gettexture('bridgitLevelColor'),
|
|
||||||
'mesh_bg_tex': bs.gettexture('doomShroomBGColor'),
|
|
||||||
'collide_bg': bs.getcollisionmesh('natureBackgroundCollide'),
|
|
||||||
'railing_collide_mesh':
|
|
||||||
(bs.getcollisionmesh('bridgitLevelRailingCollide')),
|
|
||||||
'bg_material': bs.Material()
|
|
||||||
}
|
|
||||||
data['bg_material'].add_actions(actions=('modify_part_collision',
|
|
||||||
'friction', 10.0))
|
|
||||||
return data
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
super().__init__()
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self.background = bs.newnode(
|
|
||||||
'terrain',
|
|
||||||
attrs={
|
|
||||||
'mesh': self.preloaddata['mesh_bg'],
|
|
||||||
'lighting': False,
|
|
||||||
'background': True,
|
|
||||||
'color_texture': self.preloaddata['mesh_bg_tex']
|
|
||||||
})
|
|
||||||
self.vr = bs.newnode('terrain',
|
|
||||||
attrs={
|
|
||||||
'mesh': self.preloaddata['bg_vr_fill_mesh'],
|
|
||||||
'lighting': False,
|
|
||||||
'vr_only': True,
|
|
||||||
'background': True,
|
|
||||||
'color_texture': self.preloaddata[
|
|
||||||
'mesh_bg_tex']
|
|
||||||
})
|
|
||||||
gnode = bs.getactivity().globalsnode
|
|
||||||
gnode.tint = (1.3, 1.2, 1.0)
|
|
||||||
gnode.ambient_color = (1.3, 1.2, 1.0)
|
|
||||||
gnode.vignette_outer = (0.57, 0.57, 0.57)
|
|
||||||
gnode.vignette_inner = (0.9, 0.9, 0.9)
|
|
||||||
gnode.vr_camera_offset = (0, -0.8, -1.1)
|
|
||||||
gnode.vr_near_clip = 0.5
|
|
||||||
|
|
||||||
def is_point_near_edge(self,
|
|
||||||
point: babase.Vec3,
|
|
||||||
running: bool = False) -> bool:
|
|
||||||
box_position = self.defs.boxes['edge_box'][0:3]
|
|
||||||
box_scale = self.defs.boxes['edge_box'][6:9]
|
|
||||||
xpos = (point.x - box_position[0]) / box_scale[0]
|
|
||||||
zpos = (point.z - box_position[2]) / box_scale[2]
|
|
||||||
return xpos < -0.5 or xpos > 0.5 or zpos < -0.5 or zpos > 0.5
|
|
||||||
|
|
||||||
def _handle_player_collide(self):
|
|
||||||
try:
|
|
||||||
player = bs.getcollision().opposingnode.getdelegate(
|
|
||||||
PlayerSpaz, True)
|
|
||||||
except bs.NotFoundError:
|
|
||||||
return
|
|
||||||
if player.is_alive():
|
|
||||||
player.shatter(True)
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
bs._map.register_map(WoodenFloor)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
776
dist/ba_root/mods/games/dodge_the_ball.py
vendored
776
dist/ba_root/mods/games/dodge_the_ball.py
vendored
|
|
@ -1,776 +0,0 @@
|
||||||
"""
|
|
||||||
|
|
||||||
DondgeTheBall minigame by EmperoR#4098
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Feel free to edit.
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from enum import Enum
|
|
||||||
from random import choice
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.bomb import Blast
|
|
||||||
from bascenev1lib.actor.onscreencountdown import OnScreenCountdown
|
|
||||||
from bascenev1lib.actor.popuptext import PopupText
|
|
||||||
from bascenev1lib.actor.powerupbox import PowerupBox
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import NoReturn, Sequence, Any
|
|
||||||
|
|
||||||
|
|
||||||
# Type of ball in this game
|
|
||||||
class BallType(Enum):
|
|
||||||
""" Types of ball """
|
|
||||||
EASY = 0
|
|
||||||
# Decrease the next ball shooting speed(not ball speed).
|
|
||||||
# Ball color is yellow.
|
|
||||||
MEDIUM = 1
|
|
||||||
# increase the next ball shooting speed(not ball speed).
|
|
||||||
# target the head of player.
|
|
||||||
# Ball color is purple.
|
|
||||||
HARD = 2
|
|
||||||
# Target player according to player movement (not very accurate).
|
|
||||||
# Taget: player head.
|
|
||||||
# increase the next ball speed but less than MEDIUM.
|
|
||||||
# Ball color is crimson(purple+red = pinky color type).
|
|
||||||
|
|
||||||
|
|
||||||
# this dict decide the ball_type spawning rate like powerup box
|
|
||||||
ball_type_dict: dict[BallType, int] = {
|
|
||||||
BallType.EASY: 3,
|
|
||||||
BallType.MEDIUM: 2,
|
|
||||||
BallType.HARD: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Ball(bs.Actor):
|
|
||||||
""" Shooting Ball """
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
position: Sequence[float],
|
|
||||||
velocity: Sequence[float],
|
|
||||||
texture: babase.Texture,
|
|
||||||
body_scale: float = 1.0,
|
|
||||||
gravity_scale: float = 1.0,
|
|
||||||
) -> NoReturn:
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
|
|
||||||
ball_material = bs.Material()
|
|
||||||
ball_material.add_actions(
|
|
||||||
conditions=(
|
|
||||||
(
|
|
||||||
('we_are_younger_than', 100),
|
|
||||||
'or',
|
|
||||||
('they_are_younger_than', 100),
|
|
||||||
),
|
|
||||||
'and',
|
|
||||||
('they_have_material', shared.object_material),
|
|
||||||
),
|
|
||||||
actions=('modify_node_collision', 'collide', False),
|
|
||||||
)
|
|
||||||
|
|
||||||
self.node = bs.newnode(
|
|
||||||
'prop',
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
'body': 'sphere',
|
|
||||||
'position': position,
|
|
||||||
'velocity': velocity,
|
|
||||||
'body_scale': body_scale,
|
|
||||||
'mesh': bs.getmesh('frostyPelvis'),
|
|
||||||
'mesh_scale': body_scale,
|
|
||||||
'color_texture': texture,
|
|
||||||
'gravity_scale': gravity_scale,
|
|
||||||
'density': 4.0,
|
|
||||||
# increase density of ball so ball collide with player with heavy force. # ammm very bad grammer
|
|
||||||
'materials': (ball_material,),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# die the ball manually incase the ball doesn't fall the outside of the map
|
|
||||||
bs.timer(2.5, bs.WeakCall(self.handlemessage, bs.DieMessage()))
|
|
||||||
|
|
||||||
# i am not handling anything in this ball Class(except for diemessage).
|
|
||||||
# all game things and logics going to be in the box class
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
|
|
||||||
if isinstance(msg, bs.DieMessage):
|
|
||||||
self.node.delete()
|
|
||||||
else:
|
|
||||||
super().handlemessage(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class Box(bs.Actor):
|
|
||||||
""" A box that spawn midle of map as a decoration perpose """
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
position: Sequence[float],
|
|
||||||
velocity: Sequence[float],
|
|
||||||
) -> NoReturn:
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
# self.ball_jump = 0.0;
|
|
||||||
no_hit_material = bs.Material()
|
|
||||||
# we don't need that the box was move and collide with objects.
|
|
||||||
no_hit_material.add_actions(
|
|
||||||
conditions=(
|
|
||||||
('they_have_material', shared.pickup_material),
|
|
||||||
'or',
|
|
||||||
('they_have_material', shared.attack_material),
|
|
||||||
),
|
|
||||||
actions=('modify_part_collision', 'collide', False),
|
|
||||||
)
|
|
||||||
|
|
||||||
no_hit_material.add_actions(
|
|
||||||
conditions=(
|
|
||||||
('they_have_material', shared.object_material),
|
|
||||||
'or',
|
|
||||||
('they_dont_have_material', shared.footing_material),
|
|
||||||
),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', False),
|
|
||||||
('modify_part_collision', 'physical', False),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
self.node = bs.newnode(
|
|
||||||
'prop',
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
'body': 'box',
|
|
||||||
'position': position,
|
|
||||||
'mesh': bs.getmesh('powerup'),
|
|
||||||
'light_mesh': bs.getmesh('powerupSimple'),
|
|
||||||
'shadow_size': 0.5,
|
|
||||||
'body_scale': 1.4,
|
|
||||||
'mesh_scale': 1.4,
|
|
||||||
'color_texture': bs.gettexture('landMineLit'),
|
|
||||||
'reflection': 'powerup',
|
|
||||||
'reflection_scale': [1.0],
|
|
||||||
'materials': (no_hit_material,),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
# light
|
|
||||||
self.light = bs.newnode(
|
|
||||||
"light",
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'radius': 0.2,
|
|
||||||
'intensity': 0.8,
|
|
||||||
'color': (0.0, 1.0, 0.0),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.node.connectattr("position", self.light, "position")
|
|
||||||
# Drawing circle and circleOutline in radius of 3,
|
|
||||||
# so player can see that how close he is to the box.
|
|
||||||
# If player is inside this circle the ball speed will increase.
|
|
||||||
circle = bs.newnode(
|
|
||||||
"locator",
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'shape': 'circle',
|
|
||||||
'color': (1.0, 0.0, 0.0),
|
|
||||||
'opacity': 0.1,
|
|
||||||
'size': (6.0, 0.0, 6.0),
|
|
||||||
'draw_beauty': False,
|
|
||||||
'additive': True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.node.connectattr("position", circle, "position")
|
|
||||||
# also adding a outline cause its look nice.
|
|
||||||
circle_outline = bs.newnode(
|
|
||||||
"locator",
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'shape': 'circleOutline',
|
|
||||||
'color': (1.0, 1.0, 0.0),
|
|
||||||
'opacity': 0.1,
|
|
||||||
'size': (6.0, 0.0, 6.0),
|
|
||||||
'draw_beauty': False,
|
|
||||||
'additive': True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
self.node.connectattr("position", circle_outline, "position")
|
|
||||||
|
|
||||||
# all ball attribute that we need.
|
|
||||||
self.ball_type: BallType = BallType.EASY
|
|
||||||
self.shoot_timer: bs.Timer | None = None
|
|
||||||
self.shoot_speed: float = 0.0
|
|
||||||
# this force the shoot if player is inside the red circle.
|
|
||||||
self.force_shoot_speed: float = 0.0
|
|
||||||
self.ball_mag = 3000
|
|
||||||
self.ball_gravity: float = 1.0
|
|
||||||
self.ball_tex: babase.Texture | None = None
|
|
||||||
# only for Hard ball_type
|
|
||||||
self.player_facing_direction: list[float, float] = [0.0, 0.0]
|
|
||||||
# ball shoot soound.
|
|
||||||
self.shoot_sound = bs.getsound('laserReverse')
|
|
||||||
|
|
||||||
# same as "powerupdist"
|
|
||||||
self.ball_type_dist: list[BallType] = []
|
|
||||||
|
|
||||||
for ball in ball_type_dict:
|
|
||||||
for _ in range(ball_type_dict[ball]):
|
|
||||||
self.ball_type_dist.append(ball)
|
|
||||||
|
|
||||||
# Here main logic of game goes here.
|
|
||||||
# like shoot balls, shoot speed, anything we want goes here(except for some thing).
|
|
||||||
def start_shoot(self) -> NoReturn:
|
|
||||||
|
|
||||||
# getting all allive players in a list.
|
|
||||||
alive_players_list = self.activity.get_alive_players()
|
|
||||||
|
|
||||||
# make sure that list is not Empty.
|
|
||||||
if len(alive_players_list) > 0:
|
|
||||||
|
|
||||||
# choosing a random player from list.
|
|
||||||
target_player = choice(alive_players_list)
|
|
||||||
# highlight the target player
|
|
||||||
self.highlight_target_player(target_player)
|
|
||||||
|
|
||||||
# to finding difference between player and box.
|
|
||||||
# we just need to subtract player pos and ball pos.
|
|
||||||
# Same logic as eric applied in Target Practice Gamemode.
|
|
||||||
difference = babase.Vec3(target_player.position) - babase.Vec3(
|
|
||||||
self.node.position)
|
|
||||||
|
|
||||||
# discard Y position so ball shoot more straight.
|
|
||||||
difference[1] = 0.0
|
|
||||||
|
|
||||||
# and now, this length method returns distance in float.
|
|
||||||
# we're gonna use this value for calculating player analog stick
|
|
||||||
distance = difference.length()
|
|
||||||
|
|
||||||
# shoot a random BallType
|
|
||||||
self.upgrade_ball_type(choice(self.ball_type_dist))
|
|
||||||
|
|
||||||
# and check the ball_type and upgrade it gravity_scale, texture, next ball speed.
|
|
||||||
self.check_ball_type(self.ball_type)
|
|
||||||
|
|
||||||
# For HARD ball i am just focusing on player analog stick facing direction.
|
|
||||||
# Not very accurate and that's we need.
|
|
||||||
if self.ball_type == BallType.HARD:
|
|
||||||
self.calculate_player_analog_stick(target_player, distance)
|
|
||||||
else:
|
|
||||||
self.player_facing_direction = [0.0, 0.0]
|
|
||||||
|
|
||||||
pos = self.node.position
|
|
||||||
|
|
||||||
if self.ball_type == BallType.MEDIUM or self.ball_type == BallType.HARD:
|
|
||||||
# Target head by increasing Y pos.
|
|
||||||
# How this work? cause ball gravity_scale is ......
|
|
||||||
pos = (pos[0], pos[1] + .25, pos[2])
|
|
||||||
|
|
||||||
# ball is generating..
|
|
||||||
ball = Ball(
|
|
||||||
position=pos,
|
|
||||||
velocity=(0.0, 0.0, 0.0),
|
|
||||||
texture=self.ball_tex,
|
|
||||||
gravity_scale=self.ball_gravity,
|
|
||||||
body_scale=1.0,
|
|
||||||
).autoretain()
|
|
||||||
|
|
||||||
# shoot Animation and sound.
|
|
||||||
self.shoot_animation()
|
|
||||||
|
|
||||||
# force the shoot speed if player try to go inside the red circle.
|
|
||||||
if self.force_shoot_speed != 0.0:
|
|
||||||
self.shoot_speed = self.force_shoot_speed
|
|
||||||
|
|
||||||
# push the ball to the player
|
|
||||||
ball.node.handlemessage(
|
|
||||||
'impulse',
|
|
||||||
self.node.position[0], # ball spawn position X
|
|
||||||
self.node.position[1], # Y
|
|
||||||
self.node.position[2], # Z
|
|
||||||
0, 0, 0, # velocity x,y,z
|
|
||||||
self.ball_mag, # magnetude
|
|
||||||
0.000, # magnetude velocity
|
|
||||||
0.000, # radius
|
|
||||||
0.000, # idk
|
|
||||||
difference[0] + self.player_facing_direction[0],
|
|
||||||
# force direction X
|
|
||||||
difference[1], # force direction Y
|
|
||||||
difference[2] + self.player_facing_direction[1],
|
|
||||||
# force direction Z
|
|
||||||
)
|
|
||||||
# creating our timer and shoot the ball again.(and we create a loop)
|
|
||||||
self.shoot_timer = bs.Timer(self.shoot_speed, self.start_shoot)
|
|
||||||
|
|
||||||
def upgrade_ball_type(self, ball_type: BallType) -> NoReturn:
|
|
||||||
|
|
||||||
self.ball_type = ball_type
|
|
||||||
|
|
||||||
def check_ball_type(self, ball_type: BallType) -> NoReturn:
|
|
||||||
|
|
||||||
if ball_type == BallType.EASY:
|
|
||||||
self.shoot_speed = 0.8
|
|
||||||
self.ball_gravity = 1.0
|
|
||||||
# next ball shoot speed
|
|
||||||
self.ball_mag = 3000
|
|
||||||
# box light color and ball tex
|
|
||||||
self.light.color = (1.0, 1.0, 0.0)
|
|
||||||
self.ball_tex = bs.gettexture('egg4')
|
|
||||||
elif ball_type == BallType.MEDIUM:
|
|
||||||
self.ball_mag = 3000
|
|
||||||
# decrease the gravity scale so, ball shoot without falling and straight.
|
|
||||||
self.ball_gravity = 0.0
|
|
||||||
# next ball shoot speed.
|
|
||||||
self.shoot_speed = 0.4
|
|
||||||
# box light color and ball tex.
|
|
||||||
self.light.color = (1.0, 0.0, 1.0)
|
|
||||||
self.ball_tex = bs.gettexture('egg3')
|
|
||||||
elif ball_type == BallType.HARD:
|
|
||||||
self.ball_mag = 2500
|
|
||||||
self.ball_gravity = 0.0
|
|
||||||
# next ball shoot speed.
|
|
||||||
self.shoot_speed = 0.6
|
|
||||||
# box light color and ball tex.
|
|
||||||
self.light.color = (1.0, 0.2, 1.0)
|
|
||||||
self.ball_tex = bs.gettexture('egg1')
|
|
||||||
|
|
||||||
def shoot_animation(self) -> NoReturn:
|
|
||||||
|
|
||||||
bs.animate(
|
|
||||||
self.node,
|
|
||||||
"mesh_scale", {
|
|
||||||
0.00: 1.4,
|
|
||||||
0.05: 1.7,
|
|
||||||
0.10: 1.4,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# playing shoot sound.
|
|
||||||
# self.shoot_sound, position = self.node.position.play();
|
|
||||||
self.shoot_sound.play()
|
|
||||||
|
|
||||||
def highlight_target_player(self, player: bs.Player) -> NoReturn:
|
|
||||||
|
|
||||||
# adding light
|
|
||||||
light = bs.newnode(
|
|
||||||
"light",
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'radius': 0.0,
|
|
||||||
'intensity': 1.0,
|
|
||||||
'color': (1.0, 0.0, 0.0),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
bs.animate(
|
|
||||||
light,
|
|
||||||
"radius", {
|
|
||||||
0.05: 0.02,
|
|
||||||
0.10: 0.07,
|
|
||||||
0.15: 0.15,
|
|
||||||
0.20: 0.13,
|
|
||||||
0.25: 0.10,
|
|
||||||
0.30: 0.05,
|
|
||||||
0.35: 0.02,
|
|
||||||
0.40: 0.00,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# And a circle outline with ugly animation.
|
|
||||||
circle_outline = bs.newnode(
|
|
||||||
"locator",
|
|
||||||
owner=player.actor.node,
|
|
||||||
attrs={
|
|
||||||
'shape': 'circleOutline',
|
|
||||||
'color': (1.0, 0.0, 0.0),
|
|
||||||
'opacity': 1.0,
|
|
||||||
'draw_beauty': False,
|
|
||||||
'additive': True,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
bs.animate_array(
|
|
||||||
circle_outline,
|
|
||||||
'size',
|
|
||||||
1, {
|
|
||||||
0.05: [0.5],
|
|
||||||
0.10: [0.8],
|
|
||||||
0.15: [1.5],
|
|
||||||
0.20: [2.0],
|
|
||||||
0.25: [1.8],
|
|
||||||
0.30: [1.3],
|
|
||||||
0.35: [0.6],
|
|
||||||
0.40: [0.0],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# coonect it and...
|
|
||||||
player.actor.node.connectattr("position", light, "position")
|
|
||||||
player.actor.node.connectattr("position", circle_outline, "position")
|
|
||||||
|
|
||||||
# immediately delete the node after another player has been targeted.
|
|
||||||
self.shoot_speed = 0.5 if self.shoot_speed == 0.0 else self.shoot_speed
|
|
||||||
bs.timer(self.shoot_speed, light.delete)
|
|
||||||
bs.timer(self.shoot_speed, circle_outline.delete)
|
|
||||||
|
|
||||||
def calculate_player_analog_stick(self, player: bs.Player,
|
|
||||||
distance: float) -> NoReturn:
|
|
||||||
# at first i was very confused how i can read the player analog stick \
|
|
||||||
# then i saw TheMikirog#1984 autorun plugin code.
|
|
||||||
# and i got it how analog stick values are works.
|
|
||||||
# just need to store analog stick facing direction and need some calculation according how far player pushed analog stick.
|
|
||||||
# Notice that how vertical direction is inverted, so we need to put a minus infront of veriable.(so ball isn't shoot at wrong direction).
|
|
||||||
self.player_facing_direction[0] = player.actor.node.move_left_right
|
|
||||||
self.player_facing_direction[1] = -player.actor.node.move_up_down
|
|
||||||
|
|
||||||
# if player is too close and the player pushing his analog stick fully the ball shoot's too far away to player.
|
|
||||||
# so, we need to reduce the value of "self.player_facing_direction" to fix this problem.
|
|
||||||
if distance <= 3:
|
|
||||||
self.player_facing_direction[0] = 0.4 if \
|
|
||||||
self.player_facing_direction[0] > 0 else -0.4
|
|
||||||
self.player_facing_direction[1] = 0.4 if \
|
|
||||||
self.player_facing_direction[0] > 0 else -0.4
|
|
||||||
# same problem to long distance but in reverse, the ball can't reach to the player,
|
|
||||||
# its because player analog stick value is between 1 and -1,
|
|
||||||
# and this value is low to shoot ball forward to Player if player is too far from the box.
|
|
||||||
# so. let's increase to 1.5 if player pushed analog stick fully.
|
|
||||||
elif distance > 6.5:
|
|
||||||
# So many calculation according to how analog stick pushed by player.
|
|
||||||
# Horizontal(left-right) calculation
|
|
||||||
if self.player_facing_direction[0] > 0.4:
|
|
||||||
self.player_facing_direction[0] = 1.5
|
|
||||||
elif self.player_facing_direction[0] < -0.4:
|
|
||||||
self.player_facing_direction[0] = -1.5
|
|
||||||
else:
|
|
||||||
if self.player_facing_direction[0] > 0.0:
|
|
||||||
self.player_facing_direction[0] = 0.2
|
|
||||||
elif self.player_facing_direction[0] < 0.0:
|
|
||||||
self.player_facing_direction[0] = -0.2
|
|
||||||
else:
|
|
||||||
self.player_facing_direction[0] = 0.0
|
|
||||||
|
|
||||||
# Vertical(up-down) calculation.
|
|
||||||
if self.player_facing_direction[1] > 0.4:
|
|
||||||
self.player_facing_direction[1] = 1.5
|
|
||||||
elif self.player_facing_direction[1] < -0.4:
|
|
||||||
self.player_facing_direction[1] = -1.5
|
|
||||||
else:
|
|
||||||
if self.player_facing_direction[1] > 0.0:
|
|
||||||
self.player_facing_direction[1] = 0.2
|
|
||||||
elif self.player_facing_direction[1] < 0.0:
|
|
||||||
self.player_facing_direction[1] = -0.2
|
|
||||||
else:
|
|
||||||
self.player_facing_direction[1] = -0.0
|
|
||||||
|
|
||||||
# if we want stop the ball shootes
|
|
||||||
def stop_shoot(self) -> NoReturn:
|
|
||||||
# Kill the timer.
|
|
||||||
self.shoot_timer = None
|
|
||||||
|
|
||||||
|
|
||||||
class Player(bs.Player['Team']):
|
|
||||||
"""Our player type for this game."""
|
|
||||||
|
|
||||||
|
|
||||||
class Team(bs.Team[Player]):
|
|
||||||
"""Our team type for this game."""
|
|
||||||
|
|
||||||
|
|
||||||
# almost 80 % for game we done in box class.
|
|
||||||
# now remain things, like name, seetings, scoring, cooldonw,
|
|
||||||
# and main thing don't allow player to camp inside of box are going in this class.
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
|
|
||||||
|
|
||||||
class DodgeTheBall(bs.TeamGameActivity[Player, Team]):
|
|
||||||
# defining name, description and settings..
|
|
||||||
name = 'Dodge the ball'
|
|
||||||
description = 'Survive from shooting balls'
|
|
||||||
|
|
||||||
available_settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Cooldown',
|
|
||||||
min_value=20,
|
|
||||||
default=45,
|
|
||||||
increment=5,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Don't allow joining after we start.
|
|
||||||
allow_mid_activity_joins = False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
|
|
||||||
# We support team and ffa sessions.
|
|
||||||
return issubclass(sessiontype, bs.FreeForAllSession) or issubclass(
|
|
||||||
sessiontype, bs.DualTeamSession,
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
|
|
||||||
# This Game mode need a flat and perfect shape map where can player fall outside map.
|
|
||||||
# bombsquad have "Doom Shroom" map.
|
|
||||||
# Not perfect map for this game mode but its fine for this gamemode.
|
|
||||||
# the problem is that Doom Shroom is not a perfect circle and not flat also.
|
|
||||||
return ['Doom Shroom']
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
self.countdown_time = int(settings['Cooldown'])
|
|
||||||
|
|
||||||
self.check_player_pos_timer: bs.Timer | None = None
|
|
||||||
self.shield_drop_timer: bs.Timer | None = None
|
|
||||||
# cooldown and Box
|
|
||||||
self._countdown: OnScreenCountdown | None = None
|
|
||||||
self.box: Box | None = None
|
|
||||||
|
|
||||||
# this lists for scoring.
|
|
||||||
self.joined_player_list: list[bs.Player] = []
|
|
||||||
self.dead_player_list: list[bs.Player] = []
|
|
||||||
|
|
||||||
# normally play RUN AWAY music cause is match with our gamemode at.. my point,
|
|
||||||
# but in epic switch to EPIC.
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (
|
|
||||||
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.RUN_AWAY
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_instance_description(self) -> str | Sequence:
|
|
||||||
return 'Keep away as possible as you can'
|
|
||||||
|
|
||||||
# add a tiny text under our game name.
|
|
||||||
def get_instance_description_short(self) -> str | Sequence:
|
|
||||||
return 'Dodge the shooting balls'
|
|
||||||
|
|
||||||
def on_begin(self) -> NoReturn:
|
|
||||||
super().on_begin()
|
|
||||||
|
|
||||||
# spawn our box at middle of the map
|
|
||||||
self.box = Box(
|
|
||||||
position=(0.5, 2.7, -3.9),
|
|
||||||
velocity=(0.0, 0.0, 0.0),
|
|
||||||
).autoretain()
|
|
||||||
|
|
||||||
# create our cooldown
|
|
||||||
self._countdown = OnScreenCountdown(
|
|
||||||
duration=self.countdown_time,
|
|
||||||
endcall=self.play_victory_sound_and_end,
|
|
||||||
)
|
|
||||||
|
|
||||||
# and starts the cooldown and shootes.
|
|
||||||
bs.timer(5.0, self._countdown.start)
|
|
||||||
bs.timer(5.0, self.box.start_shoot)
|
|
||||||
|
|
||||||
# start checking all player pos.
|
|
||||||
bs.timer(5.0, self.check_player_pos)
|
|
||||||
|
|
||||||
# drop shield every ten Seconds
|
|
||||||
# need five seconds delay Because shootes start after 5 seconds.
|
|
||||||
bs.timer(15.0, self.drop_shield)
|
|
||||||
|
|
||||||
# This function returns all alive players in game.
|
|
||||||
# i thinck you see this function in Box class.
|
|
||||||
def get_alive_players(self) -> Sequence[bs.Player]:
|
|
||||||
|
|
||||||
alive_players = []
|
|
||||||
|
|
||||||
for team in self.teams:
|
|
||||||
for player in team.players:
|
|
||||||
if player.is_alive():
|
|
||||||
alive_players.append(player)
|
|
||||||
|
|
||||||
return alive_players
|
|
||||||
|
|
||||||
# let's disallowed camping inside of box by doing a blast and increasing ball shoot speed.
|
|
||||||
def check_player_pos(self):
|
|
||||||
|
|
||||||
for player in self.get_alive_players():
|
|
||||||
|
|
||||||
# same logic as applied for the ball
|
|
||||||
difference = babase.Vec3(player.position) - babase.Vec3(
|
|
||||||
self.box.node.position)
|
|
||||||
|
|
||||||
distance = difference.length()
|
|
||||||
|
|
||||||
if distance < 3:
|
|
||||||
self.box.force_shoot_speed = 0.2
|
|
||||||
else:
|
|
||||||
self.box.force_shoot_speed = 0.0
|
|
||||||
|
|
||||||
if distance < 0.5:
|
|
||||||
Blast(
|
|
||||||
position=self.box.node.position,
|
|
||||||
velocity=self.box.node.velocity,
|
|
||||||
blast_type='normal',
|
|
||||||
blast_radius=1.0,
|
|
||||||
).autoretain()
|
|
||||||
|
|
||||||
PopupText(
|
|
||||||
position=self.box.node.position,
|
|
||||||
text='Keep away from me',
|
|
||||||
random_offset=0.0,
|
|
||||||
scale=2.0,
|
|
||||||
color=self.box.light.color,
|
|
||||||
).autoretain()
|
|
||||||
|
|
||||||
# create our timer and start looping it
|
|
||||||
self.check_player_pos_timer = bs.Timer(0.1, self.check_player_pos)
|
|
||||||
|
|
||||||
# drop useless shield's too give player temptation.
|
|
||||||
def drop_shield(self) -> NoReturn:
|
|
||||||
|
|
||||||
pos = self.box.node.position
|
|
||||||
|
|
||||||
PowerupBox(
|
|
||||||
position=(pos[0] + 4.0, pos[1] + 3.0, pos[2]),
|
|
||||||
poweruptype='shield',
|
|
||||||
).autoretain()
|
|
||||||
|
|
||||||
PowerupBox(
|
|
||||||
position=(pos[0] - 4.0, pos[1] + 3.0, pos[2]),
|
|
||||||
poweruptype='shield',
|
|
||||||
).autoretain()
|
|
||||||
|
|
||||||
self.shield_drop_timer = bs.Timer(10.0, self.drop_shield)
|
|
||||||
|
|
||||||
# when cooldown time up i don't want that the game end immediately.
|
|
||||||
def play_victory_sound_and_end(self) -> NoReturn:
|
|
||||||
|
|
||||||
# kill timers
|
|
||||||
self.box.stop_shoot()
|
|
||||||
self.check_player_pos_timer = None
|
|
||||||
self.shield_drop_timer = None
|
|
||||||
|
|
||||||
bs.timer(2.0, self.end_game)
|
|
||||||
|
|
||||||
# this function runs when A player spawn in map
|
|
||||||
def spawn_player(self, player: Player) -> NoReturn:
|
|
||||||
spaz = self.spawn_player_spaz(player)
|
|
||||||
|
|
||||||
# reconnect this player's controls.
|
|
||||||
# without bomb, punch and pickup.
|
|
||||||
spaz.connect_controls_to_player(
|
|
||||||
enable_punch=False, enable_bomb=False, enable_pickup=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
# storing all players for ScorinG.
|
|
||||||
self.joined_player_list.append(player)
|
|
||||||
|
|
||||||
# Also lets have them make some noise when they die.
|
|
||||||
spaz.play_big_death_sound = True
|
|
||||||
|
|
||||||
# very helpful function to check end game when player dead or leav.
|
|
||||||
def _check_end_game(self) -> bool:
|
|
||||||
|
|
||||||
living_team_count = 0
|
|
||||||
for team in self.teams:
|
|
||||||
for player in team.players:
|
|
||||||
if player.is_alive():
|
|
||||||
living_team_count += 1
|
|
||||||
break
|
|
||||||
|
|
||||||
if living_team_count <= 0:
|
|
||||||
# kill the coutdown timer incase the all players dead before game is about to going to be end.
|
|
||||||
# so, countdown won't call the function.
|
|
||||||
# FIXE ME: it's that ok to kill this timer?
|
|
||||||
# self._countdown._timer = None;
|
|
||||||
self.end_game()
|
|
||||||
|
|
||||||
# this function called when player leave.
|
|
||||||
def on_player_leave(self, player: Player) -> NoReturn:
|
|
||||||
# Augment default behavior.
|
|
||||||
super().on_player_leave(player)
|
|
||||||
|
|
||||||
# checking end game.
|
|
||||||
self._check_end_game()
|
|
||||||
|
|
||||||
# this gamemode needs to handle only one msg "PlayerDiedMessage".
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
|
|
||||||
if isinstance(msg, bs.PlayerDiedMessage):
|
|
||||||
# Augment standard behavior.
|
|
||||||
super().handlemessage(msg)
|
|
||||||
|
|
||||||
# and storing the dead player records in our dead_player_list.
|
|
||||||
self.dead_player_list.append(msg.getplayer(Player))
|
|
||||||
|
|
||||||
# check the end game.
|
|
||||||
bs.timer(1.0, self._check_end_game)
|
|
||||||
|
|
||||||
def end_game(self):
|
|
||||||
# kill timers
|
|
||||||
self.box.stop_shoot()
|
|
||||||
self.check_player_pos_timer = None
|
|
||||||
self.shield_drop_timer = None
|
|
||||||
|
|
||||||
# here the player_dead_list and joined_player_list gonna be very helpful.
|
|
||||||
for team in self.teams:
|
|
||||||
for player in team.players:
|
|
||||||
|
|
||||||
# for scoring i am just following the index of the player_dead_list.
|
|
||||||
# for dead list...
|
|
||||||
# 0th index player dead first.
|
|
||||||
# 1st index player dead second.
|
|
||||||
# and so on...
|
|
||||||
# i think you got it... maybe
|
|
||||||
# sometime we also got a empty list
|
|
||||||
# if we got a empty list that means all players are survived or maybe only one player playing and he/she survived.
|
|
||||||
if len(self.dead_player_list) > 0:
|
|
||||||
|
|
||||||
for index, dead_player in enumerate(self.dead_player_list):
|
|
||||||
# if this condition is true we find the dead player \
|
|
||||||
# and his index with enumerate function.
|
|
||||||
if player == dead_player:
|
|
||||||
# updating with one, because i don't want to give 0 score to first dead player.
|
|
||||||
index += 1
|
|
||||||
break
|
|
||||||
# and if this statement is true we just find a survived player.
|
|
||||||
# for survived player i am giving the highest score according to how many players are joined.
|
|
||||||
elif index == len(self.dead_player_list) - 1:
|
|
||||||
index = len(self.joined_player_list)
|
|
||||||
# for survived player i am giving the highest score according to how many players are joined.
|
|
||||||
else:
|
|
||||||
index = len(self.joined_player_list)
|
|
||||||
|
|
||||||
# and here i am following Table of 10 for scoring.
|
|
||||||
# very lazY.
|
|
||||||
score = int(10 * index)
|
|
||||||
|
|
||||||
self.stats.player_scored(player, score, screenmessage=False)
|
|
||||||
|
|
||||||
# Ok now calc game results: set a score for each team and then tell \
|
|
||||||
# the game to end.
|
|
||||||
results = bs.GameResults()
|
|
||||||
|
|
||||||
# Remember that 'free-for-all' mode is simply a special form \
|
|
||||||
# of 'teams' mode where each player gets their own team, so we can \
|
|
||||||
# just always deal in teams and have all cases covered.
|
|
||||||
# hmmm... some eric comments might be helpful to you.
|
|
||||||
for team in self.teams:
|
|
||||||
|
|
||||||
max_index = 0
|
|
||||||
for player in team.players:
|
|
||||||
# for the team, we choose only one player who survived longest.
|
|
||||||
# same logic..
|
|
||||||
if len(self.dead_player_list) > 0:
|
|
||||||
for index, dead_player in enumerate(self.dead_player_list):
|
|
||||||
if player == dead_player:
|
|
||||||
index += 1
|
|
||||||
break
|
|
||||||
elif index == len(self.dead_player_list) - 1:
|
|
||||||
index = len(self.joined_player_list)
|
|
||||||
else:
|
|
||||||
index = len(self.joined_player_list)
|
|
||||||
|
|
||||||
max_index = max(max_index, index)
|
|
||||||
# set the team score
|
|
||||||
results.set_team_score(team, int(10 * max_index))
|
|
||||||
# and end the game
|
|
||||||
self.end(results=results)
|
|
||||||
16
dist/ba_root/mods/games/frozen_one.py
vendored
16
dist/ba_root/mods/games/frozen_one.py
vendored
|
|
@ -1,16 +0,0 @@
|
||||||
# Ported by your friend: Freaku
|
|
||||||
|
|
||||||
|
|
||||||
from bascenev1lib.game.chosenone import Player, ChosenOneGame
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class FrozenOneGame(ChosenOneGame):
|
|
||||||
name = 'Frozen One'
|
|
||||||
|
|
||||||
def _set_chosen_one_player(self, player: Player) -> None:
|
|
||||||
super()._set_chosen_one_player(player)
|
|
||||||
if hasattr(player, 'actor'):
|
|
||||||
player.actor.frozen = True
|
|
||||||
player.actor.node.frozen = 1
|
|
||||||
382
dist/ba_root/mods/games/handball.py
vendored
382
dist/ba_root/mods/games/handball.py
vendored
|
|
@ -1,382 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
#
|
|
||||||
"""Hockey game and support classes."""
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# (see https://ballistica.net/wiki/meta-tag-system)
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
|
||||||
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
|
|
||||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Any, Sequence, 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])
|
|
||||||
self.last_players_to_touch: dict[int, Player] = {}
|
|
||||||
self.scored = False
|
|
||||||
assert activity is not None
|
|
||||||
assert isinstance(activity, HockeyGame)
|
|
||||||
pmats = [shared.object_material, activity.puck_material]
|
|
||||||
self.node = bs.newnode('prop',
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
'mesh': activity.puck_mesh,
|
|
||||||
'color_texture': activity.puck_tex,
|
|
||||||
'body': 'sphere',
|
|
||||||
'reflection': 'soft',
|
|
||||||
'reflection_scale': [0.2],
|
|
||||||
'shadow_size': 0.8,
|
|
||||||
'is_area_of_interest': True,
|
|
||||||
'position': self._spawn_pos,
|
|
||||||
'materials': pmats
|
|
||||||
})
|
|
||||||
bs.animate(self.node, 'mesh_scale', {0: 0, 0.2: 1.3, 0.26: 1})
|
|
||||||
|
|
||||||
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.team.id] = 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."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.score = 0
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class HockeyGame(bs.TeamGameActivity[Player, Team]):
|
|
||||||
"""Ice hockey game."""
|
|
||||||
|
|
||||||
name = 'Handball'
|
|
||||||
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),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False),
|
|
||||||
|
|
||||||
]
|
|
||||||
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('hockey')
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self._scoreboard = Scoreboard()
|
|
||||||
self._cheer_sound = bs.getsound('cheer')
|
|
||||||
self._chant_sound = bs.getsound('crowdChant')
|
|
||||||
self._foghorn_sound = bs.getsound('foghorn')
|
|
||||||
self._swipsound = bs.getsound('swip')
|
|
||||||
self._whistle_sound = bs.getsound('refWhistle')
|
|
||||||
self.puck_mesh = bs.getmesh('bomb')
|
|
||||||
self.puck_tex = bs.gettexture('bonesColor')
|
|
||||||
self._puck_sound = bs.getsound('metalHit')
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (bs.MusicType.EPIC
|
|
||||||
if self._epic_mode else bs.MusicType.FOOTBALL)
|
|
||||||
self.puck_material = bs.Material()
|
|
||||||
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', False))
|
|
||||||
self.puck_material = bs.Material()
|
|
||||||
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._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)))
|
|
||||||
self._puck_spawn_pos: Optional[Sequence[float]] = None
|
|
||||||
self._score_regions: Optional[list[bs.NodeActor]] = None
|
|
||||||
self._puck: Optional[Puck] = None
|
|
||||||
self._score_to_win = int(settings['Score to Win'])
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
|
|
||||||
def get_instance_description(self) -> Union[str, Sequence]:
|
|
||||||
if self._score_to_win == 1:
|
|
||||||
return 'Score a goal.'
|
|
||||||
return 'Score ${ARG1} goals.', self._score_to_win
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> Union[str, Sequence]:
|
|
||||||
if self._score_to_win == 1:
|
|
||||||
return 'score a goal'
|
|
||||||
return 'score ${ARG1} goals', self._score_to_win
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
# Set up the two score regions.
|
|
||||||
defs = self.map.defs
|
|
||||||
self._score_regions = []
|
|
||||||
self._score_regions.append(
|
|
||||||
bs.NodeActor(
|
|
||||||
bs.newnode('region',
|
|
||||||
attrs={
|
|
||||||
'position': defs.boxes['goal1'][0:3],
|
|
||||||
'scale': defs.boxes['goal1'][6:9],
|
|
||||||
'type': 'box',
|
|
||||||
'materials': [self._score_region_material]
|
|
||||||
})))
|
|
||||||
self._score_regions.append(
|
|
||||||
bs.NodeActor(
|
|
||||||
bs.newnode('region',
|
|
||||||
attrs={
|
|
||||||
'position': defs.boxes['goal2'][0:3],
|
|
||||||
'scale': defs.boxes['goal2'][6:9],
|
|
||||||
'type': 'box',
|
|
||||||
'materials': [self._score_region_material]
|
|
||||||
})))
|
|
||||||
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.team.id] = player
|
|
||||||
|
|
||||||
def _kill_puck(self) -> None:
|
|
||||||
self._puck = None
|
|
||||||
|
|
||||||
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, score_region in enumerate(self._score_regions):
|
|
||||||
if region == score_region.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],
|
|
||||||
100,
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
def _spawn_puck(self) -> None:
|
|
||||||
self._swipsound.play()
|
|
||||||
self._whistle_sound.play()
|
|
||||||
self._flash_puck_spawn()
|
|
||||||
assert self._puck_spawn_pos is not None
|
|
||||||
self._puck = Puck(position=self._puck_spawn_pos)
|
|
||||||
1694
dist/ba_root/mods/games/hot_bomb.py
vendored
1694
dist/ba_root/mods/games/hot_bomb.py
vendored
File diff suppressed because it is too large
Load diff
52
dist/ba_root/mods/games/icy_emits.py
vendored
52
dist/ba_root/mods/games/icy_emits.py
vendored
|
|
@ -1,52 +0,0 @@
|
||||||
# Made by your friend: Freaku
|
|
||||||
|
|
||||||
|
|
||||||
import random
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.bomb import Bomb
|
|
||||||
from bascenev1lib.game.meteorshower import MeteorShowerGame
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class IcyEmitsGame(MeteorShowerGame):
|
|
||||||
name = 'Icy Emits'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype):
|
|
||||||
return ['Lake Frigid', 'Hockey Stadium']
|
|
||||||
|
|
||||||
def _drop_bomb_cluster(self) -> None:
|
|
||||||
delay = 0.0
|
|
||||||
for _i in range(random.randrange(1, 3)):
|
|
||||||
# Drop them somewhere within our bounds with velocity pointing
|
|
||||||
# toward the opposite side.
|
|
||||||
pos = (-7.3 + 15.3 * random.random(), 5.3,
|
|
||||||
-5.5 + 2.1 * random.random())
|
|
||||||
dropdir = (-1.0 if pos[0] > 0 else 1.0)
|
|
||||||
vel = (0, 10, 0)
|
|
||||||
bs.timer(delay, babase.Call(self._drop_bomb, pos, vel))
|
|
||||||
delay += 0.1
|
|
||||||
self._set_meteor_timer()
|
|
||||||
|
|
||||||
def _drop_bomb(self, position, velocity):
|
|
||||||
random_xpositions = [-10, -9, -8, -7, -6, -5, -
|
|
||||||
4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
||||||
random_zpositions = [-5, -4.5, -4, -3.5, -3, -2.5, -2, -
|
|
||||||
1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5]
|
|
||||||
bomb_position = (
|
|
||||||
random.choice(random_xpositions), 0.2, random.choice(random_zpositions))
|
|
||||||
Bomb(position=bomb_position, velocity=velocity,
|
|
||||||
bomb_type='ice').autoretain()
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export plugin
|
|
||||||
class byFreaku(babase.Plugin):
|
|
||||||
def __init__(self):
|
|
||||||
## Campaign support ##
|
|
||||||
randomPic = ['lakeFrigidPreview', 'hockeyStadiumPreview']
|
|
||||||
babase.app.classic.add_coop_practice_level(bs.Level(
|
|
||||||
name='Icy Emits', displayname='${GAME}', gametype=IcyEmitsGame,
|
|
||||||
settings={}, preview_texture_name=random.choice(randomPic)))
|
|
||||||
1285
dist/ba_root/mods/games/memory_game.py
vendored
1285
dist/ba_root/mods/games/memory_game.py
vendored
File diff suppressed because it is too large
Load diff
312
dist/ba_root/mods/games/musical_flags.py
vendored
312
dist/ba_root/mods/games/musical_flags.py
vendored
|
|
@ -1,312 +0,0 @@
|
||||||
# Made by MattZ45986 on GitHub
|
|
||||||
# Ported by your friend: Freaku
|
|
||||||
|
|
||||||
|
|
||||||
# Bug Fixes & Improvements as well...
|
|
||||||
|
|
||||||
# Join BCS:
|
|
||||||
# https://discord.gg/ucyaesh
|
|
||||||
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import math
|
|
||||||
import random
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.flag import Flag, FlagPickedUpMessage
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Any, Type, List, Union, Sequence
|
|
||||||
|
|
||||||
|
|
||||||
class Player(bs.Player['Team']):
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.done: bool = False
|
|
||||||
self.survived: bool = True
|
|
||||||
|
|
||||||
|
|
||||||
class Team(bs.Team[Player]):
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.score = 0
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class MFGame(bs.TeamGameActivity[Player, Team]):
|
|
||||||
name = 'Musical Flags'
|
|
||||||
description = "Don't be the one stuck without a flag!"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_available_settings(
|
|
||||||
cls, sessiontype: Type[bs.Session]) -> List[babase.Setting]:
|
|
||||||
settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Max Round Time',
|
|
||||||
min_value=15,
|
|
||||||
default=25,
|
|
||||||
increment=5,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False),
|
|
||||||
bs.BoolSetting('Enable Running', default=True),
|
|
||||||
bs.BoolSetting('Enable Punching', default=False),
|
|
||||||
bs.BoolSetting('Enable Bottom Credit', True)
|
|
||||||
]
|
|
||||||
return settings
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
|
|
||||||
return (issubclass(sessiontype, bs.DualTeamSession)
|
|
||||||
or issubclass(sessiontype, bs.FreeForAllSession))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
|
|
||||||
return ['Doom Shroom']
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
self.nodes = []
|
|
||||||
self._dingsound = bs.getsound('dingSmall')
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
self.credit_text = bool(settings['Enable Bottom Credit'])
|
|
||||||
self.is_punch = bool(settings['Enable Punching'])
|
|
||||||
self.is_run = bool(settings['Enable Running'])
|
|
||||||
|
|
||||||
self._textRound = bs.newnode('text',
|
|
||||||
attrs={'text': '',
|
|
||||||
'position': (0, -38),
|
|
||||||
'scale': 1,
|
|
||||||
'shadow': 1.0,
|
|
||||||
'flatness': 1.0,
|
|
||||||
'color': (1.0, 0.0, 1.0),
|
|
||||||
'opacity': 1,
|
|
||||||
'v_attach': 'top',
|
|
||||||
'h_attach': 'center',
|
|
||||||
'h_align': 'center',
|
|
||||||
'v_align': 'center'})
|
|
||||||
self.round_time = int(settings['Max Round Time'])
|
|
||||||
self.reset_round_time = int(settings['Max Round Time'])
|
|
||||||
self.should_die_occur = True
|
|
||||||
self.round_time_textnode = bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'text': "", 'flatness': 1.0,
|
|
||||||
'h_align': 'center',
|
|
||||||
'h_attach': 'center',
|
|
||||||
'v_attach': 'top',
|
|
||||||
'v_align': 'center',
|
|
||||||
'position': (0, -15),
|
|
||||||
'scale': 0.9,
|
|
||||||
'color': (1, 0.7, 0.9)})
|
|
||||||
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
# A cool music, matching our gamemode theme
|
|
||||||
self.default_music = bs.MusicType.FLAG_CATCHER
|
|
||||||
|
|
||||||
def get_instance_description(self) -> Union[str, Sequence]:
|
|
||||||
return 'Catch Flag for yourself'
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> Union[str, Sequence]:
|
|
||||||
return 'Catch Flag for yourself'
|
|
||||||
|
|
||||||
def on_player_join(self, player: Player) -> None:
|
|
||||||
if self.has_begun():
|
|
||||||
bs.broadcastmessage(
|
|
||||||
bs.Lstr(resource='playerDelayedJoinText',
|
|
||||||
subs=[('${PLAYER}', player.getname(full=True))]),
|
|
||||||
color=(0, 1, 0), transient=True)
|
|
||||||
player.survived = False
|
|
||||||
return
|
|
||||||
self.spawn_player(player)
|
|
||||||
|
|
||||||
def on_player_leave(self, player: Player) -> None:
|
|
||||||
super().on_player_leave(player)
|
|
||||||
# A departing player may trigger game-over.
|
|
||||||
bs.timer(0, self.checkEnd)
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
self.roundNum = 0
|
|
||||||
self.numPickedUp = 0
|
|
||||||
self.nodes = []
|
|
||||||
self.flags = []
|
|
||||||
self.spawned = []
|
|
||||||
if self.credit_text:
|
|
||||||
t = bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'text': "Ported by Freaku\nMade by MattZ45986",
|
|
||||||
# Disable 'Enable Bottom Credits' when making playlist, No need to edit this lovely...
|
|
||||||
'scale': 0.7,
|
|
||||||
'position': (0, 0),
|
|
||||||
'shadow': 0.5,
|
|
||||||
'flatness': 1.2,
|
|
||||||
'color': (1, 1, 1),
|
|
||||||
'h_align': 'center',
|
|
||||||
'v_attach': 'bottom'})
|
|
||||||
self.makeRound()
|
|
||||||
self._textRound.text = 'Round ' + str(self.roundNum)
|
|
||||||
bs.timer(3, self.checkEnd)
|
|
||||||
self.keepcalling = bs.timer(1, self._timeround, True)
|
|
||||||
|
|
||||||
def _timeround(self):
|
|
||||||
if self.round_time == 0 and self.should_die_occur:
|
|
||||||
self.should_die_occur = False
|
|
||||||
self.round_time_textnode.opacity = 0
|
|
||||||
bs.broadcastmessage('Proceeding Round...')
|
|
||||||
for player in self.spawned:
|
|
||||||
if not player.done:
|
|
||||||
try:
|
|
||||||
player.survived = False
|
|
||||||
player.actor.handlemessage(bs.StandMessage((0, 3, -2)))
|
|
||||||
bs.timer(0.5, bs.Call(player.actor.handlemessage,
|
|
||||||
bs.FreezeMessage()))
|
|
||||||
bs.timer(1.5, bs.Call(player.actor.handlemessage,
|
|
||||||
bs.FreezeMessage()))
|
|
||||||
bs.timer(2.5, bs.Call(player.actor.handlemessage,
|
|
||||||
bs.FreezeMessage()))
|
|
||||||
bs.timer(3, bs.Call(player.actor.handlemessage,
|
|
||||||
bs.ShouldShatterMessage()))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
bs.timer(3.5, self.killRound)
|
|
||||||
bs.timer(3.55, self.makeRound)
|
|
||||||
self.round_time_textnode.opacity = 0
|
|
||||||
self.round_time = self.reset_round_time
|
|
||||||
else:
|
|
||||||
self.round_time_textnode.text = "Time: " + str(self.round_time)
|
|
||||||
self.round_time -= 1
|
|
||||||
|
|
||||||
def makeRound(self):
|
|
||||||
for player in self.players:
|
|
||||||
if player.survived:
|
|
||||||
player.team.score += 1
|
|
||||||
self.roundNum += 1
|
|
||||||
self._textRound.text = 'Round ' + str(self.roundNum)
|
|
||||||
self.flags = []
|
|
||||||
self.spawned = []
|
|
||||||
self.should_die_occur = True
|
|
||||||
self.round_time = self.reset_round_time
|
|
||||||
self.round_time_textnode.opacity = 1
|
|
||||||
angle = random.randint(0, 359)
|
|
||||||
c = 0
|
|
||||||
for player in self.players:
|
|
||||||
if player.survived:
|
|
||||||
c += 1
|
|
||||||
spacing = 10
|
|
||||||
for player in self.players:
|
|
||||||
player.done = False
|
|
||||||
if player.survived:
|
|
||||||
if not player.is_alive():
|
|
||||||
self.spawn_player(player, (.5, 5, -4))
|
|
||||||
self.spawned.append(player)
|
|
||||||
try:
|
|
||||||
spacing = 360 // (c)
|
|
||||||
except:
|
|
||||||
self.checkEnd()
|
|
||||||
colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 1, 0), (1, 0, 1),
|
|
||||||
(0, 1, 1), (0, 0, 0),
|
|
||||||
(0.5, 0.8, 0), (0, 0.8, 0.5), (0.8, 0.25, 0.7),
|
|
||||||
(0, 0.27, 0.55), (2, 2, 0.6), (0.4, 3, 0.85)]
|
|
||||||
|
|
||||||
# Add support for more than 13 players
|
|
||||||
if c > 12:
|
|
||||||
for i in range(c - 12):
|
|
||||||
colors.append((random.uniform(0.1, 1), random.uniform(
|
|
||||||
0.1, 1), random.uniform(0.1, 1)))
|
|
||||||
|
|
||||||
# Smart Mathematics:
|
|
||||||
# All Flags spawn same distance from the players
|
|
||||||
for i in range(c - 1):
|
|
||||||
angle += spacing
|
|
||||||
angle %= 360
|
|
||||||
x = 6 * math.sin(math.degrees(angle))
|
|
||||||
z = 6 * math.cos(math.degrees(angle))
|
|
||||||
flag = Flag(position=(x + .5, 5, z - 4),
|
|
||||||
color=colors[i]).autoretain()
|
|
||||||
self.flags.append(flag)
|
|
||||||
|
|
||||||
def killRound(self):
|
|
||||||
self.numPickedUp = 0
|
|
||||||
for player in self.players:
|
|
||||||
if player.is_alive():
|
|
||||||
player.actor.handlemessage(bs.DieMessage())
|
|
||||||
for flag in self.flags:
|
|
||||||
flag.node.delete()
|
|
||||||
for light in self.nodes:
|
|
||||||
light.delete()
|
|
||||||
|
|
||||||
def spawn_player(self, player: Player, pos: tuple = (0, 0, 0)) -> bs.Actor:
|
|
||||||
spaz = self.spawn_player_spaz(player)
|
|
||||||
if pos == (0, 0, 0):
|
|
||||||
pos = (-.5 + random.random() * 2, 3 + random.random() * 2,
|
|
||||||
-5 + random.random() * 2)
|
|
||||||
spaz.connect_controls_to_player(enable_punch=self.is_punch,
|
|
||||||
enable_bomb=False,
|
|
||||||
enable_run=self.is_run)
|
|
||||||
spaz.handlemessage(bs.StandMessage(pos))
|
|
||||||
return spaz
|
|
||||||
|
|
||||||
def check_respawn(self, player):
|
|
||||||
if not player.done and player.survived:
|
|
||||||
self.respawn_player(player, 2.5)
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
|
|
||||||
if isinstance(msg, bs.PlayerDiedMessage):
|
|
||||||
super().handlemessage(msg)
|
|
||||||
player = msg.getplayer(Player)
|
|
||||||
bs.timer(0.1, bs.Call(self.check_respawn, player))
|
|
||||||
bs.timer(0.5, self.checkEnd)
|
|
||||||
elif isinstance(msg, FlagPickedUpMessage):
|
|
||||||
self.numPickedUp += 1
|
|
||||||
msg.node.getdelegate(PlayerSpaz, True).getplayer(Player,
|
|
||||||
True).done = True
|
|
||||||
l = bs.newnode('light',
|
|
||||||
owner=None,
|
|
||||||
attrs={'color': msg.node.color,
|
|
||||||
'position': (msg.node.position_center),
|
|
||||||
'intensity': 1})
|
|
||||||
self.nodes.append(l)
|
|
||||||
msg.flag.handlemessage(bs.DieMessage())
|
|
||||||
msg.node.handlemessage(bs.DieMessage())
|
|
||||||
msg.node.delete()
|
|
||||||
if self.numPickedUp == len(self.flags):
|
|
||||||
self.round_time_textnode.opacity = 0
|
|
||||||
self.round_time = self.reset_round_time
|
|
||||||
for player in self.spawned:
|
|
||||||
if not player.done:
|
|
||||||
try:
|
|
||||||
player.survived = False
|
|
||||||
bs.broadcastmessage("No Flag? " + player.getname())
|
|
||||||
player.actor.handlemessage(
|
|
||||||
bs.StandMessage((0, 3, -2)))
|
|
||||||
bs.timer(0.5, bs.Call(player.actor.handlemessage,
|
|
||||||
bs.FreezeMessage()))
|
|
||||||
bs.timer(3, bs.Call(player.actor.handlemessage,
|
|
||||||
bs.ShouldShatterMessage()))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
bs.timer(3.5, self.killRound)
|
|
||||||
bs.timer(3.55, self.makeRound)
|
|
||||||
else:
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def checkEnd(self):
|
|
||||||
i = 0
|
|
||||||
for player in self.players:
|
|
||||||
if player.survived:
|
|
||||||
i += 1
|
|
||||||
if i <= 1:
|
|
||||||
for player in self.players:
|
|
||||||
if player.survived:
|
|
||||||
player.team.score += 10
|
|
||||||
bs.timer(2.5, self.end_game)
|
|
||||||
|
|
||||||
def end_game(self) -> None:
|
|
||||||
results = bs.GameResults()
|
|
||||||
for team in self.teams:
|
|
||||||
results.set_team_score(team, team.score)
|
|
||||||
self.end(results=results)
|
|
||||||
644
dist/ba_root/mods/games/quake_original.py
vendored
644
dist/ba_root/mods/games/quake_original.py
vendored
|
|
@ -1,644 +0,0 @@
|
||||||
# Created By Idk
|
|
||||||
# Ported to 1.7 by Yan
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import random
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
|
||||||
from bascenev1lib.actor.powerupbox import PowerupBox as Powerup
|
|
||||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TouchedToSpaz(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TouchedToAnything(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TouchedToFootingMaterial(object):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class QuakeBallFactory(object):
|
|
||||||
"""Components used by QuakeBall stuff
|
|
||||||
|
|
||||||
category: Game Classes
|
|
||||||
|
|
||||||
"""
|
|
||||||
_STORENAME = babase.storagename()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get(cls) -> QuakeBallFactory:
|
|
||||||
"""Get/create a shared bascenev1lib.actor.bomb.BombFactory object."""
|
|
||||||
activity = bs.getactivity()
|
|
||||||
factory = activity.customdata.get(cls._STORENAME)
|
|
||||||
if factory is None:
|
|
||||||
factory = QuakeBallFactory()
|
|
||||||
activity.customdata[cls._STORENAME] = factory
|
|
||||||
assert isinstance(factory, QuakeBallFactory)
|
|
||||||
return factory
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
|
|
||||||
self.ball_material = bs.Material()
|
|
||||||
|
|
||||||
self.ball_material.add_actions(
|
|
||||||
conditions=(
|
|
||||||
(('we_are_younger_than', 5), 'or', ('they_are_younger_than', 50)),
|
|
||||||
'and', ('they_have_material', shared.object_material)),
|
|
||||||
actions=(('modify_node_collision', 'collide', False)))
|
|
||||||
|
|
||||||
self.ball_material.add_actions(
|
|
||||||
conditions=('they_have_material', shared.pickup_material),
|
|
||||||
actions=(('modify_part_collision', 'use_node_collide', False)))
|
|
||||||
|
|
||||||
self.ball_material.add_actions(
|
|
||||||
actions=('modify_part_collision', 'friction', 0))
|
|
||||||
|
|
||||||
self.ball_material.add_actions(
|
|
||||||
conditions=('they_have_material', shared.player_material),
|
|
||||||
actions=(('modify_part_collision', 'physical', False),
|
|
||||||
('message', 'our_node', 'at_connect', TouchedToSpaz())))
|
|
||||||
|
|
||||||
self.ball_material.add_actions(
|
|
||||||
conditions=(
|
|
||||||
('they_dont_have_material', shared.player_material), 'and',
|
|
||||||
('they_have_material', shared.object_material)),
|
|
||||||
actions=('message', 'our_node', 'at_connect', TouchedToAnything()))
|
|
||||||
|
|
||||||
self.ball_material.add_actions(
|
|
||||||
conditions=(
|
|
||||||
('they_dont_have_material', shared.player_material), 'and',
|
|
||||||
('they_have_material', shared.footing_material)),
|
|
||||||
actions=(
|
|
||||||
'message', 'our_node', 'at_connect', TouchedToFootingMaterial()))
|
|
||||||
|
|
||||||
def give(self, spaz):
|
|
||||||
spaz.punch_callback = self.shot
|
|
||||||
self.last_shot = int(bs.time() * 1000)
|
|
||||||
|
|
||||||
def shot(self, spaz):
|
|
||||||
time = int(bs.time() * 1000)
|
|
||||||
if time - self.last_shot > 0.6:
|
|
||||||
self.last_shot = time
|
|
||||||
p1 = spaz.node.position_center
|
|
||||||
p2 = spaz.node.position_forward
|
|
||||||
direction = [p1[0] - p2[0], p2[1] - p1[1], p1[2] - p2[2]]
|
|
||||||
direction[1] = 0.0
|
|
||||||
|
|
||||||
mag = 10.0 / babase.Vec3(*direction).length()
|
|
||||||
vel = [v * mag for v in direction]
|
|
||||||
QuakeBall(
|
|
||||||
position=spaz.node.position,
|
|
||||||
velocity=(vel[0] * 2, vel[1] * 2, vel[2] * 2),
|
|
||||||
owner=spaz._player,
|
|
||||||
source_player=spaz._player,
|
|
||||||
color=spaz.node.color).autoretain()
|
|
||||||
|
|
||||||
|
|
||||||
class QuakeBall(bs.Actor):
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
position=(0, 5, 0),
|
|
||||||
velocity=(0, 2, 0),
|
|
||||||
source_player=None,
|
|
||||||
owner=None,
|
|
||||||
color=(random.random(), random.random(), random.random()),
|
|
||||||
light_radius=0
|
|
||||||
):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
b_shared = QuakeBallFactory.get()
|
|
||||||
|
|
||||||
self.source_player = source_player
|
|
||||||
self.owner = owner
|
|
||||||
|
|
||||||
self.node = bs.newnode('prop', delegate=self, attrs={
|
|
||||||
'position': position,
|
|
||||||
'velocity': velocity,
|
|
||||||
'mesh': bs.getmesh('impactBomb'),
|
|
||||||
'body': 'sphere',
|
|
||||||
'color_texture': bs.gettexture('bunnyColor'),
|
|
||||||
'mesh_scale': 0.2,
|
|
||||||
'is_area_of_interest': True,
|
|
||||||
'body_scale': 0.8,
|
|
||||||
'materials': [shared.object_material,
|
|
||||||
b_shared.ball_material]})
|
|
||||||
|
|
||||||
self.light_node = bs.newnode('light', attrs={
|
|
||||||
'position': position,
|
|
||||||
'color': color,
|
|
||||||
'radius': 0.1 + light_radius,
|
|
||||||
'volume_intensity_scale': 15.0})
|
|
||||||
|
|
||||||
self.node.connectattr('position', self.light_node, 'position')
|
|
||||||
self.emit_time = bs.Timer(0.015, bs.WeakCall(self.emit), repeat=True)
|
|
||||||
self.life_time = bs.Timer(5.0, bs.WeakCall(self.handlemessage,
|
|
||||||
bs.DieMessage()))
|
|
||||||
|
|
||||||
def emit(self):
|
|
||||||
bs.emitfx(
|
|
||||||
position=self.node.position,
|
|
||||||
velocity=self.node.velocity,
|
|
||||||
count=10,
|
|
||||||
scale=0.4,
|
|
||||||
spread=0.01,
|
|
||||||
chunk_type='spark')
|
|
||||||
|
|
||||||
def handlemessage(self, m):
|
|
||||||
if isinstance(m, TouchedToAnything):
|
|
||||||
node = bs.getcollision().opposingnode
|
|
||||||
if node is not None and node.exists():
|
|
||||||
v = self.node.velocity
|
|
||||||
t = self.node.position
|
|
||||||
hitdir = self.node.velocity
|
|
||||||
m = self.node
|
|
||||||
node.handlemessage(
|
|
||||||
bs.HitMessage(
|
|
||||||
pos=t,
|
|
||||||
velocity=v,
|
|
||||||
magnitude=babase.Vec3(*v).length() * 40,
|
|
||||||
velocity_magnitude=babase.Vec3(*v).length() * 40,
|
|
||||||
radius=0,
|
|
||||||
srcnode=self.node,
|
|
||||||
source_player=self.source_player,
|
|
||||||
force_direction=hitdir))
|
|
||||||
|
|
||||||
self.node.handlemessage(bs.DieMessage())
|
|
||||||
|
|
||||||
elif isinstance(m, bs.DieMessage):
|
|
||||||
if self.node.exists():
|
|
||||||
velocity = self.node.velocity
|
|
||||||
explosion = bs.newnode('explosion', attrs={
|
|
||||||
'position': self.node.position,
|
|
||||||
'velocity': (
|
|
||||||
velocity[0], max(-1.0, velocity[1]), velocity[2]),
|
|
||||||
'radius': 1,
|
|
||||||
'big': False})
|
|
||||||
|
|
||||||
bs.getsound(random.choice(
|
|
||||||
['impactHard', 'impactHard2', 'impactHard3'])).play(),
|
|
||||||
position = self.node.position
|
|
||||||
|
|
||||||
self.emit_time = None
|
|
||||||
self.light_node.delete()
|
|
||||||
self.node.delete()
|
|
||||||
|
|
||||||
elif isinstance(m, bs.OutOfBoundsMessage):
|
|
||||||
self.handlemessage(bs.DieMessage())
|
|
||||||
|
|
||||||
elif isinstance(m, bs.HitMessage):
|
|
||||||
self.node.handlemessage('impulse', m.pos[0], m.pos[1], m.pos[2],
|
|
||||||
m.velocity[0], m.velocity[1], m.velocity[2],
|
|
||||||
1.0 * m.magnitude,
|
|
||||||
1.0 * m.velocity_magnitude, m.radius, 0,
|
|
||||||
m.force_direction[0], m.force_direction[1],
|
|
||||||
m.force_direction[2])
|
|
||||||
|
|
||||||
elif isinstance(m, TouchedToSpaz):
|
|
||||||
node = bs.getcollision().opposingnode
|
|
||||||
if node is not None and node.exists() and node != self.owner \
|
|
||||||
and node.getdelegate(object)._player.team != self.owner.team:
|
|
||||||
node.handlemessage(bs.FreezeMessage())
|
|
||||||
v = self.node.velocity
|
|
||||||
t = self.node.position
|
|
||||||
hitdir = self.node.velocity
|
|
||||||
|
|
||||||
node.handlemessage(
|
|
||||||
bs.HitMessage(
|
|
||||||
pos=t,
|
|
||||||
velocity=(10, 10, 10),
|
|
||||||
magnitude=50,
|
|
||||||
velocity_magnitude=50,
|
|
||||||
radius=0,
|
|
||||||
srcnode=self.node,
|
|
||||||
source_player=self.source_player,
|
|
||||||
force_direction=hitdir))
|
|
||||||
|
|
||||||
self.node.handlemessage(bs.DieMessage())
|
|
||||||
|
|
||||||
elif isinstance(m, TouchedToFootingMaterial):
|
|
||||||
bs.getsound('blip').play(),
|
|
||||||
position = self.node.position
|
|
||||||
else:
|
|
||||||
super().handlemessage(m)
|
|
||||||
|
|
||||||
|
|
||||||
class Player(bs.Player['Team']):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class Team(bs.Team[Player]):
|
|
||||||
"""Our team type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.score = 0
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
|
|
||||||
|
|
||||||
class QuakeGame(bs.TeamGameActivity[Player, Team]):
|
|
||||||
"""A game type based on acquiring kills."""
|
|
||||||
|
|
||||||
name = 'Quake'
|
|
||||||
description = 'Kill a set number of enemies to win.'
|
|
||||||
|
|
||||||
# Print messages when players die since it matters here.
|
|
||||||
announce_player_deaths = True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_session_type(cls, sessiontype: type[bs.Session]) -> bool:
|
|
||||||
return issubclass(sessiontype, bs.DualTeamSession) or issubclass(
|
|
||||||
sessiontype, bs.FreeForAllSession
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype: type[bs.Session]) -> list[str]:
|
|
||||||
return ['Doom Shroom', 'Monkey Face', 'Football Stadium']
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_available_settings(
|
|
||||||
cls, sessiontype: type[bs.Session]
|
|
||||||
) -> list[babase.Setting]:
|
|
||||||
settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Kills to Win Per Player',
|
|
||||||
min_value=1,
|
|
||||||
default=5,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Graphics',
|
|
||||||
choices=[
|
|
||||||
('Normal', 1),
|
|
||||||
('High', 2)
|
|
||||||
],
|
|
||||||
default=1),
|
|
||||||
bs.BoolSetting('Fast Movespeed', default=True),
|
|
||||||
bs.BoolSetting('Enable Jump', default=False),
|
|
||||||
bs.BoolSetting('Enable Pickup', default=False),
|
|
||||||
bs.BoolSetting('Enable Bomb', default=False),
|
|
||||||
bs.BoolSetting('Obstacles', default=False),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Obstacles Shape',
|
|
||||||
choices=[
|
|
||||||
('Cube', 1),
|
|
||||||
('Sphere', 2),
|
|
||||||
('Puck', 3),
|
|
||||||
('Egg', 4),
|
|
||||||
('Random', 5),
|
|
||||||
],
|
|
||||||
default=1),
|
|
||||||
bs.BoolSetting('Obstacles Bounces Shots', default=False),
|
|
||||||
bs.IntSetting(
|
|
||||||
'Obstacle Count',
|
|
||||||
min_value=1,
|
|
||||||
default=16,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Random Obstacle Color', default=True),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False),
|
|
||||||
]
|
|
||||||
return settings
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
self._scoreboard = Scoreboard()
|
|
||||||
self._score_to_win: int | None = None
|
|
||||||
self._dingsound = bs.getsound('dingSmall')
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
self._kills_to_win_per_player = int(settings['Kills to Win Per Player'])
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
self._allow_negative_scores = bool(
|
|
||||||
settings.get('Allow Negative Scores', False)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Base class overrides.
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (
|
|
||||||
bs.MusicType.EPIC if self._epic_mode else bs.MusicType.TO_THE_DEATH
|
|
||||||
)
|
|
||||||
self.settings = settings
|
|
||||||
|
|
||||||
def get_instance_description(self) -> str | Sequence:
|
|
||||||
return 'Crush ${ARG1} of your enemies.', self._score_to_win
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> str | Sequence:
|
|
||||||
return 'kill ${ARG1} enemies', self._score_to_win
|
|
||||||
|
|
||||||
def on_team_join(self, team: Team) -> None:
|
|
||||||
if self.has_begun():
|
|
||||||
self._update_scoreboard()
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
self.dingsound = bs.getsound('dingSmall')
|
|
||||||
self.setup_standard_time_limit(self._time_limit)
|
|
||||||
|
|
||||||
self.drop_shield()
|
|
||||||
self.drop_shield_timer = bs.Timer(8.001, bs.WeakCall(self.drop_shield),
|
|
||||||
repeat=True)
|
|
||||||
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
if self.settings['Obstacles']:
|
|
||||||
count = self.settings['Obstacle Count']
|
|
||||||
map = bs.getactivity()._map.getname()
|
|
||||||
for i in range(count):
|
|
||||||
if map == 'Football Stadium':
|
|
||||||
radius = (random.uniform(-10, 1),
|
|
||||||
6,
|
|
||||||
random.uniform(-4.5, 4.5)) \
|
|
||||||
if i > count / 2 else (
|
|
||||||
random.uniform(10, 1), 6, random.uniform(-4.5, 4.5))
|
|
||||||
else:
|
|
||||||
radius = (random.uniform(-10, 1),
|
|
||||||
6,
|
|
||||||
random.uniform(-8, 8)) \
|
|
||||||
if i > count / 2 else (
|
|
||||||
random.uniform(10, 1), 6, random.uniform(-8, 8))
|
|
||||||
|
|
||||||
Obstacle(
|
|
||||||
position=radius,
|
|
||||||
graphics=self.settings['Graphics'],
|
|
||||||
random_color=self.settings['Random Obstacle Color'],
|
|
||||||
rebound=self.settings['Obstacles Bounces Shots'],
|
|
||||||
shape=int(self.settings['Obstacles Shape'])).autoretain()
|
|
||||||
|
|
||||||
if self.settings['Graphics'] == 2:
|
|
||||||
bs.getactivity().globalsnode.tint = (bs.getactivity(
|
|
||||||
).globalsnode.tint[0] - 0.6, bs.getactivity().globalsnode.tint[
|
|
||||||
1] - 0.6,
|
|
||||||
bs.getactivity().globalsnode.tint[
|
|
||||||
2] - 0.6)
|
|
||||||
light = bs.newnode('light', attrs={
|
|
||||||
'position': (9, 10, 0) if map == 'Football Stadium' else (
|
|
||||||
6, 7, -2)
|
|
||||||
if not map == 'Rampage' else (
|
|
||||||
6, 11, -2) if not map == 'The Pad' else (6, 8.5, -2),
|
|
||||||
'color': (0.4, 0.4, 0.45),
|
|
||||||
'radius': 1,
|
|
||||||
'intensity': 6,
|
|
||||||
'volume_intensity_scale': 10.0})
|
|
||||||
|
|
||||||
light2 = bs.newnode('light', attrs={
|
|
||||||
'position': (-9, 10, 0) if map == 'Football Stadium' else (
|
|
||||||
-6, 7, -2)
|
|
||||||
if not map == 'Rampage' else (
|
|
||||||
-6, 11, -2) if not map == 'The Pad' else (-6, 8.5, -2),
|
|
||||||
'color': (0.4, 0.4, 0.45),
|
|
||||||
'radius': 1,
|
|
||||||
'intensity': 6,
|
|
||||||
'volume_intensity_scale': 10.0})
|
|
||||||
|
|
||||||
if len(self.teams) > 0:
|
|
||||||
self._score_to_win = self.settings['Kills to Win Per Player'] * \
|
|
||||||
max(1, max(len(t.players) for t in self.teams))
|
|
||||||
else:
|
|
||||||
self._score_to_win = self.settings['Kills to Win Per Player']
|
|
||||||
self._update_scoreboard()
|
|
||||||
|
|
||||||
def drop_shield(self):
|
|
||||||
p = Powerup(
|
|
||||||
poweruptype='shield',
|
|
||||||
position=(
|
|
||||||
random.uniform(-10, 10), 6, random.uniform(-5, 5))).autoretain()
|
|
||||||
|
|
||||||
bs.getsound('dingSmall').play()
|
|
||||||
|
|
||||||
p_light = bs.newnode('light', attrs={
|
|
||||||
'position': (0, 0, 0),
|
|
||||||
'color': (0.3, 0.0, 0.4),
|
|
||||||
'radius': 0.3,
|
|
||||||
'intensity': 2,
|
|
||||||
'volume_intensity_scale': 10.0})
|
|
||||||
|
|
||||||
p.node.connectattr('position', p_light, 'position')
|
|
||||||
|
|
||||||
bs.animate(p_light, 'intensity', {0: 2, 8000: 0})
|
|
||||||
|
|
||||||
def check_exists():
|
|
||||||
if p is None or p.node.exists() == False:
|
|
||||||
delete_light()
|
|
||||||
del_checker()
|
|
||||||
|
|
||||||
self._checker = bs.Timer(0.1, babase.Call(check_exists), repeat=True)
|
|
||||||
|
|
||||||
def del_checker():
|
|
||||||
if self._checker is not None:
|
|
||||||
self._checker = None
|
|
||||||
|
|
||||||
def delete_light():
|
|
||||||
if p_light.exists():
|
|
||||||
p_light.delete()
|
|
||||||
|
|
||||||
bs.timer(6.9, babase.Call(del_checker))
|
|
||||||
bs.timer(7.0, babase.Call(delete_light))
|
|
||||||
|
|
||||||
def spawn_player(self, player: bs.Player):
|
|
||||||
spaz = self.spawn_player_spaz(player)
|
|
||||||
QuakeBallFactory().give(spaz)
|
|
||||||
spaz.connect_controls_to_player(
|
|
||||||
enable_jump=self.settings['Enable Jump'],
|
|
||||||
enable_punch=True,
|
|
||||||
enable_pickup=self.settings['Enable Pickup'],
|
|
||||||
enable_bomb=self.settings['Enable Bomb'],
|
|
||||||
enable_run=True,
|
|
||||||
enable_fly=False)
|
|
||||||
|
|
||||||
if self.settings['Fast Movespeed']:
|
|
||||||
spaz.node.hockey = True
|
|
||||||
spaz.spaz_light = bs.newnode('light', attrs={
|
|
||||||
'position': (0, 0, 0),
|
|
||||||
'color': spaz.node.color,
|
|
||||||
'radius': 0.12,
|
|
||||||
'intensity': 1,
|
|
||||||
'volume_intensity_scale': 10.0})
|
|
||||||
|
|
||||||
spaz.node.connectattr('position', spaz.spaz_light, 'position')
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
|
|
||||||
if isinstance(msg, bs.PlayerDiedMessage):
|
|
||||||
|
|
||||||
# Augment standard behavior.
|
|
||||||
super().handlemessage(msg)
|
|
||||||
|
|
||||||
player = msg.getplayer(Player)
|
|
||||||
self.respawn_player(player)
|
|
||||||
|
|
||||||
killer = msg.getkillerplayer(Player)
|
|
||||||
if hasattr(player.actor, 'spaz_light'):
|
|
||||||
player.actor.spaz_light.delete()
|
|
||||||
if killer is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Handle team-kills.
|
|
||||||
if killer.team is player.team:
|
|
||||||
|
|
||||||
# In free-for-all, killing yourself loses you a point.
|
|
||||||
if isinstance(self.session, bs.FreeForAllSession):
|
|
||||||
new_score = player.team.score - 1
|
|
||||||
if not self._allow_negative_scores:
|
|
||||||
new_score = max(0, new_score)
|
|
||||||
player.team.score = new_score
|
|
||||||
|
|
||||||
# In teams-mode it gives a point to the other team.
|
|
||||||
else:
|
|
||||||
self._dingsound.play()
|
|
||||||
for team in self.teams:
|
|
||||||
if team is not killer.team:
|
|
||||||
team.score += 1
|
|
||||||
|
|
||||||
# Killing someone on another team nets a kill.
|
|
||||||
else:
|
|
||||||
killer.team.score += 1
|
|
||||||
self._dingsound.play()
|
|
||||||
|
|
||||||
# In FFA show scores since its hard to find on the scoreboard.
|
|
||||||
if isinstance(killer.actor, PlayerSpaz) and killer.actor:
|
|
||||||
killer.actor.set_score_text(
|
|
||||||
str(killer.team.score) + '/' + str(self._score_to_win),
|
|
||||||
color=killer.team.color,
|
|
||||||
flash=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
self._update_scoreboard()
|
|
||||||
|
|
||||||
# If someone has won, set a timer to end shortly.
|
|
||||||
# (allows the dust to clear and draws to occur if deaths are
|
|
||||||
# close enough)
|
|
||||||
assert self._score_to_win is not None
|
|
||||||
if any(team.score >= self._score_to_win for team in self.teams):
|
|
||||||
bs.timer(0.5, self.end_game)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _update_scoreboard(self) -> None:
|
|
||||||
for team in self.teams:
|
|
||||||
self._scoreboard.set_team_value(
|
|
||||||
team, team.score, self._score_to_win
|
|
||||||
)
|
|
||||||
|
|
||||||
def end_game(self) -> None:
|
|
||||||
results = bs.GameResults()
|
|
||||||
for team in self.teams:
|
|
||||||
results.set_team_score(team, team.score)
|
|
||||||
self.end(results=results)
|
|
||||||
|
|
||||||
|
|
||||||
class Obstacle(bs.Actor):
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
position: tuple(float, float, float),
|
|
||||||
graphics: bool,
|
|
||||||
random_color: bool,
|
|
||||||
rebound: bool,
|
|
||||||
shape: int) -> None:
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
if shape == 1:
|
|
||||||
mesh = 'tnt'
|
|
||||||
body = 'crate'
|
|
||||||
elif shape == 2:
|
|
||||||
mesh = 'bomb'
|
|
||||||
body = 'sphere'
|
|
||||||
elif shape == 3:
|
|
||||||
mesh = 'puck'
|
|
||||||
body = 'puck'
|
|
||||||
elif shape == 4:
|
|
||||||
mesh = 'egg'
|
|
||||||
body = 'capsule'
|
|
||||||
elif shape == 5:
|
|
||||||
pair = random.choice([
|
|
||||||
{'mesh': 'tnt', 'body': 'crate'},
|
|
||||||
{'mesh': 'bomb', 'body': 'sphere'},
|
|
||||||
{'mesh': 'puckModel', 'body': 'puck'},
|
|
||||||
{'mesh': 'egg', 'body': 'capsule'}
|
|
||||||
])
|
|
||||||
mesh = pair['mesh']
|
|
||||||
body = pair['body']
|
|
||||||
|
|
||||||
self.node = bs.newnode('prop', delegate=self, attrs={
|
|
||||||
'position': position,
|
|
||||||
'mesh': bs.getmesh(mesh),
|
|
||||||
'body': body,
|
|
||||||
'body_scale': 1.3,
|
|
||||||
'mesh_scale': 1.3,
|
|
||||||
'reflection': 'powerup',
|
|
||||||
'reflection_scale': [0.7],
|
|
||||||
'color_texture': bs.gettexture('bunnyColor'),
|
|
||||||
'materials': [
|
|
||||||
shared.footing_material if rebound else shared.object_material,
|
|
||||||
shared.footing_material]})
|
|
||||||
|
|
||||||
if graphics == 2:
|
|
||||||
self.light_node = bs.newnode('light', attrs={
|
|
||||||
'position': (0, 0, 0),
|
|
||||||
'color': ((0.8, 0.2, 0.2) if i < count / 2 else (0.2, 0.2, 0.8))
|
|
||||||
if not random_color else ((
|
|
||||||
random.uniform(0, 1.1), random.uniform(0, 1.1),
|
|
||||||
random.uniform(0, 1.1))),
|
|
||||||
'radius': 0.2,
|
|
||||||
'intensity': 1,
|
|
||||||
'volume_intensity_scale': 10.0})
|
|
||||||
|
|
||||||
self.node.connectattr('position', self.light_node, 'position')
|
|
||||||
|
|
||||||
def handlemessage(self, m):
|
|
||||||
if isinstance(m, bs.DieMessage):
|
|
||||||
if self.node.exists():
|
|
||||||
if hasattr(self, 'light_node'):
|
|
||||||
self.light_node.delete()
|
|
||||||
self.node.delete()
|
|
||||||
|
|
||||||
elif isinstance(m, bs.OutOfBoundsMessage):
|
|
||||||
if self.node.exists():
|
|
||||||
self.handlemessage(bs.DieMessage())
|
|
||||||
|
|
||||||
elif isinstance(m, bs.HitMessage):
|
|
||||||
self.node.handlemessage('impulse', m.pos[0], m.pos[1], m.pos[2],
|
|
||||||
m.velocity[0], m.velocity[1], m.velocity[2],
|
|
||||||
m.magnitude, m.velocity_magnitude, m.radius,
|
|
||||||
0,
|
|
||||||
m.velocity[0], m.velocity[1], m.velocity[2])
|
|
||||||
376
dist/ba_root/mods/games/soccer.py
vendored
376
dist/ba_root/mods/games/soccer.py
vendored
|
|
@ -1,376 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
# BY Stary_Agent
|
|
||||||
"""Hockey game and support classes."""
|
|
||||||
|
|
||||||
# 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 bascenev1 as bs
|
|
||||||
import bauiv1 as bui
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
|
||||||
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
|
|
||||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
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])
|
|
||||||
self.last_players_to_touch: Dict[int, Player] = {}
|
|
||||||
self.scored = False
|
|
||||||
assert activity is not None
|
|
||||||
assert isinstance(activity, HockeyGame)
|
|
||||||
pmats = [shared.object_material, activity.puck_material]
|
|
||||||
self.node = bs.newnode('prop',
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
'mesh': activity.puck_model,
|
|
||||||
'color_texture': activity.puck_tex,
|
|
||||||
'body': 'sphere',
|
|
||||||
'reflection': 'soft',
|
|
||||||
'reflection_scale': [0.2],
|
|
||||||
'shadow_size': 0.5,
|
|
||||||
'is_area_of_interest': True,
|
|
||||||
'position': self._spawn_pos,
|
|
||||||
'materials': pmats
|
|
||||||
})
|
|
||||||
bs.animate(self.node, 'mesh_scale', {0: 0, 0.2: 1.3, 0.26: 1})
|
|
||||||
|
|
||||||
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.team.id] = 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."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.score = 0
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class HockeyGame(bs.TeamGameActivity[Player, Team]):
|
|
||||||
"""Ice hockey game."""
|
|
||||||
|
|
||||||
name = 'Epic Soccer'
|
|
||||||
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),
|
|
||||||
('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]:
|
|
||||||
assert babase.app.classic is not None
|
|
||||||
return babase.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_model = bs.getmesh('bomb')
|
|
||||||
self.puck_tex = bs.gettexture('landMine')
|
|
||||||
self.puck_scored_tex = bs.gettexture('landMineLit')
|
|
||||||
self._puck_sound = bs.getsound('metalHit')
|
|
||||||
self.puck_material = bs.Material()
|
|
||||||
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._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)))
|
|
||||||
self._puck_spawn_pos: Optional[Sequence[float]] = None
|
|
||||||
self._score_regions: Optional[List[bs.NodeActor]] = None
|
|
||||||
self._puck: Optional[Puck] = None
|
|
||||||
self._score_to_win = int(settings['Score to Win'])
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
|
|
||||||
def get_instance_description(self) -> Union[str, Sequence]:
|
|
||||||
if self._score_to_win == 1:
|
|
||||||
return 'Score a goal.'
|
|
||||||
return 'Score ${ARG1} goals.', self._score_to_win
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> Union[str, Sequence]:
|
|
||||||
if self._score_to_win == 1:
|
|
||||||
return 'score a goal'
|
|
||||||
return 'score ${ARG1} goals', self._score_to_win
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
# Set up the two score regions.
|
|
||||||
defs = self.map.defs
|
|
||||||
self._score_regions = []
|
|
||||||
self._score_regions.append(
|
|
||||||
bs.NodeActor(
|
|
||||||
bs.newnode('region',
|
|
||||||
attrs={
|
|
||||||
'position': defs.boxes['goal1'][0:3],
|
|
||||||
'scale': defs.boxes['goal1'][6:9],
|
|
||||||
'type': 'box',
|
|
||||||
'materials': [self._score_region_material]
|
|
||||||
})))
|
|
||||||
self._score_regions.append(
|
|
||||||
bs.NodeActor(
|
|
||||||
bs.newnode('region',
|
|
||||||
attrs={
|
|
||||||
'position': defs.boxes['goal2'][0:3],
|
|
||||||
'scale': defs.boxes['goal2'][6:9],
|
|
||||||
'type': 'box',
|
|
||||||
'materials': [self._score_region_material]
|
|
||||||
})))
|
|
||||||
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.team.id] = player
|
|
||||||
|
|
||||||
def _kill_puck(self) -> None:
|
|
||||||
self._puck = None
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def _spawn_puck(self) -> None:
|
|
||||||
self._swipsound.play()
|
|
||||||
self._whistle_sound.play()
|
|
||||||
self._flash_puck_spawn()
|
|
||||||
assert self._puck_spawn_pos is not None
|
|
||||||
self._puck = Puck(position=self._puck_spawn_pos)
|
|
||||||
603
dist/ba_root/mods/games/super_duel.py
vendored
603
dist/ba_root/mods/games/super_duel.py
vendored
|
|
@ -1,603 +0,0 @@
|
||||||
"""New Duel / Created by: byANG3L"""
|
|
||||||
|
|
||||||
# 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 bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
|
||||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
|
||||||
from bascenev1lib.game.elimination import Icon
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Any, Type, List, Union, Sequence, Optional
|
|
||||||
|
|
||||||
|
|
||||||
class SuperSpaz(PlayerSpaz):
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
player: bs.Player,
|
|
||||||
color: Sequence[float] = (1.0, 1.0, 1.0),
|
|
||||||
highlight: Sequence[float] = (0.5, 0.5, 0.5),
|
|
||||||
character: str = 'Spaz',
|
|
||||||
super_punch: bool = False,
|
|
||||||
powerups_expire: bool = True):
|
|
||||||
super().__init__(player=player,
|
|
||||||
color=color,
|
|
||||||
highlight=highlight,
|
|
||||||
character=character,
|
|
||||||
powerups_expire=powerups_expire)
|
|
||||||
self._super_punch = super_punch
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
from bascenev1lib.actor.spaz import PunchHitMessage
|
|
||||||
from bascenev1lib.actor.bomb import Blast
|
|
||||||
if isinstance(msg, PunchHitMessage):
|
|
||||||
super().handlemessage(msg)
|
|
||||||
node = bs.getcollision().opposingnode
|
|
||||||
if self._super_punch:
|
|
||||||
if node.getnodetype() == 'spaz':
|
|
||||||
if not node.frozen:
|
|
||||||
node.frozen = True
|
|
||||||
node.handlemessage(babase.FreezeMessage())
|
|
||||||
bs.getsound('freeze').play()
|
|
||||||
bs.getsound('superPunch').play()
|
|
||||||
bs.getsound('punchStrong02').play()
|
|
||||||
Blast(position=node.position,
|
|
||||||
velocity=node.velocity,
|
|
||||||
blast_radius=0.0,
|
|
||||||
blast_type='normal').autoretain()
|
|
||||||
else:
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class Player(bs.Player['Team']):
|
|
||||||
"""Our player type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.icons: List[Icon] = []
|
|
||||||
self.in_game: bool = False
|
|
||||||
self.playervs1: bool = False
|
|
||||||
self.playervs2: bool = False
|
|
||||||
self.light: bool = False
|
|
||||||
|
|
||||||
|
|
||||||
class Team(bs.Team[Player]):
|
|
||||||
"""Our team type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.score = 0
|
|
||||||
|
|
||||||
|
|
||||||
lang = bs.app.lang.language
|
|
||||||
if lang == 'Spanish':
|
|
||||||
enable_powerups = 'Habilitar Potenciadores'
|
|
||||||
night_mode = 'Modo Noche'
|
|
||||||
fight_delay = 'Tiempo entre Pelea'
|
|
||||||
very_fast = 'Muy Rápido'
|
|
||||||
fast = 'Rápido'
|
|
||||||
normal = 'Normal'
|
|
||||||
slow = 'Lento'
|
|
||||||
very_slow = 'Muy Lento'
|
|
||||||
none = 'Ninguno'
|
|
||||||
super_punch = 'Super Golpe'
|
|
||||||
box_mode = 'Modo Caja'
|
|
||||||
boxing_gloves = 'Guantes de Boxeo'
|
|
||||||
else:
|
|
||||||
enable_powerups = 'Enable Powerups'
|
|
||||||
night_mode = 'Night Mode'
|
|
||||||
fight_delay = 'Fight Delay'
|
|
||||||
very_fast = 'Very Fast'
|
|
||||||
fast = 'Fast'
|
|
||||||
normal = 'Normal'
|
|
||||||
slow = 'Slow'
|
|
||||||
very_slow = 'Very Slow'
|
|
||||||
super_punch = 'Super Punch'
|
|
||||||
box_mode = 'Box Mode'
|
|
||||||
boxing_gloves = 'Boxing Gloves'
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
|
|
||||||
|
|
||||||
class NewDuelGame(bs.TeamGameActivity[Player, Team]):
|
|
||||||
"""A game type based on acquiring kills."""
|
|
||||||
|
|
||||||
name = 'Duel'
|
|
||||||
description = 'Kill a set number of enemies to win.'
|
|
||||||
|
|
||||||
# Print messages when players die since it matters here.
|
|
||||||
announce_player_deaths = True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_available_settings(
|
|
||||||
cls, sessiontype: Type[bs.Session]) -> List[babase.Setting]:
|
|
||||||
settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Kills to Win Per Player',
|
|
||||||
min_value=1,
|
|
||||||
default=5,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting(enable_powerups, default=False),
|
|
||||||
bs.BoolSetting(boxing_gloves, default=False),
|
|
||||||
bs.BoolSetting(night_mode, default=False),
|
|
||||||
bs.BoolSetting(super_punch, default=False),
|
|
||||||
bs.BoolSetting(box_mode, default=False),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False),
|
|
||||||
bs.BoolSetting('Allow Negative Scores', default=False),
|
|
||||||
]
|
|
||||||
return settings
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
|
|
||||||
return (issubclass(sessiontype, bs.FreeForAllSession))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
|
|
||||||
return bs.app.classic.getmaps('melee')
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
self._scoreboard = Scoreboard()
|
|
||||||
self._score_to_win: Optional[int] = None
|
|
||||||
self._dingsound = bs.getsound('dingSmall')
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
self._kills_to_win_per_player = int(
|
|
||||||
settings['Kills to Win Per Player'])
|
|
||||||
self._enable_powerups = bool(settings[enable_powerups])
|
|
||||||
self._night_mode = bool(settings[night_mode])
|
|
||||||
self._fight_delay: float = 0
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
self._allow_negative_scores = bool(
|
|
||||||
settings.get('Allow Negative Scores', False))
|
|
||||||
self._super_punch = bool(settings[super_punch])
|
|
||||||
self._box_mode = bool(settings[box_mode])
|
|
||||||
self._boxing_gloves = bool(settings[boxing_gloves])
|
|
||||||
self._vs_text: Optional[bs.Actor] = None
|
|
||||||
self.spawn_order: List[Player] = []
|
|
||||||
self._players_vs_1: bool = False
|
|
||||||
self._players_vs_2: bool = False
|
|
||||||
self._first_countdown: bool = True
|
|
||||||
self._count_1 = bs.getsound('announceOne')
|
|
||||||
self._count_2 = bs.getsound('announceTwo')
|
|
||||||
self._count_3 = bs.getsound('announceThree')
|
|
||||||
self._boxing_bell = bs.getsound('boxingBell')
|
|
||||||
|
|
||||||
# Base class overrides.
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (bs.MusicType.EPIC if self._epic_mode else
|
|
||||||
bs.MusicType.TO_THE_DEATH)
|
|
||||||
|
|
||||||
def get_instance_description(self) -> Union[str, Sequence]:
|
|
||||||
return 'Crush ${ARG1} of your enemies.', self._score_to_win
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> Union[str, Sequence]:
|
|
||||||
return 'kill ${ARG1} enemies', self._score_to_win
|
|
||||||
|
|
||||||
def on_player_join(self, player: Player) -> None:
|
|
||||||
self.spawn_order.append(player)
|
|
||||||
self._update_order()
|
|
||||||
|
|
||||||
def on_player_leave(self, player: Player) -> None:
|
|
||||||
super().on_player_leave(player)
|
|
||||||
player.icons = []
|
|
||||||
if player.playervs1:
|
|
||||||
player.playervs1 = False
|
|
||||||
self._players_vs_1 = False
|
|
||||||
player.in_game = False
|
|
||||||
elif player.playervs2:
|
|
||||||
player.playervs2 = False
|
|
||||||
self._players_vs_2 = False
|
|
||||||
player.in_game = False
|
|
||||||
if player in self.spawn_order:
|
|
||||||
self.spawn_order.remove(player)
|
|
||||||
bs.timer(0.2, self._update_order)
|
|
||||||
|
|
||||||
def on_transition_in(self) -> None:
|
|
||||||
super().on_transition_in()
|
|
||||||
if self._night_mode:
|
|
||||||
gnode = bs.getactivity().globalsnode
|
|
||||||
gnode.tint = (0.3, 0.3, 0.3)
|
|
||||||
|
|
||||||
def on_team_join(self, team: Team) -> None:
|
|
||||||
if self.has_begun():
|
|
||||||
self._update_scoreboard()
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
self.setup_standard_time_limit(self._time_limit)
|
|
||||||
if self._enable_powerups:
|
|
||||||
self.setup_standard_powerup_drops()
|
|
||||||
self._vs_text = bs.NodeActor(
|
|
||||||
bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'position': (0, 105),
|
|
||||||
'h_attach': 'center',
|
|
||||||
'h_align': 'center',
|
|
||||||
'maxwidth': 200,
|
|
||||||
'shadow': 0.5,
|
|
||||||
'vr_depth': 390,
|
|
||||||
'scale': 0.6,
|
|
||||||
'v_attach': 'bottom',
|
|
||||||
'color': (0.8, 0.8, 0.3, 1.0),
|
|
||||||
'text': babase.Lstr(resource='vsText')
|
|
||||||
}))
|
|
||||||
|
|
||||||
# Base kills needed to win on the size of the largest team.
|
|
||||||
self._score_to_win = (self._kills_to_win_per_player *
|
|
||||||
max(1, max(len(t.players) for t in self.teams)))
|
|
||||||
self._update_scoreboard()
|
|
||||||
bs.timer(1.0, self._update, repeat=True)
|
|
||||||
|
|
||||||
def _update(self) -> None:
|
|
||||||
if len(self.players) == 1:
|
|
||||||
'self.end_game()'
|
|
||||||
|
|
||||||
def spawn_player(self, player: PlayerType) -> bs.Actor:
|
|
||||||
# pylint: disable=too-many-locals
|
|
||||||
# pylint: disable=cyclic-import
|
|
||||||
from babase import _math
|
|
||||||
from bascenev1._coopsession import CoopSession
|
|
||||||
from bascenev1lib.actor.spazfactory import SpazFactory
|
|
||||||
factory = SpazFactory.get()
|
|
||||||
name = player.getname()
|
|
||||||
color = player.color
|
|
||||||
highlight = player.highlight
|
|
||||||
|
|
||||||
light_color = _math.normalized_color(color)
|
|
||||||
display_color = babase.safecolor(color, target_intensity=0.75)
|
|
||||||
spaz = SuperSpaz(color=color,
|
|
||||||
highlight=highlight,
|
|
||||||
character=player.character,
|
|
||||||
player=player,
|
|
||||||
super_punch=True if self._super_punch else False)
|
|
||||||
|
|
||||||
player.actor = spaz
|
|
||||||
assert spaz.node
|
|
||||||
|
|
||||||
# If this is co-op and we're on Courtyard or Runaround, add the
|
|
||||||
# material that allows us to collide with the player-walls.
|
|
||||||
# FIXME: Need to generalize this.
|
|
||||||
if isinstance(self.session, CoopSession) and self.map.getname() in [
|
|
||||||
'Courtyard', 'Tower D'
|
|
||||||
]:
|
|
||||||
mat = self.map.preloaddata['collide_with_wall_material']
|
|
||||||
assert isinstance(spaz.node.materials, tuple)
|
|
||||||
assert isinstance(spaz.node.roller_materials, tuple)
|
|
||||||
spaz.node.materials += (mat,)
|
|
||||||
spaz.node.roller_materials += (mat,)
|
|
||||||
|
|
||||||
spaz.node.name = name
|
|
||||||
spaz.node.name_color = display_color
|
|
||||||
spaz.connect_controls_to_player()
|
|
||||||
|
|
||||||
self._spawn_sound.play(1, position=spaz.node.position)
|
|
||||||
light = bs.newnode('light', attrs={'color': light_color})
|
|
||||||
spaz.node.connectattr('position', light, 'position')
|
|
||||||
bs.animate(light, 'intensity', {0: 0, 0.25: 1, 0.5: 0})
|
|
||||||
bs.timer(0.5, light.delete)
|
|
||||||
|
|
||||||
pos1 = [self.map.get_start_position(0), 90]
|
|
||||||
pos2 = [self.map.get_start_position(1), 270]
|
|
||||||
pos3 = []
|
|
||||||
|
|
||||||
for x in self.players:
|
|
||||||
if x.is_alive():
|
|
||||||
if x is player:
|
|
||||||
continue
|
|
||||||
p = x.actor.node.position
|
|
||||||
if 0.0 not in (p[0], p[2]):
|
|
||||||
if p[0] <= 0:
|
|
||||||
pos3.append(pos2[0])
|
|
||||||
else:
|
|
||||||
pos3.append(pos1[0])
|
|
||||||
|
|
||||||
spaz.handlemessage(
|
|
||||||
bs.StandMessage(pos1[0] if player.playervs1 else pos2[0],
|
|
||||||
pos1[1] if player.playervs1 else pos2[1]))
|
|
||||||
|
|
||||||
if any(pos3):
|
|
||||||
spaz.handlemessage(bs.StandMessage(pos3[0]))
|
|
||||||
|
|
||||||
if self._super_punch:
|
|
||||||
spaz._punch_power_scale = factory.punch_power_scale_gloves = 10
|
|
||||||
spaz.equip_boxing_gloves()
|
|
||||||
lfx = bs.newnode(
|
|
||||||
'light',
|
|
||||||
attrs={
|
|
||||||
'color': color,
|
|
||||||
'radius': 0.3,
|
|
||||||
'intensity': 0.3})
|
|
||||||
|
|
||||||
def sp_fx():
|
|
||||||
if not spaz.node:
|
|
||||||
lfx.delete()
|
|
||||||
return
|
|
||||||
bs.emitfx(position=spaz.node.position,
|
|
||||||
velocity=spaz.node.velocity,
|
|
||||||
count=5,
|
|
||||||
scale=0.5,
|
|
||||||
spread=0.5,
|
|
||||||
chunk_type='spark')
|
|
||||||
bs.emitfx(position=spaz.node.position,
|
|
||||||
velocity=spaz.node.velocity,
|
|
||||||
count=2,
|
|
||||||
scale=0.8,
|
|
||||||
spread=0.3,
|
|
||||||
chunk_type='spark')
|
|
||||||
if lfx:
|
|
||||||
spaz.node.connectattr('position', lfx, 'position')
|
|
||||||
|
|
||||||
bs.timer(0.1, sp_fx, repeat=True)
|
|
||||||
|
|
||||||
if self._box_mode:
|
|
||||||
spaz.node.color_texture = bs.gettexture('tnt')
|
|
||||||
spaz.node.color_mask_texture = bs.gettexture('tnt')
|
|
||||||
spaz.node.color = (1, 1, 1)
|
|
||||||
spaz.node.highlight = (1, 1, 1)
|
|
||||||
spaz.node.head_mesh = None
|
|
||||||
spaz.node.torso_mesh = bs.getmesh('tnt')
|
|
||||||
spaz.node.style = 'cyborg'
|
|
||||||
|
|
||||||
if self._boxing_gloves:
|
|
||||||
spaz.equip_boxing_gloves()
|
|
||||||
|
|
||||||
return spaz
|
|
||||||
|
|
||||||
def _update_spawn(self) -> None:
|
|
||||||
if self._players_vs_1 or self._players_vs_2:
|
|
||||||
for player in self.players:
|
|
||||||
if player.playervs1 or player.playervs2:
|
|
||||||
if not player.is_alive():
|
|
||||||
self.spawn_player(player)
|
|
||||||
# player.actor.disconnect_controls_from_player()
|
|
||||||
|
|
||||||
if self._night_mode:
|
|
||||||
if not player.light:
|
|
||||||
player.light = True
|
|
||||||
light = bs.newnode(
|
|
||||||
'light',
|
|
||||||
owner=player.node,
|
|
||||||
attrs={
|
|
||||||
'radius': 0.3,
|
|
||||||
'intensity': 0.6,
|
|
||||||
'height_attenuated': False,
|
|
||||||
'color': player.color
|
|
||||||
})
|
|
||||||
player.node.connectattr(
|
|
||||||
'position', light, 'position')
|
|
||||||
else:
|
|
||||||
player.actor.disconnect_controls_from_player()
|
|
||||||
|
|
||||||
bs.timer(0.0, self._countdown)
|
|
||||||
# bs.timer(0.1, self._clear_all_objects)
|
|
||||||
|
|
||||||
def _countdown(self) -> None:
|
|
||||||
self._first_countdown = False
|
|
||||||
if self._fight_delay == 0:
|
|
||||||
for player in self.players:
|
|
||||||
if player.playervs1 or player.playervs2:
|
|
||||||
if not player.is_alive():
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
player.actor.connect_controls_to_player()
|
|
||||||
else:
|
|
||||||
bs.timer(self._fight_delay, self.count3)
|
|
||||||
|
|
||||||
def start(self) -> None:
|
|
||||||
self._count_text('FIGHT')
|
|
||||||
self._boxing_bell.play()
|
|
||||||
for player in self.players:
|
|
||||||
if player.playervs1 or player.playervs2:
|
|
||||||
if not player.is_alive():
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
player.actor.connect_controls_to_player()
|
|
||||||
|
|
||||||
def count(self) -> None:
|
|
||||||
self._count_text('1')
|
|
||||||
self._count_1.play()
|
|
||||||
bs.timer(self._fight_delay, self.start)
|
|
||||||
|
|
||||||
def count2(self) -> None:
|
|
||||||
self._count_text('2')
|
|
||||||
self._count_2.play()
|
|
||||||
bs.timer(self._fight_delay, self.count)
|
|
||||||
|
|
||||||
def count3(self) -> None:
|
|
||||||
self._count_text('3')
|
|
||||||
self._count_3.play()
|
|
||||||
bs.timer(self._fight_delay, self.count2)
|
|
||||||
|
|
||||||
def _count_text(self, num: str) -> None:
|
|
||||||
self.node = bs.newnode('text',
|
|
||||||
attrs={
|
|
||||||
'v_attach': 'center',
|
|
||||||
'h_attach': 'center',
|
|
||||||
'h_align': 'center',
|
|
||||||
'color': (1, 1, 0.5, 1),
|
|
||||||
'flatness': 0.5,
|
|
||||||
'shadow': 0.5,
|
|
||||||
'position': (0, 18),
|
|
||||||
'text': num
|
|
||||||
})
|
|
||||||
if self._fight_delay == 0.7:
|
|
||||||
bs.animate(self.node, 'scale',
|
|
||||||
{0: 0, 0.1: 3.9, 0.64: 4.3, 0.68: 0})
|
|
||||||
elif self._fight_delay == 0.4:
|
|
||||||
bs.animate(self.node, 'scale',
|
|
||||||
{0: 0, 0.1: 3.9, 0.34: 4.3, 0.38: 0})
|
|
||||||
else:
|
|
||||||
bs.animate(self.node, 'scale',
|
|
||||||
{0: 0, 0.1: 3.9, 0.92: 4.3, 0.96: 0})
|
|
||||||
cmb = bs.newnode('combine', owner=self.node, attrs={'size': 4})
|
|
||||||
cmb.connectattr('output', self.node, 'color')
|
|
||||||
bs.animate(cmb, 'input0', {0: 1.0, 0.15: 1.0}, loop=True)
|
|
||||||
bs.animate(cmb, 'input1', {0: 1.0, 0.15: 0.5}, loop=True)
|
|
||||||
bs.animate(cmb, 'input2', {0: 0.1, 0.15: 0.0}, loop=True)
|
|
||||||
cmb.input3 = 1.0
|
|
||||||
bs.timer(self._fight_delay, self.node.delete)
|
|
||||||
|
|
||||||
def _update_order(self) -> None:
|
|
||||||
for player in self.spawn_order:
|
|
||||||
assert isinstance(player, Player)
|
|
||||||
if not player.is_alive():
|
|
||||||
if not self._players_vs_1:
|
|
||||||
self._players_vs_1 = True
|
|
||||||
player.playervs1 = True
|
|
||||||
player.in_game = True
|
|
||||||
self.spawn_order.remove(player)
|
|
||||||
self._update_spawn()
|
|
||||||
elif not self._players_vs_2:
|
|
||||||
self._players_vs_2 = True
|
|
||||||
player.playervs2 = True
|
|
||||||
player.in_game = True
|
|
||||||
self.spawn_order.remove(player)
|
|
||||||
self._update_spawn()
|
|
||||||
self._update_icons()
|
|
||||||
|
|
||||||
def _update_icons(self) -> None:
|
|
||||||
# pylint: disable=too-many-branches
|
|
||||||
|
|
||||||
for player in self.players:
|
|
||||||
player.icons = []
|
|
||||||
|
|
||||||
if player.in_game:
|
|
||||||
if player.playervs1:
|
|
||||||
xval = -60
|
|
||||||
x_offs = -78
|
|
||||||
elif player.playervs2:
|
|
||||||
xval = 60
|
|
||||||
x_offs = 78
|
|
||||||
player.icons.append(
|
|
||||||
Icon(player,
|
|
||||||
position=(xval, 40),
|
|
||||||
scale=1.0,
|
|
||||||
name_maxwidth=130,
|
|
||||||
name_scale=0.8,
|
|
||||||
flatness=0.0,
|
|
||||||
shadow=0.5,
|
|
||||||
show_death=True,
|
|
||||||
show_lives=False))
|
|
||||||
else:
|
|
||||||
xval = 125
|
|
||||||
xval2 = -125
|
|
||||||
x_offs = 78
|
|
||||||
for player in self.spawn_order:
|
|
||||||
player.icons.append(
|
|
||||||
Icon(player,
|
|
||||||
position=(xval, 25),
|
|
||||||
scale=0.5,
|
|
||||||
name_maxwidth=75,
|
|
||||||
name_scale=1.0,
|
|
||||||
flatness=1.0,
|
|
||||||
shadow=1.0,
|
|
||||||
show_death=False,
|
|
||||||
show_lives=False))
|
|
||||||
xval += x_offs * 0.56
|
|
||||||
player.icons.append(
|
|
||||||
Icon(player,
|
|
||||||
position=(xval2, 25),
|
|
||||||
scale=0.5,
|
|
||||||
name_maxwidth=75,
|
|
||||||
name_scale=1.0,
|
|
||||||
flatness=1.0,
|
|
||||||
shadow=1.0,
|
|
||||||
show_death=False,
|
|
||||||
show_lives=False))
|
|
||||||
xval2 -= x_offs * 0.56
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
|
|
||||||
if isinstance(msg, bs.PlayerDiedMessage):
|
|
||||||
|
|
||||||
# Augment standard behavior.
|
|
||||||
super().handlemessage(msg)
|
|
||||||
|
|
||||||
player = msg.getplayer(Player)
|
|
||||||
|
|
||||||
if player.playervs1:
|
|
||||||
player.playervs1 = False
|
|
||||||
self._players_vs_1 = False
|
|
||||||
player.in_game = False
|
|
||||||
self.spawn_order.append(player)
|
|
||||||
elif player.playervs2:
|
|
||||||
player.playervs2 = False
|
|
||||||
self._players_vs_2 = False
|
|
||||||
player.in_game = False
|
|
||||||
self.spawn_order.append(player)
|
|
||||||
bs.timer(0.2, self._update_order)
|
|
||||||
|
|
||||||
killer = msg.getkillerplayer(Player)
|
|
||||||
if killer is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Handle team-kills.
|
|
||||||
if killer.team is player.team:
|
|
||||||
|
|
||||||
# In free-for-all, killing yourself loses you a point.
|
|
||||||
if isinstance(self.session, bs.FreeForAllSession):
|
|
||||||
new_score = player.team.score - 1
|
|
||||||
if not self._allow_negative_scores:
|
|
||||||
new_score = max(0, new_score)
|
|
||||||
player.team.score = new_score
|
|
||||||
|
|
||||||
# In teams-mode it gives a point to the other team.
|
|
||||||
else:
|
|
||||||
self._dingsound.play()
|
|
||||||
for team in self.teams:
|
|
||||||
if team is not killer.team:
|
|
||||||
team.score += 1
|
|
||||||
|
|
||||||
# Killing someone on another team nets a kill.
|
|
||||||
else:
|
|
||||||
killer.team.score += 1
|
|
||||||
self._dingsound.play()
|
|
||||||
|
|
||||||
# In FFA show scores since its hard to find on the scoreboard.
|
|
||||||
if isinstance(killer.actor, PlayerSpaz) and killer.actor:
|
|
||||||
killer.actor.set_score_text(str(killer.team.score) + '/' +
|
|
||||||
str(self._score_to_win),
|
|
||||||
color=killer.team.color,
|
|
||||||
flash=True)
|
|
||||||
|
|
||||||
self._update_scoreboard()
|
|
||||||
|
|
||||||
# If someone has won, set a timer to end shortly.
|
|
||||||
# (allows the dust to clear and draws to occur if deaths are
|
|
||||||
# close enough)
|
|
||||||
assert self._score_to_win is not None
|
|
||||||
if any(team.score >= self._score_to_win for team in self.teams):
|
|
||||||
bs.timer(0.5, self.end_game)
|
|
||||||
else:
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _update_scoreboard(self) -> None:
|
|
||||||
for team in self.teams:
|
|
||||||
self._scoreboard.set_team_value(team, team.score,
|
|
||||||
self._score_to_win)
|
|
||||||
|
|
||||||
def end_game(self) -> None:
|
|
||||||
results = bs.GameResults()
|
|
||||||
for team in self.teams:
|
|
||||||
results.set_team_score(team, team.score)
|
|
||||||
self.end(results=results)
|
|
||||||
959
dist/ba_root/mods/games/supersmash.py
vendored
959
dist/ba_root/mods/games/supersmash.py
vendored
|
|
@ -1,959 +0,0 @@
|
||||||
# To learn more, see https://ballistica.net/wiki/meta-tag-system
|
|
||||||
# ba_meta require api 8
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import random
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from babase import _math
|
|
||||||
from bascenev1lib.actor.bomb import Bomb, Blast
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz, PlayerSpazHurtMessage
|
|
||||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
|
||||||
from bascenev1lib.actor.spaz import Spaz
|
|
||||||
from bascenev1lib.actor.spazfactory import SpazFactory
|
|
||||||
from bascenev1lib.game.elimination import Icon, Player, Team
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Any, Type, List, Sequence, Optional
|
|
||||||
|
|
||||||
|
|
||||||
class Icon(Icon):
|
|
||||||
def update_for_lives(self) -> None:
|
|
||||||
"""Update for the target player's current lives."""
|
|
||||||
if self._player:
|
|
||||||
lives = self._player.lives
|
|
||||||
else:
|
|
||||||
lives = 0
|
|
||||||
if self._show_lives:
|
|
||||||
if lives > 1:
|
|
||||||
self._lives_text.text = 'x' + str(lives - 1)
|
|
||||||
else:
|
|
||||||
self._lives_text.text = ''
|
|
||||||
if lives == 0:
|
|
||||||
self._name_text.opacity = 0.2
|
|
||||||
assert self.node
|
|
||||||
self.node.color = (0.7, 0.3, 0.3)
|
|
||||||
self.node.opacity = 0.2
|
|
||||||
|
|
||||||
|
|
||||||
class PowBox(Bomb):
|
|
||||||
|
|
||||||
def __init__(self,
|
|
||||||
position: Sequence[float] = (0.0, 1.0, 0.0),
|
|
||||||
velocity: Sequence[float] = (0.0, 0.0, 0.0)) -> None:
|
|
||||||
Bomb.__init__(self,
|
|
||||||
position,
|
|
||||||
velocity,
|
|
||||||
bomb_type='tnt',
|
|
||||||
blast_radius=2.5,
|
|
||||||
source_player=None,
|
|
||||||
owner=None)
|
|
||||||
self.set_pow_text()
|
|
||||||
|
|
||||||
def set_pow_text(self) -> None:
|
|
||||||
m = bs.newnode('math',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={'input1': (0, 0.7, 0),
|
|
||||||
'operation': 'add'})
|
|
||||||
self.node.connectattr('position', m, 'input2')
|
|
||||||
|
|
||||||
self._pow_text = bs.newnode('text',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={'text': 'POW!',
|
|
||||||
'in_world': True,
|
|
||||||
'shadow': 1.0,
|
|
||||||
'flatness': 1.0,
|
|
||||||
'color': (1, 1, 0.4),
|
|
||||||
'scale': 0.0,
|
|
||||||
'h_align': 'center'})
|
|
||||||
m.connectattr('output', self._pow_text, 'position')
|
|
||||||
bs.animate(self._pow_text, 'scale', {0: 0.0, 1.0: 0.01})
|
|
||||||
|
|
||||||
def pow(self) -> None:
|
|
||||||
self.explode()
|
|
||||||
|
|
||||||
def handlemessage(self, m: Any) -> Any:
|
|
||||||
if isinstance(m, babase.PickedUpMessage):
|
|
||||||
self._heldBy = m.node
|
|
||||||
elif isinstance(m, bs.DroppedMessage):
|
|
||||||
bs.timer(0.6, self.pow)
|
|
||||||
Bomb.handlemessage(self, m)
|
|
||||||
|
|
||||||
|
|
||||||
class SSPlayerSpaz(PlayerSpaz):
|
|
||||||
multiplyer = 1
|
|
||||||
is_dead = False
|
|
||||||
|
|
||||||
def oob_effect(self) -> None:
|
|
||||||
if self.is_dead:
|
|
||||||
return
|
|
||||||
self.is_dead = True
|
|
||||||
if self.multiplyer > 1.25:
|
|
||||||
blast_type = 'tnt'
|
|
||||||
radius = min(self.multiplyer * 5, 20)
|
|
||||||
else:
|
|
||||||
# penalty for killing people with low multiplyer
|
|
||||||
blast_type = 'ice'
|
|
||||||
radius = 7.5
|
|
||||||
Blast(position=self.node.position,
|
|
||||||
blast_radius=radius,
|
|
||||||
blast_type=blast_type).autoretain()
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
if isinstance(msg, bs.HitMessage):
|
|
||||||
if not self.node:
|
|
||||||
return None
|
|
||||||
if self.node.invincible:
|
|
||||||
SpazFactory.get().block_sound.play(1.0,
|
|
||||||
position=self.node.position)
|
|
||||||
return True
|
|
||||||
|
|
||||||
# If we were recently hit, don't count this as another.
|
|
||||||
# (so punch flurries and bomb pileups essentially count as 1 hit)
|
|
||||||
local_time = int(bs.time() * 1000)
|
|
||||||
assert isinstance(local_time, int)
|
|
||||||
if (self._last_hit_time is None
|
|
||||||
or local_time - self._last_hit_time > 1000):
|
|
||||||
self._num_times_hit += 1
|
|
||||||
self._last_hit_time = local_time
|
|
||||||
|
|
||||||
mag = msg.magnitude * self.impact_scale
|
|
||||||
velocity_mag = msg.velocity_magnitude * self.impact_scale
|
|
||||||
damage_scale = 0.22
|
|
||||||
|
|
||||||
# If they've got a shield, deliver it to that instead.
|
|
||||||
if self.shield:
|
|
||||||
if msg.flat_damage:
|
|
||||||
damage = msg.flat_damage * self.impact_scale
|
|
||||||
else:
|
|
||||||
# Hit our spaz with an impulse but tell it to only return
|
|
||||||
# theoretical damage; not apply the impulse.
|
|
||||||
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], mag,
|
|
||||||
velocity_mag, msg.radius, 1, msg.force_direction[0],
|
|
||||||
msg.force_direction[1], msg.force_direction[2])
|
|
||||||
damage = damage_scale * self.node.damage
|
|
||||||
|
|
||||||
assert self.shield_hitpoints is not None
|
|
||||||
self.shield_hitpoints -= int(damage)
|
|
||||||
self.shield.hurt = (
|
|
||||||
1.0 -
|
|
||||||
float(self.shield_hitpoints) / self.shield_hitpoints_max)
|
|
||||||
|
|
||||||
# Its a cleaner event if a hit just kills the shield
|
|
||||||
# without damaging the player.
|
|
||||||
# However, massive damage events should still be able to
|
|
||||||
# damage the player. This hopefully gives us a happy medium.
|
|
||||||
max_spillover = SpazFactory.get().max_shield_spillover_damage
|
|
||||||
if self.shield_hitpoints <= 0:
|
|
||||||
|
|
||||||
# FIXME: Transition out perhaps?
|
|
||||||
self.shield.delete()
|
|
||||||
self.shield = None
|
|
||||||
SpazFactory.get().shield_down_sound.play(1.0,
|
|
||||||
position=self.node.position)
|
|
||||||
|
|
||||||
# Emit some cool looking sparks when the shield dies.
|
|
||||||
npos = self.node.position
|
|
||||||
bs.emitfx(position=(npos[0], npos[1] + 0.9, npos[2]),
|
|
||||||
velocity=self.node.velocity,
|
|
||||||
count=random.randrange(20, 30),
|
|
||||||
scale=1.0,
|
|
||||||
spread=0.6,
|
|
||||||
chunk_type='spark')
|
|
||||||
|
|
||||||
else:
|
|
||||||
SpazFactory.get().shield_hit_sound.play(0.5,
|
|
||||||
position=self.node.position)
|
|
||||||
|
|
||||||
# Emit some cool looking sparks on shield hit.
|
|
||||||
assert msg.force_direction is not None
|
|
||||||
bs.emitfx(position=msg.pos,
|
|
||||||
velocity=(msg.force_direction[0] * 1.0,
|
|
||||||
msg.force_direction[1] * 1.0,
|
|
||||||
msg.force_direction[2] * 1.0),
|
|
||||||
count=min(30, 5 + int(damage * 0.005)),
|
|
||||||
scale=0.5,
|
|
||||||
spread=0.3,
|
|
||||||
chunk_type='spark')
|
|
||||||
|
|
||||||
# If they passed our spillover threshold,
|
|
||||||
# pass damage along to spaz.
|
|
||||||
if self.shield_hitpoints <= -max_spillover:
|
|
||||||
leftover_damage = -max_spillover - self.shield_hitpoints
|
|
||||||
shield_leftover_ratio = leftover_damage / damage
|
|
||||||
|
|
||||||
# Scale down the magnitudes applied to spaz accordingly.
|
|
||||||
mag *= shield_leftover_ratio
|
|
||||||
velocity_mag *= shield_leftover_ratio
|
|
||||||
else:
|
|
||||||
return True # Good job shield!
|
|
||||||
else:
|
|
||||||
shield_leftover_ratio = 1.0
|
|
||||||
|
|
||||||
if msg.flat_damage:
|
|
||||||
damage = int(msg.flat_damage * self.impact_scale *
|
|
||||||
shield_leftover_ratio)
|
|
||||||
else:
|
|
||||||
# Hit it with an impulse and get the resulting damage.
|
|
||||||
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], mag,
|
|
||||||
velocity_mag, msg.radius, 0, msg.force_direction[0],
|
|
||||||
msg.force_direction[1], msg.force_direction[2])
|
|
||||||
|
|
||||||
damage = int(damage_scale * self.node.damage)
|
|
||||||
self.node.handlemessage('hurt_sound')
|
|
||||||
|
|
||||||
# Play punch impact sound based on damage if it was a punch.
|
|
||||||
if msg.hit_type == 'punch':
|
|
||||||
self.on_punched(damage)
|
|
||||||
|
|
||||||
# If damage was significant, lets show it.
|
|
||||||
# if damage > 350:
|
|
||||||
# assert msg.force_direction is not None
|
|
||||||
# babase.show_damage_count('-' + str(int(damage / 10)) + '%',
|
|
||||||
# msg.pos, msg.force_direction)
|
|
||||||
|
|
||||||
# Let's always add in a super-punch sound with boxing
|
|
||||||
# gloves just to differentiate them.
|
|
||||||
if msg.hit_subtype == 'super_punch':
|
|
||||||
SpazFactory.get().punch_sound_stronger.play(1.0,
|
|
||||||
position=self.node.position)
|
|
||||||
if damage > 500:
|
|
||||||
sounds = SpazFactory.get().punch_sound_strong
|
|
||||||
sound = sounds[random.randrange(len(sounds))]
|
|
||||||
else:
|
|
||||||
sound = SpazFactory.get().punch_sound
|
|
||||||
sound.play(1.0, position=self.node.position)
|
|
||||||
|
|
||||||
# Throw up some chunks.
|
|
||||||
assert msg.force_direction is not None
|
|
||||||
bs.emitfx(position=msg.pos,
|
|
||||||
velocity=(msg.force_direction[0] * 0.5,
|
|
||||||
msg.force_direction[1] * 0.5,
|
|
||||||
msg.force_direction[2] * 0.5),
|
|
||||||
count=min(10, 1 + int(damage * 0.0025)),
|
|
||||||
scale=0.3,
|
|
||||||
spread=0.03)
|
|
||||||
|
|
||||||
bs.emitfx(position=msg.pos,
|
|
||||||
chunk_type='sweat',
|
|
||||||
velocity=(msg.force_direction[0] * 1.3,
|
|
||||||
msg.force_direction[1] * 1.3 + 5.0,
|
|
||||||
msg.force_direction[2] * 1.3),
|
|
||||||
count=min(30, 1 + int(damage * 0.04)),
|
|
||||||
scale=0.9,
|
|
||||||
spread=0.28)
|
|
||||||
|
|
||||||
# Momentary flash.
|
|
||||||
hurtiness = damage * 0.003
|
|
||||||
punchpos = (msg.pos[0] + msg.force_direction[0] * 0.02,
|
|
||||||
msg.pos[1] + msg.force_direction[1] * 0.02,
|
|
||||||
msg.pos[2] + msg.force_direction[2] * 0.02)
|
|
||||||
flash_color = (1.0, 0.8, 0.4)
|
|
||||||
light = bs.newnode(
|
|
||||||
'light',
|
|
||||||
attrs={
|
|
||||||
'position': punchpos,
|
|
||||||
'radius': 0.12 + hurtiness * 0.12,
|
|
||||||
'intensity': 0.3 * (1.0 + 1.0 * hurtiness),
|
|
||||||
'height_attenuated': False,
|
|
||||||
'color': flash_color
|
|
||||||
})
|
|
||||||
bs.timer(0.06, light.delete)
|
|
||||||
|
|
||||||
flash = bs.newnode('flash',
|
|
||||||
attrs={
|
|
||||||
'position': punchpos,
|
|
||||||
'size': 0.17 + 0.17 * hurtiness,
|
|
||||||
'color': flash_color
|
|
||||||
})
|
|
||||||
bs.timer(0.06, flash.delete)
|
|
||||||
|
|
||||||
if msg.hit_type == 'impact':
|
|
||||||
assert msg.force_direction is not None
|
|
||||||
bs.emitfx(position=msg.pos,
|
|
||||||
velocity=(msg.force_direction[0] * 2.0,
|
|
||||||
msg.force_direction[1] * 2.0,
|
|
||||||
msg.force_direction[2] * 2.0),
|
|
||||||
count=min(10, 1 + int(damage * 0.01)),
|
|
||||||
scale=0.4,
|
|
||||||
spread=0.1)
|
|
||||||
if self.hitpoints > 0:
|
|
||||||
|
|
||||||
# It's kinda crappy to die from impacts, so lets reduce
|
|
||||||
# impact damage by a reasonable amount *if* it'll keep us alive
|
|
||||||
if msg.hit_type == 'impact' and damage > self.hitpoints:
|
|
||||||
# Drop damage to whatever puts us at 10 hit points,
|
|
||||||
# or 200 less than it used to be whichever is greater
|
|
||||||
# (so it *can* still kill us if its high enough)
|
|
||||||
newdamage = max(damage - 200, self.hitpoints - 10)
|
|
||||||
damage = newdamage
|
|
||||||
self.node.handlemessage('flash')
|
|
||||||
|
|
||||||
# If we're holding something, drop it.
|
|
||||||
if damage > 0.0 and self.node.hold_node:
|
|
||||||
self.node.hold_node = None
|
|
||||||
# self.hitpoints -= damage
|
|
||||||
self.multiplyer += min(damage / 2000, 0.15)
|
|
||||||
if damage / 2000 > 0.05:
|
|
||||||
self.set_score_text(
|
|
||||||
str(int((self.multiplyer - 1) * 100)) + '%')
|
|
||||||
# self.node.hurt = 1.0 - float(
|
|
||||||
# self.hitpoints) / self.hitpoints_max
|
|
||||||
self.node.hurt = 0.0
|
|
||||||
|
|
||||||
# If we're cursed, *any* damage blows us up.
|
|
||||||
if self._cursed and damage > 0:
|
|
||||||
bs.timer(
|
|
||||||
0.05,
|
|
||||||
bs.WeakCall(self.curse_explode,
|
|
||||||
msg.get_source_player(bs.Player)))
|
|
||||||
|
|
||||||
# If we're frozen, shatter.. otherwise die if we hit zero
|
|
||||||
# if self.frozen and (damage > 200 or self.hitpoints <= 0):
|
|
||||||
# self.shatter()
|
|
||||||
# elif self.hitpoints <= 0:
|
|
||||||
# self.node.handlemessage(
|
|
||||||
# bs.DieMessage(how=babase.DeathType.IMPACT))
|
|
||||||
|
|
||||||
# If we're dead, take a look at the smoothed damage value
|
|
||||||
# (which gives us a smoothed average of recent damage) and shatter
|
|
||||||
# us if its grown high enough.
|
|
||||||
# if self.hitpoints <= 0:
|
|
||||||
# damage_avg = self.node.damage_smoothed * damage_scale
|
|
||||||
# if damage_avg > 1000:
|
|
||||||
# self.shatter()
|
|
||||||
|
|
||||||
source_player = msg.get_source_player(type(self._player))
|
|
||||||
if source_player:
|
|
||||||
self.last_player_attacked_by = source_player
|
|
||||||
self.last_attacked_time = bs.time()
|
|
||||||
self.last_attacked_type = (msg.hit_type, msg.hit_subtype)
|
|
||||||
Spaz.handlemessage(self,
|
|
||||||
bs.HitMessage) # Augment standard behavior.
|
|
||||||
activity = self._activity()
|
|
||||||
if activity is not None and self._player.exists():
|
|
||||||
activity.handlemessage(PlayerSpazHurtMessage(self))
|
|
||||||
|
|
||||||
elif isinstance(msg, bs.DieMessage):
|
|
||||||
self.oob_effect()
|
|
||||||
super().handlemessage(msg)
|
|
||||||
elif isinstance(msg, bs.PowerupMessage):
|
|
||||||
if msg.poweruptype == 'health':
|
|
||||||
if self.multiplyer > 2:
|
|
||||||
self.multiplyer *= 0.5
|
|
||||||
else:
|
|
||||||
self.multiplyer *= 0.75
|
|
||||||
self.multiplyer = max(1, self.multiplyer)
|
|
||||||
self.set_score_text(str(int((self.multiplyer - 1) * 100)) + "%")
|
|
||||||
super().handlemessage(msg)
|
|
||||||
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."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.score = 0
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class SuperSmash(bs.TeamGameActivity[Player, Team]):
|
|
||||||
name = 'Super Smash'
|
|
||||||
description = 'Knock everyone off the map.'
|
|
||||||
|
|
||||||
# Print messages when players die since it matters here.
|
|
||||||
announce_player_deaths = True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_available_settings(
|
|
||||||
cls, sessiontype: Type[bs.Session]) -> List[babase.Setting]:
|
|
||||||
settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Kills to Win Per Player',
|
|
||||||
min_value=1,
|
|
||||||
default=5,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Boxing Gloves', default=False),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False),
|
|
||||||
]
|
|
||||||
if issubclass(sessiontype, bs.FreeForAllSession):
|
|
||||||
settings.append(
|
|
||||||
bs.BoolSetting('Allow Negative Scores', default=False))
|
|
||||||
return settings
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
|
|
||||||
return (issubclass(sessiontype, bs.DualTeamSession)
|
|
||||||
or issubclass(sessiontype, bs.FreeForAllSession))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
|
|
||||||
maps = bs.app.classic.getmaps('melee')
|
|
||||||
for m in ['Lake Frigid', 'Hockey Stadium', 'Football Stadium']:
|
|
||||||
# remove maps without bounds
|
|
||||||
maps.remove(m)
|
|
||||||
return maps
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
self._scoreboard = Scoreboard()
|
|
||||||
self._score_to_win: int | None = None
|
|
||||||
self._dingsound = bs.getsound('dingSmall')
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
self._kills_to_win_per_player = int(
|
|
||||||
settings['Kills to Win Per Player'])
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
self._allow_negative_scores = bool(
|
|
||||||
settings.get('Allow Negative Scores', False))
|
|
||||||
self._boxing_gloves = bool(settings['Boxing Gloves'])
|
|
||||||
|
|
||||||
# Base class overrides.
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (bs.MusicType.EPIC if self._epic_mode else
|
|
||||||
bs.MusicType.SURVIVAL)
|
|
||||||
|
|
||||||
def get_instance_description(self) -> str | Sequence:
|
|
||||||
return 'Knock everyone off the map.'
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> str | Sequence:
|
|
||||||
return 'Knock off the map.'
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
self._start_time = bs.time()
|
|
||||||
self.setup_standard_time_limit(self._time_limit)
|
|
||||||
self.setup_standard_powerup_drops(enable_tnt=False)
|
|
||||||
self._pow = None
|
|
||||||
self._tnt_drop_timer = bs.timer(1.0 * 0.30,
|
|
||||||
bs.WeakCall(self._drop_pow_box),
|
|
||||||
repeat=True)
|
|
||||||
|
|
||||||
# Base kills needed to win on the size of the largest team.
|
|
||||||
self._score_to_win = (self._kills_to_win_per_player *
|
|
||||||
max(1, max(len(t.players) for t in self.teams)))
|
|
||||||
self._update_scoreboard()
|
|
||||||
|
|
||||||
def _drop_pow_box(self) -> None:
|
|
||||||
if self._pow is not None and self._pow:
|
|
||||||
return
|
|
||||||
if len(self.map.tnt_points) == 0:
|
|
||||||
return
|
|
||||||
pos = random.choice(self.map.tnt_points)
|
|
||||||
pos = (pos[0], pos[1] + 1, pos[2])
|
|
||||||
self._pow = PowBox(position=pos, velocity=(0.0, 1.0, 0.0))
|
|
||||||
|
|
||||||
def spawn_player(self, player: Player) -> bs.Actor:
|
|
||||||
if isinstance(self.session, bs.DualTeamSession):
|
|
||||||
position = self.map.get_start_position(player.team.id)
|
|
||||||
else:
|
|
||||||
# otherwise do free-for-all spawn locations
|
|
||||||
position = self.map.get_ffa_start_position(self.players)
|
|
||||||
angle = None
|
|
||||||
|
|
||||||
name = player.getname()
|
|
||||||
light_color = _math.normalized_color(player.color)
|
|
||||||
display_color = babase.safecolor(player.color, target_intensity=0.75)
|
|
||||||
|
|
||||||
spaz = SSPlayerSpaz(color=player.color,
|
|
||||||
highlight=player.highlight,
|
|
||||||
character=player.character,
|
|
||||||
player=player)
|
|
||||||
|
|
||||||
player.actor = spaz
|
|
||||||
assert spaz.node
|
|
||||||
|
|
||||||
# If this is co-op and we're on Courtyard or Runaround, add the
|
|
||||||
# material that allows us to collide with the player-walls.
|
|
||||||
# FIXME: Need to generalize this.
|
|
||||||
if isinstance(self.session, bs.CoopSession) and self.map.getname() in [
|
|
||||||
'Courtyard', 'Tower D'
|
|
||||||
]:
|
|
||||||
mat = self.map.preloaddata['collide_with_wall_material']
|
|
||||||
assert isinstance(spaz.node.materials, tuple)
|
|
||||||
assert isinstance(spaz.node.roller_materials, tuple)
|
|
||||||
spaz.node.materials += (mat,)
|
|
||||||
spaz.node.roller_materials += (mat,)
|
|
||||||
|
|
||||||
spaz.node.name = name
|
|
||||||
spaz.node.name_color = display_color
|
|
||||||
spaz.connect_controls_to_player()
|
|
||||||
|
|
||||||
# Move to the stand position and add a flash of light.
|
|
||||||
spaz.handlemessage(
|
|
||||||
bs.StandMessage(
|
|
||||||
position,
|
|
||||||
angle if angle is not None else random.uniform(0, 360)))
|
|
||||||
self._spawn_sound.play(1, position=spaz.node.position)
|
|
||||||
light = bs.newnode('light', attrs={'color': light_color})
|
|
||||||
spaz.node.connectattr('position', light, 'position')
|
|
||||||
bs.animate(light, 'intensity', {0: 0, 0.25: 1, 0.5: 0})
|
|
||||||
bs.timer(0.5, light.delete)
|
|
||||||
|
|
||||||
if self._boxing_gloves:
|
|
||||||
spaz.equip_boxing_gloves()
|
|
||||||
|
|
||||||
return spaz
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
|
|
||||||
if isinstance(msg, bs.PlayerDiedMessage):
|
|
||||||
# Augment standard behavior.
|
|
||||||
super().handlemessage(msg)
|
|
||||||
|
|
||||||
player = msg.getplayer(Player)
|
|
||||||
self.respawn_player(player)
|
|
||||||
|
|
||||||
killer = msg.getkillerplayer(Player)
|
|
||||||
if killer is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Handle team-kills.
|
|
||||||
if killer.team is player.team:
|
|
||||||
|
|
||||||
# In free-for-all, killing yourself loses you a point.
|
|
||||||
if isinstance(self.session, bs.FreeForAllSession):
|
|
||||||
new_score = player.team.score - 1
|
|
||||||
if not self._allow_negative_scores:
|
|
||||||
new_score = max(0, new_score)
|
|
||||||
player.team.score = new_score
|
|
||||||
|
|
||||||
# In teams-mode it gives a point to the other team.
|
|
||||||
else:
|
|
||||||
self._dingsound.play()
|
|
||||||
for team in self.teams:
|
|
||||||
if team is not killer.team:
|
|
||||||
team.score += 1
|
|
||||||
|
|
||||||
# Killing someone on another team nets a kill.
|
|
||||||
else:
|
|
||||||
killer.team.score += 1
|
|
||||||
self._dingsound.play()
|
|
||||||
|
|
||||||
# In FFA show scores since its hard to find on the scoreboard.
|
|
||||||
if isinstance(killer.actor, SSPlayerSpaz) and killer.actor:
|
|
||||||
killer.actor.set_score_text(str(killer.team.score) + '/' +
|
|
||||||
str(self._score_to_win),
|
|
||||||
color=killer.team.color,
|
|
||||||
flash=True)
|
|
||||||
|
|
||||||
self._update_scoreboard()
|
|
||||||
|
|
||||||
# If someone has won, set a timer to end shortly.
|
|
||||||
# (allows the dust to clear and draws to occur if deaths are
|
|
||||||
# close enough)
|
|
||||||
assert self._score_to_win is not None
|
|
||||||
if any(team.score >= self._score_to_win for team in self.teams):
|
|
||||||
bs.timer(0.5, self.end_game)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _update_scoreboard(self) -> None:
|
|
||||||
for team in self.teams:
|
|
||||||
self._scoreboard.set_team_value(team, team.score,
|
|
||||||
self._score_to_win)
|
|
||||||
|
|
||||||
def end_game(self) -> None:
|
|
||||||
results = bs.GameResults()
|
|
||||||
for team in self.teams:
|
|
||||||
results.set_team_score(team, team.score)
|
|
||||||
self.end(results=results)
|
|
||||||
|
|
||||||
|
|
||||||
class Player2(bs.Player['Team']):
|
|
||||||
"""Our player type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.lives = 0
|
|
||||||
self.icons: List[Icon] = []
|
|
||||||
|
|
||||||
|
|
||||||
class Team2(bs.Team[Player]):
|
|
||||||
"""Our team type for this game."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.survival_seconds: Optional[int] = None
|
|
||||||
self.spawn_order: List[Player] = []
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class SuperSmashElimination(bs.TeamGameActivity[Player2, Team2]):
|
|
||||||
name = 'Super Smash Elimination'
|
|
||||||
description = 'Knock everyone off the map.'
|
|
||||||
scoreconfig = bs.ScoreConfig(label='Survived',
|
|
||||||
scoretype=bs.ScoreType.SECONDS,
|
|
||||||
none_is_winner=True)
|
|
||||||
|
|
||||||
# Print messages when players die since it matters here.
|
|
||||||
announce_player_deaths = True
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_available_settings(
|
|
||||||
cls, sessiontype: Type[bs.Session]) -> List[babase.Setting]:
|
|
||||||
settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Lives (0 = Unlimited)',
|
|
||||||
min_value=0,
|
|
||||||
default=3,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Boxing Gloves', default=False),
|
|
||||||
bs.BoolSetting('Epic Mode', default=False),
|
|
||||||
]
|
|
||||||
return settings
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def supports_session_type(cls, sessiontype: Type[bs.Session]) -> bool:
|
|
||||||
return (issubclass(sessiontype, bs.DualTeamSession)
|
|
||||||
or issubclass(sessiontype, bs.FreeForAllSession))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype: Type[bs.Session]) -> List[str]:
|
|
||||||
maps = bs.app.classic.getmaps('melee')
|
|
||||||
for m in ['Lake Frigid', 'Hockey Stadium', 'Football Stadium']:
|
|
||||||
# remove maps without bounds
|
|
||||||
maps.remove(m)
|
|
||||||
return maps
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
self.lives = int(settings['Lives (0 = Unlimited)'])
|
|
||||||
self.time_limit_only = (self.lives == 0)
|
|
||||||
if self.time_limit_only:
|
|
||||||
settings['Time Limit'] = max(60, settings['Time Limit'])
|
|
||||||
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
|
|
||||||
self._start_time: Optional[float] = 1.0
|
|
||||||
|
|
||||||
self._boxing_gloves = bool(settings['Boxing Gloves'])
|
|
||||||
self._solo_mode = bool(settings.get('Solo Mode', False))
|
|
||||||
|
|
||||||
# Base class overrides.
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (bs.MusicType.EPIC if self._epic_mode else
|
|
||||||
bs.MusicType.SURVIVAL)
|
|
||||||
|
|
||||||
def get_instance_description(self) -> str | Sequence:
|
|
||||||
return 'Knock everyone off the map.'
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> str | Sequence:
|
|
||||||
return 'Knock off the map.'
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
self._start_time = bs.time()
|
|
||||||
self.setup_standard_time_limit(self._time_limit)
|
|
||||||
self.setup_standard_powerup_drops(enable_tnt=False)
|
|
||||||
self._pow = None
|
|
||||||
self._tnt_drop_timer = bs.timer(1.0 * 0.30,
|
|
||||||
bs.WeakCall(self._drop_pow_box),
|
|
||||||
repeat=True)
|
|
||||||
self._update_icons()
|
|
||||||
bs.timer(1.0, self.check_end_game, repeat=True)
|
|
||||||
|
|
||||||
def _drop_pow_box(self) -> None:
|
|
||||||
if self._pow is not None and self._pow:
|
|
||||||
return
|
|
||||||
if len(self.map.tnt_points) == 0:
|
|
||||||
return
|
|
||||||
pos = random.choice(self.map.tnt_points)
|
|
||||||
pos = (pos[0], pos[1] + 1, pos[2])
|
|
||||||
self._pow = PowBox(position=pos, velocity=(0.0, 1.0, 0.0))
|
|
||||||
|
|
||||||
def on_player_join(self, player: Player) -> None:
|
|
||||||
|
|
||||||
if self.has_begun():
|
|
||||||
if (all(teammate.lives == 0 for teammate in player.team.players)
|
|
||||||
and player.team.survival_seconds is None):
|
|
||||||
player.team.survival_seconds = 0
|
|
||||||
bs.broadcastmessage(
|
|
||||||
babase.Lstr(resource='playerDelayedJoinText',
|
|
||||||
subs=[('${PLAYER}', player.getname(full=True))]),
|
|
||||||
color=(0, 1, 0),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
player.lives = self.lives
|
|
||||||
# create our icon and spawn
|
|
||||||
player.icons = [Icon(player,
|
|
||||||
position=(0.0, 50),
|
|
||||||
scale=0.8)]
|
|
||||||
if player.lives > 0 or self.time_limit_only:
|
|
||||||
self.spawn_player(player)
|
|
||||||
|
|
||||||
# dont waste time doing this until begin
|
|
||||||
if self.has_begun():
|
|
||||||
self._update_icons()
|
|
||||||
|
|
||||||
def on_player_leave(self, player: Player) -> None:
|
|
||||||
super().on_player_leave(player)
|
|
||||||
player.icons = None
|
|
||||||
|
|
||||||
# update icons in a moment since our team
|
|
||||||
# will be gone from the list then
|
|
||||||
bs.timer(0.0, self._update_icons)
|
|
||||||
bs.timer(0.1, self.check_end_game, repeat=True)
|
|
||||||
|
|
||||||
def _update_icons(self) -> None:
|
|
||||||
# pylint: disable=too-many-branches
|
|
||||||
|
|
||||||
# In free-for-all mode, everyone is just lined up along the bottom.
|
|
||||||
if isinstance(self.session, bs.FreeForAllSession):
|
|
||||||
count = len(self.teams)
|
|
||||||
x_offs = 85
|
|
||||||
xval = x_offs * (count - 1) * -0.5
|
|
||||||
for team in self.teams:
|
|
||||||
if len(team.players) > 1:
|
|
||||||
print('WTF have', len(team.players), 'players in ffa team')
|
|
||||||
elif len(team.players) == 1:
|
|
||||||
player = team.players[0]
|
|
||||||
if len(player.icons) != 1:
|
|
||||||
print(
|
|
||||||
'WTF have',
|
|
||||||
len(player.icons),
|
|
||||||
'icons in non-solo elim')
|
|
||||||
for icon in player.icons:
|
|
||||||
icon.set_position_and_scale((xval, 30), 0.7)
|
|
||||||
icon.update_for_lives()
|
|
||||||
xval += x_offs
|
|
||||||
|
|
||||||
# In teams mode we split up teams.
|
|
||||||
else:
|
|
||||||
if self._solo_mode:
|
|
||||||
# First off, clear out all icons.
|
|
||||||
for player in self.players:
|
|
||||||
player.icons = []
|
|
||||||
|
|
||||||
# Now for each team, cycle through our available players
|
|
||||||
# adding icons.
|
|
||||||
for team in self.teams:
|
|
||||||
if team.id == 0:
|
|
||||||
xval = -60
|
|
||||||
x_offs = -78
|
|
||||||
else:
|
|
||||||
xval = 60
|
|
||||||
x_offs = 78
|
|
||||||
is_first = True
|
|
||||||
test_lives = 1
|
|
||||||
while True:
|
|
||||||
players_with_lives = [
|
|
||||||
p for p in team.spawn_order
|
|
||||||
if p and p.lives >= test_lives
|
|
||||||
]
|
|
||||||
if not players_with_lives:
|
|
||||||
break
|
|
||||||
for player in players_with_lives:
|
|
||||||
player.icons.append(
|
|
||||||
Icon(player,
|
|
||||||
position=(xval, (40 if is_first else 25)),
|
|
||||||
scale=1.0 if is_first else 0.5,
|
|
||||||
name_maxwidth=130 if is_first else 75,
|
|
||||||
name_scale=0.8 if is_first else 1.0,
|
|
||||||
flatness=0.0 if is_first else 1.0,
|
|
||||||
shadow=0.5 if is_first else 1.0,
|
|
||||||
show_death=is_first,
|
|
||||||
show_lives=False))
|
|
||||||
xval += x_offs * (0.8 if is_first else 0.56)
|
|
||||||
is_first = False
|
|
||||||
test_lives += 1
|
|
||||||
# Non-solo mode.
|
|
||||||
else:
|
|
||||||
for team in self.teams:
|
|
||||||
if team.id == 0:
|
|
||||||
xval = -50
|
|
||||||
x_offs = -85
|
|
||||||
else:
|
|
||||||
xval = 50
|
|
||||||
x_offs = 85
|
|
||||||
for player in team.players:
|
|
||||||
if len(player.icons) != 1:
|
|
||||||
print(
|
|
||||||
'WTF have',
|
|
||||||
len(player.icons),
|
|
||||||
'icons in non-solo elim')
|
|
||||||
for icon in player.icons:
|
|
||||||
icon.set_position_and_scale((xval, 30), 0.7)
|
|
||||||
icon.update_for_lives()
|
|
||||||
xval += x_offs
|
|
||||||
|
|
||||||
# overriding the default character spawning..
|
|
||||||
def spawn_player(self, player: Player) -> bs.Actor:
|
|
||||||
if isinstance(self.session, bs.DualTeamSession):
|
|
||||||
position = self.map.get_start_position(player.team.id)
|
|
||||||
else:
|
|
||||||
# otherwise do free-for-all spawn locations
|
|
||||||
position = self.map.get_ffa_start_position(self.players)
|
|
||||||
angle = None
|
|
||||||
|
|
||||||
name = player.getname()
|
|
||||||
light_color = _math.normalized_color(player.color)
|
|
||||||
display_color = babase.safecolor(player.color, target_intensity=0.75)
|
|
||||||
|
|
||||||
spaz = SSPlayerSpaz(color=player.color,
|
|
||||||
highlight=player.highlight,
|
|
||||||
character=player.character,
|
|
||||||
player=player)
|
|
||||||
|
|
||||||
player.actor = spaz
|
|
||||||
assert spaz.node
|
|
||||||
|
|
||||||
# If this is co-op and we're on Courtyard or Runaround, add the
|
|
||||||
# material that allows us to collide with the player-walls.
|
|
||||||
# FIXME: Need to generalize this.
|
|
||||||
if isinstance(self.session, bs.CoopSession) and self.map.getname() in [
|
|
||||||
'Courtyard', 'Tower D'
|
|
||||||
]:
|
|
||||||
mat = self.map.preloaddata['collide_with_wall_material']
|
|
||||||
assert isinstance(spaz.node.materials, tuple)
|
|
||||||
assert isinstance(spaz.node.roller_materials, tuple)
|
|
||||||
spaz.node.materials += (mat,)
|
|
||||||
spaz.node.roller_materials += (mat,)
|
|
||||||
|
|
||||||
spaz.node.name = name
|
|
||||||
spaz.node.name_color = display_color
|
|
||||||
spaz.connect_controls_to_player()
|
|
||||||
|
|
||||||
# Move to the stand position and add a flash of light.
|
|
||||||
spaz.handlemessage(
|
|
||||||
bs.StandMessage(
|
|
||||||
position,
|
|
||||||
angle if angle is not None else random.uniform(0, 360)))
|
|
||||||
self._spawn_sound.play(1, position=spaz.node.position)
|
|
||||||
light = bs.newnode('light', attrs={'color': light_color})
|
|
||||||
spaz.node.connectattr('position', light, 'position')
|
|
||||||
bs.animate(light, 'intensity', {0: 0, 0.25: 1, 0.5: 0})
|
|
||||||
bs.timer(0.5, light.delete)
|
|
||||||
|
|
||||||
# If we have any icons, update their state.
|
|
||||||
for icon in player.icons:
|
|
||||||
icon.handle_player_spawned()
|
|
||||||
|
|
||||||
if self._boxing_gloves:
|
|
||||||
spaz.equip_boxing_gloves()
|
|
||||||
|
|
||||||
return spaz
|
|
||||||
|
|
||||||
def _get_total_team_lives(self, team: Team) -> int:
|
|
||||||
return sum(player.lives for player in team.players)
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
|
|
||||||
if isinstance(msg, bs.PlayerDiedMessage):
|
|
||||||
# Augment standard behavior.
|
|
||||||
super().handlemessage(msg)
|
|
||||||
player: Player = msg.getplayer(Player)
|
|
||||||
|
|
||||||
player.lives -= 1
|
|
||||||
if player.lives < 0:
|
|
||||||
player.lives = 0
|
|
||||||
|
|
||||||
# if we have any icons, update their state
|
|
||||||
for icon in player.icons:
|
|
||||||
icon.handle_player_died()
|
|
||||||
|
|
||||||
# play big death sound on our last death
|
|
||||||
# or for every one in solo mode
|
|
||||||
if player.lives == 0:
|
|
||||||
SpazFactory.get().single_player_death_sound.play()
|
|
||||||
|
|
||||||
# if we hit zero lives we're dead and the game might be over
|
|
||||||
if player.lives == 0 and not self.time_limit_only:
|
|
||||||
# If the whole team is now dead, mark their survival time.
|
|
||||||
if self._get_total_team_lives(player.team) == 0:
|
|
||||||
assert self._start_time is not None
|
|
||||||
player.team.survival_seconds = int(bs.time() -
|
|
||||||
self._start_time)
|
|
||||||
# we still have lives; yay!
|
|
||||||
else:
|
|
||||||
self.respawn_player(player)
|
|
||||||
|
|
||||||
bs.timer(0.1, self.check_end_game, repeat=True)
|
|
||||||
|
|
||||||
else:
|
|
||||||
return super().handlemessage(msg)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def check_end_game(self) -> None:
|
|
||||||
if len(self._get_living_teams()) < 2:
|
|
||||||
bs.timer(0.5, self.end_game)
|
|
||||||
|
|
||||||
def _get_living_teams(self) -> List[Team]:
|
|
||||||
return [
|
|
||||||
team for team in self.teams
|
|
||||||
if len(team.players) > 0 and any(player.lives > 0
|
|
||||||
for player in team.players)
|
|
||||||
]
|
|
||||||
|
|
||||||
def end_game(self) -> None:
|
|
||||||
if self.has_ended():
|
|
||||||
return
|
|
||||||
results = bs.GameResults()
|
|
||||||
self._vs_text = None # Kill our 'vs' if its there.
|
|
||||||
for team in self.teams:
|
|
||||||
results.set_team_score(team, team.survival_seconds)
|
|
||||||
self.end(results=results)
|
|
||||||
902
dist/ba_root/mods/games/volleyball.py
vendored
902
dist/ba_root/mods/games/volleyball.py
vendored
|
|
@ -1,902 +0,0 @@
|
||||||
# Volley Ball (final)
|
|
||||||
|
|
||||||
# Made by your friend: Freaku
|
|
||||||
|
|
||||||
|
|
||||||
# Join BCS:
|
|
||||||
# https://discord.gg/ucyaesh
|
|
||||||
|
|
||||||
|
|
||||||
# My GitHub:
|
|
||||||
# https://github.com/Freaku17/BombSquad-Mods-byFreaku
|
|
||||||
|
|
||||||
|
|
||||||
# CHANGELOG:
|
|
||||||
"""
|
|
||||||
## 2021
|
|
||||||
- Fixed Puck's mass/size/positions/texture/effects
|
|
||||||
- Fixed Goal positions
|
|
||||||
- Better center wall
|
|
||||||
- Added 1 more map
|
|
||||||
- Added more customisable options
|
|
||||||
- Map lights locators are now looped (thus reducing the size of the file and lengthy work...)
|
|
||||||
- Merged map & minigame in one file
|
|
||||||
- Puck spawns according to scored team
|
|
||||||
- Also puck now spawns in airrr
|
|
||||||
- Server support added :)
|
|
||||||
- Fixed **LOTS** of errors/bugs
|
|
||||||
|
|
||||||
## 2022
|
|
||||||
- Code cleanup
|
|
||||||
- More accurate Goal positions
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor.bomb import BombFactory
|
|
||||||
from bascenev1lib.actor.playerspaz import PlayerSpaz
|
|
||||||
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
|
|
||||||
from bascenev1lib.actor.scoreboard import Scoreboard
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
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):
|
|
||||||
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.05, position[2])
|
|
||||||
self.last_players_to_touch: Dict[int, Player] = {}
|
|
||||||
self.scored = False
|
|
||||||
assert activity is not None
|
|
||||||
assert isinstance(activity, VolleyBallGame)
|
|
||||||
pmats = [shared.object_material, activity.puck_material]
|
|
||||||
self.node = bs.newnode('prop',
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
'mesh': activity.puck_mesh,
|
|
||||||
'color_texture': activity.puck_tex,
|
|
||||||
'body': 'sphere',
|
|
||||||
'reflection': 'soft',
|
|
||||||
'reflection_scale': [0.2],
|
|
||||||
'shadow_size': 0.6,
|
|
||||||
'mesh_scale': 0.4,
|
|
||||||
'body_scale': 1.07,
|
|
||||||
'is_area_of_interest': True,
|
|
||||||
'position': self._spawn_pos,
|
|
||||||
'materials': pmats
|
|
||||||
})
|
|
||||||
|
|
||||||
# Since it rolls on spawn, lets make gravity
|
|
||||||
# to 0, and when another node (bomb/spaz)
|
|
||||||
# touches it. It'll act back as our normie puck!
|
|
||||||
bs.animate(self.node, 'gravity_scale', {0: -0.1, 0.2: 1}, False)
|
|
||||||
# When other node touches, it realises its new gravity_scale
|
|
||||||
|
|
||||||
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.team.id] = 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."""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.score = 0
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class VolleyBallGame(bs.TeamGameActivity[Player, Team]):
|
|
||||||
name = 'Volley Ball'
|
|
||||||
description = 'Score some goals.\nby \ue048Freaku'
|
|
||||||
available_settings = [
|
|
||||||
bs.IntSetting(
|
|
||||||
'Score to Win',
|
|
||||||
min_value=1,
|
|
||||||
default=1,
|
|
||||||
increment=1,
|
|
||||||
),
|
|
||||||
bs.IntChoiceSetting(
|
|
||||||
'Time Limit',
|
|
||||||
choices=[
|
|
||||||
('None', 0),
|
|
||||||
('1 Minute', 60),
|
|
||||||
('2 Minutes', 120),
|
|
||||||
('5 Minutes', 300),
|
|
||||||
('10 Minutes', 600),
|
|
||||||
('20 Minutes', 1200),
|
|
||||||
],
|
|
||||||
default=0,
|
|
||||||
),
|
|
||||||
bs.FloatChoiceSetting(
|
|
||||||
'Respawn Times',
|
|
||||||
choices=[
|
|
||||||
('Shorter', 0.25),
|
|
||||||
('Short', 0.5),
|
|
||||||
('Normal', 1.0),
|
|
||||||
('Long', 2.0),
|
|
||||||
('Longer', 4.0),
|
|
||||||
],
|
|
||||||
default=1.0,
|
|
||||||
),
|
|
||||||
bs.BoolSetting('Epic Mode', True),
|
|
||||||
bs.BoolSetting('Night Mode', False),
|
|
||||||
bs.BoolSetting('Icy Floor', True),
|
|
||||||
bs.BoolSetting('Disable Punch', False),
|
|
||||||
bs.BoolSetting('Disable Bombs', False),
|
|
||||||
bs.BoolSetting('Enable Bottom Credits', True),
|
|
||||||
]
|
|
||||||
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 ['Open Field', 'Closed Arena']
|
|
||||||
|
|
||||||
def __init__(self, settings: dict):
|
|
||||||
super().__init__(settings)
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self._scoreboard = Scoreboard()
|
|
||||||
self._cheer_sound = bs.getsound('cheer')
|
|
||||||
self._chant_sound = bs.getsound('crowdChant')
|
|
||||||
self._foghorn_sound = bs.getsound('foghorn')
|
|
||||||
self._swipsound = bs.getsound('swip')
|
|
||||||
self._whistle_sound = bs.getsound('refWhistle')
|
|
||||||
self.puck_mesh = bs.getmesh('shield')
|
|
||||||
self.puck_tex = bs.gettexture('gameCircleIcon')
|
|
||||||
self._puck_sound = bs.getsound('metalHit')
|
|
||||||
self.puck_material = bs.Material()
|
|
||||||
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._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)))
|
|
||||||
|
|
||||||
self._wall_material = bs.Material()
|
|
||||||
self._fake_wall_material = bs.Material()
|
|
||||||
self._wall_material.add_actions(
|
|
||||||
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'friction', 100000),
|
|
||||||
))
|
|
||||||
self._wall_material.add_actions(
|
|
||||||
conditions=('they_have_material', shared.pickup_material),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', False),
|
|
||||||
))
|
|
||||||
|
|
||||||
self._wall_material.add_actions(
|
|
||||||
conditions=(('we_are_younger_than', 100),
|
|
||||||
'and',
|
|
||||||
('they_have_material', shared.object_material)),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', False),
|
|
||||||
))
|
|
||||||
self._wall_material.add_actions(
|
|
||||||
conditions=('they_have_material', shared.footing_material),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'friction', 9999.5),
|
|
||||||
))
|
|
||||||
self._wall_material.add_actions(
|
|
||||||
conditions=('they_have_material', BombFactory.get().blast_material),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', False),
|
|
||||||
('modify_part_collision', 'physical', False)
|
|
||||||
|
|
||||||
))
|
|
||||||
self._fake_wall_material.add_actions(
|
|
||||||
conditions=('they_have_material', shared.player_material),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', True),
|
|
||||||
('modify_part_collision', 'physical', True)
|
|
||||||
|
|
||||||
))
|
|
||||||
self.blocks = []
|
|
||||||
|
|
||||||
self._net_wall_material = bs.Material()
|
|
||||||
self._net_wall_material.add_actions(
|
|
||||||
conditions=('they_have_material', shared.player_material),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', True),
|
|
||||||
('modify_part_collision', 'physical', True)
|
|
||||||
|
|
||||||
))
|
|
||||||
|
|
||||||
self._net_wall_material.add_actions(
|
|
||||||
conditions=('they_have_material', shared.object_material),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', True),
|
|
||||||
))
|
|
||||||
self._net_wall_material.add_actions(
|
|
||||||
conditions=('they_have_material', self.puck_material),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', True),
|
|
||||||
))
|
|
||||||
self._net_wall_material.add_actions(
|
|
||||||
conditions=('we_are_older_than', 1),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', True),
|
|
||||||
))
|
|
||||||
self.net_blocc = []
|
|
||||||
|
|
||||||
self._puck_spawn_pos: Optional[Sequence[float]] = None
|
|
||||||
self._score_regions: Optional[List[bs.NodeActor]] = None
|
|
||||||
self._puck: Optional[Puck] = None
|
|
||||||
self._score_to_win = int(settings['Score to Win'])
|
|
||||||
self._punchie_ = bool(settings['Disable Punch'])
|
|
||||||
self._night_mode = bool(settings['Night Mode'])
|
|
||||||
self._bombies_ = bool(settings['Disable Bombs'])
|
|
||||||
self._time_limit = float(settings['Time Limit'])
|
|
||||||
self._icy_flooor = bool(settings['Icy Floor'])
|
|
||||||
self.credit_text = bool(settings['Enable Bottom Credits'])
|
|
||||||
self._epic_mode = bool(settings['Epic Mode'])
|
|
||||||
# Base class overrides.
|
|
||||||
self.slow_motion = self._epic_mode
|
|
||||||
self.default_music = (bs.MusicType.EPIC if self._epic_mode else
|
|
||||||
bs.MusicType.TO_THE_DEATH)
|
|
||||||
|
|
||||||
def get_instance_description(self) -> Union[str, Sequence]:
|
|
||||||
if self._score_to_win == 1:
|
|
||||||
return 'Score a goal.'
|
|
||||||
return 'Score ${ARG1} goals.', self._score_to_win
|
|
||||||
|
|
||||||
def get_instance_description_short(self) -> Union[str, Sequence]:
|
|
||||||
if self._score_to_win == 1:
|
|
||||||
return 'score a goal'
|
|
||||||
return 'score ${ARG1} goals', self._score_to_win
|
|
||||||
|
|
||||||
def on_begin(self) -> None:
|
|
||||||
super().on_begin()
|
|
||||||
|
|
||||||
self.setup_standard_time_limit(self._time_limit)
|
|
||||||
if self._night_mode:
|
|
||||||
bs.getactivity().globalsnode.tint = (0.5, 0.7, 1)
|
|
||||||
self._puck_spawn_pos = self.map.get_flag_position(None)
|
|
||||||
self._spawn_puck()
|
|
||||||
|
|
||||||
# Set up the two score regions.
|
|
||||||
self._score_regions = []
|
|
||||||
self._score_regions.append(
|
|
||||||
bs.NodeActor(
|
|
||||||
bs.newnode('region',
|
|
||||||
attrs={
|
|
||||||
'position': (5.7, 0, -0.065),
|
|
||||||
'scale': (10.7, 0.001, 8),
|
|
||||||
'type': 'box',
|
|
||||||
'materials': [self._score_region_material]
|
|
||||||
})))
|
|
||||||
self._score_regions.append(
|
|
||||||
bs.NodeActor(
|
|
||||||
bs.newnode('region',
|
|
||||||
attrs={
|
|
||||||
'position': (-5.7, 0, -0.065),
|
|
||||||
'scale': (10.7, 0.001, 8),
|
|
||||||
'type': 'box',
|
|
||||||
'materials': [self._score_region_material]
|
|
||||||
})))
|
|
||||||
self._update_scoreboard()
|
|
||||||
self._chant_sound.play()
|
|
||||||
if self.credit_text:
|
|
||||||
t = bs.newnode('text',
|
|
||||||
attrs={'text': "Created by Freaku\nVolleyBall",
|
|
||||||
# Disable 'Enable Bottom Credits' when making playlist, No need to edit this lovely...
|
|
||||||
'scale': 0.7,
|
|
||||||
'position': (0, 0),
|
|
||||||
'shadow': 0.5,
|
|
||||||
'flatness': 1.2,
|
|
||||||
'color': (1, 1, 1),
|
|
||||||
'h_align': 'center',
|
|
||||||
'v_attach': 'bottom'})
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
self.blocks.append(bs.NodeActor(
|
|
||||||
bs.newnode('region', attrs={'position': (0, 2.4, 0), 'scale': (
|
|
||||||
0.8, 6, 20), 'type': 'box', 'materials': (
|
|
||||||
self._fake_wall_material,)})))
|
|
||||||
|
|
||||||
self.net_blocc.append(bs.NodeActor(
|
|
||||||
bs.newnode('region', attrs={'position': (0, 0, 0), 'scale': (
|
|
||||||
0.6, 2.4, 20), 'type': 'box', 'materials': (
|
|
||||||
self._net_wall_material,)})))
|
|
||||||
|
|
||||||
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.team.id] = player
|
|
||||||
|
|
||||||
def _kill_puck(self) -> None:
|
|
||||||
self._puck = None
|
|
||||||
|
|
||||||
def _handle_score(self) -> None:
|
|
||||||
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
|
|
||||||
|
|
||||||
# Change puck Spawn
|
|
||||||
if team.id == 0: # left side scored
|
|
||||||
self._puck_spawn_pos = (5, 0.42, 0)
|
|
||||||
elif team.id == 1: # right side scored
|
|
||||||
self._puck_spawn_pos = (-5, 0.42, 0)
|
|
||||||
else: # normally shouldn't occur
|
|
||||||
self._puck_spawn_pos = (0, 0.42, 0)
|
|
||||||
# Easy pizzy
|
|
||||||
|
|
||||||
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],
|
|
||||||
100,
|
|
||||||
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
|
|
||||||
|
|
||||||
# Kill the puck (it'll respawn itself shortly).
|
|
||||||
bs.emitfx(position=bs.getcollision().position, count=int(
|
|
||||||
6.0 + 7.0 * 12), scale=3, spread=0.5, chunk_type='spark')
|
|
||||||
bs.timer(0.7, self._kill_puck)
|
|
||||||
|
|
||||||
bs.cameraflash(duration=7.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 on_transition_in(self) -> None:
|
|
||||||
super().on_transition_in()
|
|
||||||
activity = bs.getactivity()
|
|
||||||
if self._icy_flooor:
|
|
||||||
activity.map.is_hockey = True
|
|
||||||
|
|
||||||
def _update_scoreboard(self) -> None:
|
|
||||||
winscore = self._score_to_win
|
|
||||||
for team in self.teams:
|
|
||||||
self._scoreboard.set_team_value(team, team.score, winscore)
|
|
||||||
|
|
||||||
# overriding the default character spawning..
|
|
||||||
def spawn_player(self, player: Player) -> bs.Actor:
|
|
||||||
spaz = self.spawn_player_spaz(player)
|
|
||||||
|
|
||||||
if self._bombies_:
|
|
||||||
# We want the button to work, just no bombs...
|
|
||||||
spaz.bomb_count = 0
|
|
||||||
# Imagine not being able to swipe those colorful buttons ;(
|
|
||||||
|
|
||||||
if self._punchie_:
|
|
||||||
spaz.connect_controls_to_player(enable_punch=False)
|
|
||||||
|
|
||||||
return spaz
|
|
||||||
|
|
||||||
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(2.2, self._spawn_puck)
|
|
||||||
else:
|
|
||||||
super().handlemessage(msg)
|
|
||||||
|
|
||||||
def _flash_puck_spawn(self) -> None:
|
|
||||||
# Effect >>>>>> Flashly
|
|
||||||
bs.emitfx(position=self._puck_spawn_pos, count=int(
|
|
||||||
6.0 + 7.0 * 12), scale=1.7, spread=0.4, chunk_type='spark')
|
|
||||||
|
|
||||||
def _spawn_puck(self) -> None:
|
|
||||||
self._swipsound.play()
|
|
||||||
self._whistle_sound.play()
|
|
||||||
self._flash_puck_spawn()
|
|
||||||
assert self._puck_spawn_pos is not None
|
|
||||||
self._puck = Puck(position=self._puck_spawn_pos)
|
|
||||||
|
|
||||||
|
|
||||||
class Pointzz:
|
|
||||||
points, boxes = {}, {}
|
|
||||||
points['spawn1'] = (-8.03866, 0.02275, 0.0) + (0.5, 0.05, 4.0)
|
|
||||||
points['spawn2'] = (8.82311, 0.01092, 0.0) + (0.5, 0.05, 4.0)
|
|
||||||
boxes['area_of_interest_bounds'] = (0.0, 1.18575, 0.43262) + \
|
|
||||||
(0, 0, 0) + (
|
|
||||||
29.81803, 11.57249, 18.89134)
|
|
||||||
boxes['map_bounds'] = (0.0, 1.185751251, 0.4326226188) + (0.0, 0.0, 0.0) + (
|
|
||||||
42.09506485, 22.81173179, 29.76723155)
|
|
||||||
|
|
||||||
|
|
||||||
class PointzzforH:
|
|
||||||
points, boxes = {}, {}
|
|
||||||
boxes['area_of_interest_bounds'] = (0.0, 0.7956858119, 0.0) + \
|
|
||||||
(0.0, 0.0, 0.0) + (
|
|
||||||
30.80223883, 0.5961646365, 13.88431707)
|
|
||||||
boxes['map_bounds'] = (0.0, 0.7956858119, -0.4689020853) + (
|
|
||||||
0.0, 0.0, 0.0) + (
|
|
||||||
35.16182389, 12.18696164, 21.52869693)
|
|
||||||
points['spawn1'] = (-6.835352227, 0.02305323209, 0.0) + (1.0, 1.0, 3.0)
|
|
||||||
points['spawn2'] = (6.857415055, 0.03938567998, 0.0) + (1.0, 1.0, 3.0)
|
|
||||||
|
|
||||||
|
|
||||||
class VolleyBallMap(bs.Map):
|
|
||||||
defs = Pointzz()
|
|
||||||
name = "Open Field"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_play_types(cls) -> List[str]:
|
|
||||||
return []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_preview_texture_name(cls) -> str:
|
|
||||||
return 'footballStadiumPreview'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def on_preload(cls) -> Any:
|
|
||||||
data: Dict[str, Any] = {
|
|
||||||
'mesh': bs.getmesh('footballStadium'),
|
|
||||||
'vr_fill_mesh': bs.getmesh('footballStadiumVRFill'),
|
|
||||||
'collision_mesh': bs.getcollisionmesh('footballStadiumCollide'),
|
|
||||||
'tex': bs.gettexture('footballStadium')
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
x = -5
|
|
||||||
while x < 5:
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (0, 0, x),
|
|
||||||
'color': (1, 1, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (0, .25, x),
|
|
||||||
'color': (1, 1, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (0, .5, x),
|
|
||||||
'color': (1, 1, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (0, .75, x),
|
|
||||||
'color': (1, 1, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (0, 1, x),
|
|
||||||
'color': (1, 1, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
x = x + 0.5
|
|
||||||
|
|
||||||
y = -1
|
|
||||||
while y > -11:
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (y, 0.01, 4),
|
|
||||||
'color': (0, 0, 1),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (y, 0.01, -4),
|
|
||||||
'color': (0, 0, 1),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (-y, 0.01, 4),
|
|
||||||
'color': (1, 0, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (-y, 0.01, -4),
|
|
||||||
'color': (1, 0, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
y -= 1
|
|
||||||
|
|
||||||
z = 0
|
|
||||||
while z < 5:
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (11, 0.01, z),
|
|
||||||
'color': (1, 0, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (11, 0.01, -z),
|
|
||||||
'color': (1, 0, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (-11, 0.01, z),
|
|
||||||
'color': (0, 0, 1),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (
|
|
||||||
-11, 0.01, -z),
|
|
||||||
'color': (0, 0, 1),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
z += 1
|
|
||||||
|
|
||||||
self.node = bs.newnode(
|
|
||||||
'terrain',
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
'mesh': self.preloaddata['mesh'],
|
|
||||||
'collision_mesh': self.preloaddata['collision_mesh'],
|
|
||||||
'color_texture': self.preloaddata['tex'],
|
|
||||||
'materials': [shared.footing_material]
|
|
||||||
})
|
|
||||||
bs.newnode('terrain',
|
|
||||||
attrs={
|
|
||||||
'mesh': self.preloaddata['vr_fill_mesh'],
|
|
||||||
'lighting': False,
|
|
||||||
'vr_only': True,
|
|
||||||
'background': True,
|
|
||||||
'color_texture': self.preloaddata['tex']
|
|
||||||
})
|
|
||||||
gnode = bs.getactivity().globalsnode
|
|
||||||
gnode.tint = (1.3, 1.2, 1.0)
|
|
||||||
gnode.ambient_color = (1.3, 1.2, 1.0)
|
|
||||||
gnode.vignette_outer = (0.57, 0.57, 0.57)
|
|
||||||
gnode.vignette_inner = (0.9, 0.9, 0.9)
|
|
||||||
gnode.vr_camera_offset = (0, -0.8, -1.1)
|
|
||||||
gnode.vr_near_clip = 0.5
|
|
||||||
|
|
||||||
|
|
||||||
class VolleyBallMapH(bs.Map):
|
|
||||||
defs = PointzzforH()
|
|
||||||
name = 'Closed Arena'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_play_types(cls) -> List[str]:
|
|
||||||
return []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_preview_texture_name(cls) -> str:
|
|
||||||
return 'hockeyStadiumPreview'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def on_preload(cls) -> Any:
|
|
||||||
data: Dict[str, Any] = {
|
|
||||||
'meshs': (bs.getmesh('hockeyStadiumOuter'),
|
|
||||||
bs.getmesh('hockeyStadiumInner')),
|
|
||||||
'vr_fill_mesh': bs.getmesh('footballStadiumVRFill'),
|
|
||||||
'collision_mesh': bs.getcollisionmesh('hockeyStadiumCollide'),
|
|
||||||
'tex': bs.gettexture('hockeyStadium'),
|
|
||||||
}
|
|
||||||
mat = bs.Material()
|
|
||||||
mat.add_actions(actions=('modify_part_collision', 'friction', 0.01))
|
|
||||||
data['ice_material'] = mat
|
|
||||||
return data
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
super().__init__()
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
x = -5
|
|
||||||
while x < 5:
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (0, 0, x),
|
|
||||||
'color': (1, 1, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (0, .25, x),
|
|
||||||
'color': (1, 1, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (0, .5, x),
|
|
||||||
'color': (1, 1, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (0, .75, x),
|
|
||||||
'color': (1, 1, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (0, 1, x),
|
|
||||||
'color': (1, 1, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
x = x + 0.5
|
|
||||||
|
|
||||||
y = -1
|
|
||||||
while y > -11:
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (y, 0.01, 4),
|
|
||||||
'color': (0, 0, 1),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (y, 0.01, -4),
|
|
||||||
'color': (0, 0, 1),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (-y, 0.01, 4),
|
|
||||||
'color': (1, 0, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (-y, 0.01, -4),
|
|
||||||
'color': (1, 0, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
y -= 1
|
|
||||||
|
|
||||||
z = 0
|
|
||||||
while z < 5:
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (11, 0.01, z),
|
|
||||||
'color': (1, 0, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (11, 0.01, -z),
|
|
||||||
'color': (1, 0, 0),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (-11, 0.01, z),
|
|
||||||
'color': (0, 0, 1),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
self.zone = bs.newnode('locator', attrs={'shape': 'circle',
|
|
||||||
'position': (
|
|
||||||
-11, 0.01, -z),
|
|
||||||
'color': (0, 0, 1),
|
|
||||||
'opacity': 1,
|
|
||||||
'draw_beauty': True,
|
|
||||||
'additive': False,
|
|
||||||
'size': [0.40]})
|
|
||||||
z += 1
|
|
||||||
|
|
||||||
self.node = bs.newnode('terrain',
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
'mesh':
|
|
||||||
None,
|
|
||||||
'collision_mesh':
|
|
||||||
# we dont want Goalposts...
|
|
||||||
bs.getcollisionmesh(
|
|
||||||
'footballStadiumCollide'),
|
|
||||||
'color_texture':
|
|
||||||
self.preloaddata['tex'],
|
|
||||||
'materials': [
|
|
||||||
shared.footing_material]
|
|
||||||
})
|
|
||||||
bs.newnode('terrain',
|
|
||||||
attrs={
|
|
||||||
'mesh': self.preloaddata['vr_fill_mesh'],
|
|
||||||
'vr_only': True,
|
|
||||||
'lighting': False,
|
|
||||||
'background': True,
|
|
||||||
})
|
|
||||||
mats = [shared.footing_material]
|
|
||||||
self.floor = bs.newnode('terrain',
|
|
||||||
attrs={
|
|
||||||
'mesh': self.preloaddata['meshs'][1],
|
|
||||||
'color_texture': self.preloaddata['tex'],
|
|
||||||
'opacity': 0.92,
|
|
||||||
'opacity_in_low_or_medium_quality': 1.0,
|
|
||||||
'materials': mats,
|
|
||||||
'color': (0.4, 0.9, 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
self.background = bs.newnode(
|
|
||||||
'terrain',
|
|
||||||
attrs={
|
|
||||||
'mesh': bs.getmesh('natureBackground'),
|
|
||||||
'lighting': False,
|
|
||||||
'background': True,
|
|
||||||
'color': (0.5, 0.30, 0.4)
|
|
||||||
})
|
|
||||||
|
|
||||||
gnode = bs.getactivity().globalsnode
|
|
||||||
gnode.floor_reflection = True
|
|
||||||
gnode.debris_friction = 0.3
|
|
||||||
gnode.debris_kill_height = -0.3
|
|
||||||
gnode.tint = (1.2, 1.3, 1.33)
|
|
||||||
gnode.ambient_color = (1.15, 1.25, 1.6)
|
|
||||||
gnode.vignette_outer = (0.66, 0.67, 0.73)
|
|
||||||
gnode.vignette_inner = (0.93, 0.93, 0.95)
|
|
||||||
gnode.vr_camera_offset = (0, -0.8, -1.1)
|
|
||||||
gnode.vr_near_clip = 0.5
|
|
||||||
# self.is_hockey = True
|
|
||||||
|
|
||||||
|
|
||||||
bs._map.register_map(VolleyBallMap)
|
|
||||||
bs._map.register_map(VolleyBallMapH)
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export plugin
|
|
||||||
class byFreaku(babase.Plugin):
|
|
||||||
def __init__(self):
|
|
||||||
# Reason of plugin:
|
|
||||||
# To register maps.
|
|
||||||
#
|
|
||||||
# Then why not include function here?
|
|
||||||
# On server upon first launch, plugins are not activated,
|
|
||||||
# (same can be case for user if disabled auto-enable plugins)
|
|
||||||
pass
|
|
||||||
31
dist/ba_root/mods/games/yeeting_party.py
vendored
31
dist/ba_root/mods/games/yeeting_party.py
vendored
|
|
@ -1,31 +0,0 @@
|
||||||
# Made by your friend: Freaku
|
|
||||||
|
|
||||||
|
|
||||||
from bascenev1lib.game.deathmatch import Player, DeathMatchGame
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class YeetingGame(DeathMatchGame):
|
|
||||||
"""A game of yeeting people out of map"""
|
|
||||||
|
|
||||||
name = 'Yeeting Party!'
|
|
||||||
description = 'Yeet your enemies out of the map'
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_supported_maps(cls, sessiontype):
|
|
||||||
return ['Bridgit', 'Rampage', 'Monkey Face']
|
|
||||||
|
|
||||||
def get_instance_description(self):
|
|
||||||
return 'Yeet ${ARG1} enemies out of the map!', self._score_to_win
|
|
||||||
|
|
||||||
def get_instance_description_short(self):
|
|
||||||
return 'yeet ${ARG1} enemies', self._score_to_win
|
|
||||||
|
|
||||||
def setup_standard_powerup_drops(self, enable_tnt: bool = True) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def spawn_player(self, player: Player):
|
|
||||||
spaz = self.spawn_player_spaz(player)
|
|
||||||
spaz.connect_controls_to_player(enable_punch=False, enable_bomb=False)
|
|
||||||
return spaz
|
|
||||||
7
dist/ba_root/mods/playersData/__init__.py
vendored
7
dist/ba_root/mods/playersData/__init__.py
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
"""Common bits of functionality shared between all efro projects.
|
|
||||||
|
|
||||||
Things in here should be hardened, highly type-safe, and well-covered by unit
|
|
||||||
tests since they are widely used in live client and server code.
|
|
||||||
|
|
||||||
license : MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
34
dist/ba_root/mods/playersData/blacklist.json
vendored
34
dist/ba_root/mods/playersData/blacklist.json
vendored
|
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"ban": {
|
|
||||||
"ids": {
|
|
||||||
"pb-234": {
|
|
||||||
"till": "2023-06-07 21:59:20",
|
|
||||||
"reason": "auto ban for spam"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ips": {
|
|
||||||
"19.168.0.0.1": {
|
|
||||||
"till": "2023-06-07 21:59:20",
|
|
||||||
"reason": "auto ban for spam"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"deviceids": {
|
|
||||||
"sdfdsfwr3": {
|
|
||||||
"till": "2023-06-07 21:59:20",
|
|
||||||
"reason": "auto ban for spam"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"muted-ids": {
|
|
||||||
"pb-IF4iU0QaEw==": {
|
|
||||||
"till": "2023-06-19 19:44:47",
|
|
||||||
"reason": "manually from website"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"kick-vote-disabled": {
|
|
||||||
"pb-JiNJARBaXEFBVF9HFkNXXF1EF0ZaRlZE": {
|
|
||||||
"till": "2023-06-12 19:37:48",
|
|
||||||
"reason": "manually from website"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
17
dist/ba_root/mods/playersData/custom.json
vendored
17
dist/ba_root/mods/playersData/custom.json
vendored
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"customeffects": {
|
|
||||||
"pb-IF4TVWwZUQ==": [
|
|
||||||
"spark"
|
|
||||||
],
|
|
||||||
"pb-smoothy-effect": [
|
|
||||||
"spark"
|
|
||||||
],
|
|
||||||
"pb-testingroles": [
|
|
||||||
"highlightshine"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"customtag": {
|
|
||||||
"pb-id5y54y54": "smoothy",
|
|
||||||
"pb-smoothybro": "they smoothy"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
17
dist/ba_root/mods/playersData/custom.json.backup
vendored
17
dist/ba_root/mods/playersData/custom.json.backup
vendored
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"customeffects": {
|
|
||||||
"pb-IF4TVWwZUQ==": [
|
|
||||||
"spark"
|
|
||||||
],
|
|
||||||
"pb-smoothy-effect": [
|
|
||||||
"spark"
|
|
||||||
],
|
|
||||||
"pb-testingroles": [
|
|
||||||
"highlightshine"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"customtag": {
|
|
||||||
"pb-id5y54y54": "smoothy",
|
|
||||||
"pb-smoothybro": "they smoothy"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
749
dist/ba_root/mods/playersData/pdata.py
vendored
749
dist/ba_root/mods/playersData/pdata.py
vendored
|
|
@ -1,749 +0,0 @@
|
||||||
"""Module to manage players data."""
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# (see https://ballistica.net/wiki/meta-tag-system)
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import _thread
|
|
||||||
import copy
|
|
||||||
import datetime
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import time
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
import _bascenev1
|
|
||||||
import setting
|
|
||||||
from serverData import serverdata
|
|
||||||
from tools.ServerUpdate import checkSpammer
|
|
||||||
from tools.file_handle import OpenJson
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
# pylint: disable=import-error
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
pass
|
|
||||||
|
|
||||||
settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
PLAYERS_DATA_PATH = os.path.join(
|
|
||||||
babase.env()["python_directory_user"], "playersdata" + os.sep
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CacheData: # pylint: disable=too-few-public-methods
|
|
||||||
"""Stores the cache data."""
|
|
||||||
|
|
||||||
roles: dict = {}
|
|
||||||
data: dict = {}
|
|
||||||
custom: dict = {}
|
|
||||||
profiles: dict = {}
|
|
||||||
whitelist: list[str] = []
|
|
||||||
blacklist: dict = {}
|
|
||||||
|
|
||||||
|
|
||||||
def get_info(account_id: str) -> dict | None:
|
|
||||||
"""Returns the information about player.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
account_id of the client
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
dict | None
|
|
||||||
information of client
|
|
||||||
"""
|
|
||||||
profiles = get_profiles()
|
|
||||||
if account_id in profiles:
|
|
||||||
return profiles[account_id]
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_profiles() -> dict:
|
|
||||||
"""Returns the profiles of all players.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
dict
|
|
||||||
profiles of the players
|
|
||||||
"""
|
|
||||||
if CacheData.profiles == {}:
|
|
||||||
try:
|
|
||||||
if os.stat(PLAYERS_DATA_PATH + "profiles.json").st_size > 1000000:
|
|
||||||
newpath = f'{PLAYERS_DATA_PATH}profiles-{str(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))}.json'
|
|
||||||
shutil.copyfile(PLAYERS_DATA_PATH + "profiles.json", newpath)
|
|
||||||
profiles = {"pb-sdf": {}}
|
|
||||||
print("resetting profiles")
|
|
||||||
else:
|
|
||||||
f = open(PLAYERS_DATA_PATH + "profiles.json", "r")
|
|
||||||
profiles = json.load(f)
|
|
||||||
f.close()
|
|
||||||
print("loading old proiles.json")
|
|
||||||
CacheData.profiles = profiles
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
f = open(PLAYERS_DATA_PATH + "profiles.json.backup", "r")
|
|
||||||
profiles = json.load(f)
|
|
||||||
print(e)
|
|
||||||
print("exception happened , falling back to profiles.json.backup")
|
|
||||||
CacheData.profiles = profiles
|
|
||||||
f.close()
|
|
||||||
return profiles
|
|
||||||
else:
|
|
||||||
return CacheData.profiles
|
|
||||||
|
|
||||||
|
|
||||||
def get_profiles_archive_index():
|
|
||||||
return [x for x in os.listdir(PLAYERS_DATA_PATH) if
|
|
||||||
x.startswith("profiles")]
|
|
||||||
|
|
||||||
|
|
||||||
def get_old_profiles(filename):
|
|
||||||
try:
|
|
||||||
f = open(PLAYERS_DATA_PATH + filename, "r")
|
|
||||||
profiles = json.load(f)
|
|
||||||
return profiles
|
|
||||||
except:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
def get_blacklist() -> dict:
|
|
||||||
if CacheData.blacklist == {}:
|
|
||||||
try:
|
|
||||||
with open(PLAYERS_DATA_PATH + "blacklist.json", "r") as f:
|
|
||||||
CacheData.blacklist = json.load(f)
|
|
||||||
except:
|
|
||||||
print('error opening blacklist json')
|
|
||||||
return {
|
|
||||||
"ban": {
|
|
||||||
"ids": {},
|
|
||||||
"ips": {},
|
|
||||||
"deviceids": {}
|
|
||||||
},
|
|
||||||
"muted-ids": {},
|
|
||||||
"kick-vote-disabled": {}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CacheData.blacklist
|
|
||||||
|
|
||||||
|
|
||||||
def update_blacklist():
|
|
||||||
with open(PLAYERS_DATA_PATH + "blacklist.json", "w") as f:
|
|
||||||
json.dump(CacheData.blacklist, f, indent=4)
|
|
||||||
|
|
||||||
|
|
||||||
def commit_profiles(data={}) -> None:
|
|
||||||
"""Commits the given profiles in the database.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
profiles of all players
|
|
||||||
"""
|
|
||||||
# with OpenJson(PLAYERS_DATA_PATH + "profiles.json") as profiles_file:
|
|
||||||
# profiles_file.dump(CacheData.profiles, indent=4)
|
|
||||||
|
|
||||||
|
|
||||||
def get_detailed_info(pbid):
|
|
||||||
main_account = get_info(pbid)
|
|
||||||
if main_account == None:
|
|
||||||
return "No info"
|
|
||||||
linked_accounts = ' '.join(main_account["display_string"])
|
|
||||||
ip = main_account["lastIP"]
|
|
||||||
deviceid = main_account["deviceUUID"]
|
|
||||||
otheraccounts = ""
|
|
||||||
dob = main_account["accountAge"]
|
|
||||||
profiles = get_profiles()
|
|
||||||
for key, value in profiles.items():
|
|
||||||
if ("lastIP" in value and value["lastIP"] == ip) or (
|
|
||||||
"deviceUUID" in value and value["deviceUUID"] == deviceid):
|
|
||||||
otheraccounts += ' '.join(value["display_string"])
|
|
||||||
return f"Accounts:{linked_accounts} \n other accounts {otheraccounts} \n created on {dob}"
|
|
||||||
|
|
||||||
|
|
||||||
def add_profile(
|
|
||||||
account_id: str,
|
|
||||||
display_string: str,
|
|
||||||
current_name: str,
|
|
||||||
account_age: int,
|
|
||||||
) -> None:
|
|
||||||
"""Adds the profile in database.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
account id of the client
|
|
||||||
display_string : str
|
|
||||||
display string of the client
|
|
||||||
current_name : str
|
|
||||||
name of the client
|
|
||||||
account_age : int
|
|
||||||
account_age of the account
|
|
||||||
"""
|
|
||||||
profiles = get_profiles()
|
|
||||||
profiles[account_id] = {
|
|
||||||
"display_string": display_string,
|
|
||||||
"profiles": [],
|
|
||||||
"name": current_name,
|
|
||||||
"accountAge": account_age,
|
|
||||||
"registerOn": time.time(),
|
|
||||||
"spamCount": 0,
|
|
||||||
"lastSpam": time.time(),
|
|
||||||
"totaltimeplayer": 0,
|
|
||||||
}
|
|
||||||
CacheData.profiles = profiles
|
|
||||||
|
|
||||||
serverdata.clients[account_id] = profiles[account_id]
|
|
||||||
serverdata.clients[account_id]["warnCount"] = 0
|
|
||||||
serverdata.clients[account_id]["lastWarned"] = time.time()
|
|
||||||
serverdata.clients[account_id]["verified"] = False
|
|
||||||
serverdata.clients[account_id]["rejoincount"] = 1
|
|
||||||
serverdata.clients[account_id]["lastJoin"] = time.time()
|
|
||||||
cid = 113
|
|
||||||
for ros in bs.get_game_roster():
|
|
||||||
if ros['account_id'] == account_id:
|
|
||||||
cid = ros['client_id']
|
|
||||||
ip = _bascenev1.get_client_ip(cid)
|
|
||||||
serverdata.clients[account_id]["lastIP"] = ip
|
|
||||||
serverdata.recents.append(
|
|
||||||
{"client_id": cid, "deviceId": display_string, "pbid": account_id})
|
|
||||||
serverdata.recents = serverdata.recents[-20:]
|
|
||||||
device_id = _bascenev1.get_client_public_device_uuid(cid)
|
|
||||||
if (device_id == None):
|
|
||||||
device_id = _bascenev1.get_client_device_uuid(cid)
|
|
||||||
checkSpammer({'id': account_id, 'display': display_string,
|
|
||||||
'ip': ip, 'device': device_id})
|
|
||||||
if device_id in get_blacklist()["ban"]["deviceids"] or account_id in \
|
|
||||||
get_blacklist()["ban"]["ids"]:
|
|
||||||
bs.disconnect_client(cid)
|
|
||||||
serverdata.clients[account_id]["deviceUUID"] = device_id
|
|
||||||
|
|
||||||
|
|
||||||
def update_display_string(account_id: str, display_string: str) -> None:
|
|
||||||
"""Updates the display string of the account.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
account id of the client
|
|
||||||
display_string : str
|
|
||||||
new display string to be updated
|
|
||||||
"""
|
|
||||||
profiles = get_profiles()
|
|
||||||
if account_id in profiles:
|
|
||||||
profiles[account_id]["display_string"] = display_string
|
|
||||||
CacheData.profiles = profiles
|
|
||||||
commit_profiles()
|
|
||||||
|
|
||||||
|
|
||||||
def update_profile(
|
|
||||||
account_id: str,
|
|
||||||
display_string: str = None,
|
|
||||||
allprofiles: list[str] = None,
|
|
||||||
name: str = None,
|
|
||||||
) -> None:
|
|
||||||
"""Updates the profile of client.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
account id of the client
|
|
||||||
display_string : str, optional
|
|
||||||
display string of the account, by default None
|
|
||||||
allprofiles : list[str], optional
|
|
||||||
all profiles of the client, by default None
|
|
||||||
name : str, optional
|
|
||||||
name to be updated, by default None
|
|
||||||
"""
|
|
||||||
|
|
||||||
profiles = get_profiles()
|
|
||||||
|
|
||||||
if profiles is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if account_id in profiles and display_string is not None:
|
|
||||||
if display_string not in profiles[account_id]["display_string"]:
|
|
||||||
profiles[account_id]["display_string"].append(display_string)
|
|
||||||
|
|
||||||
if allprofiles is not None:
|
|
||||||
for profile in allprofiles:
|
|
||||||
if profile not in profiles[account_id]["profiles"]:
|
|
||||||
profiles[account_id]["profiles"].append(profile)
|
|
||||||
|
|
||||||
if name is not None:
|
|
||||||
profiles[account_id]["name"] = name
|
|
||||||
CacheData.profiles = profiles
|
|
||||||
commit_profiles()
|
|
||||||
|
|
||||||
|
|
||||||
def ban_player(account_id: str, duration_in_days: float, reason: str) -> None:
|
|
||||||
"""Bans the player.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
account id of the player to be banned
|
|
||||||
"""
|
|
||||||
current_profiles = get_profiles()
|
|
||||||
ip = ""
|
|
||||||
device_id = ""
|
|
||||||
if account_id in current_profiles:
|
|
||||||
ip = current_profiles[account_id]["lastIP"]
|
|
||||||
device_id = current_profiles[account_id]["deviceUUID"]
|
|
||||||
|
|
||||||
ban_time = datetime.now() + timedelta(days=duration_in_days)
|
|
||||||
|
|
||||||
CacheData.blacklist["ban"]["ips"][ip] = {"till": ban_time.strftime(
|
|
||||||
"%Y-%m-%d %H:%M:%S"), "reason": f'linked with account {account_id}'}
|
|
||||||
CacheData.blacklist["ban"]["ids"][account_id] = {
|
|
||||||
"till": ban_time.strftime("%Y-%m-%d %H:%M:%S"), "reason": reason}
|
|
||||||
CacheData.blacklist["ban"]["deviceids"][device_id] = {
|
|
||||||
"till": ban_time.strftime(
|
|
||||||
"%Y-%m-%d %H:%M:%S"), "reason": f'linked with account {account_id}'}
|
|
||||||
_thread.start_new_thread(update_blacklist, ())
|
|
||||||
|
|
||||||
|
|
||||||
def unban_player(account_id):
|
|
||||||
current_profiles = get_profiles()
|
|
||||||
ip = ""
|
|
||||||
device_id = ""
|
|
||||||
if account_id in current_profiles:
|
|
||||||
ip = current_profiles[account_id]["lastIP"]
|
|
||||||
device_id = current_profiles[account_id]["deviceUUID"]
|
|
||||||
|
|
||||||
CacheData.blacklist["ban"]["ips"].pop(ip, None)
|
|
||||||
CacheData.blacklist["ban"]["deviceids"].pop(device_id, None)
|
|
||||||
CacheData.blacklist["ban"]["ids"].pop(account_id, None)
|
|
||||||
_thread.start_new_thread(update_blacklist, ())
|
|
||||||
|
|
||||||
|
|
||||||
def disable_kick_vote(account_id, duration, reason):
|
|
||||||
ban_time = datetime.now() + timedelta(days=duration)
|
|
||||||
CacheData.blacklist["kick-vote-disabled"][account_id] = {
|
|
||||||
"till": ban_time.strftime(
|
|
||||||
"%Y-%m-%d %H:%M:%S"), "reason": reason}
|
|
||||||
_thread.start_new_thread(update_blacklist, ())
|
|
||||||
|
|
||||||
|
|
||||||
def enable_kick_vote(account_id):
|
|
||||||
CacheData.blacklist["kick-vote-disabled"].pop(account_id, None)
|
|
||||||
_thread.start_new_thread(update_blacklist, ())
|
|
||||||
|
|
||||||
|
|
||||||
def mute(account_id: str, duration_in_days: float, reason: str) -> None:
|
|
||||||
"""Mutes the player.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
acccount id of the player to be muted
|
|
||||||
"""
|
|
||||||
ban_time = datetime.now() + timedelta(days=duration_in_days)
|
|
||||||
|
|
||||||
CacheData.blacklist["muted-ids"][account_id] = {"till": ban_time.strftime(
|
|
||||||
"%Y-%m-%d %H:%M:%S"), "reason": reason}
|
|
||||||
_thread.start_new_thread(update_blacklist, ())
|
|
||||||
|
|
||||||
|
|
||||||
def unmute(account_id: str) -> None:
|
|
||||||
"""Unmutes the player.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
acccount id of the player to be unmuted
|
|
||||||
"""
|
|
||||||
CacheData.blacklist["muted-ids"].pop(account_id, None)
|
|
||||||
_thread.start_new_thread(update_blacklist, ())
|
|
||||||
|
|
||||||
|
|
||||||
def update_spam(account_id: str, spam_count: int, last_spam: float) -> None:
|
|
||||||
"""Updates the spam time and count.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
account id of the client
|
|
||||||
spam_count : int
|
|
||||||
spam count to be added
|
|
||||||
last_spam : float
|
|
||||||
last spam time
|
|
||||||
"""
|
|
||||||
profiles = get_profiles()
|
|
||||||
if account_id in profiles:
|
|
||||||
profiles[account_id]["spamCount"] = spam_count
|
|
||||||
profiles[account_id]["lastSpam"] = last_spam
|
|
||||||
CacheData.profiles = profiles
|
|
||||||
commit_profiles(profiles)
|
|
||||||
|
|
||||||
|
|
||||||
def commit_roles(data: dict) -> None:
|
|
||||||
"""Commits the roles in database.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
data : dict
|
|
||||||
data to be commited
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
return
|
|
||||||
|
|
||||||
# with OpenJson(PLAYERS_DATA_PATH + "roles.json") as roles_file:
|
|
||||||
# roles_file.format(data)
|
|
||||||
|
|
||||||
|
|
||||||
def get_roles() -> dict:
|
|
||||||
"""Returns the roles.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
dict
|
|
||||||
roles
|
|
||||||
"""
|
|
||||||
if CacheData.roles == {}:
|
|
||||||
try:
|
|
||||||
f = open(PLAYERS_DATA_PATH + "roles.json", "r")
|
|
||||||
roles = json.load(f)
|
|
||||||
f.close()
|
|
||||||
CacheData.roles = roles
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
f = open(PLAYERS_DATA_PATH + "roles.json.backup", "r")
|
|
||||||
roles = json.load(f)
|
|
||||||
f.close()
|
|
||||||
CacheData.roles = roles
|
|
||||||
return CacheData.roles
|
|
||||||
|
|
||||||
|
|
||||||
def create_role(role: str) -> None:
|
|
||||||
"""Ceates the role.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
role : str
|
|
||||||
role to be created
|
|
||||||
"""
|
|
||||||
roles = get_roles()
|
|
||||||
|
|
||||||
if role in roles:
|
|
||||||
return
|
|
||||||
|
|
||||||
roles[role] = {
|
|
||||||
"tag": role,
|
|
||||||
"tagcolor": [1, 1, 1],
|
|
||||||
"commands": [],
|
|
||||||
"ids": [],
|
|
||||||
}
|
|
||||||
CacheData.roles = roles
|
|
||||||
commit_roles(roles)
|
|
||||||
|
|
||||||
|
|
||||||
def add_player_role(role: str, account_id: str) -> None:
|
|
||||||
"""Adds the player to the role.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
role : str
|
|
||||||
role to be added
|
|
||||||
account_id : str
|
|
||||||
account id of the client
|
|
||||||
"""
|
|
||||||
roles = get_roles()
|
|
||||||
|
|
||||||
if role in roles:
|
|
||||||
if account_id not in roles[role]["ids"]:
|
|
||||||
roles[role]["ids"].append(account_id)
|
|
||||||
CacheData.roles = roles
|
|
||||||
commit_roles(roles)
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("no role such")
|
|
||||||
|
|
||||||
|
|
||||||
def remove_player_role(role: str, account_id: str) -> str:
|
|
||||||
"""Removes the role from player.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
role : str
|
|
||||||
role to br removed
|
|
||||||
account_id : str
|
|
||||||
account id of the client
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
status of the removing role
|
|
||||||
"""
|
|
||||||
roles = get_roles()
|
|
||||||
if role in roles:
|
|
||||||
roles[role]["ids"].remove(account_id)
|
|
||||||
CacheData.roles = roles
|
|
||||||
commit_roles(roles)
|
|
||||||
return "removed from " + role
|
|
||||||
return "role not exists"
|
|
||||||
|
|
||||||
|
|
||||||
def add_command_role(role: str, command: str) -> str:
|
|
||||||
"""Adds the command to the role.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
role : str
|
|
||||||
role to add the command
|
|
||||||
command : str
|
|
||||||
command to be added
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
status of the adding command
|
|
||||||
"""
|
|
||||||
roles = get_roles()
|
|
||||||
if role in roles:
|
|
||||||
if command not in roles[role]["commands"]:
|
|
||||||
roles[role]["commands"].append(command)
|
|
||||||
CacheData.roles = roles
|
|
||||||
commit_roles(roles)
|
|
||||||
return "command added to " + role
|
|
||||||
return "command not exists"
|
|
||||||
|
|
||||||
|
|
||||||
def remove_command_role(role: str, command: str) -> str:
|
|
||||||
"""Removes the command from the role.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
role : str
|
|
||||||
role to remove command from
|
|
||||||
command : str
|
|
||||||
command to be removed
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
status of the removing command
|
|
||||||
"""
|
|
||||||
roles = get_roles()
|
|
||||||
if role in roles:
|
|
||||||
if command in roles[role]["commands"]:
|
|
||||||
roles[role]["commands"].remove(command)
|
|
||||||
CacheData.roles = roles
|
|
||||||
commit_roles(roles)
|
|
||||||
return "command added to " + role
|
|
||||||
return "command not exists"
|
|
||||||
|
|
||||||
|
|
||||||
def change_role_tag(role: str, tag: str) -> str:
|
|
||||||
"""Changes the tag of the role.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
role : str
|
|
||||||
role to chnage the tag
|
|
||||||
tag : str
|
|
||||||
tag to be added
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
str
|
|
||||||
status of the adding tag
|
|
||||||
"""
|
|
||||||
roles = get_roles()
|
|
||||||
if role in roles:
|
|
||||||
roles[role]["tag"] = tag
|
|
||||||
CacheData.roles = roles
|
|
||||||
commit_roles(roles)
|
|
||||||
return "tag changed"
|
|
||||||
return "role not exists"
|
|
||||||
|
|
||||||
|
|
||||||
def get_player_roles(account_id: str) -> list[str]:
|
|
||||||
"""Returns the avalibe roles of the account.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
account id of the client
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
list[str]
|
|
||||||
list of the roles
|
|
||||||
"""
|
|
||||||
|
|
||||||
roles = get_roles()
|
|
||||||
have_roles = []
|
|
||||||
for role in roles:
|
|
||||||
if account_id in roles[role]["ids"]:
|
|
||||||
have_roles.append(role)
|
|
||||||
return have_roles
|
|
||||||
|
|
||||||
|
|
||||||
def get_custom() -> dict:
|
|
||||||
"""Returns the custom effects.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
dict
|
|
||||||
custom effects
|
|
||||||
"""
|
|
||||||
if CacheData.custom == {}:
|
|
||||||
try:
|
|
||||||
f = open(PLAYERS_DATA_PATH + "custom.json", "r")
|
|
||||||
custom = json.load(f)
|
|
||||||
f.close()
|
|
||||||
CacheData.custom = custom
|
|
||||||
except:
|
|
||||||
f = open(PLAYERS_DATA_PATH + "custom.json.backup", "r")
|
|
||||||
custom = json.load(f)
|
|
||||||
f.close()
|
|
||||||
CacheData.custom = custom
|
|
||||||
for account_id in custom["customeffects"]:
|
|
||||||
custom["customeffects"][account_id] = [
|
|
||||||
custom["customeffects"][account_id]] if type(
|
|
||||||
custom["customeffects"][account_id]) is str else \
|
|
||||||
custom["customeffects"][account_id]
|
|
||||||
|
|
||||||
return CacheData.custom
|
|
||||||
|
|
||||||
|
|
||||||
def set_effect(effect: str, account_id: str) -> None:
|
|
||||||
"""Sets the costum effect for the player.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
effect : str
|
|
||||||
effect to be added to the player
|
|
||||||
accout_id : str
|
|
||||||
account id of the client
|
|
||||||
"""
|
|
||||||
custom = get_custom()
|
|
||||||
if account_id in custom["customeffects"]:
|
|
||||||
effects = [custom["customeffects"][account_id]] if type(
|
|
||||||
custom["customeffects"][account_id]) is str else \
|
|
||||||
custom["customeffects"][account_id]
|
|
||||||
effects.append(effect)
|
|
||||||
custom["customeffects"][account_id] = effects
|
|
||||||
else:
|
|
||||||
custom["customeffects"][account_id] = [effect]
|
|
||||||
CacheData.custom = custom
|
|
||||||
commit_c()
|
|
||||||
|
|
||||||
|
|
||||||
def set_tag(tag: str, account_id: str) -> None:
|
|
||||||
"""Sets the custom tag to the player.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
tag : str
|
|
||||||
tag to be added to the player
|
|
||||||
account_id : str
|
|
||||||
account id of the client
|
|
||||||
"""
|
|
||||||
custom = get_custom()
|
|
||||||
custom["customtag"][account_id] = tag
|
|
||||||
CacheData.custom = custom
|
|
||||||
commit_c()
|
|
||||||
|
|
||||||
|
|
||||||
def update_roles(roles):
|
|
||||||
CacheData.roles = roles
|
|
||||||
|
|
||||||
|
|
||||||
def get_custom_perks():
|
|
||||||
return CacheData.custom
|
|
||||||
|
|
||||||
|
|
||||||
def update_custom_perks(custom):
|
|
||||||
CacheData.custom = custom
|
|
||||||
|
|
||||||
|
|
||||||
def remove_effect(account_id: str) -> None:
|
|
||||||
"""Removes the effect from player.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
account id of the client
|
|
||||||
"""
|
|
||||||
custom = get_custom()
|
|
||||||
custom["customeffects"].pop(account_id)
|
|
||||||
CacheData.custom = custom
|
|
||||||
|
|
||||||
|
|
||||||
def remove_tag(account_id: str) -> None:
|
|
||||||
"""Removes the tag from the player
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
account_id : str
|
|
||||||
account id of the client
|
|
||||||
"""
|
|
||||||
custom = get_custom()
|
|
||||||
custom["customtag"].pop(account_id)
|
|
||||||
CacheData.custom = custom
|
|
||||||
|
|
||||||
|
|
||||||
def commit_c():
|
|
||||||
"""Commits the custom data into the custom.json."""
|
|
||||||
# with OpenJson(PLAYERS_DATA_PATH + "custom.json") as custom_file:
|
|
||||||
# custom_file.dump(CacheData.custom, indent=4)
|
|
||||||
|
|
||||||
|
|
||||||
def update_toppers(topper_list: list[str]) -> None:
|
|
||||||
"""Updates the topper list into top5 role.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
topper_list : list[str]
|
|
||||||
list of the topper players
|
|
||||||
"""
|
|
||||||
roles = get_roles()
|
|
||||||
if "top5" not in roles:
|
|
||||||
create_role("top5")
|
|
||||||
CacheData.roles["top5"]["ids"] = topper_list
|
|
||||||
commit_roles(roles)
|
|
||||||
|
|
||||||
|
|
||||||
def load_white_list() -> None:
|
|
||||||
"""Loads the whitelist."""
|
|
||||||
with OpenJson(PLAYERS_DATA_PATH + "whitelist.json") as whitelist_file:
|
|
||||||
data = whitelist_file.load()
|
|
||||||
for account_id in data:
|
|
||||||
CacheData.whitelist.append(account_id)
|
|
||||||
|
|
||||||
|
|
||||||
def load_cache():
|
|
||||||
""" to be called on server boot"""
|
|
||||||
get_profiles()
|
|
||||||
get_custom()
|
|
||||||
get_roles()
|
|
||||||
|
|
||||||
|
|
||||||
def dump_cache():
|
|
||||||
if CacheData.profiles != {}:
|
|
||||||
shutil.copyfile(PLAYERS_DATA_PATH + "profiles.json",
|
|
||||||
PLAYERS_DATA_PATH + "profiles.json.backup")
|
|
||||||
profiles = copy.deepcopy(CacheData.profiles)
|
|
||||||
with open(PLAYERS_DATA_PATH + "profiles.json", "w") as f:
|
|
||||||
json.dump(profiles, f, indent=4)
|
|
||||||
if CacheData.roles != {}:
|
|
||||||
shutil.copyfile(PLAYERS_DATA_PATH + "roles.json",
|
|
||||||
PLAYERS_DATA_PATH + "roles.json.backup")
|
|
||||||
roles = copy.deepcopy(CacheData.roles)
|
|
||||||
with open(PLAYERS_DATA_PATH + "roles.json", "w") as f:
|
|
||||||
json.dump(roles, f, indent=4)
|
|
||||||
if CacheData.custom != {}:
|
|
||||||
shutil.copyfile(PLAYERS_DATA_PATH + "custom.json",
|
|
||||||
PLAYERS_DATA_PATH + "custom.json.backup")
|
|
||||||
custom = copy.deepcopy(CacheData.custom)
|
|
||||||
with open(PLAYERS_DATA_PATH + "custom.json", "w") as f:
|
|
||||||
json.dump(custom, f, indent=4)
|
|
||||||
time.sleep(60)
|
|
||||||
dump_cache()
|
|
||||||
22996
dist/ba_root/mods/playersData/profiles.json
vendored
22996
dist/ba_root/mods/playersData/profiles.json
vendored
File diff suppressed because one or more lines are too long
22996
dist/ba_root/mods/playersData/profiles.json.backup
vendored
22996
dist/ba_root/mods/playersData/profiles.json.backup
vendored
File diff suppressed because one or more lines are too long
92
dist/ba_root/mods/playersData/roles.json
vendored
92
dist/ba_root/mods/playersData/roles.json
vendored
|
|
@ -1,92 +0,0 @@
|
||||||
{
|
|
||||||
"owner": {
|
|
||||||
"tag": "\\cowner\\c",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
0.6,
|
|
||||||
0.4
|
|
||||||
],
|
|
||||||
"commands": [
|
|
||||||
"ALL"
|
|
||||||
],
|
|
||||||
"ids": [
|
|
||||||
"pb-IF48VWkBFQ",
|
|
||||||
"pb-JiNJARBaXEFBVF9HFkNXXF1EF0ZaRlZE",
|
|
||||||
"pb-IF4TVWwZUQ==",
|
|
||||||
"pb-IF4SVW9dEg==",
|
|
||||||
"pb-IF5XUm9eAg=="
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"admin": {
|
|
||||||
"tag": "\ue043admin\ue043",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [
|
|
||||||
"createrole"
|
|
||||||
],
|
|
||||||
"ids": [
|
|
||||||
"pb-IF4TVWwZUQ=="
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"vip": {
|
|
||||||
"tag": "vip",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [],
|
|
||||||
"ids": []
|
|
||||||
},
|
|
||||||
"smoothy": {
|
|
||||||
"tag": "smoothy",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [],
|
|
||||||
"ids": []
|
|
||||||
},
|
|
||||||
"pros": {
|
|
||||||
"tag": "pros",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [],
|
|
||||||
"ids": []
|
|
||||||
},
|
|
||||||
"top5": {
|
|
||||||
"tag": "top5",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [],
|
|
||||||
"ids": [
|
|
||||||
"pb-IF4VAk4a"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"bypass-warn": {
|
|
||||||
"tag": "",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [],
|
|
||||||
"ids": [
|
|
||||||
"pb-IF5XUm9eAg==",
|
|
||||||
"pb-IF43VUwlAg==",
|
|
||||||
"pb-IF4iVUc5Cg==",
|
|
||||||
"pb-IF4vNnMJ",
|
|
||||||
"pb-IF4TVWwZUQ=="
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
92
dist/ba_root/mods/playersData/roles.json.backup
vendored
92
dist/ba_root/mods/playersData/roles.json.backup
vendored
|
|
@ -1,92 +0,0 @@
|
||||||
{
|
|
||||||
"owner": {
|
|
||||||
"tag": "\\cowner\\c",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
0.6,
|
|
||||||
0.4
|
|
||||||
],
|
|
||||||
"commands": [
|
|
||||||
"ALL"
|
|
||||||
],
|
|
||||||
"ids": [
|
|
||||||
"pb-IF48VWkBFQ",
|
|
||||||
"pb-JiNJARBaXEFBVF9HFkNXXF1EF0ZaRlZE",
|
|
||||||
"pb-IF4TVWwZUQ==",
|
|
||||||
"pb-IF4SVW9dEg==",
|
|
||||||
"pb-IF5XUm9eAg=="
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"admin": {
|
|
||||||
"tag": "\ue043admin\ue043",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [
|
|
||||||
"createrole"
|
|
||||||
],
|
|
||||||
"ids": [
|
|
||||||
"pb-IF4TVWwZUQ=="
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"vip": {
|
|
||||||
"tag": "vip",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [],
|
|
||||||
"ids": []
|
|
||||||
},
|
|
||||||
"smoothy": {
|
|
||||||
"tag": "smoothy",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [],
|
|
||||||
"ids": []
|
|
||||||
},
|
|
||||||
"pros": {
|
|
||||||
"tag": "pros",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [],
|
|
||||||
"ids": []
|
|
||||||
},
|
|
||||||
"top5": {
|
|
||||||
"tag": "top5",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [],
|
|
||||||
"ids": [
|
|
||||||
"pb-IF4VAk4a"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"bypass-warn": {
|
|
||||||
"tag": "",
|
|
||||||
"tagcolor": [
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"commands": [],
|
|
||||||
"ids": [
|
|
||||||
"pb-IF5XUm9eAg==",
|
|
||||||
"pb-IF43VUwlAg==",
|
|
||||||
"pb-IF4iVUc5Cg==",
|
|
||||||
"pb-IF4vNnMJ",
|
|
||||||
"pb-IF4TVWwZUQ=="
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"pb-IF41U2scIg==": {
|
|
||||||
"subscribers": [
|
|
||||||
"jrz6Rg"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"pb-IF4IVFkKNA==": {
|
|
||||||
"subscribers": [
|
|
||||||
"jrz6Rg"
|
|
||||||
],
|
|
||||||
"name": "\ue063ATTITUDEB2"
|
|
||||||
},
|
|
||||||
"pb-IF4CKhYn": {
|
|
||||||
"subscribers": [
|
|
||||||
"jrz6Rg"
|
|
||||||
],
|
|
||||||
"name": "\ue032Rico Un\u2122\u00a9\u00ae"
|
|
||||||
},
|
|
||||||
"pb-IF4jF1NY": {
|
|
||||||
"subscribers": [
|
|
||||||
"jrz6Rg"
|
|
||||||
],
|
|
||||||
"name": "\ue01eHoemaster"
|
|
||||||
},
|
|
||||||
"pb-IF4wVVk8Jg==": {
|
|
||||||
"subscribers": [
|
|
||||||
"jrz6Rg"
|
|
||||||
],
|
|
||||||
"name": "\ue063Homulilly"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"pb-IF41U2scIg==": {
|
|
||||||
"subscribers": [
|
|
||||||
"jrz6Rg"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"pb-IF4IVFkKNA==": {
|
|
||||||
"subscribers": [
|
|
||||||
"jrz6Rg"
|
|
||||||
],
|
|
||||||
"name": "\ue063ATTITUDEB2"
|
|
||||||
},
|
|
||||||
"pb-IF4CKhYn": {
|
|
||||||
"subscribers": [
|
|
||||||
"jrz6Rg"
|
|
||||||
],
|
|
||||||
"name": "\ue032Rico Un\u2122\u00a9\u00ae"
|
|
||||||
},
|
|
||||||
"pb-IF4jF1NY": {
|
|
||||||
"subscribers": [
|
|
||||||
"jrz6Rg"
|
|
||||||
],
|
|
||||||
"name": "\ue01eHoemaster"
|
|
||||||
},
|
|
||||||
"pb-IF4wVVk8Jg==": {
|
|
||||||
"subscribers": [
|
|
||||||
"jrz6Rg"
|
|
||||||
],
|
|
||||||
"name": "\ue063Homulilly"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
10
dist/ba_root/mods/playersData/subscriptions.json
vendored
10
dist/ba_root/mods/playersData/subscriptions.json
vendored
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"jrz6Rg": {
|
|
||||||
"endpoint": "https://wns2-pn1p.notify.windows.com/w/?token=BQYAAAACsvYS2nXaGg2g2jLcjLjp4bwznS9iOrr3BCN%2bvTCn%2fHqOyzKJSI3IqeLRM0klolKhnswEn0YOYDgvEnAW0bJRJsSW8nmg0y8RZoY5%2bGnVGqv8VXoGafhDTJOb5%2bKfgRRz01qtOW2qQogWRj3dRsVw%2fEw6dliZOl3SC3eBfgOg5dIkiIFSvl%2bVDnbCzW%2bESVJe9aYuffk%2b1XvvTtcxpoUzQMvcoa7fXhrcJtb%2foZWLGj3sWOrGA3D1gnMuuoSnRqH8qsHTqP2BkFiXm2E860qxocYKDVWNZkkbWGw1PwNsZe3U9qO1fyDkmWpdljZf7hFZ9eS9bauneNBqKw7H6IGl",
|
|
||||||
"expirationTime": null,
|
|
||||||
"keys": {
|
|
||||||
"p256dh": "BOG4r91J0rFq8YveeCMv4E9zAIOpqyAadsqAQ0R0yWSKVCUE06gMGB52ofsCNEhtBH3_lYs9Uf0ecT-iVGFeziA",
|
|
||||||
"auth": "yNEWaUfEYLWZlyBNdZIDrg"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"jrz6Rg": {
|
|
||||||
"endpoint": "https://wns2-pn1p.notify.windows.com/w/?token=BQYAAAACsvYS2nXaGg2g2jLcjLjp4bwznS9iOrr3BCN%2bvTCn%2fHqOyzKJSI3IqeLRM0klolKhnswEn0YOYDgvEnAW0bJRJsSW8nmg0y8RZoY5%2bGnVGqv8VXoGafhDTJOb5%2bKfgRRz01qtOW2qQogWRj3dRsVw%2fEw6dliZOl3SC3eBfgOg5dIkiIFSvl%2bVDnbCzW%2bESVJe9aYuffk%2b1XvvTtcxpoUzQMvcoa7fXhrcJtb%2foZWLGj3sWOrGA3D1gnMuuoSnRqH8qsHTqP2BkFiXm2E860qxocYKDVWNZkkbWGw1PwNsZe3U9qO1fyDkmWpdljZf7hFZ9eS9bauneNBqKw7H6IGl",
|
|
||||||
"expirationTime": null,
|
|
||||||
"keys": {
|
|
||||||
"p256dh": "BOG4r91J0rFq8YveeCMv4E9zAIOpqyAadsqAQ0R0yWSKVCUE06gMGB52ofsCNEhtBH3_lYs9Uf0ecT-iVGFeziA",
|
|
||||||
"auth": "yNEWaUfEYLWZlyBNdZIDrg"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
8
dist/ba_root/mods/playersData/whitelist.json
vendored
8
dist/ba_root/mods/playersData/whitelist.json
vendored
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"pb-IF4VAk4a": [
|
|
||||||
"\ue030Server127089"
|
|
||||||
],
|
|
||||||
"pb-IF5VVxI5Fg==": [
|
|
||||||
"\ue020id just to remeber pbid here"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
24
dist/ba_root/mods/plugins/__init__.py
vendored
24
dist/ba_root/mods/plugins/__init__.py
vendored
|
|
@ -1,24 +0,0 @@
|
||||||
"""Common bits of functionality shared between all efro projects.
|
|
||||||
|
|
||||||
Things in here should be hardened, highly type-safe, and well-covered by unit
|
|
||||||
tests since they are widely used in live client and server code.
|
|
||||||
|
|
||||||
license : MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# (see https://ballistica.net/wiki/meta-tag-system)
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export plugin
|
|
||||||
class Init(babase.Plugin): # pylint: disable=too-few-public-methods
|
|
||||||
"""Initializes all of the plugins in the directory."""
|
|
||||||
579
dist/ba_root/mods/plugins/auto_stunt.py
vendored
579
dist/ba_root/mods/plugins/auto_stunt.py
vendored
|
|
@ -1,579 +0,0 @@
|
||||||
# ba_meta require api 8
|
|
||||||
# AutoStunt mod by - Mr.Smoothy x Rikko
|
|
||||||
# https://discord.gg/ucyaesh
|
|
||||||
# https://bombsquad.ga
|
|
||||||
# Dont modify redistribute this plugin , if want to use features of this plugin in your mod write logic in seprate file
|
|
||||||
# and import this as module.
|
|
||||||
# If want to contribute in this original module, raise PR on github https://github.com/bombsquad-community/plugin-manager
|
|
||||||
|
|
||||||
import json
|
|
||||||
import math
|
|
||||||
import os
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
import bascenev1lib
|
|
||||||
import bauiv1 as bui
|
|
||||||
from bascenev1lib.actor import spaz
|
|
||||||
from bascenev1lib.actor.image import Image
|
|
||||||
from bascenev1lib.actor.powerupbox import PowerupBoxFactory
|
|
||||||
from bascenev1lib.actor.spazfactory import SpazFactory
|
|
||||||
from bascenev1lib.actor.text import Text
|
|
||||||
from bascenev1lib.game.elimination import EliminationGame
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
CONTROLS_CENTER = (0, 0)
|
|
||||||
CONTROLS_SCALE = 1
|
|
||||||
|
|
||||||
BASE_STUNTS_DIRECTORY = os.path.join(_babase.env()["python_directory_user"],
|
|
||||||
"CustomStunts")
|
|
||||||
PLAYERS_STUNT_INFO = {}
|
|
||||||
|
|
||||||
STUNT_CACHE = {}
|
|
||||||
original_on_begin = bs._activity.Activity.on_begin
|
|
||||||
original_chatmessage = bs.chatmessage
|
|
||||||
|
|
||||||
|
|
||||||
class ControlsUI:
|
|
||||||
|
|
||||||
def on_jump_press(activity):
|
|
||||||
activity._jump_image.node.color = list(
|
|
||||||
channel * 2 for channel in activity._jump_image.node.color[:3]) + [
|
|
||||||
1]
|
|
||||||
|
|
||||||
def on_jump_release(activity):
|
|
||||||
activity._jump_image.node.color = list(
|
|
||||||
channel * 0.5 for channel in
|
|
||||||
activity._jump_image.node.color[:3]) + [1]
|
|
||||||
|
|
||||||
def on_pickup_press(activity):
|
|
||||||
activity._pickup_image.node.color = list(
|
|
||||||
channel * 2 for channel in
|
|
||||||
activity._pickup_image.node.color[:3]) + [1]
|
|
||||||
|
|
||||||
def on_pickup_release(activity):
|
|
||||||
activity._pickup_image.node.color = list(
|
|
||||||
channel * 0.5 for channel in
|
|
||||||
activity._pickup_image.node.color[:3]) + [1]
|
|
||||||
|
|
||||||
def on_punch_press(activity):
|
|
||||||
activity._punch_image.node.color = list(
|
|
||||||
channel * 2 for channel in activity._punch_image.node.color[:3]) + [
|
|
||||||
1]
|
|
||||||
|
|
||||||
def on_punch_release(activity):
|
|
||||||
activity._punch_image.node.color = list(
|
|
||||||
channel * 0.5 for channel in
|
|
||||||
activity._punch_image.node.color[:3]) + [1]
|
|
||||||
|
|
||||||
def on_bomb_press(activity):
|
|
||||||
activity._bomb_image.node.color = list(
|
|
||||||
channel * 2 for channel in activity._bomb_image.node.color[:3]) + [
|
|
||||||
1]
|
|
||||||
|
|
||||||
def on_bomb_release(activity):
|
|
||||||
activity._bomb_image.node.color = list(
|
|
||||||
channel * 0.5 for channel in
|
|
||||||
activity._bomb_image.node.color[:3]) + [1]
|
|
||||||
|
|
||||||
def on_move_ud(activity, value):
|
|
||||||
activity.set_stick_image_position(activity,
|
|
||||||
x=activity.stick_image_position_x,
|
|
||||||
y=value)
|
|
||||||
|
|
||||||
def on_move_lr(activity, value):
|
|
||||||
activity.set_stick_image_position(activity, x=value,
|
|
||||||
y=activity.stick_image_position_y)
|
|
||||||
|
|
||||||
def display(activity):
|
|
||||||
activity._jump_image.node.color = list(
|
|
||||||
activity._jump_image.node.color[:3]) + [1]
|
|
||||||
activity._pickup_image.node.color = list(
|
|
||||||
activity._pickup_image.node.color[:3]) + [1]
|
|
||||||
activity._punch_image.node.color = list(
|
|
||||||
activity._punch_image.node.color[:3]) + [1]
|
|
||||||
activity._bomb_image.node.color = list(
|
|
||||||
activity._bomb_image.node.color[:3]) + [1]
|
|
||||||
activity._stick_base_image.opacity = 1.0
|
|
||||||
activity._stick_nub_image.opacity = 1.0
|
|
||||||
|
|
||||||
def hide(activity):
|
|
||||||
activity._jump_image.node.color = list(
|
|
||||||
activity._jump_image.node.color[:3]) + [0]
|
|
||||||
activity._pickup_image.node.color = list(
|
|
||||||
activity._pickup_image.node.color[:3]) + [0]
|
|
||||||
activity._punch_image.node.color = list(
|
|
||||||
activity._punch_image.node.color[:3]) + [0]
|
|
||||||
activity._bomb_image.node.color = list(
|
|
||||||
activity._bomb_image.node.color[:3]) + [0]
|
|
||||||
activity._stick_base_image.opacity = 0.0
|
|
||||||
activity._stick_nub_image.opacity = 0.0
|
|
||||||
|
|
||||||
|
|
||||||
CONTROLS_UI_MAP = {
|
|
||||||
"JUMP_PRESS": ControlsUI.on_jump_press,
|
|
||||||
"JUMP_RELEASE": ControlsUI.on_jump_release,
|
|
||||||
"PICKUP_PRESS": ControlsUI.on_pickup_press,
|
|
||||||
"PICKUP_RELEASE": ControlsUI.on_pickup_release,
|
|
||||||
"PUNCH_PRESS": ControlsUI.on_punch_press,
|
|
||||||
"PUNCH_RELEASE": ControlsUI.on_punch_release,
|
|
||||||
"BOMB_PRESS": ControlsUI.on_bomb_press,
|
|
||||||
"BOMB_RELEASE": ControlsUI.on_bomb_release,
|
|
||||||
"UP_DOWN": ControlsUI.on_move_ud,
|
|
||||||
"LEFT_RIGHT": ControlsUI.on_move_lr
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class NewSpaz(bascenev1lib.actor.spaz.Spaz):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.move_map = {
|
|
||||||
"UP_DOWN": self.on_move_up_down,
|
|
||||||
"LEFT_RIGHT": self.on_move_left_right,
|
|
||||||
"HOLD_POSITION": self.on_hold_position_press,
|
|
||||||
"HOLD_RELEASE": self.on_hold_position_release,
|
|
||||||
"JUMP_PRESS": self.on_jump_press,
|
|
||||||
"JUMP_RELEASE": self.on_jump_release,
|
|
||||||
"PICKUP_PRESS": self.on_pickup_press,
|
|
||||||
"PICKUP_RELEASE": self.on_pickup_release,
|
|
||||||
"PUNCH_PRESS": self.on_punch_press,
|
|
||||||
"PUNCH_RELEASE": self.on_punch_release,
|
|
||||||
"BOMB_PRESS": self.on_bomb_press,
|
|
||||||
"BOMB_RELEASE": self.on_bomb_release,
|
|
||||||
"RUN": self.on_run,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class NewPlayerSpaz(bascenev1lib.actor.playerspaz.PlayerSpaz):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.move_map = {
|
|
||||||
"UP_DOWN": self.on_move_up_down,
|
|
||||||
"LEFT_RIGHT": self.on_move_left_right,
|
|
||||||
"HOLD_POSITION": self.on_hold_position_press,
|
|
||||||
"HOLD_RELEASE": self.on_hold_position_release,
|
|
||||||
"JUMP_PRESS": self.on_jump_press,
|
|
||||||
"JUMP_RELEASE": self.on_jump_release,
|
|
||||||
"PICKUP_PRESS": self.on_pickup_press,
|
|
||||||
"PICKUP_RELEASE": self.on_pickup_release,
|
|
||||||
"PUNCH_PRESS": self.on_punch_press,
|
|
||||||
"PUNCH_RELEASE": self.on_punch_release,
|
|
||||||
"BOMB_PRESS": self.on_bomb_press,
|
|
||||||
"BOMB_RELEASE": self.on_bomb_release,
|
|
||||||
"RUN": self.on_run,
|
|
||||||
}
|
|
||||||
self.mirror_spaz = []
|
|
||||||
self.source_player.in_replay = False
|
|
||||||
self.source_player.mirror_mode = False
|
|
||||||
|
|
||||||
def _handle_action(self, action, value: Optional[float] = None) -> None:
|
|
||||||
if self.source_player.sessionplayer in PLAYERS_STUNT_INFO:
|
|
||||||
PLAYERS_STUNT_INFO[self.source_player.sessionplayer].append({
|
|
||||||
"time": bs.time() - self.source_player.recording_start_time,
|
|
||||||
"move": {
|
|
||||||
"action": action,
|
|
||||||
"value": value,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
elif self.source_player.in_replay:
|
|
||||||
ui_activation = CONTROLS_UI_MAP.get(action)
|
|
||||||
if ui_activation:
|
|
||||||
if action in ["UP_DOWN", "LEFT_RIGHT"]:
|
|
||||||
ui_activation(self.source_player.actor._activity(), value)
|
|
||||||
else:
|
|
||||||
ui_activation(self.source_player.actor._activity())
|
|
||||||
elif self.source_player.mirror_mode:
|
|
||||||
for mspaz in self.mirror_spaz:
|
|
||||||
if mspaz and mspaz.node.exists():
|
|
||||||
if action in ["UP_DOWN", "LEFT_RIGHT", "RUN"]:
|
|
||||||
mspaz.move_map[action](value)
|
|
||||||
else:
|
|
||||||
mspaz.move_map[action]()
|
|
||||||
|
|
||||||
def on_move_up_down(self, value: float, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("UP_DOWN", value)
|
|
||||||
super().on_move_up_down(value, *args, **kwargs)
|
|
||||||
|
|
||||||
def on_move_left_right(self, value: float, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("LEFT_RIGHT", value)
|
|
||||||
super().on_move_left_right(value, *args, **kwargs)
|
|
||||||
|
|
||||||
def on_hold_position_press(self, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("HOLD_POSITION")
|
|
||||||
super().on_hold_position_press(*args, **kwargs)
|
|
||||||
|
|
||||||
def on_hold_position_release(self, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("HOLD_RELEASE")
|
|
||||||
super().on_hold_position_release(*args, **kwargs)
|
|
||||||
|
|
||||||
def on_jump_press(self, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("JUMP_PRESS")
|
|
||||||
super().on_jump_press(*args, **kwargs)
|
|
||||||
|
|
||||||
def on_jump_release(self, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("JUMP_RELEASE")
|
|
||||||
super().on_jump_release(*args, **kwargs)
|
|
||||||
|
|
||||||
def on_pickup_press(self, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("PICKUP_PRESS")
|
|
||||||
super().on_pickup_press(*args, **kwargs)
|
|
||||||
|
|
||||||
def on_pickup_release(self, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("PICKUP_RELEASE")
|
|
||||||
super().on_pickup_release(*args, **kwargs)
|
|
||||||
|
|
||||||
def on_punch_press(self, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("PUNCH_PRESS")
|
|
||||||
super().on_punch_press(*args, **kwargs)
|
|
||||||
|
|
||||||
def on_punch_release(self, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("PUNCH_RELEASE")
|
|
||||||
super().on_punch_release(*args, **kwargs)
|
|
||||||
|
|
||||||
def on_bomb_press(self, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("BOMB_PRESS")
|
|
||||||
super().on_bomb_press(*args, **kwargs)
|
|
||||||
|
|
||||||
def on_bomb_release(self, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("BOMB_RELEASE")
|
|
||||||
super().on_bomb_release(*args, **kwargs)
|
|
||||||
|
|
||||||
def on_run(self, value: float, *args, **kwargs) -> None:
|
|
||||||
self._handle_action("RUN", value)
|
|
||||||
super().on_run(value, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_player_replay_end(player):
|
|
||||||
player.in_replay = False
|
|
||||||
ControlsUI.hide(player.actor._activity())
|
|
||||||
|
|
||||||
|
|
||||||
def get_player_from_client_id(client_id, activity=None):
|
|
||||||
activity = activity or _babase.get_foreground_host_activity()
|
|
||||||
for player in activity.players:
|
|
||||||
if player.sessionplayer.inputdevice.client_id == client_id:
|
|
||||||
return player
|
|
||||||
raise bs.SessionPlayerNotFound()
|
|
||||||
|
|
||||||
|
|
||||||
def mirror(clieid):
|
|
||||||
player = get_player_from_client_id(clieid)
|
|
||||||
spawn_mirror_spaz(player)
|
|
||||||
|
|
||||||
|
|
||||||
def capture(player):
|
|
||||||
with babase.Context(player.actor._activity()):
|
|
||||||
player.recording_start_time = bs.time()
|
|
||||||
PLAYERS_STUNT_INFO[player.sessionplayer] = []
|
|
||||||
|
|
||||||
|
|
||||||
def save(player, stunt_name):
|
|
||||||
stunt_path = f"{os.path.join(BASE_STUNTS_DIRECTORY, stunt_name)}.json"
|
|
||||||
os.makedirs(BASE_STUNTS_DIRECTORY, exist_ok=True)
|
|
||||||
with open(stunt_path, "w") as fout:
|
|
||||||
json.dump(PLAYERS_STUNT_INFO[player.sessionplayer], fout, indent=2)
|
|
||||||
del PLAYERS_STUNT_INFO[player.sessionplayer]
|
|
||||||
|
|
||||||
|
|
||||||
def replay(player, stunt_name):
|
|
||||||
stunt_path = f"{os.path.join(BASE_STUNTS_DIRECTORY, stunt_name)}.json"
|
|
||||||
if stunt_name in STUNT_CACHE:
|
|
||||||
stunt = STUNT_CACHE[stunt_name]
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
with open(stunt_path, "r") as fin:
|
|
||||||
stunt = json.load(fin)
|
|
||||||
STUNT_CACHE[stunt_name] = stunt
|
|
||||||
except:
|
|
||||||
bs.broadcastmessage(f"{stunt_name} doesn't exists")
|
|
||||||
return
|
|
||||||
player.in_replay = True
|
|
||||||
with babase.Context(player.actor._activity()):
|
|
||||||
ControlsUI.display(player.actor._activity())
|
|
||||||
for move in stunt:
|
|
||||||
value = move["move"]["value"]
|
|
||||||
if value is None:
|
|
||||||
bs.timer(
|
|
||||||
move["time"],
|
|
||||||
babase.Call(player.actor.move_map[move["move"]["action"]])
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
bs.timer(
|
|
||||||
move["time"],
|
|
||||||
babase.Call(player.actor.move_map[move["move"]["action"]],
|
|
||||||
move["move"]["value"])
|
|
||||||
)
|
|
||||||
last_move_time = move["time"]
|
|
||||||
time_to_hide_controls = last_move_time + 1
|
|
||||||
bs.timer(time_to_hide_controls,
|
|
||||||
babase.Call(handle_player_replay_end, player))
|
|
||||||
|
|
||||||
|
|
||||||
def spawn_mirror_spaz(player):
|
|
||||||
player.mirror_mode = True
|
|
||||||
with babase.Context(player.actor._activity()):
|
|
||||||
bot = spaz.Spaz(player.color, player.highlight,
|
|
||||||
character=player.character).autoretain()
|
|
||||||
bot.handlemessage(babase.StandMessage(
|
|
||||||
(player.actor.node.position[0], player.actor.node.position[1],
|
|
||||||
player.actor.node.position[2] + 1), 93))
|
|
||||||
bot.node.name = player.actor.node.name
|
|
||||||
bot.node.name_color = player.actor.node.name_color
|
|
||||||
player.actor.mirror_spaz.append(bot)
|
|
||||||
|
|
||||||
|
|
||||||
def ghost(player, stunt_name):
|
|
||||||
stunt_path = f"{os.path.join(BASE_STUNTS_DIRECTORY, stunt_name)}.json"
|
|
||||||
if stunt_name in STUNT_CACHE:
|
|
||||||
stunt = STUNT_CACHE[stunt_name]
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
with open(stunt_path, "r") as fin:
|
|
||||||
stunt = json.load(fin)
|
|
||||||
STUNT_CACHE[stunt_name] = stunt
|
|
||||||
except:
|
|
||||||
bs.broadcastmessage(f"{stunt_name} doesn't exists")
|
|
||||||
return
|
|
||||||
player.in_replay = True
|
|
||||||
|
|
||||||
with player.actor._activity().context:
|
|
||||||
bot = spaz.Spaz((1, 0, 0), character="Spaz").autoretain()
|
|
||||||
bot.handlemessage(bs.StandMessage(player.actor.node.position, 93))
|
|
||||||
give_ghost_power(bot)
|
|
||||||
ControlsUI.display(player.actor._activity())
|
|
||||||
for move in stunt:
|
|
||||||
value = move["move"]["value"]
|
|
||||||
if value is None:
|
|
||||||
bs.timer(
|
|
||||||
move["time"],
|
|
||||||
babase.Call(bot.move_map[move["move"]["action"]])
|
|
||||||
)
|
|
||||||
ui_activation = CONTROLS_UI_MAP.get(move["move"]["action"])
|
|
||||||
if ui_activation:
|
|
||||||
bs.timer(
|
|
||||||
move["time"],
|
|
||||||
babase.Call(ui_activation, player.actor._activity())
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
bs.timer(
|
|
||||||
move["time"],
|
|
||||||
babase.Call(bot.move_map[move["move"]["action"]],
|
|
||||||
move["move"]["value"])
|
|
||||||
)
|
|
||||||
ui_activation = CONTROLS_UI_MAP.get(move["move"]["action"])
|
|
||||||
|
|
||||||
if ui_activation:
|
|
||||||
bs.timer(
|
|
||||||
move["time"],
|
|
||||||
babase.Call(ui_activation, player.actor._activity(),
|
|
||||||
move["move"]["value"])
|
|
||||||
)
|
|
||||||
last_move_time = move["time"]
|
|
||||||
time_to_hide_controls = last_move_time + 1
|
|
||||||
bs.timer(time_to_hide_controls,
|
|
||||||
babase.Call(handle_player_replay_end, player))
|
|
||||||
bs.timer(time_to_hide_controls, babase.Call(bot.node.delete))
|
|
||||||
|
|
||||||
|
|
||||||
def give_ghost_power(spaz):
|
|
||||||
spaz.node.invincible = True
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
factory = SpazFactory.get()
|
|
||||||
ghost = bs.Material()
|
|
||||||
# smoothy hecks
|
|
||||||
ghost.add_actions(
|
|
||||||
conditions=(('they_have_material', factory.spaz_material), 'or',
|
|
||||||
('they_have_material', shared.player_material), 'or',
|
|
||||||
('they_have_material', shared.attack_material), 'or',
|
|
||||||
('they_have_material', shared.pickup_material), 'or',
|
|
||||||
('they_have_material',
|
|
||||||
PowerupBoxFactory.get().powerup_accept_material)),
|
|
||||||
actions=(
|
|
||||||
('modify_part_collision', 'collide', False),
|
|
||||||
('modify_part_collision', 'physical', False)
|
|
||||||
))
|
|
||||||
mats = list(spaz.node.materials)
|
|
||||||
roller = list(spaz.node.roller_materials)
|
|
||||||
ext = list(spaz.node.extras_material)
|
|
||||||
pick = list(spaz.node.pickup_materials)
|
|
||||||
punch = list(spaz.node.punch_materials)
|
|
||||||
|
|
||||||
mats.append(ghost)
|
|
||||||
roller.append(ghost)
|
|
||||||
ext.append(ghost)
|
|
||||||
pick.append(ghost)
|
|
||||||
punch.append(ghost)
|
|
||||||
|
|
||||||
spaz.node.materials = tuple(mats)
|
|
||||||
spaz.node.roller_materials = tuple(roller)
|
|
||||||
spaz.node.extras_material = tuple(ext)
|
|
||||||
spaz.node.pickup_materials = tuple(pick)
|
|
||||||
spaz.node.punch_materials = tuple(pick)
|
|
||||||
|
|
||||||
|
|
||||||
def new_chatmessage(msg):
|
|
||||||
if not msg.startswith("*"):
|
|
||||||
return original_chatmessage(msg)
|
|
||||||
|
|
||||||
stripped_msg = msg[1:]
|
|
||||||
msg_splits = stripped_msg.split(maxsplit=3)
|
|
||||||
command = msg_splits[0]
|
|
||||||
|
|
||||||
client_id = -1
|
|
||||||
player = get_player_from_client_id(client_id)
|
|
||||||
|
|
||||||
if command == "start":
|
|
||||||
capture(player)
|
|
||||||
bs.chatmessage("Recording started for {}.".format(
|
|
||||||
player.getname(),
|
|
||||||
))
|
|
||||||
return original_chatmessage(msg)
|
|
||||||
|
|
||||||
stunt_name = " ".join(msg_splits[1:])
|
|
||||||
|
|
||||||
if command == "save":
|
|
||||||
if len(msg_splits) < 2:
|
|
||||||
bs.broadcastmessage("Enter name of stunt eg : *save bombjump")
|
|
||||||
return original_chatmessage(msg)
|
|
||||||
save(player, stunt_name)
|
|
||||||
bs.chatmessage('Recording "{}" by {} saved.'.format(
|
|
||||||
stunt_name,
|
|
||||||
player.getname(),
|
|
||||||
))
|
|
||||||
elif command == "stunt":
|
|
||||||
if len(msg_splits) < 2:
|
|
||||||
bs.broadcastmessage("Enter name of stunt eg : *stunt bombjump")
|
|
||||||
return original_chatmessage(msg)
|
|
||||||
replay(player, stunt_name)
|
|
||||||
bs.chatmessage('Replaying "{}" on {}.'.format(
|
|
||||||
stunt_name,
|
|
||||||
player.getname(),
|
|
||||||
))
|
|
||||||
elif command == "learn":
|
|
||||||
if len(msg_splits) < 2:
|
|
||||||
bs.broadcastmessage("Enter name of stunt eg : *learn bombjump")
|
|
||||||
return original_chatmessage(msg)
|
|
||||||
ghost(player, stunt_name)
|
|
||||||
bs.chatmessage('Replaying "{}" on {}.'.format(
|
|
||||||
stunt_name,
|
|
||||||
player.getname(),
|
|
||||||
))
|
|
||||||
elif command == "mirror":
|
|
||||||
spawn_mirror_spaz(player)
|
|
||||||
return original_chatmessage(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def set_stick_image_position(self, x: float, y: float) -> None:
|
|
||||||
# Clamp this to a circle.
|
|
||||||
len_squared = x * x + y * y
|
|
||||||
if len_squared > 1.0:
|
|
||||||
length = math.sqrt(len_squared)
|
|
||||||
mult = 1.0 / length
|
|
||||||
x *= mult
|
|
||||||
y *= mult
|
|
||||||
|
|
||||||
self.stick_image_position_x = x
|
|
||||||
self.stick_image_position_y = y
|
|
||||||
offs = 50.0
|
|
||||||
assert self._scale is not None
|
|
||||||
p = [
|
|
||||||
self._stick_nub_position[0] + x * offs * 0.6,
|
|
||||||
self._stick_nub_position[1] + y * offs * 0.6
|
|
||||||
]
|
|
||||||
c = list(self._stick_nub_image_color)
|
|
||||||
if abs(x) > 0.1 or abs(y) > 0.1:
|
|
||||||
c[0] *= 2.0
|
|
||||||
c[1] *= 4.0
|
|
||||||
c[2] *= 2.0
|
|
||||||
assert self._stick_nub_image is not None
|
|
||||||
self._stick_nub_image.position = p
|
|
||||||
self._stick_nub_image.color = c
|
|
||||||
c = list(self._stick_base_image_color)
|
|
||||||
if abs(x) > 0.1 or abs(y) > 0.1:
|
|
||||||
c[0] *= 1.5
|
|
||||||
c[1] *= 1.5
|
|
||||||
c[2] *= 1.5
|
|
||||||
assert self._stick_base_image is not None
|
|
||||||
self._stick_base_image.color = c
|
|
||||||
|
|
||||||
|
|
||||||
def on_begin(self, *args, **kwargs) -> None:
|
|
||||||
self._jump_image = Image(
|
|
||||||
bs.gettexture('buttonJump'),
|
|
||||||
position=(385, 160),
|
|
||||||
scale=(50, 50),
|
|
||||||
color=[0.1, 0.45, 0.1, 0]
|
|
||||||
)
|
|
||||||
self._pickup_image = Image(
|
|
||||||
bs.gettexture('buttonPickUp'),
|
|
||||||
position=(385, 240),
|
|
||||||
scale=(50, 50),
|
|
||||||
color=[0, 0.35, 0, 0]
|
|
||||||
)
|
|
||||||
self._punch_image = Image(
|
|
||||||
bs.gettexture('buttonPunch'),
|
|
||||||
position=(345, 200),
|
|
||||||
scale=(50, 50),
|
|
||||||
color=[0.45, 0.45, 0, 0]
|
|
||||||
)
|
|
||||||
self._bomb_image = Image(
|
|
||||||
bs.gettexture('buttonBomb'),
|
|
||||||
position=(425, 200),
|
|
||||||
scale=(50, 50),
|
|
||||||
color=[0.45, 0.1, 0.1, 0]
|
|
||||||
)
|
|
||||||
self.stick_image_position_x = self.stick_image_position_y = 0.0
|
|
||||||
self._stick_base_position = p = (-328, 200)
|
|
||||||
self._stick_base_image_color = c2 = (0.25, 0.25, 0.25, 1.0)
|
|
||||||
self._stick_base_image = bs.newnode(
|
|
||||||
'image',
|
|
||||||
attrs={
|
|
||||||
'texture': bs.gettexture('nub'),
|
|
||||||
'absolute_scale': True,
|
|
||||||
'vr_depth': -40,
|
|
||||||
'position': p,
|
|
||||||
'scale': (220.0 * 0.6, 220.0 * 0.6),
|
|
||||||
'color': c2
|
|
||||||
})
|
|
||||||
self._stick_nub_position = p = (-328, 200)
|
|
||||||
self._stick_nub_image_color = c3 = (0.4, 0.4, 0.4, 1.0)
|
|
||||||
self._stick_nub_image = bs.newnode('image',
|
|
||||||
attrs={
|
|
||||||
'texture': bs.gettexture('nub'),
|
|
||||||
'absolute_scale': True,
|
|
||||||
'position': p,
|
|
||||||
'scale': (110 * 0.6, 110 * 0.66),
|
|
||||||
'color': c3
|
|
||||||
})
|
|
||||||
self._stick_base_image.opacity = 0.0
|
|
||||||
self._stick_nub_image.opacity = 0.0
|
|
||||||
self.set_stick_image_position = set_stick_image_position
|
|
||||||
return original_on_begin(self, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export plugin
|
|
||||||
class byHeySmoothy(babase.Plugin):
|
|
||||||
def on_app_running(self):
|
|
||||||
bui.set_party_icon_always_visible(True)
|
|
||||||
bs._activity.Activity.on_begin = on_begin
|
|
||||||
# _babase.chatmessage = new_chatmessage
|
|
||||||
bascenev1lib.actor.playerspaz.PlayerSpaz = NewPlayerSpaz
|
|
||||||
bascenev1lib.actor.spaz.Spaz = NewSpaz
|
|
||||||
|
|
||||||
|
|
||||||
# lets define a sample elimination game that can use super power of this plugin
|
|
||||||
|
|
||||||
from plugins import auto_stunt
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export bascenev1.GameActivity
|
|
||||||
class BroEliminaition(EliminationGame):
|
|
||||||
name = 'BroElimination'
|
|
||||||
description = 'Elimination Game with dual character control'
|
|
||||||
|
|
||||||
def spawn_player(self, player) -> bs.Actor:
|
|
||||||
super().spawn_player(player)
|
|
||||||
auto_stunt.spawn_mirror_spaz(player)
|
|
||||||
298
dist/ba_root/mods/plugins/bcs_plugin.py
vendored
298
dist/ba_root/mods/plugins/bcs_plugin.py
vendored
|
|
@ -1,298 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# coding: utf-8
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# from gunicorn.app.base import BaseApplication
|
|
||||||
# from gunicorn.workers import ggevent as gevent_worker
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
from functools import wraps
|
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
from flask import Flask, request, jsonify
|
|
||||||
|
|
||||||
# import uvicorn
|
|
||||||
from . import bombsquad_service
|
|
||||||
|
|
||||||
os.environ['FLASK_APP'] = 'bombsquadflaskapi.py'
|
|
||||||
os.environ['FLASK_ENV'] = 'development'
|
|
||||||
log = logging.getLogger('werkzeug')
|
|
||||||
log.setLevel(logging.ERROR)
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config["DEBUG"] = False
|
|
||||||
SECRET_KEY = 'default'
|
|
||||||
|
|
||||||
|
|
||||||
@app.after_request
|
|
||||||
def add_cors_headers(response):
|
|
||||||
# Allow requests from any origin
|
|
||||||
response.headers['Access-Control-Allow-Origin'] = '*'
|
|
||||||
# Allow specific headers
|
|
||||||
response.headers[
|
|
||||||
'Access-Control-Allow-Headers'] = 'Content-Type,Authorization,Secret-Key'
|
|
||||||
# Allow specific HTTP methods
|
|
||||||
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
def check_admin(func):
|
|
||||||
@wraps(func)
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
if "Secret-Key" not in request.headers or request.headers[
|
|
||||||
"Secret-Key"] != SECRET_KEY:
|
|
||||||
return jsonify({"message": "Invalid secret key provided."}), 401
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET'])
|
|
||||||
def home():
|
|
||||||
return '''Nothing here :)'''
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/live-stats', methods=['GET'])
|
|
||||||
def get_live_stats():
|
|
||||||
return jsonify(bombsquad_service.get_stats()), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/top-200', methods=['GET'])
|
|
||||||
def get_top200():
|
|
||||||
return jsonify(bombsquad_service.get_top_200()), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/subscribe', methods=['POST'])
|
|
||||||
def subscribe_player():
|
|
||||||
try:
|
|
||||||
data = request.get_json()
|
|
||||||
bombsquad_service.subscribe_player(
|
|
||||||
data["subscription"], data["player_id"], data["name"])
|
|
||||||
response = {
|
|
||||||
'message': f'Subscribed {data["name"]} successfully , will send confirmation notification to test'}
|
|
||||||
return jsonify(response), 201
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify(
|
|
||||||
{'message': 'Error processing request', 'error': str(e)}), 400
|
|
||||||
|
|
||||||
|
|
||||||
# ============ Admin only =========
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/login', methods=['POST'])
|
|
||||||
@check_admin
|
|
||||||
def login():
|
|
||||||
return jsonify({"message": "Successful"}), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/current-leaderboard', methods=['GET'])
|
|
||||||
@check_admin
|
|
||||||
def get_complete_leaderboard():
|
|
||||||
return jsonify(bombsquad_service.get_complete_leaderboard()), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/server-settings', methods=['GET'])
|
|
||||||
@check_admin
|
|
||||||
def get_server_settings():
|
|
||||||
return jsonify(bombsquad_service.get_server_settings()), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/roles', methods=['GET'])
|
|
||||||
@check_admin
|
|
||||||
def get_roles():
|
|
||||||
return jsonify(bombsquad_service.get_roles()), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/roles', methods=['POST'])
|
|
||||||
@check_admin
|
|
||||||
def update_roles():
|
|
||||||
try:
|
|
||||||
data = request.get_json()
|
|
||||||
bombsquad_service.update_roles(data)
|
|
||||||
response = {
|
|
||||||
'message': 'Roles updated successfully'}
|
|
||||||
return jsonify(response), 201
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify(
|
|
||||||
{'message': 'Error processing request', 'error': str(e)}), 400
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/perks', methods=['GET'])
|
|
||||||
@check_admin
|
|
||||||
def get_perks():
|
|
||||||
return jsonify(bombsquad_service.get_perks()), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/perks', methods=['POST'])
|
|
||||||
@check_admin
|
|
||||||
def update_perks():
|
|
||||||
try:
|
|
||||||
data = request.get_json()
|
|
||||||
bombsquad_service.update_perks(data)
|
|
||||||
response = {
|
|
||||||
'message': 'Custom perks updated successfully'}
|
|
||||||
return jsonify(response), 201
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify(
|
|
||||||
{'message': 'Error processing request', 'error': str(e)}), 400
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/server-settings', methods=['POST'])
|
|
||||||
@check_admin
|
|
||||||
def update_server_settings():
|
|
||||||
try:
|
|
||||||
data = request.get_json()
|
|
||||||
bombsquad_service.update_server_settings(data)
|
|
||||||
response = {
|
|
||||||
'message': 'Settings updated successfully, server may need restart'}
|
|
||||||
return jsonify(response), 201
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify(
|
|
||||||
{'message': 'Error processing request', 'error': str(e)}), 400
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/db-list', methods=['GET'])
|
|
||||||
@check_admin
|
|
||||||
def fetch_dB_list():
|
|
||||||
key = request.args.get('type')
|
|
||||||
if key is None:
|
|
||||||
return "type required", 400
|
|
||||||
if key == "logs":
|
|
||||||
return jsonify(bombsquad_service.get_logs_db_list()), 200
|
|
||||||
elif key == "players":
|
|
||||||
return jsonify(bombsquad_service.get_profiles_db_list()), 200
|
|
||||||
else:
|
|
||||||
return jsonify({"message": "Invalid db type"}), 400
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/search-logs', methods=['GET'])
|
|
||||||
@check_admin
|
|
||||||
def search_logs():
|
|
||||||
key = request.args.get('key')
|
|
||||||
db = request.args.get('db')
|
|
||||||
if key is None or db is None:
|
|
||||||
return jsonify({"message": "key and db required"}), 400
|
|
||||||
return jsonify(bombsquad_service.get_matching_logs(key, db)), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/search-player', methods=['GET'])
|
|
||||||
@check_admin
|
|
||||||
def search_players():
|
|
||||||
key = request.args.get('key')
|
|
||||||
db = request.args.get('db')
|
|
||||||
if key is None or db is None:
|
|
||||||
return jsonify({"message": "key and db required"}), 400
|
|
||||||
return jsonify(bombsquad_service.search_player_profile(key, db)), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/get-player-info', methods=['GET'])
|
|
||||||
@check_admin
|
|
||||||
def get_player():
|
|
||||||
account_id = request.args.get('account-id')
|
|
||||||
if account_id is None:
|
|
||||||
return jsonify({"message": "account-id required"}), 400
|
|
||||||
return jsonify(bombsquad_service.get_player_details(account_id)), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/update-player', methods=['POST'])
|
|
||||||
@check_admin
|
|
||||||
def update_player():
|
|
||||||
account_id = request.args.get('account-id')
|
|
||||||
action = request.args.get('action')
|
|
||||||
duration = int(request.args.get('duration'))
|
|
||||||
if account_id is None or action is None:
|
|
||||||
return "account-id and action required", 400
|
|
||||||
if action == "ban":
|
|
||||||
bombsquad_service.ban_player(account_id, duration)
|
|
||||||
elif action == "unban":
|
|
||||||
bombsquad_service.unban_player(account_id)
|
|
||||||
elif action == "mute":
|
|
||||||
bombsquad_service.mute_player(account_id, duration)
|
|
||||||
elif action == "unmute":
|
|
||||||
bombsquad_service.unmute_player(account_id)
|
|
||||||
elif action == "disable-kick-vote":
|
|
||||||
bombsquad_service.disable_kick_vote(account_id, duration)
|
|
||||||
elif action == "enable-kick-vote":
|
|
||||||
bombsquad_service.enable_kick_vote(account_id)
|
|
||||||
else:
|
|
||||||
return jsonify({"message": "Invalid Action"}), 400
|
|
||||||
return jsonify({"message": f"{action} done"}), 201
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/config', methods=['GET'])
|
|
||||||
@check_admin
|
|
||||||
def get_config():
|
|
||||||
return jsonify(bombsquad_service.get_server_config()), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/action', methods=['POST'])
|
|
||||||
@check_admin
|
|
||||||
def do_action():
|
|
||||||
action = request.args.get('action')
|
|
||||||
value = request.args.get('value')
|
|
||||||
bombsquad_service.do_action(action, value)
|
|
||||||
return jsonify({"message": f'{action} done'}), 200
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/config', methods=['POST'])
|
|
||||||
@check_admin
|
|
||||||
def update_server_config():
|
|
||||||
try:
|
|
||||||
data = request.get_json()
|
|
||||||
bombsquad_service.update_server_config(data)
|
|
||||||
response = {
|
|
||||||
'message': 'config updated successfully, server will restart'}
|
|
||||||
return jsonify(response), 201
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify(
|
|
||||||
{'message': 'Error processing request', 'error': str(e)}), 400
|
|
||||||
|
|
||||||
|
|
||||||
# from flask_asgi import FlaskASGI
|
|
||||||
# asgi_app = FlaskASGI(app)
|
|
||||||
|
|
||||||
|
|
||||||
# class FlaskApplication(BaseApplication):
|
|
||||||
# def __init__(self, app, options=None):
|
|
||||||
# self.options = options or {}
|
|
||||||
# self.application = app
|
|
||||||
# super(FlaskApplication, self).__init__()
|
|
||||||
|
|
||||||
# def load_config(self):
|
|
||||||
# config = {key: value for key, value in self.options.items(
|
|
||||||
# ) if key in self.cfg.settings and value is not None}
|
|
||||||
# for key, value in config.items():
|
|
||||||
# self.cfg.set(key.lower(), value)
|
|
||||||
|
|
||||||
# def load(self):
|
|
||||||
# return self.application
|
|
||||||
|
|
||||||
|
|
||||||
# def start_uvicorn():
|
|
||||||
# uvicorn.run("main:app", host='0.0.0.0', port=5000,
|
|
||||||
# reload=False, log_level="debug", workers=3, use_colors=True, no_signal=True)
|
|
||||||
# flask_run = _thread.start_new_thread(app.run, ("0.0.0.0", 5000, False))
|
|
||||||
|
|
||||||
def run_server():
|
|
||||||
from waitress import serve
|
|
||||||
serve(app, host="0.0.0.0", port=_babase.get_game_port())
|
|
||||||
|
|
||||||
|
|
||||||
def enable(password):
|
|
||||||
global SECRET_KEY
|
|
||||||
SECRET_KEY = password
|
|
||||||
t = Thread(target=run_server)
|
|
||||||
t.start()
|
|
||||||
# uvicorn_thread = threading.Thread(target=start_uvicorn)
|
|
||||||
# uvicorn_thread.start()
|
|
||||||
# options = {
|
|
||||||
# 'bind': '0.0.0.0:8000',
|
|
||||||
# 'workers': 4,
|
|
||||||
# 'worker_class': 'gevent'
|
|
||||||
# }
|
|
||||||
|
|
||||||
# flask_app = FlaskApplication(app, options)
|
|
||||||
# gevent_worker.GeventWorker(app.wsgi_app).init_process()
|
|
||||||
# flask_app.run()
|
|
||||||
290
dist/ba_root/mods/plugins/bombsquad_service.py
vendored
290
dist/ba_root/mods/plugins/bombsquad_service.py
vendored
|
|
@ -1,290 +0,0 @@
|
||||||
import babase
|
|
||||||
import _thread
|
|
||||||
import os
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
import setting
|
|
||||||
import yaml
|
|
||||||
from babase._gameactivity import GameActivity
|
|
||||||
from playersData import pdata
|
|
||||||
from serverData import serverdata
|
|
||||||
from stats import mystats
|
|
||||||
from typing import Type
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
from tools import servercheck, logger, notification_manager
|
|
||||||
|
|
||||||
stats = {}
|
|
||||||
leaderboard = {}
|
|
||||||
top200 = {}
|
|
||||||
vapidkeys = {}
|
|
||||||
serverinfo = {}
|
|
||||||
|
|
||||||
|
|
||||||
class BsDataThread(object):
|
|
||||||
def __init__(self):
|
|
||||||
global stats
|
|
||||||
stats["name"] = _babase.app.server._config.party_name
|
|
||||||
stats["discord"] = "https://discord.gg/ucyaesh"
|
|
||||||
stats["vapidKey"] = notification_manager.get_vapid_keys()["public_key"]
|
|
||||||
|
|
||||||
self.refresh_stats_cache_timer = bs.Timer(8, babase.Call(
|
|
||||||
self.refreshStats),
|
|
||||||
babase.TimeType.REAL,
|
|
||||||
repeat=True)
|
|
||||||
self.refresh_leaderboard_cache_timer = bs.Timer(10, babase.Call(
|
|
||||||
self.refreshLeaderboard), babase.TimeType.REAL, repeat=True)
|
|
||||||
|
|
||||||
def startThread(self):
|
|
||||||
_thread.start_new_thread(self.refreshLeaderboard, ())
|
|
||||||
|
|
||||||
def refreshLeaderboard(self):
|
|
||||||
global leaderboard
|
|
||||||
global top200
|
|
||||||
|
|
||||||
lboard = mystats.get_cached_stats()
|
|
||||||
leaderboard = lboard
|
|
||||||
sorted_data = sorted(lboard.values(), key=lambda x: x["rank"])
|
|
||||||
top_200_players = sorted_data[:200]
|
|
||||||
|
|
||||||
top200 = {player["aid"]: player for player in top_200_players}
|
|
||||||
|
|
||||||
def refreshStats(self):
|
|
||||||
global stats
|
|
||||||
global serverinfo
|
|
||||||
liveplayers = {}
|
|
||||||
nextMap = ''
|
|
||||||
currentMap = ''
|
|
||||||
|
|
||||||
for i in babase.internal.get_game_roster():
|
|
||||||
try:
|
|
||||||
liveplayers[i['account_id']] = {
|
|
||||||
'name': i['players'][0]['name_full'],
|
|
||||||
'client_id': i['client_id'],
|
|
||||||
'device_id': i['display_string']}
|
|
||||||
except:
|
|
||||||
liveplayers[i['account_id']] = {
|
|
||||||
'name': "<in-lobby>", 'client_id': i['client_id'],
|
|
||||||
'device_id': i['display_string']}
|
|
||||||
try:
|
|
||||||
nextMap = bs.get_foreground_host_session(
|
|
||||||
).get_next_game_description().evaluate()
|
|
||||||
|
|
||||||
current_game_spec = bs.get_foreground_host_session()._current_game_spec
|
|
||||||
gametype: Type[GameActivity] = current_game_spec['resolved_type']
|
|
||||||
|
|
||||||
currentMap = gametype.get_settings_display_string(
|
|
||||||
current_game_spec).evaluate()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
current_games = {'current': currentMap, 'next': nextMap}
|
|
||||||
# system={'cpu':"p.cpu_percent()",'ram':p.virtual_memory().percent}
|
|
||||||
system = {'cpu': "null", 'ram': 'null'}
|
|
||||||
stats['system'] = system
|
|
||||||
stats['roster'] = liveplayers
|
|
||||||
stats['chats'] = babase.internal.get_chat_messages()
|
|
||||||
stats['playlist'] = current_games
|
|
||||||
stats['teamInfo'] = self.getTeamInfo()
|
|
||||||
stats["sessionType"] = type(
|
|
||||||
babase.internal.get_foreground_host_session()).__name__
|
|
||||||
|
|
||||||
# print(self.getTeamInfo());
|
|
||||||
|
|
||||||
def getTeamInfo(self):
|
|
||||||
data = {}
|
|
||||||
session = bs.get_foreground_host_session()
|
|
||||||
if session:
|
|
||||||
teams = session.sessionteams
|
|
||||||
for team in teams:
|
|
||||||
data[str(team.id)] = {'name': team.name if isinstance(team.name,
|
|
||||||
str) else team.name.evaluate(),
|
|
||||||
'color': list(team.color),
|
|
||||||
'score': team.customdata['score'],
|
|
||||||
'players': []
|
|
||||||
}
|
|
||||||
for player in team.players:
|
|
||||||
teamplayer = {'name': player.getname(),
|
|
||||||
'device_id': player.inputdevice.get_v1_account_name(
|
|
||||||
True),
|
|
||||||
'inGame': player.in_game,
|
|
||||||
'character': player.character,
|
|
||||||
'account_id': player.get_v1_account_id()
|
|
||||||
}
|
|
||||||
data[str(team.id)]['players'].append(teamplayer)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
BsDataThread()
|
|
||||||
|
|
||||||
|
|
||||||
def get_stats():
|
|
||||||
return stats
|
|
||||||
|
|
||||||
|
|
||||||
def get_complete_leaderboard():
|
|
||||||
return leaderboard
|
|
||||||
|
|
||||||
|
|
||||||
def get_top_200():
|
|
||||||
return top200
|
|
||||||
|
|
||||||
|
|
||||||
def get_server_settings():
|
|
||||||
return setting.get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
def update_server_settings(settings):
|
|
||||||
logger.log(f'updating server settings, request from web')
|
|
||||||
setting.commit(settings)
|
|
||||||
|
|
||||||
|
|
||||||
def get_roles():
|
|
||||||
return pdata.get_roles()
|
|
||||||
|
|
||||||
|
|
||||||
def get_perks():
|
|
||||||
# TODO wire with spaz_effects to fetch list of effects.
|
|
||||||
return {"perks": pdata.get_custom_perks(),
|
|
||||||
"availableEffects": ["spark", "glow", "fairydust", "sparkground",
|
|
||||||
"sweat", "sweatground", "distortion", "shine",
|
|
||||||
"highlightshine", "scorch", "ice", "iceground",
|
|
||||||
"slime", "metal", "splinter", "rainbow"]}
|
|
||||||
|
|
||||||
|
|
||||||
def update_perks(custom):
|
|
||||||
logger.log(f'updating custom perks, request from web')
|
|
||||||
pdata.update_custom_perks(custom)
|
|
||||||
|
|
||||||
|
|
||||||
def update_roles(roles):
|
|
||||||
logger.log("updated roles from web")
|
|
||||||
return pdata.update_roles(roles)
|
|
||||||
|
|
||||||
|
|
||||||
def get_profiles_db_list():
|
|
||||||
return pdata.get_profiles_archive_index()
|
|
||||||
|
|
||||||
|
|
||||||
def get_logs_db_list():
|
|
||||||
return serverdata.get_stats_index()
|
|
||||||
|
|
||||||
|
|
||||||
def get_matching_logs(key: str, filename: str):
|
|
||||||
logs = serverdata.read_logs(filename)
|
|
||||||
matching_lines = [line.strip() for line in logs.split('\n') if key in line]
|
|
||||||
return matching_lines
|
|
||||||
|
|
||||||
|
|
||||||
def search_player_profile(search_key: str, db: str):
|
|
||||||
selectedDB = {}
|
|
||||||
if db == "profiles.json":
|
|
||||||
selectedDB = pdata.get_profiles()
|
|
||||||
|
|
||||||
elif db in pdata.get_profiles_archive_index():
|
|
||||||
selectedDB = pdata.get_old_profiles(db)
|
|
||||||
|
|
||||||
matching_objects = {}
|
|
||||||
count = 0
|
|
||||||
for key in selectedDB.keys():
|
|
||||||
if (search_key == key or
|
|
||||||
any(search_key.lower() in s.lower() for s in
|
|
||||||
selectedDB[key].get("display_string", [])) or
|
|
||||||
search_key.lower() in selectedDB[key].get("name", "").lower()):
|
|
||||||
matching_objects[key] = selectedDB[key]
|
|
||||||
count += 1
|
|
||||||
if count > 50:
|
|
||||||
break
|
|
||||||
return matching_objects
|
|
||||||
|
|
||||||
|
|
||||||
def get_player_details(account_id: str):
|
|
||||||
current_time = datetime.now()
|
|
||||||
current_profiles = pdata.get_profiles()
|
|
||||||
ip = ""
|
|
||||||
device_id = ""
|
|
||||||
if account_id in current_profiles:
|
|
||||||
ip = current_profiles[account_id]["lastIP"]
|
|
||||||
device_id = current_profiles[account_id]["deviceUUID"]
|
|
||||||
extra_info = pdata.get_detailed_info(account_id)
|
|
||||||
isBanned = False
|
|
||||||
isMuted = False
|
|
||||||
isKickVoteDisabled = False
|
|
||||||
haveBanReason = servercheck.check_ban(ip, device_id, account_id, False)
|
|
||||||
if haveBanReason:
|
|
||||||
isBanned = True
|
|
||||||
extra_info += " , Banned for > " + haveBanReason
|
|
||||||
if account_id in pdata.get_blacklist()[
|
|
||||||
"muted-ids"] and current_time < datetime.strptime(
|
|
||||||
pdata.get_blacklist()["muted-ids"][account_id]["till"],
|
|
||||||
"%Y-%m-%d %H:%M:%S"):
|
|
||||||
isMuted = True
|
|
||||||
extra_info += f', Muted for > {pdata.get_blacklist()["muted-ids"][account_id]["reason"]} , till > {pdata.get_blacklist()["muted-ids"][account_id]["till"]} ,'
|
|
||||||
if account_id in pdata.get_blacklist()[
|
|
||||||
"kick-vote-disabled"] and current_time < datetime.strptime(
|
|
||||||
pdata.get_blacklist()["kick-vote-disabled"][account_id]["till"],
|
|
||||||
"%Y-%m-%d %H:%M:%S"):
|
|
||||||
isKickVoteDisabled = True
|
|
||||||
extra_info += f', Kick vote disabled for > {pdata.get_blacklist()["kick-vote-disabled"][account_id]["reason"]} , till > {pdata.get_blacklist()["kick-vote-disabled"][account_id]["till"]} '
|
|
||||||
|
|
||||||
return {"extra": extra_info, "isBan": isBanned, "isMuted": isMuted,
|
|
||||||
"isKickVoteDisabled": isKickVoteDisabled}
|
|
||||||
|
|
||||||
|
|
||||||
def unban_player(account_id):
|
|
||||||
logger.log(f'unbanning {account_id} , request from web')
|
|
||||||
pdata.unban_player(account_id)
|
|
||||||
|
|
||||||
|
|
||||||
def unmute_player(account_id):
|
|
||||||
logger.log(f'unmuting {account_id} , request from web')
|
|
||||||
pdata.unmute(account_id)
|
|
||||||
|
|
||||||
|
|
||||||
def enable_kick_vote(account_id):
|
|
||||||
logger.log(f'enabling kick vote for {account_id} , request from web')
|
|
||||||
pdata.enable_kick_vote(account_id)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO take duration input
|
|
||||||
|
|
||||||
|
|
||||||
def ban_player(account_id, duration):
|
|
||||||
logger.log(f'banning {account_id} , request from web')
|
|
||||||
pdata.ban_player(account_id, duration, "manually from website")
|
|
||||||
|
|
||||||
|
|
||||||
def mute_player(account_id, duration):
|
|
||||||
logger.log(f'muting {account_id} , request from web')
|
|
||||||
pdata.mute(account_id, duration, "manually from website")
|
|
||||||
|
|
||||||
|
|
||||||
def disable_kick_vote(account_id, duration):
|
|
||||||
logger.log(f'disable {account_id} , request from web')
|
|
||||||
pdata.disable_kick_vote(account_id, duration, "manually from website")
|
|
||||||
|
|
||||||
|
|
||||||
def get_server_config():
|
|
||||||
return _babase.app.server._config.__dict__
|
|
||||||
|
|
||||||
|
|
||||||
def update_server_config(config):
|
|
||||||
current_dir = os.getcwd()
|
|
||||||
file_path = os.path.join(current_dir, '..', 'config.yaml')
|
|
||||||
|
|
||||||
with open(file_path, "w") as f:
|
|
||||||
f.write(yaml.dump(config))
|
|
||||||
|
|
||||||
|
|
||||||
def do_action(action, value):
|
|
||||||
if action == "message":
|
|
||||||
_babase.pushcall(babase.Call(_babase.chatmessage, value),
|
|
||||||
from_other_thread=True)
|
|
||||||
elif action == "quit":
|
|
||||||
_babase.pushcall(babase.Call(_babase.quit), from_other_thread=True)
|
|
||||||
|
|
||||||
|
|
||||||
def subscribe_player(sub, account_id, name):
|
|
||||||
notification_manager.subscribe(sub, account_id, name)
|
|
||||||
355
dist/ba_root/mods/plugins/character_chooser.py
vendored
355
dist/ba_root/mods/plugins/character_chooser.py
vendored
|
|
@ -1,355 +0,0 @@
|
||||||
# ba_meta require api 8
|
|
||||||
|
|
||||||
'''
|
|
||||||
Character Chooser by Mr.Smoothy
|
|
||||||
|
|
||||||
This plugin will let you choose your character from lobby.
|
|
||||||
|
|
||||||
Install this plugin on your Phone/PC or on Server
|
|
||||||
|
|
||||||
If installed on server :- this will also let players choose server specific custom characters . so no more sharing of character file with all players,
|
|
||||||
just install this plugin on server ...and players can pick character from lobby .
|
|
||||||
|
|
||||||
Use:-
|
|
||||||
> select your profile (focus on color and name)
|
|
||||||
> press ready (punch)
|
|
||||||
> now use UP/DOWN buttons to scroll character list
|
|
||||||
> Press ready again (punch) to join the game
|
|
||||||
> or press Bomb button to go back to profile choosing menu
|
|
||||||
> END
|
|
||||||
|
|
||||||
Watch : https://www.youtube.com/watch?v=hNmv2l-NahE
|
|
||||||
Join : https://discord.gg/ucyaesh
|
|
||||||
Contact : discord mr.smoothy#5824
|
|
||||||
|
|
||||||
|
|
||||||
Share this plugin with your server owner /admins to use it online
|
|
||||||
|
|
||||||
:)
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bauiv1 as bui
|
|
||||||
from babase._error import print_error
|
|
||||||
from babase._language import Lstr
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Any, List, Dict, Union, Sequence, Optional
|
|
||||||
import weakref
|
|
||||||
from bascenev1._lobby import ChangeMessage, PlayerReadyMessage
|
|
||||||
from bascenev1 import _lobby
|
|
||||||
from bascenev1lib.actor.spazappearance import *
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, vpos: float, sessionplayer: bs.SessionPlayer,
|
|
||||||
lobby: 'Lobby') -> None:
|
|
||||||
self._deek_sound = bs.getsound('deek')
|
|
||||||
self._click_sound = bs.getsound('click01')
|
|
||||||
self._punchsound = bs.getsound('punch01')
|
|
||||||
self._swish_sound = bs.getsound('punchSwish')
|
|
||||||
self._errorsound = bs.getsound('error')
|
|
||||||
self._mask_texture = bs.gettexture('characterIconMask')
|
|
||||||
self._vpos = vpos
|
|
||||||
self._lobby = weakref.ref(lobby)
|
|
||||||
self._sessionplayer = sessionplayer
|
|
||||||
self._inited = False
|
|
||||||
self._dead = False
|
|
||||||
self._text_node: Optional[bs.Node] = None
|
|
||||||
self._profilename = ''
|
|
||||||
self._profilenames: List[str] = []
|
|
||||||
self._ready: bool = False
|
|
||||||
self._character_names: List[str] = []
|
|
||||||
self._last_change: Sequence[Union[float, int]] = (0, 0)
|
|
||||||
self._profiles: Dict[str, Dict[str, Any]] = {}
|
|
||||||
|
|
||||||
app = babase.app
|
|
||||||
|
|
||||||
self.bakwas_chars = ["Lee", "Todd McBurton", "Zola", "Butch", "Witch",
|
|
||||||
"warrior",
|
|
||||||
"Middle-Man", "Alien", "OldLady", "Gladiator",
|
|
||||||
"Wrestler", "Gretel", "Robot"]
|
|
||||||
|
|
||||||
# Load available player profiles either from the local config or
|
|
||||||
# from the remote device.
|
|
||||||
self.reload_profiles()
|
|
||||||
for name in bs.app.classic.spaz_appearances:
|
|
||||||
if name not in self._character_names and name not in self.bakwas_chars:
|
|
||||||
self._character_names.append(name)
|
|
||||||
# Note: this is just our local index out of available teams; *not*
|
|
||||||
# the team-id!
|
|
||||||
self._selected_team_index: int = self.lobby.next_add_team
|
|
||||||
|
|
||||||
# Store a persistent random character index and colors; we'll use this
|
|
||||||
# for the '_random' profile. Let's use their input_device id to seed
|
|
||||||
# it. This will give a persistent character for them between games
|
|
||||||
# and will distribute characters nicely if everyone is random.
|
|
||||||
self._random_color, self._random_highlight = (
|
|
||||||
bs.get_player_profile_colors(None))
|
|
||||||
|
|
||||||
# To calc our random character we pick a random one out of our
|
|
||||||
# unlocked list and then locate that character's index in the full
|
|
||||||
# list.
|
|
||||||
char_index_offset = app.classic.lobby_random_char_index_offset
|
|
||||||
self._random_character_index = (
|
|
||||||
(sessionplayer.inputdevice.id + char_index_offset) %
|
|
||||||
len(self._character_names))
|
|
||||||
|
|
||||||
# Attempt to set an initial profile based on what was used previously
|
|
||||||
# for this input-device, etc.
|
|
||||||
self._profileindex = self._select_initial_profile()
|
|
||||||
self._profilename = self._profilenames[self._profileindex]
|
|
||||||
|
|
||||||
self._text_node = bs.newnode('text',
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
'position': (-100, self._vpos),
|
|
||||||
'maxwidth': 190,
|
|
||||||
'shadow': 0.5,
|
|
||||||
'vr_depth': -20,
|
|
||||||
'h_align': 'left',
|
|
||||||
'v_align': 'center',
|
|
||||||
'v_attach': 'top'
|
|
||||||
})
|
|
||||||
bs.animate(self._text_node, 'scale', {0: 0, 0.1: 1.0})
|
|
||||||
self.icon = bs.newnode('image',
|
|
||||||
owner=self._text_node,
|
|
||||||
attrs={
|
|
||||||
'position': (-130, self._vpos + 20),
|
|
||||||
'mask_texture': self._mask_texture,
|
|
||||||
'vr_depth': -10,
|
|
||||||
'attach': 'topCenter'
|
|
||||||
})
|
|
||||||
|
|
||||||
bs.animate_array(self.icon, 'scale', 2, {0: (0, 0), 0.1: (45, 45)})
|
|
||||||
|
|
||||||
# Set our initial name to '<choosing player>' in case anyone asks.
|
|
||||||
self._sessionplayer.setname(
|
|
||||||
Lstr(resource='choosingPlayerText').evaluate(), real=False)
|
|
||||||
|
|
||||||
# Init these to our rando but they should get switched to the
|
|
||||||
# selected profile (if any) right after.
|
|
||||||
self._character_index = self._random_character_index
|
|
||||||
self._color = self._random_color
|
|
||||||
self._highlight = self._random_highlight
|
|
||||||
self.characterchooser = False
|
|
||||||
self.update_from_profile()
|
|
||||||
self.update_position()
|
|
||||||
self._inited = True
|
|
||||||
|
|
||||||
self._set_ready(False)
|
|
||||||
|
|
||||||
|
|
||||||
def _set_ready(self, ready: bool) -> None:
|
|
||||||
# pylint: disable=cyclic-import
|
|
||||||
from bauiv1lib.profile import browser as pbrowser
|
|
||||||
from babase._general import Call
|
|
||||||
profilename = self._profilenames[self._profileindex]
|
|
||||||
|
|
||||||
# Handle '_edit' as a special case.
|
|
||||||
if profilename == '_edit' and ready:
|
|
||||||
with _babase.Context('ui'):
|
|
||||||
pbrowser.ProfileBrowserWindow(in_main_menu=False)
|
|
||||||
|
|
||||||
# Give their input-device UI ownership too
|
|
||||||
# (prevent someone else from snatching it in crowded games)
|
|
||||||
_babase.set_ui_input_device(self._sessionplayer.inputdevice)
|
|
||||||
return
|
|
||||||
|
|
||||||
if ready == False:
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
babase.InputType.LEFT_PRESS,
|
|
||||||
Call(self.handlemessage, ChangeMessage('team', -1)))
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
babase.InputType.RIGHT_PRESS,
|
|
||||||
Call(self.handlemessage, ChangeMessage('team', 1)))
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
babase.InputType.BOMB_PRESS,
|
|
||||||
Call(self.handlemessage, ChangeMessage('character', 1)))
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
babase.InputType.UP_PRESS,
|
|
||||||
Call(self.handlemessage, ChangeMessage('profileindex', -1)))
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
babase.InputType.DOWN_PRESS,
|
|
||||||
Call(self.handlemessage, ChangeMessage('profileindex', 1)))
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
(babase.InputType.JUMP_PRESS, babase.InputType.PICK_UP_PRESS,
|
|
||||||
babase.InputType.PUNCH_PRESS),
|
|
||||||
Call(self.handlemessage, ChangeMessage('ready', 1)))
|
|
||||||
self._ready = False
|
|
||||||
self._update_text()
|
|
||||||
self._sessionplayer.setname('untitled', real=False)
|
|
||||||
elif ready == True:
|
|
||||||
self.characterchooser = True
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
(babase.InputType.LEFT_PRESS, babase.InputType.RIGHT_PRESS,
|
|
||||||
babase.InputType.UP_PRESS, babase.InputType.DOWN_PRESS,
|
|
||||||
babase.InputType.JUMP_PRESS, babase.InputType.BOMB_PRESS,
|
|
||||||
babase.InputType.PICK_UP_PRESS), self._do_nothing)
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
(babase.InputType.UP_PRESS),
|
|
||||||
Call(self.handlemessage, ChangeMessage('characterchooser', -1)))
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
(babase.InputType.DOWN_PRESS),
|
|
||||||
Call(self.handlemessage, ChangeMessage('characterchooser', 1)))
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
(babase.InputType.BOMB_PRESS),
|
|
||||||
Call(self.handlemessage, ChangeMessage('ready', 0)))
|
|
||||||
|
|
||||||
self._sessionplayer.assigninput(
|
|
||||||
(babase.InputType.JUMP_PRESS, babase.InputType.PICK_UP_PRESS,
|
|
||||||
babase.InputType.PUNCH_PRESS),
|
|
||||||
Call(self.handlemessage, ChangeMessage('ready', 2)))
|
|
||||||
|
|
||||||
# Store the last profile picked by this input for reuse.
|
|
||||||
input_device = self._sessionplayer.inputdevice
|
|
||||||
name = input_device.name
|
|
||||||
unique_id = input_device.unique_identifier
|
|
||||||
device_profiles = _babase.app.config.setdefault(
|
|
||||||
'Default Player Profiles', {})
|
|
||||||
|
|
||||||
# Make an exception if we have no custom profiles and are set
|
|
||||||
# to random; in that case we'll want to start picking up custom
|
|
||||||
# profiles if/when one is made so keep our setting cleared.
|
|
||||||
special = ('_random', '_edit', '__account__')
|
|
||||||
have_custom_profiles = any(p not in special
|
|
||||||
for p in self._profiles)
|
|
||||||
|
|
||||||
profilekey = name + ' ' + unique_id
|
|
||||||
if profilename == '_random' and not have_custom_profiles:
|
|
||||||
if profilekey in device_profiles:
|
|
||||||
del device_profiles[profilekey]
|
|
||||||
else:
|
|
||||||
device_profiles[profilekey] = profilename
|
|
||||||
_babase.app.config.commit()
|
|
||||||
|
|
||||||
# Set this player's short and full name.
|
|
||||||
self._sessionplayer.setname(self._getname(),
|
|
||||||
self._getname(full=True),
|
|
||||||
real=True)
|
|
||||||
self._ready = True
|
|
||||||
self._update_text()
|
|
||||||
else:
|
|
||||||
|
|
||||||
# Inform the session that this player is ready.
|
|
||||||
bs.getsession().handlemessage(PlayerReadyMessage(self))
|
|
||||||
|
|
||||||
|
|
||||||
def handlemessage(self, msg: Any) -> Any:
|
|
||||||
"""Standard generic message handler."""
|
|
||||||
|
|
||||||
if isinstance(msg, ChangeMessage):
|
|
||||||
self._handle_repeat_message_attack()
|
|
||||||
|
|
||||||
# If we've been removed from the lobby, ignore this stuff.
|
|
||||||
if self._dead:
|
|
||||||
print_error('chooser got ChangeMessage after dying')
|
|
||||||
return
|
|
||||||
|
|
||||||
if not self._text_node:
|
|
||||||
print_error('got ChangeMessage after nodes died')
|
|
||||||
return
|
|
||||||
if msg.what == 'characterchooser':
|
|
||||||
self._click_sound.play()
|
|
||||||
# update our index in our local list of characters
|
|
||||||
self._character_index = ((self._character_index + msg.value) %
|
|
||||||
len(self._character_names))
|
|
||||||
self._update_text()
|
|
||||||
self._update_icon()
|
|
||||||
|
|
||||||
if msg.what == 'team':
|
|
||||||
sessionteams = self.lobby.sessionteams
|
|
||||||
if len(sessionteams) > 1:
|
|
||||||
self._swish_sound.play()
|
|
||||||
self._selected_team_index = (
|
|
||||||
(self._selected_team_index + msg.value) %
|
|
||||||
len(sessionteams))
|
|
||||||
self._update_text()
|
|
||||||
self.update_position()
|
|
||||||
self._update_icon()
|
|
||||||
|
|
||||||
elif msg.what == 'profileindex':
|
|
||||||
if len(self._profilenames) == 1:
|
|
||||||
|
|
||||||
# This should be pretty hard to hit now with
|
|
||||||
# automatic local accounts.
|
|
||||||
bui.getsound('error').play()
|
|
||||||
else:
|
|
||||||
|
|
||||||
# Pick the next player profile and assign our name
|
|
||||||
# and character based on that.
|
|
||||||
self._deek_sound.play()
|
|
||||||
self._profileindex = ((self._profileindex + msg.value) %
|
|
||||||
len(self._profilenames))
|
|
||||||
self.update_from_profile()
|
|
||||||
|
|
||||||
elif msg.what == 'character':
|
|
||||||
self._click_sound.play()
|
|
||||||
self.characterchooser = True
|
|
||||||
# update our index in our local list of characters
|
|
||||||
self._character_index = ((self._character_index + msg.value) %
|
|
||||||
len(self._character_names))
|
|
||||||
self._update_text()
|
|
||||||
self._update_icon()
|
|
||||||
|
|
||||||
elif msg.what == 'ready':
|
|
||||||
self._handle_ready_msg(msg.value)
|
|
||||||
|
|
||||||
|
|
||||||
def _update_text(self) -> None:
|
|
||||||
assert self._text_node is not None
|
|
||||||
if self._ready:
|
|
||||||
|
|
||||||
# Once we're ready, we've saved the name, so lets ask the system
|
|
||||||
# for it so we get appended numbers and stuff.
|
|
||||||
text = Lstr(value=self._sessionplayer.getname(full=True))
|
|
||||||
if self.characterchooser:
|
|
||||||
text = Lstr(value='${A}\n${B}',
|
|
||||||
subs=[('${A}', text),
|
|
||||||
('${B}', Lstr(value="" + self._character_names[
|
|
||||||
self._character_index]))])
|
|
||||||
self._text_node.scale = 0.8
|
|
||||||
else:
|
|
||||||
text = Lstr(value='${A} (${B})',
|
|
||||||
subs=[('${A}', text),
|
|
||||||
('${B}', Lstr(resource='readyText'))])
|
|
||||||
else:
|
|
||||||
text = Lstr(value=self._getname(full=True))
|
|
||||||
self._text_node.scale = 1.0
|
|
||||||
|
|
||||||
can_switch_teams = len(self.lobby.sessionteams) > 1
|
|
||||||
|
|
||||||
# Flash as we're coming in.
|
|
||||||
fin_color = _babase.safecolor(self.get_color()) + (1,)
|
|
||||||
if not self._inited:
|
|
||||||
bs.animate_array(self._text_node, 'color', 4, {
|
|
||||||
0.15: fin_color,
|
|
||||||
0.25: (2, 2, 2, 1),
|
|
||||||
0.35: fin_color
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
|
|
||||||
# Blend if we're in teams mode; switch instantly otherwise.
|
|
||||||
if can_switch_teams:
|
|
||||||
bs.animate_array(self._text_node, 'color', 4, {
|
|
||||||
0: self._text_node.color,
|
|
||||||
0.1: fin_color
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
self._text_node.color = fin_color
|
|
||||||
|
|
||||||
self._text_node.text = text
|
|
||||||
|
|
||||||
|
|
||||||
# ba_meta export plugin
|
|
||||||
def enable():
|
|
||||||
_lobby.Chooser.__init__ = __init__
|
|
||||||
_lobby.Chooser._set_ready = _set_ready
|
|
||||||
|
|
||||||
_lobby.Chooser._update_text = _update_text
|
|
||||||
_lobby.Chooser.handlemessage = handlemessage
|
|
||||||
350
dist/ba_root/mods/plugins/color_explosion.py
vendored
350
dist/ba_root/mods/plugins/color_explosion.py
vendored
|
|
@ -1,350 +0,0 @@
|
||||||
"""Define a simple example plugin."""
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import random
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1lib.actor import bomb
|
|
||||||
from bascenev1lib.actor.bomb import BombFactory
|
|
||||||
from bascenev1lib.gameutils import SharedObjects
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from typing import Sequence
|
|
||||||
|
|
||||||
|
|
||||||
def new_blast_init(
|
|
||||||
self,
|
|
||||||
position: Sequence[float] = (0.0, 1.0, 0.0),
|
|
||||||
velocity: Sequence[float] = (0.0, 0.0, 0.0),
|
|
||||||
blast_radius: float = 2.0,
|
|
||||||
blast_type: str = "normal",
|
|
||||||
source_player: bs.Player = None,
|
|
||||||
hit_type: str = "explosion",
|
|
||||||
hit_subtype: str = "normal",
|
|
||||||
):
|
|
||||||
"""Instantiate with given values."""
|
|
||||||
|
|
||||||
# bah; get off my lawn!
|
|
||||||
# pylint: disable=too-many-locals
|
|
||||||
# pylint: disable=too-many-statements
|
|
||||||
|
|
||||||
bs.Actor.__init__(self) # super method can't be used here
|
|
||||||
|
|
||||||
shared = SharedObjects.get()
|
|
||||||
factory = BombFactory.get()
|
|
||||||
|
|
||||||
self.blast_type = blast_type
|
|
||||||
self._source_player = source_player
|
|
||||||
self.hit_type = hit_type
|
|
||||||
self.hit_subtype = hit_subtype
|
|
||||||
self.radius = blast_radius
|
|
||||||
|
|
||||||
# Set our position a bit lower so we throw more things upward.
|
|
||||||
rmats = (factory.blast_material, shared.attack_material)
|
|
||||||
self.node = bs.newnode(
|
|
||||||
"region",
|
|
||||||
delegate=self,
|
|
||||||
attrs={
|
|
||||||
"position": (position[0], position[1] - 0.1, position[2]),
|
|
||||||
"scale": (self.radius, self.radius, self.radius),
|
|
||||||
"type": "sphere",
|
|
||||||
"materials": rmats,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
bs.timer(0.05, self.node.delete)
|
|
||||||
|
|
||||||
# Throw in an explosion and flash.
|
|
||||||
evel = (velocity[0], max(-1.0, velocity[1]), velocity[2])
|
|
||||||
explosion = bs.newnode(
|
|
||||||
"explosion",
|
|
||||||
attrs={
|
|
||||||
"position": position,
|
|
||||||
"velocity": evel,
|
|
||||||
"radius": self.radius,
|
|
||||||
"big": (self.blast_type == "tnt"),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if self.blast_type == "ice":
|
|
||||||
explosion.color = (0, 0.05, 0.4)
|
|
||||||
|
|
||||||
bs.timer(1.0, explosion.delete)
|
|
||||||
|
|
||||||
if self.blast_type != "ice":
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(1.0 + random.random() * 4),
|
|
||||||
emit_type="tendrils",
|
|
||||||
tendril_type="thin_smoke",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(4.0 + random.random() * 4),
|
|
||||||
emit_type="tendrils",
|
|
||||||
tendril_type="ice" if self.blast_type == "ice" else "smoke",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
emit_type="distortion",
|
|
||||||
spread=1.0 if self.blast_type == "tnt" else 2.0,
|
|
||||||
)
|
|
||||||
|
|
||||||
# And emit some shrapnel.
|
|
||||||
if self.blast_type == "ice":
|
|
||||||
|
|
||||||
def emit() -> None:
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=30,
|
|
||||||
spread=2.0,
|
|
||||||
scale=0.4,
|
|
||||||
chunk_type="ice",
|
|
||||||
emit_type="stickers",
|
|
||||||
)
|
|
||||||
|
|
||||||
# It looks better if we delay a bit.
|
|
||||||
bs.timer(0.05, emit)
|
|
||||||
|
|
||||||
elif self.blast_type == "sticky":
|
|
||||||
|
|
||||||
def emit() -> None:
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(4.0 + random.random() * 8),
|
|
||||||
spread=0.7,
|
|
||||||
chunk_type="slime",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(4.0 + random.random() * 8),
|
|
||||||
scale=0.5,
|
|
||||||
spread=0.7,
|
|
||||||
chunk_type="slime",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=15,
|
|
||||||
scale=0.6,
|
|
||||||
chunk_type="slime",
|
|
||||||
emit_type="stickers",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=20,
|
|
||||||
scale=0.7,
|
|
||||||
chunk_type="spark",
|
|
||||||
emit_type="stickers",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(6.0 + random.random() * 12),
|
|
||||||
scale=0.8,
|
|
||||||
spread=1.5,
|
|
||||||
chunk_type="spark",
|
|
||||||
)
|
|
||||||
|
|
||||||
# It looks better if we delay a bit.
|
|
||||||
bs.timer(0.05, emit)
|
|
||||||
|
|
||||||
elif self.blast_type == "impact":
|
|
||||||
|
|
||||||
def emit() -> None:
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(4.0 + random.random() * 8),
|
|
||||||
scale=0.8,
|
|
||||||
chunk_type="metal",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(4.0 + random.random() * 8),
|
|
||||||
scale=0.4,
|
|
||||||
chunk_type="metal",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=20,
|
|
||||||
scale=0.7,
|
|
||||||
chunk_type="spark",
|
|
||||||
emit_type="stickers",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(8.0 + random.random() * 15),
|
|
||||||
scale=0.8,
|
|
||||||
spread=1.5,
|
|
||||||
chunk_type="spark",
|
|
||||||
)
|
|
||||||
|
|
||||||
# It looks better if we delay a bit.
|
|
||||||
bs.timer(0.05, emit)
|
|
||||||
|
|
||||||
else: # Regular or land mine bomb shrapnel.
|
|
||||||
|
|
||||||
def emit() -> None:
|
|
||||||
if self.blast_type != "tnt":
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(4.0 + random.random() * 8),
|
|
||||||
chunk_type="rock",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(4.0 + random.random() * 8),
|
|
||||||
scale=0.5,
|
|
||||||
chunk_type="rock",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=30,
|
|
||||||
scale=1.0 if self.blast_type == "tnt" else 0.7,
|
|
||||||
chunk_type="spark",
|
|
||||||
emit_type="stickers",
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(18.0 + random.random() * 20),
|
|
||||||
scale=1.0 if self.blast_type == "tnt" else 0.8,
|
|
||||||
spread=1.5,
|
|
||||||
chunk_type="spark",
|
|
||||||
)
|
|
||||||
|
|
||||||
# TNT throws splintery chunks.
|
|
||||||
if self.blast_type == "tnt":
|
|
||||||
def emit_splinters() -> None:
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(20.0 + random.random() * 25),
|
|
||||||
scale=0.8,
|
|
||||||
spread=1.0,
|
|
||||||
chunk_type="splinter",
|
|
||||||
)
|
|
||||||
|
|
||||||
bs.timer(0.01, emit_splinters)
|
|
||||||
|
|
||||||
# Every now and then do a sparky one.
|
|
||||||
if self.blast_type == "tnt" or random.random() < 0.1:
|
|
||||||
def emit_extra_sparks() -> None:
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=int(10.0 + random.random() * 20),
|
|
||||||
scale=0.8,
|
|
||||||
spread=1.5,
|
|
||||||
chunk_type="spark",
|
|
||||||
)
|
|
||||||
|
|
||||||
bs.timer(0.02, emit_extra_sparks)
|
|
||||||
|
|
||||||
# It looks better if we delay a bit.
|
|
||||||
bs.timer(0.05, emit)
|
|
||||||
|
|
||||||
lcolor = (0.6, 0.6, 1.0) if self.blast_type == "ice" else (1, 0.3, 0.1)
|
|
||||||
light = bs.newnode(
|
|
||||||
"light",
|
|
||||||
attrs={"position": position,
|
|
||||||
"volume_intensity_scale": 10.0, "color": lcolor},
|
|
||||||
)
|
|
||||||
|
|
||||||
scl = random.uniform(0.6, 0.9)
|
|
||||||
scorch_radius = light_radius = self.radius
|
|
||||||
if self.blast_type == "tnt":
|
|
||||||
light_radius *= 1.4
|
|
||||||
scorch_radius *= 1.15
|
|
||||||
scl *= 3.0
|
|
||||||
|
|
||||||
iscale = 1.6
|
|
||||||
bs.animate(
|
|
||||||
light,
|
|
||||||
"intensity",
|
|
||||||
{
|
|
||||||
0: 2.0 * iscale,
|
|
||||||
scl * 0.02: 0.1 * iscale,
|
|
||||||
scl * 0.025: 0.2 * iscale,
|
|
||||||
scl * 0.05: 17.0 * iscale,
|
|
||||||
scl * 0.06: 5.0 * iscale,
|
|
||||||
scl * 0.08: 4.0 * iscale,
|
|
||||||
scl * 0.2: 0.6 * iscale,
|
|
||||||
scl * 2.0: 0.00 * iscale,
|
|
||||||
scl * 3.0: 0.0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
bs.animate(
|
|
||||||
light,
|
|
||||||
"radius",
|
|
||||||
{
|
|
||||||
0: light_radius * 0.2,
|
|
||||||
scl * 0.05: light_radius * 0.55,
|
|
||||||
scl * 0.1: light_radius * 0.3,
|
|
||||||
scl * 0.3: light_radius * 0.15,
|
|
||||||
scl * 1.0: light_radius * 0.05,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
bs.timer(scl * 3.0, light.delete)
|
|
||||||
|
|
||||||
# Make a scorch that fades over time.
|
|
||||||
scorch = bs.newnode(
|
|
||||||
"scorch",
|
|
||||||
attrs={
|
|
||||||
"position": position,
|
|
||||||
"size": scorch_radius * 0.5,
|
|
||||||
"big": (self.blast_type == "tnt"),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if self.blast_type == "ice":
|
|
||||||
scorch.color = (1, 1, 1.5)
|
|
||||||
else:
|
|
||||||
scorch.color = (random.random(), random.random(), random.random())
|
|
||||||
|
|
||||||
bs.animate(scorch, "presence", {3.000: 1, 13.000: 0})
|
|
||||||
bs.timer(13.0, scorch.delete)
|
|
||||||
|
|
||||||
if self.blast_type == "ice":
|
|
||||||
factory.hiss_sound.play(position=light.position)
|
|
||||||
|
|
||||||
lpos = light.position
|
|
||||||
factory.random_explode_sound().play(position=lpos)
|
|
||||||
factory.debris_fall_sound.play(position=lpos)
|
|
||||||
|
|
||||||
bs.camerashake(intensity=5.0 if self.blast_type == "tnt" else 1.0)
|
|
||||||
|
|
||||||
# TNT is more epic.
|
|
||||||
if self.blast_type == "tnt":
|
|
||||||
factory.random_explode_sound().play(position=lpos)
|
|
||||||
|
|
||||||
def _extra_boom() -> None:
|
|
||||||
factory.random_explode_sound().play(position=lpos)
|
|
||||||
|
|
||||||
bs.timer(0.25, _extra_boom)
|
|
||||||
|
|
||||||
def _extra_debris_sound() -> None:
|
|
||||||
factory.debris_fall_sound.play(position=lpos)
|
|
||||||
factory.wood_debris_fall_sound.play(position=lpos)
|
|
||||||
|
|
||||||
bs.timer(0.4, _extra_debris_sound)
|
|
||||||
|
|
||||||
|
|
||||||
def enable() -> None:
|
|
||||||
bomb.Blast.__init__ = new_blast_init
|
|
||||||
125
dist/ba_root/mods/plugins/colorfulmaps2.py
vendored
125
dist/ba_root/mods/plugins/colorfulmaps2.py
vendored
|
|
@ -1,125 +0,0 @@
|
||||||
# This plugin developed fro Bombsquad Server, and I don't know how to make UI
|
|
||||||
# Just edit Config before starting server
|
|
||||||
# by: Lirik
|
|
||||||
# Further edited/Fixed by:Freak
|
|
||||||
from random import choice
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
from bascenev1._map import Map
|
|
||||||
|
|
||||||
CONFIGS = {
|
|
||||||
"Radius": 2.0,
|
|
||||||
"Blinking": False,
|
|
||||||
"AdaptivePos": True,
|
|
||||||
"IgnoreOnMaps": [],
|
|
||||||
"Colors": {
|
|
||||||
"Intensity": 0.5,
|
|
||||||
"Animate": True,
|
|
||||||
"Random": True,
|
|
||||||
"LeftSide": (1, 0, 1),
|
|
||||||
"RightSide": (0, 0, 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def get_random_color():
|
|
||||||
"""Fetches random color every time for our nodes"""
|
|
||||||
|
|
||||||
choices = [0, 1, 2, 3]
|
|
||||||
return (choice(choices), choice(choices), choice(choices))
|
|
||||||
|
|
||||||
|
|
||||||
def get_colors():
|
|
||||||
"""Fucntion for getting colors for our light node based on configs"""
|
|
||||||
|
|
||||||
if CONFIGS["Colors"]["Random"]:
|
|
||||||
return get_random_color(), get_random_color()
|
|
||||||
return CONFIGS["Colors"]["LeftSide"], CONFIGS["Colors"]["RightSide"]
|
|
||||||
|
|
||||||
|
|
||||||
# Add more perfect positions for all maps
|
|
||||||
def get_adaptive_pos(name: str) -> tuple:
|
|
||||||
"""Fuction for getting pecfect positions for the current map
|
|
||||||
|
|
||||||
Args:
|
|
||||||
name (str): Name of the map
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
[tuple]: tuple containing left and right position respectively
|
|
||||||
"""
|
|
||||||
adaptive = {"Crag Castle": ((-6, 7, -7), (6, 7, -7))}
|
|
||||||
|
|
||||||
if name in adaptive and CONFIGS["AdaptivePos"]:
|
|
||||||
return adaptive[name]
|
|
||||||
return (-10, 7, -3), (10, 7, -3)
|
|
||||||
|
|
||||||
|
|
||||||
def Map___init__(func):
|
|
||||||
"""Redefined method for babase.Map"""
|
|
||||||
|
|
||||||
def wrapper(self, vr_overlay_offset=None):
|
|
||||||
func(self, vr_overlay_offset)
|
|
||||||
|
|
||||||
name = self.getname()
|
|
||||||
|
|
||||||
if name in CONFIGS["IgnoreOnMaps"]:
|
|
||||||
return
|
|
||||||
|
|
||||||
left_color, right_color = get_colors()
|
|
||||||
left_pos, right_pos = get_adaptive_pos(name)
|
|
||||||
|
|
||||||
self.left_light = bs.newnode(
|
|
||||||
"light",
|
|
||||||
attrs={
|
|
||||||
"position": left_pos,
|
|
||||||
"radius": CONFIGS["Radius"],
|
|
||||||
"intensity": CONFIGS["Colors"]["Intensity"],
|
|
||||||
"color": left_color,
|
|
||||||
"volume_intensity_scale": 10,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
self.right_light = bs.newnode(
|
|
||||||
"light",
|
|
||||||
attrs={
|
|
||||||
"position": right_pos,
|
|
||||||
"radius": CONFIGS["Radius"],
|
|
||||||
"intensity": CONFIGS["Colors"]["Intensity"],
|
|
||||||
"color": right_color,
|
|
||||||
"volume_intensity_scale": 10,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
bs.animate(
|
|
||||||
self.left_light,
|
|
||||||
"radius",
|
|
||||||
{0: 0, 1.5: 0.5, 3: CONFIGS["Radius"]},
|
|
||||||
loop=True if CONFIGS["Blinking"] else False,
|
|
||||||
)
|
|
||||||
bs.animate(
|
|
||||||
self.right_light,
|
|
||||||
"radius",
|
|
||||||
{0: 0, 1.5: 0.5, 3: CONFIGS["Radius"]},
|
|
||||||
loop=True if CONFIGS["Blinking"] else False,
|
|
||||||
)
|
|
||||||
|
|
||||||
if CONFIGS["Colors"]["Animate"]:
|
|
||||||
bs.animate_array(
|
|
||||||
self.left_light,
|
|
||||||
"color",
|
|
||||||
3,
|
|
||||||
{
|
|
||||||
0: get_random_color(),
|
|
||||||
1: get_random_color(),
|
|
||||||
2: get_random_color(),
|
|
||||||
3: get_random_color(),
|
|
||||||
4: get_random_color(),
|
|
||||||
5: get_random_color(),
|
|
||||||
},
|
|
||||||
loop=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
Map.__init__ = Map___init__(Map.__init__)
|
|
||||||
2678
dist/ba_root/mods/plugins/elPatronPowerups.py
vendored
2678
dist/ba_root/mods/plugins/elPatronPowerups.py
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,60 +0,0 @@
|
||||||
"""Module to update `setting.json`."""
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# (see https://ballistica.net/wiki/meta-tag-system)
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from bascenev1lib.actor.spazappearance import Appearance
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def register_character(name: str, char: dict) -> None:
|
|
||||||
"""Registers the character in the game."""
|
|
||||||
t = Appearance(name.split(".")[0])
|
|
||||||
t.color_texture = char['color_texture']
|
|
||||||
t.color_mask_texture = char['color_mask']
|
|
||||||
t.default_color = (0.6, 0.6, 0.6)
|
|
||||||
t.default_highlight = (0, 1, 0)
|
|
||||||
t.icon_texture = char['icon_texture']
|
|
||||||
t.icon_mask_texture = char['icon_mask_texture']
|
|
||||||
t.head_mesh = char['head']
|
|
||||||
t.torso_mesh = char['torso']
|
|
||||||
t.pelvis_mesh = char['pelvis']
|
|
||||||
t.upper_arm_mesh = char['upper_arm']
|
|
||||||
t.forearm_mesh = char['forearm']
|
|
||||||
t.hand_mesh = char['hand']
|
|
||||||
t.upper_leg_mesh = char['upper_leg']
|
|
||||||
t.lower_leg_mesh = char['lower_leg']
|
|
||||||
t.toes_mesh = char['toes_mesh']
|
|
||||||
t.jump_sounds = char['jump_sounds']
|
|
||||||
t.attack_sounds = char['attack_sounds']
|
|
||||||
t.impact_sounds = char['impact_sounds']
|
|
||||||
t.death_sounds = char['death_sounds']
|
|
||||||
t.pickup_sounds = char['pickup_sounds']
|
|
||||||
t.fall_sounds = char['fall_sounds']
|
|
||||||
t.style = char['style']
|
|
||||||
|
|
||||||
|
|
||||||
def enable() -> None:
|
|
||||||
path = os.path.join(_babase.env()["python_directory_user"],
|
|
||||||
"custom_characters" + os.sep)
|
|
||||||
|
|
||||||
if not os.path.isdir(path):
|
|
||||||
os.makedirs(path)
|
|
||||||
|
|
||||||
files = os.listdir(path)
|
|
||||||
|
|
||||||
for file in files:
|
|
||||||
if file.endswith(".json"):
|
|
||||||
with open(path + file) as json_file:
|
|
||||||
character = json.load(json_file)
|
|
||||||
register_character(file, character)
|
|
||||||
143
dist/ba_root/mods/plugins/wavedash.py
vendored
143
dist/ba_root/mods/plugins/wavedash.py
vendored
|
|
@ -1,143 +0,0 @@
|
||||||
"""Wavedash by TheMikirog
|
|
||||||
|
|
||||||
This is an early version of the plugin. Feedback appreciated!
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import math
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
import bascenev1lib
|
|
||||||
from bascenev1lib.actor.spaz import Spaz
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MikiWavedashTest:
|
|
||||||
class FootConnectMessage:
|
|
||||||
"""Spaz started touching the ground"""
|
|
||||||
|
|
||||||
class FootDisconnectMessage:
|
|
||||||
"""Spaz stopped touching the ground"""
|
|
||||||
|
|
||||||
def wavedash(self) -> None:
|
|
||||||
if not self.node:
|
|
||||||
return
|
|
||||||
|
|
||||||
isMoving = abs(self.node.move_up_down) >= 0.5 or abs(
|
|
||||||
self.node.move_left_right) >= 0.5
|
|
||||||
|
|
||||||
if self._dead or not self.grounded or not isMoving:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.node.knockout > 0.0 or self.frozen or self.node.hold_node:
|
|
||||||
return
|
|
||||||
|
|
||||||
t_ms = bs.time(timeformat=babase.TimeFormat.MILLISECONDS)
|
|
||||||
assert isinstance(t_ms, int)
|
|
||||||
|
|
||||||
if t_ms - self.last_wavedash_time_ms >= self._wavedash_cooldown:
|
|
||||||
|
|
||||||
move = [self.node.move_left_right, -self.node.move_up_down]
|
|
||||||
vel = [self.node.velocity[0], self.node.velocity[2]]
|
|
||||||
|
|
||||||
move_length = math.hypot(move[0], move[1])
|
|
||||||
vel_length = math.hypot(vel[0], vel[1])
|
|
||||||
if vel_length < 1.25:
|
|
||||||
return
|
|
||||||
move_norm = [m / move_length for m in move]
|
|
||||||
vel_norm = [v / vel_length for v in vel]
|
|
||||||
dot = sum(x * y for x, y in zip(move_norm, vel_norm))
|
|
||||||
turn_power = min(round(math.acos(dot) / math.pi, 2) * 1.3, 1)
|
|
||||||
if turn_power < 0.2:
|
|
||||||
return
|
|
||||||
|
|
||||||
boost_power = math.sqrt(
|
|
||||||
math.pow(vel[0], 2) + math.pow(vel[1], 2)) * 1.2
|
|
||||||
boost_power = min(pow(boost_power, 4), 160)
|
|
||||||
# print(boost_power * turn_power)
|
|
||||||
|
|
||||||
self.last_wavedash_time_ms = t_ms
|
|
||||||
|
|
||||||
# FX
|
|
||||||
bs.emitfx(position=self.node.position,
|
|
||||||
velocity=(vel[0] * 0.5, -1, vel[1] * 0.5),
|
|
||||||
chunk_type='sweat',
|
|
||||||
count=8,
|
|
||||||
scale=boost_power / 160 * turn_power,
|
|
||||||
spread=0.25)
|
|
||||||
|
|
||||||
# Boost itself
|
|
||||||
pos = self.node.position
|
|
||||||
for i in range(6):
|
|
||||||
self.node.handlemessage('impulse', pos[0],
|
|
||||||
-0.1 + pos[1] + i * 0.1, pos[2],
|
|
||||||
0, 0, 0,
|
|
||||||
boost_power * turn_power,
|
|
||||||
boost_power * turn_power, 0, 0,
|
|
||||||
move[0], 0, move[1])
|
|
||||||
|
|
||||||
def new_spaz_init(func):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
func(*args, **kwargs)
|
|
||||||
|
|
||||||
# args[0] = self
|
|
||||||
args[0]._wavedash_cooldown = 30
|
|
||||||
args[0].last_wavedash_time_ms = -9999
|
|
||||||
args[0].grounded = 0
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
bascenev1lib.actor.spaz.Spaz.__init__ = new_spaz_init(
|
|
||||||
bascenev1lib.actor.spaz.Spaz.__init__)
|
|
||||||
|
|
||||||
def new_factory(func):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
func(*args, **kwargs)
|
|
||||||
|
|
||||||
args[0].roller_material.add_actions(
|
|
||||||
conditions=('they_have_material',
|
|
||||||
bascenev1lib.gameutils.SharedObjects.get().footing_material),
|
|
||||||
actions=(('message', 'our_node', 'at_connect',
|
|
||||||
MikiWavedashTest.FootConnectMessage),
|
|
||||||
('message', 'our_node', 'at_disconnect',
|
|
||||||
MikiWavedashTest.FootDisconnectMessage)))
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
bascenev1lib.actor.spazfactory.SpazFactory.__init__ = new_factory(
|
|
||||||
bascenev1lib.actor.spazfactory.SpazFactory.__init__)
|
|
||||||
|
|
||||||
def new_handlemessage(func):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
if args[1] == MikiWavedashTest.FootConnectMessage:
|
|
||||||
args[0].grounded += 1
|
|
||||||
elif args[1] == MikiWavedashTest.FootDisconnectMessage:
|
|
||||||
if args[0].grounded > 0:
|
|
||||||
args[0].grounded -= 1
|
|
||||||
|
|
||||||
func(*args, **kwargs)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
bascenev1lib.actor.spaz.Spaz.handlemessage = new_handlemessage(
|
|
||||||
bascenev1lib.actor.spaz.Spaz.handlemessage)
|
|
||||||
|
|
||||||
def new_on_run(func):
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
if args[0]._last_run_value < args[1] and args[1] > 0.8:
|
|
||||||
MikiWavedashTest.wavedash(args[0])
|
|
||||||
func(*args, **kwargs)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
bascenev1lib.actor.spaz.Spaz.on_run = new_on_run(
|
|
||||||
bascenev1lib.actor.spaz.Spaz.on_run)
|
|
||||||
167
dist/ba_root/mods/port.py
vendored
167
dist/ba_root/mods/port.py
vendored
|
|
@ -1,167 +0,0 @@
|
||||||
# Usage: port_7_to_8.py <plugin-name> <client/server type of mod>
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
def port(file_path):
|
|
||||||
with open(file_path, "rb") as fin:
|
|
||||||
print("Porting " + os.path.basename(file_path))
|
|
||||||
content = fin.read().decode("utf-8")
|
|
||||||
if "# ba_meta require api 8" in content:
|
|
||||||
return
|
|
||||||
content = content.replace("# ba_meta require api 7",
|
|
||||||
"# ba_meta require api 8")
|
|
||||||
content = content.replace("# ba_meta export game",
|
|
||||||
"# ba_meta export bascenev1.GameActivity")
|
|
||||||
|
|
||||||
content = content.replace("user_agent_string", "legacy_user_agent_string")
|
|
||||||
content = content.replace("_ba.", "_babase.")
|
|
||||||
content = content.replace("_ba.", "_babase.")
|
|
||||||
content = content.replace("ba.", "babase.")
|
|
||||||
content = content.replace("import _ba\n", "import _babase")
|
|
||||||
content = re.sub(r'\bimport _ba\b', "import _babase", content)
|
|
||||||
content = re.sub(r'\bimport ba(\b|\.(\w+))',
|
|
||||||
"import babase\nimport bauiv1 as bui\nimport bascenev1 as bs",
|
|
||||||
content)
|
|
||||||
content = content.replace("babase.app.ui", "bui.app.ui_v1")
|
|
||||||
content = content.replace("babase.app.accounts_v1",
|
|
||||||
"bui.app.classic.accounts")
|
|
||||||
|
|
||||||
###################################################################################
|
|
||||||
# Comment out one of these as per your requirements, depending whether to
|
|
||||||
# stay local or if it'll also be needed to transmitted to the clients.
|
|
||||||
|
|
||||||
## For local:
|
|
||||||
if False:
|
|
||||||
content = content.replace("_babase.screenmessage", "bui.screenmessage")
|
|
||||||
content = content.replace("babase.screenmessage", "bui.screenmessage")
|
|
||||||
content = content.replace("babase.getsound", "bui.getsound")
|
|
||||||
content = content.replace("babase.gettexture", "bui.gettexture")
|
|
||||||
content = content.replace("babase.getmesh", "bui.getmesh")
|
|
||||||
content = content.replace("babase.getcollisionmesh",
|
|
||||||
"bui.getcollisionmesh")
|
|
||||||
else:
|
|
||||||
## For transmission:
|
|
||||||
content = content.replace("babase.screenmessage", "bs.broadcastmessage")
|
|
||||||
content = content.replace("babase.getsound", "bs.getsound")
|
|
||||||
content = content.replace("babase.getmesh", "bs.getmesh")
|
|
||||||
content = content.replace("babase.getcollisionmesh",
|
|
||||||
"bs.getcollisionmesh")
|
|
||||||
###################################################################################
|
|
||||||
content = content.replace("babase.open_url", "bui.open_url")
|
|
||||||
content = content.replace("babase.IntSetting", "bs.IntSetting")
|
|
||||||
content = content.replace("babase.IntChoiceSetting", "bs.IntChoiceSetting")
|
|
||||||
content = content.replace("babase.FloatChoiceSetting",
|
|
||||||
"bs.FloatChoiceSetting")
|
|
||||||
content = content.replace("babase.BoolSetting", "bs.BoolSetting")
|
|
||||||
content = content.replace("babase.Actor", "bs.Actor")
|
|
||||||
content = content.replace("babase.Player", "bs.Player")
|
|
||||||
content = content.replace("babase.PlayerDiedMessage",
|
|
||||||
"bs.PlayerDiedMessage")
|
|
||||||
content = content.replace("babase.time", "bs.time")
|
|
||||||
content = content.replace("babase.Timer", "bs.Timer")
|
|
||||||
content = content.replace("babase.newnode", "bs.newnode")
|
|
||||||
content = content.replace("babase.Node", "bs.Node")
|
|
||||||
content = content.replace("babase.emitfx", "bs.emitfx")
|
|
||||||
content = content.replace("babase.animate", "bs.animate")
|
|
||||||
content = content.replace("babase.FreeForAllSession",
|
|
||||||
"bs.FreeForAllSession")
|
|
||||||
content = content.replace("babase.DualTeamSession", "bs.DualTeamSession")
|
|
||||||
content = content.replace("babase.MultiTeamSession", "bs.MultiTeamSession")
|
|
||||||
content = content.replace("babase.TeamGameActivity", "bs.TeamGameActivity")
|
|
||||||
content = content.replace("babase.Team", "bs.Team")
|
|
||||||
content = content.replace("babase.Session", "bs.Session")
|
|
||||||
content = content.replace("babase.Material", "bs.Material")
|
|
||||||
content = content.replace("babase.WeakCall", "bs.WeakCall")
|
|
||||||
content = content.replace("babase.DieMessage", "bs.DieMessage")
|
|
||||||
content = content.replace("babase.OutOfBoundsMessage",
|
|
||||||
"bs.OutOfBoundsMessage")
|
|
||||||
content = content.replace("babase.DroppedMessage", "bs.DroppedMessage")
|
|
||||||
content = content.replace("babase.HitMessage", "bs.HitMessage")
|
|
||||||
content = content.replace("babase.NotFoundError", "bs.NotFoundError")
|
|
||||||
content = content.replace("babase.getcollision", "bs.getcollision")
|
|
||||||
content = content.replace("babase.app.lang", "bs.app.lang")
|
|
||||||
content = content.replace("babase.MusicType", "bs.MusicType")
|
|
||||||
content = content.replace("babase.gettexture", "bs.gettexture")
|
|
||||||
content = content.replace("babase.getactivity", "bs.getactivity")
|
|
||||||
content = content.replace("babase.getactivity", "bs.getactivity")
|
|
||||||
content = content.replace("babase.CelebrateMessage", "bs.CelebrateMessage")
|
|
||||||
content = content.replace("babase.ScoreConfig", "bs.ScoreConfig")
|
|
||||||
content = content.replace("babase.ScoreType", "bs.ScoreType")
|
|
||||||
content = content.replace("babase.GameResults", "bs.GameResults")
|
|
||||||
content = content.replace("babase.getmaps", "bs.app.classic.getmaps")
|
|
||||||
content = content.replace("babase.cameraflash", "bs.cameraflash")
|
|
||||||
content = content.replace("babase.getmodel", "bs.getmesh")
|
|
||||||
content = content.replace("model", "mesh")
|
|
||||||
|
|
||||||
content = content.replace("babase.Window", "bui.Window")
|
|
||||||
content = content.replace("babase.Widget", "bui.Widget")
|
|
||||||
content = content.replace("babase.widget", "bui.widget")
|
|
||||||
content = content.replace("babase.containerwidget", "bui.containerwidget")
|
|
||||||
content = content.replace("babase.scrollwidget", "bui.scrollwidget")
|
|
||||||
content = content.replace("babase.buttonwidget", "bui.buttonwidget")
|
|
||||||
content = content.replace("babase.textwidget", "bui.textwidget")
|
|
||||||
content = content.replace("babase.checkboxwidget", "bui.checkboxwidget")
|
|
||||||
content = content.replace("babase.imagewidget", "bui.imagewidget")
|
|
||||||
content = content.replace("_bui", "bui")
|
|
||||||
# Converting `ba.playsound(abc)` to `abc.play()` is tricky.
|
|
||||||
# Do it manually in case regex substitution fails.
|
|
||||||
content = re.sub(
|
|
||||||
r'babase\.playsound\(\s*([^,\n]+),\s*([^,\n]+),\s*position=([^,\n]+)\)',
|
|
||||||
r'\1.play(\2, position=\3)',
|
|
||||||
content,
|
|
||||||
flags=re.MULTILINE
|
|
||||||
)
|
|
||||||
content = re.sub("babase\.playsound\((.+?), (.+?), (.+?)\)",
|
|
||||||
"\\1.play(\\2, \\3)", content)
|
|
||||||
content = re.sub(
|
|
||||||
r'babase\.playsound\(([^,\n]+),\s*position=([^,\n]+)\)',
|
|
||||||
r'\1.play(position=\2)',
|
|
||||||
content
|
|
||||||
)
|
|
||||||
content = re.sub("babase\.playsound\((.*)\)", "\\1.play()", content)
|
|
||||||
|
|
||||||
content = content.replace("babase.internal.add_transaction",
|
|
||||||
"bui.app.plus.add_v1_account_transaction")
|
|
||||||
content = content.replace("babase.internal.run_transaction",
|
|
||||||
"bui.app.plus.run_v1_account_transaction")
|
|
||||||
content = content.replace("_babase.add_transaction",
|
|
||||||
"bui.app.plus.add_v1_account_transaction")
|
|
||||||
content = content.replace("_babase.run_transactions",
|
|
||||||
"bui.app.plus.run_v1_account_transactions")
|
|
||||||
content = content.replace("_babase.InputType", "babase.InputType")
|
|
||||||
content = content.replace("bastd.ui", "bauiv1lib")
|
|
||||||
content = content.replace("bastd", "bascenev1lib")
|
|
||||||
content = content.replace("timetype=", "")
|
|
||||||
content = content.replace("babase.columnwidget", "bui.columnwidget")
|
|
||||||
content = content.replace("_babase.get_chat_messages",
|
|
||||||
"bs.get_chat_messages")
|
|
||||||
content = content.replace("_babase.get_foreground_host_session",
|
|
||||||
"bs.get_foreground_host_session")
|
|
||||||
content = re.sub(r'bs\.Timer\(([^)]*)\bTimeType\.REAL\b([^)]*)\)',
|
|
||||||
r'babase.AppTimer(\1\2)', content)
|
|
||||||
print("Done porting to API 8 " + os.path.basename(file_path))
|
|
||||||
with open(file_path, "w") as f:
|
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
|
|
||||||
def list_python_files(directory='.'):
|
|
||||||
python_files = []
|
|
||||||
|
|
||||||
for dirpath, dirnames, filenames in os.walk(directory):
|
|
||||||
for filename in filenames:
|
|
||||||
if filename.endswith('.py'):
|
|
||||||
python_files.append(os.path.join(dirpath, filename))
|
|
||||||
|
|
||||||
return python_files
|
|
||||||
|
|
||||||
|
|
||||||
def start():
|
|
||||||
current_directory = os.getcwd()
|
|
||||||
py_files = list_python_files(current_directory)
|
|
||||||
for file in py_files:
|
|
||||||
port(file)
|
|
||||||
|
|
||||||
|
|
||||||
start()
|
|
||||||
3872
dist/ba_root/mods/serverData/Chat Logs.log
vendored
3872
dist/ba_root/mods/serverData/Chat Logs.log
vendored
File diff suppressed because it is too large
Load diff
7
dist/ba_root/mods/serverData/__init__.py
vendored
7
dist/ba_root/mods/serverData/__init__.py
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
"""Common bits of functionality shared between all efro projects.
|
|
||||||
|
|
||||||
Things in here should be hardened, highly type-safe, and well-covered by unit
|
|
||||||
tests since they are widely used in live client and server code.
|
|
||||||
|
|
||||||
license : MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
0
dist/ba_root/mods/serverData/data.json
vendored
0
dist/ba_root/mods/serverData/data.json
vendored
3896
dist/ba_root/mods/serverData/joining.log
vendored
3896
dist/ba_root/mods/serverData/joining.log
vendored
File diff suppressed because it is too large
Load diff
8604
dist/ba_root/mods/serverData/logs.log
vendored
8604
dist/ba_root/mods/serverData/logs.log
vendored
File diff suppressed because it is too large
Load diff
38
dist/ba_root/mods/serverData/serverdata.py
vendored
38
dist/ba_root/mods/serverData/serverdata.py
vendored
|
|
@ -1,38 +0,0 @@
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
|
|
||||||
import fcntl
|
|
||||||
import os
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
|
|
||||||
clients = {}
|
|
||||||
cachedclients = []
|
|
||||||
muted = False
|
|
||||||
coopmode = False
|
|
||||||
ips = {}
|
|
||||||
recents = []
|
|
||||||
|
|
||||||
SERVER_DATA_PATH = os.path.join(
|
|
||||||
_babase.env()["python_directory_user"], "serverdata" + os.sep
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_stats_index():
|
|
||||||
return [x for x in os.listdir(SERVER_DATA_PATH) if "log" in x]
|
|
||||||
|
|
||||||
|
|
||||||
def read_logs(filename):
|
|
||||||
file_path = SERVER_DATA_PATH + filename
|
|
||||||
if not os.path.exists(file_path):
|
|
||||||
return ""
|
|
||||||
file = open(file_path, "r")
|
|
||||||
fcntl.flock(file.fileno(), fcntl.LOCK_SH)
|
|
||||||
contents = ""
|
|
||||||
try:
|
|
||||||
contents = file.read()
|
|
||||||
|
|
||||||
finally:
|
|
||||||
fcntl.flock(file.fileno(), fcntl.LOCK_UN)
|
|
||||||
file.close()
|
|
||||||
|
|
||||||
return contents
|
|
||||||
4452
dist/ba_root/mods/serverData/systemlogs.log
vendored
4452
dist/ba_root/mods/serverData/systemlogs.log
vendored
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
180
dist/ba_root/mods/setting.json
vendored
180
dist/ba_root/mods/setting.json
vendored
|
|
@ -1,180 +0,0 @@
|
||||||
{
|
|
||||||
"Anti-IdRevealer": false,
|
|
||||||
"ChatCommands": {
|
|
||||||
"BrodcastCommand": true
|
|
||||||
},
|
|
||||||
"HostDeviceName": "v1.4",
|
|
||||||
"HostName": "BCS",
|
|
||||||
"KickVoteMsgType": "chat",
|
|
||||||
"ScoreScreenAnnouncement": {
|
|
||||||
"enable": true,
|
|
||||||
"msg": [
|
|
||||||
"click stats button to join discord",
|
|
||||||
"watch hey smoothy youtube channel",
|
|
||||||
"downlaod new mods from discord"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ShowKickVoteStarterName": true,
|
|
||||||
"StumbledScoreScreen": true,
|
|
||||||
"WarnCooldownMinutes": 30,
|
|
||||||
"afk_remover": {
|
|
||||||
"enable": false,
|
|
||||||
"ingame_idle_time_in_secs": 60,
|
|
||||||
"kick_idle_from_lobby": true,
|
|
||||||
"lobby_idle_time_in_secs": 10
|
|
||||||
},
|
|
||||||
"afterWarnKickMsg": "Enough warnings, Goodbye have a nice day :)",
|
|
||||||
"allowInGameChat": true,
|
|
||||||
"allowTeamChat": true,
|
|
||||||
"allowVotes": true,
|
|
||||||
"autoNightMode": {
|
|
||||||
"enable": true,
|
|
||||||
"endTime": "06:00",
|
|
||||||
"fireflies": true,
|
|
||||||
"fireflies_random_color": false,
|
|
||||||
"startTime": "18:30"
|
|
||||||
},
|
|
||||||
"autoTeamBalance": true,
|
|
||||||
"ballistica_web": {
|
|
||||||
"enable": false,
|
|
||||||
"server_password": "my_secerT_password_very_hard"
|
|
||||||
},
|
|
||||||
"character_chooser": {
|
|
||||||
"enable": true
|
|
||||||
},
|
|
||||||
"colorful_explosions": {
|
|
||||||
"enable": true
|
|
||||||
},
|
|
||||||
"colorfullMap": true,
|
|
||||||
"contributeData": true,
|
|
||||||
"coopModeWithLessPlayers": {
|
|
||||||
"enable": false,
|
|
||||||
"minPlayerToExitCoop": 3
|
|
||||||
},
|
|
||||||
"custom_characters": {
|
|
||||||
"enable": true
|
|
||||||
},
|
|
||||||
"discordWebHook": {
|
|
||||||
"enable": false,
|
|
||||||
"webhookURL": "https://discord.com/api/webhooks/82649239/e7s0zyBJIuczXL7_CGSO5WM"
|
|
||||||
},
|
|
||||||
"discordbot": {
|
|
||||||
"enable": false,
|
|
||||||
"liveChat": true,
|
|
||||||
"liveStatsChannelID": 925440043672285200,
|
|
||||||
"logsChannelID": 925440079843958800,
|
|
||||||
"token": "<secret-token-here>"
|
|
||||||
},
|
|
||||||
"elPatronPowerups": {
|
|
||||||
"Quantity": {
|
|
||||||
"Curse": 1,
|
|
||||||
"Fire Bombs": 3,
|
|
||||||
"Fly Bombs": 3,
|
|
||||||
"Goodbye": 2,
|
|
||||||
"Healing Damage": 1,
|
|
||||||
"Health": 1,
|
|
||||||
"Ice Bombs": 3,
|
|
||||||
"Ice Man": 1,
|
|
||||||
"Impact Bombs": 3,
|
|
||||||
"Impairment Bombs": 2,
|
|
||||||
"Mine Bombs": 2,
|
|
||||||
"Punch": 3,
|
|
||||||
"Shield": 2,
|
|
||||||
"Speed": 2,
|
|
||||||
"Sticky Bombs": 3,
|
|
||||||
"Tank Shield": 1,
|
|
||||||
"Triple": 3
|
|
||||||
},
|
|
||||||
"enable": false,
|
|
||||||
"settings": {
|
|
||||||
"Healing Damage PTG": 72,
|
|
||||||
"Powers Gravity": true,
|
|
||||||
"Powerup Name": true,
|
|
||||||
"Powerup Scale": 1,
|
|
||||||
"Powerup Style": "Auto",
|
|
||||||
"Powerup Time": false,
|
|
||||||
"Powerup With Shield": true,
|
|
||||||
"Powerups": {
|
|
||||||
"Curse": 1,
|
|
||||||
"Fire Bombs": 3,
|
|
||||||
"Fly Bombs": 3,
|
|
||||||
"Goodbye": 2,
|
|
||||||
"Healing Damage": 1,
|
|
||||||
"Health": 1,
|
|
||||||
"Ice Bombs": 3,
|
|
||||||
"Ice Man": 1,
|
|
||||||
"Impact Bombs": 3,
|
|
||||||
"Impairment Bombs": 2,
|
|
||||||
"Mine Bombs": 2,
|
|
||||||
"Punch": 3,
|
|
||||||
"Shield": 2,
|
|
||||||
"Speed": 2,
|
|
||||||
"Sticky Bombs": 3,
|
|
||||||
"Tank Shield": 1,
|
|
||||||
"Triple": 3
|
|
||||||
},
|
|
||||||
"Tank Shield PTG": 96
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"enableHitTexts": true,
|
|
||||||
"enableTagAnimation": true,
|
|
||||||
"enableTop5effects": true,
|
|
||||||
"enableeffects": true,
|
|
||||||
"enablehptag": true,
|
|
||||||
"enablerank": true,
|
|
||||||
"enablestats": true,
|
|
||||||
"enabletags": true,
|
|
||||||
"firstTimeJoinMsg": "Welcome to the server,we r saving all your account details and chats",
|
|
||||||
"leaderboard": {
|
|
||||||
"barsBehindName": true,
|
|
||||||
"enable": true
|
|
||||||
},
|
|
||||||
"maxAccountPerIP": 3,
|
|
||||||
"maxPlayersPerDevice": 2,
|
|
||||||
"maxWarnCount": 2,
|
|
||||||
"mikirogQuickTurn": {
|
|
||||||
"enable": false
|
|
||||||
},
|
|
||||||
"minAgeToChatInHours": 78,
|
|
||||||
"minAgeToJoinInHours": 24,
|
|
||||||
"newResultBoard": true,
|
|
||||||
"playermod": {
|
|
||||||
"default_bomb": "normal",
|
|
||||||
"default_bomb_count": 1,
|
|
||||||
"default_boxing_gloves": true,
|
|
||||||
"default_shield": false
|
|
||||||
},
|
|
||||||
"playlists": {
|
|
||||||
"elim": 412172,
|
|
||||||
"epic": 412173,
|
|
||||||
"ffa": 412175,
|
|
||||||
"ffasmash": 412179,
|
|
||||||
"smash": 412151,
|
|
||||||
"soccer": 412160,
|
|
||||||
"team": 12345
|
|
||||||
},
|
|
||||||
"regularWelcomeMsg": "Welcome Back",
|
|
||||||
"sameCharacterForTeam": false,
|
|
||||||
"statsResetAfterDays": 31,
|
|
||||||
"textonmap": {
|
|
||||||
"bottom left watermark": "Owner : <owner-name> \nEditor : <bablu>\nScripts : BCS1.7.13",
|
|
||||||
"center highlights": {
|
|
||||||
"color": [
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"msg": [
|
|
||||||
"type end to start end vote",
|
|
||||||
"start msg with prefix .(dot) to send in game popup msg",
|
|
||||||
"start msg with prefix ,(comma) to send msg to teammates",
|
|
||||||
"BombSquad Community Server - BCS"
|
|
||||||
],
|
|
||||||
"randomColor": true
|
|
||||||
},
|
|
||||||
"top watermark": "Welcome to server \nIP @IP PORT @PORT"
|
|
||||||
},
|
|
||||||
"useV2Account": false,
|
|
||||||
"warnMsg": "WARNING !!!",
|
|
||||||
"whitelist": false
|
|
||||||
}
|
|
||||||
50
dist/ba_root/mods/setting.py
vendored
50
dist/ba_root/mods/setting.py
vendored
|
|
@ -1,50 +0,0 @@
|
||||||
"""Module to update `setting.json`."""
|
|
||||||
|
|
||||||
# ba_meta require api 8
|
|
||||||
# (see https://ballistica.net/wiki/meta-tag-system)
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import json
|
|
||||||
from functools import lru_cache
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
pass
|
|
||||||
|
|
||||||
SETTINGS_PATH = _babase.env().get("python_directory_user", "") + "/setting.json"
|
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=None)
|
|
||||||
def get_settings_data() -> dict:
|
|
||||||
"""Returns the dictionary of settings related to the server.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
dict
|
|
||||||
settings related to server
|
|
||||||
"""
|
|
||||||
with open(SETTINGS_PATH, mode="r", encoding="utf-8") as data:
|
|
||||||
return json.load(data)
|
|
||||||
|
|
||||||
|
|
||||||
def refresh_cache() -> None:
|
|
||||||
get_settings_data.cache_clear()
|
|
||||||
# lets cache it again
|
|
||||||
get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
def commit(data: dict) -> None:
|
|
||||||
"""Commits the data in setting file.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
data : dict
|
|
||||||
data to be commited
|
|
||||||
"""
|
|
||||||
with open(SETTINGS_PATH, mode="w", encoding="utf-8") as setting_file:
|
|
||||||
json.dump(data, setting_file, indent=4)
|
|
||||||
# settings updated ok now update the cache
|
|
||||||
refresh_cache()
|
|
||||||
7
dist/ba_root/mods/spazmod/__init__.py
vendored
7
dist/ba_root/mods/spazmod/__init__.py
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
"""Common bits of functionality shared between all efro projects.
|
|
||||||
|
|
||||||
Things in here should be hardened, highly type-safe, and well-covered by unit
|
|
||||||
tests since they are widely used in live client and server code.
|
|
||||||
|
|
||||||
license : MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
44
dist/ba_root/mods/spazmod/hitmessage.py
vendored
44
dist/ba_root/mods/spazmod/hitmessage.py
vendored
|
|
@ -1,44 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Released under the MIT License. See LICENSE for details.
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
import setting
|
|
||||||
from bascenev1lib.actor.popuptext import PopupText
|
|
||||||
|
|
||||||
our_settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
def handle_hit(mag, pos):
|
|
||||||
if not mag: return
|
|
||||||
# Send Screen Texts in enabled
|
|
||||||
if our_settings['enableHitTexts']:
|
|
||||||
try:
|
|
||||||
if mag >= 110:
|
|
||||||
PopupText("#PRO !", color=(1, 0.2, 0.2), scale=1.6,
|
|
||||||
position=pos).autoretain()
|
|
||||||
elif mag >= 93 and mag < 110:
|
|
||||||
PopupText("GOOD ONE!", color=(1, 0.3, 0.1), scale=1.6,
|
|
||||||
position=pos).autoretain()
|
|
||||||
elif mag >= 63 and mag < 93:
|
|
||||||
PopupText("OH! YEAH", color=(1, 0.5, 0.2), scale=1.6,
|
|
||||||
position=pos).autoretain()
|
|
||||||
elif mag >= 45 and mag < 63:
|
|
||||||
PopupText("WTF!", color=(0.7, 0.4, 0.2), scale=1.6,
|
|
||||||
position=pos).autoretain()
|
|
||||||
elif mag > 30 and mag < 45:
|
|
||||||
PopupText("!!!", color=(1, 1, 1), scale=1.6,
|
|
||||||
position=pos).autoretain()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
class hit_message(bs.HitMessage):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
hit_type = kwargs["hit_type"]
|
|
||||||
if hit_type == "punch":
|
|
||||||
handle_hit(kwargs['magnitude'], kwargs['pos'])
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
bs.HitMessage = hit_message
|
|
||||||
80
dist/ba_root/mods/spazmod/modifyspaz.py
vendored
80
dist/ba_root/mods/spazmod/modifyspaz.py
vendored
|
|
@ -1,80 +0,0 @@
|
||||||
from random import randint
|
|
||||||
|
|
||||||
import setting
|
|
||||||
from spazmod import tag
|
|
||||||
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
_setting = setting.get_settings_data()
|
|
||||||
|
|
||||||
if _setting['enableeffects']:
|
|
||||||
from spazmod import spaz_effects
|
|
||||||
|
|
||||||
spaz_effects.apply()
|
|
||||||
|
|
||||||
|
|
||||||
def update_name():
|
|
||||||
from stats import mystats
|
|
||||||
stat = mystats.get_all_stats()
|
|
||||||
ros = bs.get_game_roster()
|
|
||||||
for i in ros:
|
|
||||||
if i['account_id']:
|
|
||||||
name = i['display_string']
|
|
||||||
aid = i['account_id']
|
|
||||||
if aid in stat:
|
|
||||||
stat[aid]['name'] = name
|
|
||||||
mystats.dump_stats(stat)
|
|
||||||
|
|
||||||
|
|
||||||
# all activites related to modify spaz by any how will be here
|
|
||||||
|
|
||||||
|
|
||||||
def main(spaz, node, player):
|
|
||||||
if _setting['enablehptag']:
|
|
||||||
tag.addhp(node, spaz)
|
|
||||||
if _setting['enabletags']:
|
|
||||||
tag.addtag(node, player)
|
|
||||||
if _setting['enablerank']:
|
|
||||||
tag.addrank(node, player)
|
|
||||||
if _setting["playermod"]['default_boxing_gloves']:
|
|
||||||
spaz.equip_boxing_gloves()
|
|
||||||
if _setting['playermod']['default_shield']:
|
|
||||||
spaz.equip_shields()
|
|
||||||
spaz.bomb_type_default = _setting['playermod']['default_bomb']
|
|
||||||
spaz.bomb_count = _setting['playermod']['default_bomb_count']
|
|
||||||
# update_name() will add threading here later . it was adding delay on game start
|
|
||||||
|
|
||||||
|
|
||||||
def getCharacter(player, character):
|
|
||||||
if _setting["sameCharacterForTeam"]:
|
|
||||||
|
|
||||||
if "character" in player.team.sessionteam.customdata:
|
|
||||||
return player.team.sessionteam.customdata["character"]
|
|
||||||
|
|
||||||
return character
|
|
||||||
|
|
||||||
|
|
||||||
def getRandomCharacter(otherthen):
|
|
||||||
characters = list(babase.app.spaz_appearances.keys())
|
|
||||||
invalid_characters = ["Snake Shadow", "Lee", "Zola", "Butch", "Witch",
|
|
||||||
"Middle-Man", "Alien", "OldLady", "Wrestler",
|
|
||||||
"Gretel", "Robot"]
|
|
||||||
|
|
||||||
while True:
|
|
||||||
val = randint(0, len(characters) - 1)
|
|
||||||
ch = characters[val]
|
|
||||||
if ch not in invalid_characters and ch not in otherthen:
|
|
||||||
return ch
|
|
||||||
|
|
||||||
|
|
||||||
def setTeamCharacter():
|
|
||||||
if not _setting["sameCharacterForTeam"]:
|
|
||||||
return
|
|
||||||
used = []
|
|
||||||
teams = babase.internal.get_foreground_host_session().sessionteams
|
|
||||||
if len(teams) < 10:
|
|
||||||
for team in teams:
|
|
||||||
character = getRandomCharacter(used)
|
|
||||||
used.append(character)
|
|
||||||
team.name = character
|
|
||||||
team.customdata["character"] = character
|
|
||||||
388
dist/ba_root/mods/spazmod/spaz_effects.py
vendored
388
dist/ba_root/mods/spazmod/spaz_effects.py
vendored
|
|
@ -1,388 +0,0 @@
|
||||||
import functools
|
|
||||||
import random
|
|
||||||
|
|
||||||
import setting
|
|
||||||
from playersData import pdata
|
|
||||||
from stats import mystats
|
|
||||||
from typing import Sequence
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
import bascenev1lib
|
|
||||||
from bascenev1lib.actor.playerspaz import *
|
|
||||||
|
|
||||||
_settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
RANK_EFFECT_MAP = {
|
|
||||||
1: ["rainbow", "shine"],
|
|
||||||
2: ["sweat"],
|
|
||||||
3: ["metal"],
|
|
||||||
4: ["iceground"],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def effect(repeat_interval=0):
|
|
||||||
def _activator(method):
|
|
||||||
@functools.wraps(method)
|
|
||||||
def _inner_activator(self, *args, **kwargs):
|
|
||||||
def _caller():
|
|
||||||
try:
|
|
||||||
method(self, *args, **kwargs)
|
|
||||||
except:
|
|
||||||
if self is None or not self.is_alive() or not self.node.exists():
|
|
||||||
self._activations = []
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
effect_activation = bs.Timer(repeat_interval, babase.Call(_caller),
|
|
||||||
repeat=repeat_interval > 0)
|
|
||||||
self._activations.append(effect_activation)
|
|
||||||
|
|
||||||
return _inner_activator
|
|
||||||
|
|
||||||
return _activator
|
|
||||||
|
|
||||||
|
|
||||||
def node(check_interval=0):
|
|
||||||
def _activator(method):
|
|
||||||
@functools.wraps(method)
|
|
||||||
def _inner_activator(self):
|
|
||||||
node = method(self)
|
|
||||||
|
|
||||||
def _caller():
|
|
||||||
if self is None or not self.is_alive() or not self.node.exists():
|
|
||||||
node.delete()
|
|
||||||
self._activations = []
|
|
||||||
|
|
||||||
node_activation = bs.Timer(check_interval, babase.Call(_caller),
|
|
||||||
repeat=check_interval > 0)
|
|
||||||
try:
|
|
||||||
self._activations.append(node_activation)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return _inner_activator
|
|
||||||
|
|
||||||
return _activator
|
|
||||||
|
|
||||||
|
|
||||||
class NewPlayerSpaz(PlayerSpaz):
|
|
||||||
def __init__(self,
|
|
||||||
player: bs.Player,
|
|
||||||
color: Sequence[float],
|
|
||||||
highlight: Sequence[float],
|
|
||||||
character: str,
|
|
||||||
powerups_expire: bool = True,
|
|
||||||
*args,
|
|
||||||
**kwargs):
|
|
||||||
|
|
||||||
super().__init__(player=player,
|
|
||||||
color=color,
|
|
||||||
highlight=highlight,
|
|
||||||
character=character,
|
|
||||||
powerups_expire=powerups_expire,
|
|
||||||
*args,
|
|
||||||
**kwargs)
|
|
||||||
self._activations = []
|
|
||||||
self.effects = []
|
|
||||||
|
|
||||||
babase._asyncio._asyncio_event_loop.create_task(self.set_effects())
|
|
||||||
|
|
||||||
async def set_effects(self):
|
|
||||||
try:
|
|
||||||
account_id = self._player._sessionplayer.get_v1_account_id()
|
|
||||||
except:
|
|
||||||
return
|
|
||||||
custom_effects = pdata.get_custom()['customeffects']
|
|
||||||
|
|
||||||
if account_id in custom_effects:
|
|
||||||
self.effects = [custom_effects[account_id]] if type(
|
|
||||||
custom_effects[account_id]) is str else custom_effects[
|
|
||||||
account_id]
|
|
||||||
else:
|
|
||||||
# check if we have any effect for his rank.
|
|
||||||
if _settings['enablestats']:
|
|
||||||
stats = mystats.get_cached_stats()
|
|
||||||
if account_id in stats and _settings['enableTop5effects']:
|
|
||||||
rank = stats[account_id]["rank"]
|
|
||||||
self.effects = RANK_EFFECT_MAP[
|
|
||||||
rank] if rank in RANK_EFFECT_MAP else []
|
|
||||||
|
|
||||||
if len(self.effects) == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._effect_mappings = {
|
|
||||||
"spark": self._add_spark,
|
|
||||||
"sparkground": self._add_sparkground,
|
|
||||||
"sweat": self._add_sweat,
|
|
||||||
"sweatground": self._add_sweatground,
|
|
||||||
"distortion": self._add_distortion,
|
|
||||||
"glow": self._add_glow,
|
|
||||||
"shine": self._add_shine,
|
|
||||||
"highlightshine": self._add_highlightshine,
|
|
||||||
"scorch": self._add_scorch,
|
|
||||||
"ice": self._add_ice,
|
|
||||||
"iceground": self._add_iceground,
|
|
||||||
"slime": self._add_slime,
|
|
||||||
"metal": self._add_metal,
|
|
||||||
"splinter": self._add_splinter,
|
|
||||||
"rainbow": self._add_rainbow,
|
|
||||||
"fairydust": self._add_fairydust,
|
|
||||||
"noeffect": lambda: None,
|
|
||||||
}
|
|
||||||
|
|
||||||
for effect in self.effects:
|
|
||||||
trigger = self._effect_mappings[
|
|
||||||
effect] if effect in self._effect_mappings else lambda: None
|
|
||||||
activity = self._activity()
|
|
||||||
if activity:
|
|
||||||
with activity.context:
|
|
||||||
trigger()
|
|
||||||
|
|
||||||
@effect(repeat_interval=0.1)
|
|
||||||
def _add_spark(self):
|
|
||||||
bs.emitfx(
|
|
||||||
position=self.node.position,
|
|
||||||
velocity=self.node.velocity,
|
|
||||||
count=random.randint(1, 10),
|
|
||||||
scale=0.5,
|
|
||||||
spread=0.2,
|
|
||||||
chunk_type="spark",
|
|
||||||
)
|
|
||||||
|
|
||||||
@effect(repeat_interval=0.1)
|
|
||||||
def _add_sparkground(self):
|
|
||||||
bs.emitfx(
|
|
||||||
position=self.node.position,
|
|
||||||
velocity=self.node.velocity,
|
|
||||||
count=random.randint(1, 5),
|
|
||||||
scale=0.2,
|
|
||||||
spread=0.1,
|
|
||||||
chunk_type="spark",
|
|
||||||
emit_type="stickers",
|
|
||||||
)
|
|
||||||
|
|
||||||
@effect(repeat_interval=0.04)
|
|
||||||
def _add_sweat(self):
|
|
||||||
velocity = 4.0
|
|
||||||
calculate_position = lambda \
|
|
||||||
torso_position: torso_position - 0.25 + random.uniform(0, 0.5)
|
|
||||||
calculate_velocity = lambda node_velocity, multiplier: random.uniform(
|
|
||||||
-velocity, velocity) + node_velocity * multiplier
|
|
||||||
position = tuple(calculate_position(coordinate)
|
|
||||||
for coordinate in self.node.torso_position)
|
|
||||||
velocity = (
|
|
||||||
calculate_velocity(self.node.velocity[0], 2),
|
|
||||||
calculate_velocity(self.node.velocity[1], 4),
|
|
||||||
calculate_velocity(self.node.velocity[2], 2),
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=10,
|
|
||||||
scale=random.uniform(0.3, 1.4),
|
|
||||||
spread=0.1,
|
|
||||||
chunk_type="sweat",
|
|
||||||
)
|
|
||||||
|
|
||||||
@effect(repeat_interval=0.04)
|
|
||||||
def _add_sweatground(self):
|
|
||||||
velocity = 1.2
|
|
||||||
calculate_position = lambda \
|
|
||||||
torso_position: torso_position - 0.25 + random.uniform(0, 0.5)
|
|
||||||
calculate_velocity = lambda node_velocity, multiplier: random.uniform(
|
|
||||||
-velocity, velocity) + node_velocity * multiplier
|
|
||||||
position = tuple(calculate_position(coordinate)
|
|
||||||
for coordinate in self.node.torso_position)
|
|
||||||
velocity = (
|
|
||||||
calculate_velocity(self.node.velocity[0], 2),
|
|
||||||
calculate_velocity(self.node.velocity[1], 4),
|
|
||||||
calculate_velocity(self.node.velocity[2], 2),
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=10,
|
|
||||||
scale=random.uniform(0.1, 1.2),
|
|
||||||
spread=0.1,
|
|
||||||
chunk_type="sweat",
|
|
||||||
emit_type="stickers",
|
|
||||||
)
|
|
||||||
|
|
||||||
@effect(repeat_interval=1.0)
|
|
||||||
def _add_distortion(self):
|
|
||||||
bs.emitfx(
|
|
||||||
position=self.node.position,
|
|
||||||
spread=1.0,
|
|
||||||
emit_type="distortion"
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=self.node.position,
|
|
||||||
velocity=self.node.velocity,
|
|
||||||
count=random.randint(1, 5),
|
|
||||||
emit_type="tendrils",
|
|
||||||
tendril_type="smoke",
|
|
||||||
)
|
|
||||||
|
|
||||||
@effect(repeat_interval=3.0)
|
|
||||||
def _add_shine(self):
|
|
||||||
shine_factor = 1.2
|
|
||||||
dim_factor = 0.90
|
|
||||||
|
|
||||||
default_color = self.node.color
|
|
||||||
shiny_color = tuple(channel * shine_factor for channel in default_color)
|
|
||||||
dimmy_color = tuple(channel * dim_factor for channel in default_color)
|
|
||||||
animation = {
|
|
||||||
0: default_color,
|
|
||||||
1: dimmy_color,
|
|
||||||
2: shiny_color,
|
|
||||||
3: default_color,
|
|
||||||
}
|
|
||||||
bs.animate_array(self.node, "color", 3, animation)
|
|
||||||
|
|
||||||
@effect(repeat_interval=9.0)
|
|
||||||
def _add_highlightshine(self):
|
|
||||||
shine_factor = 1.2
|
|
||||||
dim_factor = 0.90
|
|
||||||
|
|
||||||
default_highlight = self.node.highlight
|
|
||||||
shiny_highlight = tuple(
|
|
||||||
channel * shine_factor for channel in default_highlight)
|
|
||||||
dimmy_highlight = tuple(
|
|
||||||
channel * dim_factor for channel in default_highlight)
|
|
||||||
animation = {
|
|
||||||
0: default_highlight,
|
|
||||||
3: dimmy_highlight,
|
|
||||||
6: shiny_highlight,
|
|
||||||
9: default_highlight,
|
|
||||||
}
|
|
||||||
bs.animate_array(self.node, "highlight", 3, animation)
|
|
||||||
|
|
||||||
@effect(repeat_interval=2.0)
|
|
||||||
def _add_rainbow(self):
|
|
||||||
highlight = tuple(random.random() for _ in range(3))
|
|
||||||
highlight = babase.safecolor(highlight)
|
|
||||||
animation = {
|
|
||||||
0: self.node.highlight,
|
|
||||||
2: highlight,
|
|
||||||
}
|
|
||||||
bs.animate_array(self.node, "highlight", 3, animation)
|
|
||||||
|
|
||||||
@node(check_interval=0.5)
|
|
||||||
def _add_glow(self):
|
|
||||||
glowing_light = bs.newnode(
|
|
||||||
"light",
|
|
||||||
attrs={
|
|
||||||
"color": (1.0, 0.4, 0.5),
|
|
||||||
"height_attenuated": False,
|
|
||||||
"radius": 0.4}
|
|
||||||
)
|
|
||||||
self.node.connectattr("position", glowing_light, "position")
|
|
||||||
bs.animate(
|
|
||||||
glowing_light,
|
|
||||||
"intensity",
|
|
||||||
{0: 0.0, 1: 0.2, 2: 0.0},
|
|
||||||
loop=True)
|
|
||||||
return glowing_light
|
|
||||||
|
|
||||||
@node(check_interval=0.5)
|
|
||||||
def _add_scorch(self):
|
|
||||||
scorcher = bs.newnode(
|
|
||||||
"scorch",
|
|
||||||
attrs={
|
|
||||||
"position": self.node.position,
|
|
||||||
"size": 1.00,
|
|
||||||
"big": True}
|
|
||||||
)
|
|
||||||
self.node.connectattr("position", scorcher, "position")
|
|
||||||
animation = {
|
|
||||||
0: (1, 0, 0),
|
|
||||||
1: (0, 1, 0),
|
|
||||||
2: (1, 0, 1),
|
|
||||||
3: (0, 1, 1),
|
|
||||||
4: (1, 0, 0),
|
|
||||||
}
|
|
||||||
bs.animate_array(scorcher, "color", 3, animation, loop=True)
|
|
||||||
return scorcher
|
|
||||||
|
|
||||||
@effect(repeat_interval=0.5)
|
|
||||||
def _add_ice(self):
|
|
||||||
bs.emitfx(
|
|
||||||
position=self.node.position,
|
|
||||||
velocity=self.node.velocity,
|
|
||||||
count=random.randint(2, 8),
|
|
||||||
scale=0.4,
|
|
||||||
spread=0.2,
|
|
||||||
chunk_type="ice",
|
|
||||||
)
|
|
||||||
|
|
||||||
@effect(repeat_interval=0.05)
|
|
||||||
def _add_iceground(self):
|
|
||||||
bs.emitfx(
|
|
||||||
position=self.node.position,
|
|
||||||
velocity=self.node.velocity,
|
|
||||||
count=random.randint(1, 2),
|
|
||||||
scale=random.uniform(0, 0.5),
|
|
||||||
spread=1.0,
|
|
||||||
chunk_type="ice",
|
|
||||||
emit_type="stickers",
|
|
||||||
)
|
|
||||||
|
|
||||||
@effect(repeat_interval=0.25)
|
|
||||||
def _add_slime(self):
|
|
||||||
bs.emitfx(
|
|
||||||
position=self.node.position,
|
|
||||||
velocity=self.node.velocity,
|
|
||||||
count=random.randint(1, 10),
|
|
||||||
scale=0.4,
|
|
||||||
spread=0.2,
|
|
||||||
chunk_type="slime",
|
|
||||||
)
|
|
||||||
|
|
||||||
@effect(repeat_interval=0.25)
|
|
||||||
def _add_metal(self):
|
|
||||||
bs.emitfx(
|
|
||||||
position=self.node.position,
|
|
||||||
velocity=self.node.velocity,
|
|
||||||
count=random.randint(1, 4),
|
|
||||||
scale=0.4,
|
|
||||||
spread=0.1,
|
|
||||||
chunk_type="metal",
|
|
||||||
)
|
|
||||||
|
|
||||||
@effect(repeat_interval=0.75)
|
|
||||||
def _add_splinter(self):
|
|
||||||
bs.emitfx(
|
|
||||||
position=self.node.position,
|
|
||||||
velocity=self.node.velocity,
|
|
||||||
count=random.randint(1, 5),
|
|
||||||
scale=0.5,
|
|
||||||
spread=0.2,
|
|
||||||
chunk_type="splinter",
|
|
||||||
)
|
|
||||||
|
|
||||||
@effect(repeat_interval=0.25)
|
|
||||||
def _add_fairydust(self):
|
|
||||||
velocity = 2
|
|
||||||
calculate_position = lambda \
|
|
||||||
torso_position: torso_position - 0.25 + random.uniform(0, 0.5)
|
|
||||||
calculate_velocity = lambda node_velocity, multiplier: random.uniform(
|
|
||||||
-velocity, velocity) + node_velocity * multiplier
|
|
||||||
position = tuple(calculate_position(coordinate)
|
|
||||||
for coordinate in self.node.torso_position)
|
|
||||||
velocity = (
|
|
||||||
calculate_velocity(self.node.velocity[0], 2),
|
|
||||||
calculate_velocity(self.node.velocity[1], 4),
|
|
||||||
calculate_velocity(self.node.velocity[2], 2),
|
|
||||||
)
|
|
||||||
bs.emitfx(
|
|
||||||
position=position,
|
|
||||||
velocity=velocity,
|
|
||||||
count=random.randint(1, 10),
|
|
||||||
spread=0.1,
|
|
||||||
emit_type="fairydust",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def apply() -> None:
|
|
||||||
bascenev1lib.actor.playerspaz.PlayerSpaz = NewPlayerSpaz
|
|
||||||
169
dist/ba_root/mods/spazmod/tag.py
vendored
169
dist/ba_root/mods/spazmod/tag.py
vendored
|
|
@ -1,169 +0,0 @@
|
||||||
import setting
|
|
||||||
from playersData import pdata
|
|
||||||
from stats import mystats
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1 as bs
|
|
||||||
|
|
||||||
sett = setting.get_settings_data()
|
|
||||||
|
|
||||||
|
|
||||||
def addtag(node, player):
|
|
||||||
session_player = player.sessionplayer
|
|
||||||
account_id = session_player.get_v1_account_id()
|
|
||||||
customtag_ = pdata.get_custom()
|
|
||||||
customtag = customtag_['customtag']
|
|
||||||
roles = pdata.get_roles()
|
|
||||||
p_roles = pdata.get_player_roles(account_id)
|
|
||||||
tag = None
|
|
||||||
col = (0.5, 0.5, 1) # default color for custom tags
|
|
||||||
if account_id in customtag:
|
|
||||||
tag = customtag[account_id]
|
|
||||||
elif p_roles != []:
|
|
||||||
for role in roles:
|
|
||||||
if role in p_roles:
|
|
||||||
tag = roles[role]['tag']
|
|
||||||
col = (
|
|
||||||
0.7, 0.7, 0.7) if 'tagcolor' not in roles[role] else \
|
|
||||||
roles[role]['tagcolor']
|
|
||||||
break
|
|
||||||
if tag:
|
|
||||||
Tag(node, tag, col)
|
|
||||||
|
|
||||||
|
|
||||||
def addrank(node, player):
|
|
||||||
session_player = player.sessionplayer
|
|
||||||
account_id = session_player.get_v1_account_id()
|
|
||||||
rank = mystats.getRank(account_id)
|
|
||||||
|
|
||||||
if rank:
|
|
||||||
Rank(node, rank)
|
|
||||||
|
|
||||||
|
|
||||||
def addhp(node, spaz):
|
|
||||||
def showHP():
|
|
||||||
hp = spaz.hitpoints
|
|
||||||
if spaz.node.exists():
|
|
||||||
HitPoint(owner=node, prefix=str(int(hp)),
|
|
||||||
position=(0, 1.75, 0), shad=1.4)
|
|
||||||
else:
|
|
||||||
spaz.hptimer = None
|
|
||||||
|
|
||||||
spaz.hptimer = bs.Timer(100, babase.Call(
|
|
||||||
showHP), repeat=True)
|
|
||||||
|
|
||||||
|
|
||||||
class Tag(object):
|
|
||||||
def __init__(self, owner=None, tag="somthing", col=(1, 1, 1)):
|
|
||||||
self.node = owner
|
|
||||||
|
|
||||||
mnode = bs.newnode('math',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'input1': (0, 1.5, 0),
|
|
||||||
'operation': 'add'
|
|
||||||
})
|
|
||||||
self.node.connectattr('torso_position', mnode, 'input2')
|
|
||||||
if '\\' in tag:
|
|
||||||
tag = tag.replace('\\d', ('\ue048'))
|
|
||||||
tag = tag.replace('\\c', ('\ue043'))
|
|
||||||
tag = tag.replace('\\h', ('\ue049'))
|
|
||||||
tag = tag.replace('\\s', ('\ue046'))
|
|
||||||
tag = tag.replace('\\n', ('\ue04b'))
|
|
||||||
tag = tag.replace('\\f', ('\ue04f'))
|
|
||||||
tag = tag.replace('\\g', ('\ue027'))
|
|
||||||
tag = tag.replace('\\i', ('\ue03a'))
|
|
||||||
tag = tag.replace('\\m', ('\ue04d'))
|
|
||||||
tag = tag.replace('\\t', ('\ue01f'))
|
|
||||||
tag = tag.replace('\\bs', ('\ue01e'))
|
|
||||||
tag = tag.replace('\\j', ('\ue010'))
|
|
||||||
tag = tag.replace('\\e', ('\ue045'))
|
|
||||||
tag = tag.replace('\\l', ('\ue047'))
|
|
||||||
tag = tag.replace('\\a', ('\ue020'))
|
|
||||||
tag = tag.replace('\\b', ('\ue00c'))
|
|
||||||
|
|
||||||
self.tag_text = bs.newnode('text',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'text': tag,
|
|
||||||
'in_world': True,
|
|
||||||
'shadow': 1.0,
|
|
||||||
'flatness': 1.0,
|
|
||||||
'color': tuple(col),
|
|
||||||
'scale': 0.01,
|
|
||||||
'h_align': 'center'
|
|
||||||
})
|
|
||||||
mnode.connectattr('output', self.tag_text, 'position')
|
|
||||||
if sett["enableTagAnimation"]:
|
|
||||||
bs.animate_array(node=self.tag_text, attr='color', size=3, keys={
|
|
||||||
0.2: (2, 0, 2),
|
|
||||||
0.4: (2, 2, 0),
|
|
||||||
0.6: (0, 2, 2),
|
|
||||||
0.8: (2, 0, 2),
|
|
||||||
1.0: (1, 1, 0),
|
|
||||||
1.2: (0, 1, 1),
|
|
||||||
1.4: (1, 0, 1)
|
|
||||||
}, loop=True)
|
|
||||||
|
|
||||||
|
|
||||||
class Rank(object):
|
|
||||||
def __init__(self, owner=None, rank=99):
|
|
||||||
self.node = owner
|
|
||||||
mnode = bs.newnode('math',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'input1': (0, 1.2, 0),
|
|
||||||
'operation': 'add'
|
|
||||||
})
|
|
||||||
self.node.connectattr('torso_position', mnode, 'input2')
|
|
||||||
if (rank == 1):
|
|
||||||
rank = '\ue01f' + "#" + str(rank) + '\ue01f'
|
|
||||||
elif (rank == 2):
|
|
||||||
rank = '\ue01f' + "#" + str(rank) + '\ue01f'
|
|
||||||
elif (rank == 3):
|
|
||||||
rank = '\ue01f' + "#" + str(rank) + '\ue01f'
|
|
||||||
else:
|
|
||||||
rank = "#" + str(rank)
|
|
||||||
|
|
||||||
self.rank_text = bs.newnode('text',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'text': rank,
|
|
||||||
'in_world': True,
|
|
||||||
'shadow': 1.0,
|
|
||||||
'flatness': 1.0,
|
|
||||||
'color': (1, 1, 1),
|
|
||||||
'scale': 0.01,
|
|
||||||
'h_align': 'center'
|
|
||||||
})
|
|
||||||
mnode.connectattr('output', self.rank_text, 'position')
|
|
||||||
|
|
||||||
|
|
||||||
class HitPoint(object):
|
|
||||||
def __init__(self, position=(0, 1.5, 0), owner=None, prefix='0', shad=1.2):
|
|
||||||
self.position = position
|
|
||||||
self.node = owner
|
|
||||||
m = bs.newnode('math', owner=self.node, attrs={
|
|
||||||
'input1': self.position, 'operation': 'add'})
|
|
||||||
self.node.connectattr('torso_position', m, 'input2')
|
|
||||||
prefix = int(prefix) / 10
|
|
||||||
preFix = u"\ue047" + str(prefix) + u"\ue047"
|
|
||||||
self._Text = bs.newnode('text',
|
|
||||||
owner=self.node,
|
|
||||||
attrs={
|
|
||||||
'text': preFix,
|
|
||||||
'in_world': True,
|
|
||||||
'shadow': shad,
|
|
||||||
'flatness': 1.0,
|
|
||||||
'color': (1, 1, 1) if int(
|
|
||||||
prefix) >= 20 else (1.0, 0.2, 0.2),
|
|
||||||
'scale': 0.01,
|
|
||||||
'h_align': 'center'})
|
|
||||||
m.connectattr('output', self._Text, 'position')
|
|
||||||
|
|
||||||
def a():
|
|
||||||
self._Text.delete()
|
|
||||||
m.delete()
|
|
||||||
|
|
||||||
self.timer = bs.Timer(1.2, babase.Call(
|
|
||||||
a))
|
|
||||||
7
dist/ba_root/mods/stats/__init__.py
vendored
7
dist/ba_root/mods/stats/__init__.py
vendored
|
|
@ -1,7 +0,0 @@
|
||||||
"""Common bits of functionality shared between all efro projects.
|
|
||||||
|
|
||||||
Things in here should be hardened, highly type-safe, and well-covered by unit
|
|
||||||
tests since they are widely used in live client and server code.
|
|
||||||
|
|
||||||
license : MIT, see LICENSE for more details.
|
|
||||||
"""
|
|
||||||
299
dist/ba_root/mods/stats/mystats.py
vendored
299
dist/ba_root/mods/stats/mystats.py
vendored
|
|
@ -1,299 +0,0 @@
|
||||||
import datetime
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import threading
|
|
||||||
import urllib.request
|
|
||||||
|
|
||||||
import _babase
|
|
||||||
import setting
|
|
||||||
|
|
||||||
damage_data = {}
|
|
||||||
|
|
||||||
ranks = []
|
|
||||||
top3Name = []
|
|
||||||
|
|
||||||
our_settings = setting.get_settings_data()
|
|
||||||
|
|
||||||
base_path = os.path.join(_babase.env()['python_directory_user'],
|
|
||||||
"stats" + os.sep)
|
|
||||||
statsfile = base_path + 'stats.json'
|
|
||||||
cached_stats = {}
|
|
||||||
statsDefault = {
|
|
||||||
"pb-IF4VAk4a": {
|
|
||||||
"rank": 65,
|
|
||||||
"name": "pb-IF4VAk4a",
|
|
||||||
"scores": 0,
|
|
||||||
"total_damage": 0.0,
|
|
||||||
"kills": 0,
|
|
||||||
"deaths": 0,
|
|
||||||
"games": 18,
|
|
||||||
"kd": 0.0,
|
|
||||||
"avg_score": 0.0,
|
|
||||||
"aid": "pb-IF4VAk4a",
|
|
||||||
"last_seen": "2022-04-26 17:01:13.715014"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
seasonStartDate = None
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_stats():
|
|
||||||
global seasonStartDate
|
|
||||||
if os.path.exists(statsfile):
|
|
||||||
with open(statsfile, 'r', encoding='utf8') as f:
|
|
||||||
try:
|
|
||||||
jsonData = json.loads(f.read())
|
|
||||||
except:
|
|
||||||
f = open(statsfile + ".backup", encoding='utf-8')
|
|
||||||
jsonData = json.load(f)
|
|
||||||
try:
|
|
||||||
stats = jsonData["stats"]
|
|
||||||
seasonStartDate = datetime.datetime.strptime(
|
|
||||||
jsonData["startDate"], "%d-%m-%Y")
|
|
||||||
_babase.season_ends_in_days = our_settings[
|
|
||||||
"statsResetAfterDays"] - (
|
|
||||||
datetime.datetime.now() - seasonStartDate).days
|
|
||||||
if (datetime.datetime.now() - seasonStartDate).days >= \
|
|
||||||
our_settings["statsResetAfterDays"]:
|
|
||||||
backupStatsFile()
|
|
||||||
seasonStartDate = datetime.datetime.now()
|
|
||||||
return statsDefault
|
|
||||||
return stats
|
|
||||||
except OSError as e:
|
|
||||||
print(e)
|
|
||||||
return jsonData
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
def backupStatsFile():
|
|
||||||
shutil.copy(statsfile, statsfile.replace(
|
|
||||||
".json", "") + str(
|
|
||||||
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + ".json")
|
|
||||||
|
|
||||||
|
|
||||||
def dump_stats(s: dict):
|
|
||||||
global seasonStartDate
|
|
||||||
if seasonStartDate == None:
|
|
||||||
seasonStartDate = datetime.datetime.now()
|
|
||||||
s = {"startDate": seasonStartDate.strftime("%d-%m-%Y"), "stats": s}
|
|
||||||
if os.path.exists(statsfile):
|
|
||||||
shutil.copyfile(statsfile, statsfile + ".backup")
|
|
||||||
with open(statsfile, 'w', encoding='utf8') as f:
|
|
||||||
f.write(json.dumps(s, indent=4, ensure_ascii=False))
|
|
||||||
f.close()
|
|
||||||
else:
|
|
||||||
print('Stats file not found!')
|
|
||||||
|
|
||||||
|
|
||||||
def get_stats_by_id(account_id: str):
|
|
||||||
a = get_cached_stats()
|
|
||||||
if account_id in a:
|
|
||||||
return a[account_id]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def get_cached_stats():
|
|
||||||
return cached_stats
|
|
||||||
|
|
||||||
|
|
||||||
def get_sorted_stats(stats):
|
|
||||||
entries = [(a['scores'], a['kills'], a['deaths'], a['games'],
|
|
||||||
a['name'], a['aid']) for a in stats.values()]
|
|
||||||
# this gives us a list of kills/names sorted high-to-low
|
|
||||||
entries.sort(key=lambda x: x[1] or 0, reverse=True)
|
|
||||||
return entries
|
|
||||||
|
|
||||||
|
|
||||||
def refreshStats():
|
|
||||||
global cached_stats
|
|
||||||
# lastly, write a pretty html version.
|
|
||||||
# our stats url could point at something like this...
|
|
||||||
pStats = get_all_stats()
|
|
||||||
cached_stats = pStats
|
|
||||||
entries = get_sorted_stats(pStats)
|
|
||||||
rank = 0
|
|
||||||
toppersIDs = []
|
|
||||||
_ranks = []
|
|
||||||
for entry in entries:
|
|
||||||
if True:
|
|
||||||
rank += 1
|
|
||||||
scores = str(entry[0])
|
|
||||||
kills = str(entry[1])
|
|
||||||
deaths = str(entry[2])
|
|
||||||
games = str(entry[3])
|
|
||||||
name = str(entry[4])
|
|
||||||
aid = str(entry[5])
|
|
||||||
if rank < 6:
|
|
||||||
toppersIDs.append(aid)
|
|
||||||
# The below kd and avg_score will not be added to website's html document, it will be only added in stats.json
|
|
||||||
try:
|
|
||||||
kd = str(float(kills) / float(deaths))
|
|
||||||
kd_int = kd.split('.')[0]
|
|
||||||
kd_dec = kd.split('.')[1]
|
|
||||||
p_kd = kd_int + '.' + kd_dec[:3]
|
|
||||||
except Exception:
|
|
||||||
p_kd = "0"
|
|
||||||
try:
|
|
||||||
avg_score = str(float(scores) / float(games))
|
|
||||||
avg_score_int = avg_score.split('.')[0]
|
|
||||||
avg_score_dec = avg_score.split('.')[1]
|
|
||||||
p_avg_score = avg_score_int + '.' + avg_score_dec[:3]
|
|
||||||
except Exception:
|
|
||||||
p_avg_score = "0"
|
|
||||||
if damage_data and aid in damage_data:
|
|
||||||
dmg = damage_data[aid]
|
|
||||||
dmg = str(str(dmg).split('.')[
|
|
||||||
0] + '.' + str(dmg).split('.')[1][:3])
|
|
||||||
else:
|
|
||||||
dmg = 0
|
|
||||||
|
|
||||||
_ranks.append(aid)
|
|
||||||
|
|
||||||
pStats[str(aid)]["rank"] = int(rank)
|
|
||||||
pStats[str(aid)]["scores"] = int(scores)
|
|
||||||
# not working properly
|
|
||||||
pStats[str(aid)]["total_damage"] += float(dmg)
|
|
||||||
pStats[str(aid)]["games"] = int(games)
|
|
||||||
pStats[str(aid)]["kills"] = int(kills)
|
|
||||||
pStats[str(aid)]["deaths"] = int(deaths)
|
|
||||||
pStats[str(aid)]["kd"] = float(p_kd)
|
|
||||||
pStats[str(aid)]["avg_score"] = float(p_avg_score)
|
|
||||||
|
|
||||||
global ranks
|
|
||||||
ranks = _ranks
|
|
||||||
|
|
||||||
dump_stats(pStats)
|
|
||||||
updateTop3Names(toppersIDs[0:3])
|
|
||||||
|
|
||||||
from playersData import pdata
|
|
||||||
pdata.update_toppers(toppersIDs)
|
|
||||||
|
|
||||||
|
|
||||||
def update(score_set):
|
|
||||||
"""
|
|
||||||
Given a Session's ScoreSet, tallies per-account kills
|
|
||||||
and passes them to a background thread to process and
|
|
||||||
store.
|
|
||||||
"""
|
|
||||||
# look at score-set entries to tally per-account kills for this round
|
|
||||||
|
|
||||||
account_kills = {}
|
|
||||||
account_deaths = {}
|
|
||||||
account_scores = {}
|
|
||||||
|
|
||||||
for p_entry in score_set.get_records().values():
|
|
||||||
account_id = p_entry.player.get_v1_account_id()
|
|
||||||
if account_id is not None:
|
|
||||||
account_kills.setdefault(account_id, 0) # make sure exists
|
|
||||||
account_kills[account_id] += p_entry.accum_kill_count
|
|
||||||
account_deaths.setdefault(account_id, 0) # make sure exists
|
|
||||||
account_deaths[account_id] += p_entry.accum_killed_count
|
|
||||||
account_scores.setdefault(account_id, 0) # make sure exists
|
|
||||||
account_scores[account_id] += p_entry.accumscore
|
|
||||||
# Ok; now we've got a dict of account-ids and kills.
|
|
||||||
# Now lets kick off a background thread to load existing scores
|
|
||||||
# from disk, do display-string lookups for accounts that need them,
|
|
||||||
# and write everything back to disk (along with a pretty html version)
|
|
||||||
# We use a background thread so our server doesn't hitch while doing this.
|
|
||||||
|
|
||||||
if account_scores:
|
|
||||||
UpdateThread(account_kills, account_deaths, account_scores).start()
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateThread(threading.Thread):
|
|
||||||
def __init__(self, account_kills, account_deaths, account_scores):
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
self._account_kills = account_kills
|
|
||||||
self.account_deaths = account_deaths
|
|
||||||
self.account_scores = account_scores
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
# pull our existing stats from disk
|
|
||||||
import datetime
|
|
||||||
try:
|
|
||||||
stats = get_all_stats()
|
|
||||||
except:
|
|
||||||
stats = {}
|
|
||||||
|
|
||||||
# now add this batch of kills to our persistant stats
|
|
||||||
for account_id, kill_count in self._account_kills.items():
|
|
||||||
# add a new entry for any accounts that dont have one
|
|
||||||
if account_id not in stats:
|
|
||||||
# also lets ask the master-server for their account-display-str.
|
|
||||||
# (we only do this when first creating the entry to save time,
|
|
||||||
# though it may be smart to refresh it periodically since
|
|
||||||
# it may change)
|
|
||||||
|
|
||||||
stats[account_id] = {'rank': 0,
|
|
||||||
'name': "deafult name",
|
|
||||||
'scores': 0,
|
|
||||||
'total_damage': 0,
|
|
||||||
'kills': 0,
|
|
||||||
'deaths': 0,
|
|
||||||
'games': 0,
|
|
||||||
'kd': 0,
|
|
||||||
'avg_score': 0,
|
|
||||||
'last_seen': str(datetime.datetime.now()),
|
|
||||||
'aid': str(account_id)}
|
|
||||||
|
|
||||||
# Temporary codes to change 'name_html' to 'name'
|
|
||||||
# if 'name_html' in stats[account_id]:
|
|
||||||
# stats[account_id].pop('name_html')
|
|
||||||
# stats[account_id]['name'] = 'default'
|
|
||||||
url = "http://bombsquadgame.com/bsAccountInfo?buildNumber=20258&accountID=" + account_id
|
|
||||||
data = urllib.request.urlopen(url)
|
|
||||||
if data is not None:
|
|
||||||
try:
|
|
||||||
name = json.loads(data.read())["profileDisplayString"]
|
|
||||||
except ValueError:
|
|
||||||
stats[account_id]['name'] = "???"
|
|
||||||
else:
|
|
||||||
stats[account_id]['name'] = name
|
|
||||||
|
|
||||||
# now increment their kills whether they were already there or not
|
|
||||||
|
|
||||||
stats[account_id]['kills'] += kill_count
|
|
||||||
stats[account_id]['deaths'] += self.account_deaths[account_id]
|
|
||||||
stats[account_id]['scores'] += self.account_scores[account_id]
|
|
||||||
stats[account_id]['last_seen'] = str(datetime.datetime.now())
|
|
||||||
# also incrementing the games played and adding the id
|
|
||||||
stats[account_id]['games'] += 1
|
|
||||||
stats[account_id]['aid'] = str(account_id)
|
|
||||||
# dump our stats back to disk
|
|
||||||
tempppp = None
|
|
||||||
from datetime import datetime
|
|
||||||
dump_stats(stats)
|
|
||||||
# aaand that's it! There IS no step 27!
|
|
||||||
now = datetime.now()
|
|
||||||
update_time = now.strftime("%S:%M:%H - %d %b %y")
|
|
||||||
# print(f"Added {str(len(self._account_kills))} account's stats entries. || {str(update_time)}")
|
|
||||||
refreshStats()
|
|
||||||
|
|
||||||
|
|
||||||
def getRank(acc_id):
|
|
||||||
global ranks
|
|
||||||
if ranks == []:
|
|
||||||
refreshStats()
|
|
||||||
if acc_id in ranks:
|
|
||||||
return ranks.index(acc_id) + 1
|
|
||||||
|
|
||||||
|
|
||||||
def updateTop3Names(ids):
|
|
||||||
global top3Name
|
|
||||||
names = []
|
|
||||||
for id in ids:
|
|
||||||
url = "http://bombsquadgame.com/bsAccountInfo?buildNumber=20258&accountID=" + id
|
|
||||||
data = urllib.request.urlopen(url)
|
|
||||||
if data is not None:
|
|
||||||
try:
|
|
||||||
name = json.loads(data.read())["profileDisplayString"]
|
|
||||||
if (not name):
|
|
||||||
raise ValueError
|
|
||||||
except ValueError:
|
|
||||||
names.append("???")
|
|
||||||
else:
|
|
||||||
names.append(name)
|
|
||||||
top3Name = names
|
|
||||||
18
dist/ba_root/mods/stats/stats.json
vendored
18
dist/ba_root/mods/stats/stats.json
vendored
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"startDate": "06-08-2023",
|
|
||||||
"stats": {
|
|
||||||
"pb-IF4VAk4a": {
|
|
||||||
"rank": 1,
|
|
||||||
"name": "pb-IF4VAk4a",
|
|
||||||
"scores": 0,
|
|
||||||
"total_damage": 0.0,
|
|
||||||
"kills": 0,
|
|
||||||
"deaths": 0,
|
|
||||||
"games": 18,
|
|
||||||
"kd": 0.0,
|
|
||||||
"avg_score": 0.0,
|
|
||||||
"aid": "pb-IF4VAk4a",
|
|
||||||
"last_seen": "2022-04-26 17:01:13.715014"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
18
dist/ba_root/mods/stats/stats.json.backup
vendored
18
dist/ba_root/mods/stats/stats.json.backup
vendored
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"startDate": "06-08-2023",
|
|
||||||
"stats": {
|
|
||||||
"pb-IF4VAk4a": {
|
|
||||||
"rank": 1,
|
|
||||||
"name": "pb-IF4VAk4a",
|
|
||||||
"scores": 0,
|
|
||||||
"total_damage": 0.0,
|
|
||||||
"kills": 0,
|
|
||||||
"deaths": 0,
|
|
||||||
"games": 18,
|
|
||||||
"kd": 0.0,
|
|
||||||
"avg_score": 0.0,
|
|
||||||
"aid": "pb-IF4VAk4a",
|
|
||||||
"last_seen": "2022-04-26 17:01:13.715014"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
110
dist/ba_root/mods/tools/ServerUpdate.py
vendored
110
dist/ba_root/mods/tools/ServerUpdate.py
vendored
|
|
@ -1,110 +0,0 @@
|
||||||
import _thread
|
|
||||||
import json
|
|
||||||
import time
|
|
||||||
import urllib.request
|
|
||||||
|
|
||||||
import requests
|
|
||||||
from playersData import pdata
|
|
||||||
|
|
||||||
import babase
|
|
||||||
import bascenev1
|
|
||||||
from efro.terminal import Clr
|
|
||||||
|
|
||||||
VERSION = 71
|
|
||||||
|
|
||||||
|
|
||||||
def check():
|
|
||||||
print("hey")
|
|
||||||
print(babase.app.classic)
|
|
||||||
print(babase.app.classic.server)
|
|
||||||
|
|
||||||
_thread.start_new_thread(updateProfilesJson, ())
|
|
||||||
_thread.start_new_thread(checkChangelog, ())
|
|
||||||
|
|
||||||
bascenev1.AppTimer(5, postStatus)
|
|
||||||
|
|
||||||
|
|
||||||
def updateProfilesJson():
|
|
||||||
profiles = pdata.get_profiles()
|
|
||||||
|
|
||||||
for id in profiles:
|
|
||||||
if "spamCount" not in profiles[id]:
|
|
||||||
profiles[id]["spamCount"] = 0
|
|
||||||
profiles[id]["lastSpam"] = time.time()
|
|
||||||
|
|
||||||
pdata.commit_profiles(profiles)
|
|
||||||
|
|
||||||
|
|
||||||
def postStatus():
|
|
||||||
print("post status clled finally")
|
|
||||||
link = 'https://bcsservers.ballistica.workers.dev/ping'
|
|
||||||
data = {'name': babase.app.classic.server._config.party_name,
|
|
||||||
'port': str(bascenev1.get_game_port()),
|
|
||||||
'build': babase.app.build_number,
|
|
||||||
'bcsversion': VERSION}
|
|
||||||
_thread.start_new_thread(postRequest, (link, data,))
|
|
||||||
|
|
||||||
|
|
||||||
def postRequest(link, data):
|
|
||||||
print(data)
|
|
||||||
try:
|
|
||||||
res = requests.post(link,
|
|
||||||
json=data)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def checkSpammer(data):
|
|
||||||
def checkMaster(data):
|
|
||||||
try:
|
|
||||||
res = requests.post(
|
|
||||||
'https://bcsservers.ballistica.workers.dev/checkspammer',
|
|
||||||
json=data)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
# TODO handle response and kick player based on status
|
|
||||||
|
|
||||||
_thread.start_new_thread(checkMaster, (data,))
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def fetchChangelogs():
|
|
||||||
url = "https://raw.githubusercontent.com/imayushsaini/Bombsquad-Ballistica-Modded-Server/public-server/dist/ba_root/mods/changelogs.json"
|
|
||||||
|
|
||||||
if 2 * 2 == 4:
|
|
||||||
try:
|
|
||||||
data = urllib.request.urlopen(url)
|
|
||||||
changelog = json.loads(data.read())
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return changelog
|
|
||||||
|
|
||||||
|
|
||||||
def checkChangelog():
|
|
||||||
changelog = fetchChangelogs()
|
|
||||||
if changelog == None:
|
|
||||||
print(
|
|
||||||
f'{Clr.BRED} UNABLE TO CHECK UPDATES , CHECK MANUALLY FROM URL {Clr.RST}',
|
|
||||||
flush=True)
|
|
||||||
else:
|
|
||||||
msg = ""
|
|
||||||
avail = False
|
|
||||||
for log in changelog:
|
|
||||||
if int(log) > VERSION:
|
|
||||||
avail = True
|
|
||||||
|
|
||||||
if not avail:
|
|
||||||
print(
|
|
||||||
f'{Clr.BGRN}{Clr.WHT} YOU ARE ON LATEST VERSION {Clr.RST}',
|
|
||||||
flush=True)
|
|
||||||
else:
|
|
||||||
print(f'{Clr.BYLW}{Clr.BLU} UPDATES AVAILABLE {Clr.RST}',
|
|
||||||
flush=True)
|
|
||||||
for log in changelog:
|
|
||||||
if int(log) > VERSION:
|
|
||||||
msg = changelog[log]["time"]
|
|
||||||
print(f'{Clr.CYN} {msg} {Clr.RST}', flush=True)
|
|
||||||
|
|
||||||
msg = changelog[log]["log"]
|
|
||||||
print(f'{Clr.MAG} {msg} {Clr.RST}', flush=True)
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue