mirror of
https://github.com/imayushsaini/Bombsquad-Ballistica-Modded-Server.git
synced 2025-11-07 17:36:15 +00:00
adding back
This commit is contained in:
parent
2e6f150bf8
commit
665f15fa6d
121 changed files with 259784 additions and 0 deletions
0
dist/ba_root/mods/__init__.py
vendored
Normal file
0
dist/ba_root/mods/__init__.py
vendored
Normal file
7
dist/ba_root/mods/chathandle/__init__.py
vendored
Normal file
7
dist/ba_root/mods/chathandle/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""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.
|
||||
"""
|
||||
7
dist/ba_root/mods/chathandle/chatcommands/__init__.py
vendored
Normal file
7
dist/ba_root/mods/chathandle/chatcommands/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""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.
|
||||
"""
|
||||
133
dist/ba_root/mods/chathandle/chatcommands/command_executor.py
vendored
Normal file
133
dist/ba_root/mods/chathandle/chatcommands/command_executor.py
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
# 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
|
||||
271
dist/ba_root/mods/chathandle/chatcommands/commands/cheats.py
vendored
Normal file
271
dist/ba_root/mods/chathandle/chatcommands/commands/cheats.py
vendored
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
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
|
||||
218
dist/ba_root/mods/chathandle/chatcommands/commands/fun.py
vendored
Normal file
218
dist/ba_root/mods/chathandle/chatcommands/commands/fun.py
vendored
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
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
|
||||
34
dist/ba_root/mods/chathandle/chatcommands/commands/handlers.py
vendored
Normal file
34
dist/ba_root/mods/chathandle/chatcommands/commands/handlers.py
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
""" 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)
|
||||
621
dist/ba_root/mods/chathandle/chatcommands/commands/management.py
vendored
Normal file
621
dist/ba_root/mods/chathandle/chatcommands/commands/management.py
vendored
Normal file
|
|
@ -0,0 +1,621 @@
|
|||
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")
|
||||
126
dist/ba_root/mods/chathandle/chatcommands/commands/normal_commands.py
vendored
Normal file
126
dist/ba_root/mods/chathandle/chatcommands/commands/normal_commands.py
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
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
|
||||
255
dist/ba_root/mods/chathandle/chatcommands/floater.py
vendored
Normal file
255
dist/ba_root/mods/chathandle/chatcommands/floater.py
vendored
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
# 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)
|
||||
53
dist/ba_root/mods/chathandle/chatcommands/handlers.py
vendored
Normal file
53
dist/ba_root/mods/chathandle/chatcommands/handlers.py
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# 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
|
||||
7
dist/ba_root/mods/chathandle/chatfilter/__init__.py
vendored
Normal file
7
dist/ba_root/mods/chathandle/chatfilter/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""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.
|
||||
"""
|
||||
116
dist/ba_root/mods/chathandle/chatfilter/chatfilter.py
vendored
Normal file
116
dist/ba_root/mods/chathandle/chatfilter/chatfilter.py
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# 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
|
||||
7
dist/ba_root/mods/chathandle/chatspam/__init__.py
vendored
Normal file
7
dist/ba_root/mods/chathandle/chatspam/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""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.
|
||||
"""
|
||||
3
dist/ba_root/mods/chathandle/chatspam/checkspam.py
vendored
Normal file
3
dist/ba_root/mods/chathandle/chatspam/checkspam.py
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Released under the MIT License. See LICENSE for details.
|
||||
def isspam():
|
||||
return
|
||||
83
dist/ba_root/mods/chathandle/handlechat.py
vendored
Normal file
83
dist/ba_root/mods/chathandle/handlechat.py
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# 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
|
||||
417
dist/ba_root/mods/custom_hooks.py
vendored
Normal file
417
dist/ba_root/mods/custom_hooks.py
vendored
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
"""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
Normal file
7
dist/ba_root/mods/features/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""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
Normal file
73
dist/ba_root/mods/features/afk_check.py
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# 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
Normal file
15
dist/ba_root/mods/features/announcement.py
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
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
Normal file
207
dist/ba_root/mods/features/discord_bot.py
vendored
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
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
Normal file
418
dist/ba_root/mods/features/dual_team_score.py
vendored
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
# 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
Normal file
163
dist/ba_root/mods/features/fire_flies.py
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
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
Normal file
146
dist/ba_root/mods/features/hearts.py
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
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
Normal file
20
dist/ba_root/mods/features/map_fun.py
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
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
Normal file
61
dist/ba_root/mods/features/profanity.py
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# 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
Normal file
85
dist/ba_root/mods/features/team_balancer.py
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
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
Normal file
175
dist/ba_root/mods/features/text_on_map.py
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
# 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
Normal file
145
dist/ba_root/mods/features/votingmachine.py
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# 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
Normal file
513
dist/ba_root/mods/games/alliance_elimination.py
vendored
Normal file
|
|
@ -0,0 +1,513 @@
|
|||
# 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
Normal file
199
dist/ba_root/mods/games/arms_race.py
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
# 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
Normal file
540
dist/ba_root/mods/games/big_ball.py
vendored
Normal file
|
|
@ -0,0 +1,540 @@
|
|||
# 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
Normal file
239
dist/ba_root/mods/games/boxing.py
vendored
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
# 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
Normal file
636
dist/ba_root/mods/games/collector.py
vendored
Normal file
|
|
@ -0,0 +1,636 @@
|
|||
# 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
Normal file
314
dist/ba_root/mods/games/demolition_war.py
vendored
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
# 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
Normal file
776
dist/ba_root/mods/games/dodge_the_ball.py
vendored
Normal file
|
|
@ -0,0 +1,776 @@
|
|||
"""
|
||||
|
||||
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
Normal file
16
dist/ba_root/mods/games/frozen_one.py
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# 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
Normal file
382
dist/ba_root/mods/games/handball.py
vendored
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
# 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
Normal file
1694
dist/ba_root/mods/games/hot_bomb.py
vendored
Normal file
File diff suppressed because it is too large
Load diff
52
dist/ba_root/mods/games/icy_emits.py
vendored
Normal file
52
dist/ba_root/mods/games/icy_emits.py
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# 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
Normal file
1285
dist/ba_root/mods/games/memory_game.py
vendored
Normal file
File diff suppressed because it is too large
Load diff
312
dist/ba_root/mods/games/musical_flags.py
vendored
Normal file
312
dist/ba_root/mods/games/musical_flags.py
vendored
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
# 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
Normal file
644
dist/ba_root/mods/games/quake_original.py
vendored
Normal file
|
|
@ -0,0 +1,644 @@
|
|||
# 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
Normal file
376
dist/ba_root/mods/games/soccer.py
vendored
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
# 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
Normal file
603
dist/ba_root/mods/games/super_duel.py
vendored
Normal file
|
|
@ -0,0 +1,603 @@
|
|||
"""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
Normal file
959
dist/ba_root/mods/games/supersmash.py
vendored
Normal file
|
|
@ -0,0 +1,959 @@
|
|||
# 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
Normal file
902
dist/ba_root/mods/games/volleyball.py
vendored
Normal file
|
|
@ -0,0 +1,902 @@
|
|||
# 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
Normal file
31
dist/ba_root/mods/games/yeeting_party.py
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# 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
Normal file
7
dist/ba_root/mods/playersdata/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""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
Normal file
34
dist/ba_root/mods/playersdata/blacklist.json
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"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
Normal file
17
dist/ba_root/mods/playersdata/custom.json
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"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
Normal file
17
dist/ba_root/mods/playersdata/custom.json.backup
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"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
Normal file
749
dist/ba_root/mods/playersdata/pdata.py
vendored
Normal file
|
|
@ -0,0 +1,749 @@
|
|||
"""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
Normal file
22996
dist/ba_root/mods/playersdata/profiles.json
vendored
Normal file
File diff suppressed because one or more lines are too long
22996
dist/ba_root/mods/playersdata/profiles.json.backup
vendored
Normal file
22996
dist/ba_root/mods/playersdata/profiles.json.backup
vendored
Normal file
File diff suppressed because one or more lines are too long
28928
dist/ba_root/mods/playersdata/profiles.json2022-12-06 08%3A23%3A09.458547
vendored
Normal file
28928
dist/ba_root/mods/playersdata/profiles.json2022-12-06 08%3A23%3A09.458547
vendored
Normal file
File diff suppressed because it is too large
Load diff
33262
dist/ba_root/mods/playersdata/profiles.json2022-12-06 14%3A32%3A45.695478
vendored
Normal file
33262
dist/ba_root/mods/playersdata/profiles.json2022-12-06 14%3A32%3A45.695478
vendored
Normal file
File diff suppressed because it is too large
Load diff
36666
dist/ba_root/mods/playersdata/profiles.json2022-12-06 20%3A51%3A54.236791
vendored
Normal file
36666
dist/ba_root/mods/playersdata/profiles.json2022-12-06 20%3A51%3A54.236791
vendored
Normal file
File diff suppressed because it is too large
Load diff
37588
dist/ba_root/mods/playersdata/profiles.json2022-12-07 03%3A05%3A19.300593
vendored
Normal file
37588
dist/ba_root/mods/playersdata/profiles.json2022-12-07 03%3A05%3A19.300593
vendored
Normal file
File diff suppressed because one or more lines are too long
92
dist/ba_root/mods/playersdata/roles.json
vendored
Normal file
92
dist/ba_root/mods/playersdata/roles.json
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
{
|
||||
"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
Normal file
92
dist/ba_root/mods/playersdata/roles.json.backup
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
{
|
||||
"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=="
|
||||
]
|
||||
}
|
||||
}
|
||||
31
dist/ba_root/mods/playersdata/subscribed_players.json
vendored
Normal file
31
dist/ba_root/mods/playersdata/subscribed_players.json
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
31
dist/ba_root/mods/playersdata/subscribed_players.json.backup
vendored
Normal file
31
dist/ba_root/mods/playersdata/subscribed_players.json.backup
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"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
Normal file
10
dist/ba_root/mods/playersdata/subscriptions.json
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
dist/ba_root/mods/playersdata/subscriptions.json.backup
vendored
Normal file
10
dist/ba_root/mods/playersdata/subscriptions.json.backup
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"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
Normal file
8
dist/ba_root/mods/playersdata/whitelist.json
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"pb-IF4VAk4a": [
|
||||
"\ue030Server127089"
|
||||
],
|
||||
"pb-IF5VVxI5Fg==": [
|
||||
"\ue020id just to remeber pbid here"
|
||||
]
|
||||
}
|
||||
24
dist/ba_root/mods/plugins/__init__.py
vendored
Normal file
24
dist/ba_root/mods/plugins/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
"""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
Normal file
579
dist/ba_root/mods/plugins/auto_stunt.py
vendored
Normal file
|
|
@ -0,0 +1,579 @@
|
|||
# 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
Normal file
298
dist/ba_root/mods/plugins/bcs_plugin.py
vendored
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
# -*- 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
Normal file
290
dist/ba_root/mods/plugins/bombsquad_service.py
vendored
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
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
Normal file
355
dist/ba_root/mods/plugins/character_chooser.py
vendored
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
# 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
Normal file
350
dist/ba_root/mods/plugins/color_explosion.py
vendored
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
"""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
Normal file
125
dist/ba_root/mods/plugins/colorfulmaps2.py
vendored
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
# 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
Normal file
2678
dist/ba_root/mods/plugins/elPatronPowerups.py
vendored
Normal file
File diff suppressed because it is too large
Load diff
60
dist/ba_root/mods/plugins/importcustomcharacters.py
vendored
Normal file
60
dist/ba_root/mods/plugins/importcustomcharacters.py
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
"""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
Normal file
143
dist/ba_root/mods/plugins/wavedash.py
vendored
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
"""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
Normal file
167
dist/ba_root/mods/port.py
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
# 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
Normal file
3872
dist/ba_root/mods/serverdata/Chat Logs.log
vendored
Normal file
File diff suppressed because it is too large
Load diff
9669
dist/ba_root/mods/serverdata/Chat Logs.log2022-12-07 11%3A38%3A04.373620
vendored
Normal file
9669
dist/ba_root/mods/serverdata/Chat Logs.log2022-12-07 11%3A38%3A04.373620
vendored
Normal file
File diff suppressed because it is too large
Load diff
7
dist/ba_root/mods/serverdata/__init__.py
vendored
Normal file
7
dist/ba_root/mods/serverdata/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""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
Normal file
0
dist/ba_root/mods/serverdata/data.json
vendored
Normal file
3896
dist/ba_root/mods/serverdata/joining.log
vendored
Normal file
3896
dist/ba_root/mods/serverdata/joining.log
vendored
Normal file
File diff suppressed because it is too large
Load diff
11008
dist/ba_root/mods/serverdata/joining.log2022-12-12 11%3A36%3A33.942405
vendored
Normal file
11008
dist/ba_root/mods/serverdata/joining.log2022-12-12 11%3A36%3A33.942405
vendored
Normal file
File diff suppressed because it is too large
Load diff
8604
dist/ba_root/mods/serverdata/logs.log
vendored
Normal file
8604
dist/ba_root/mods/serverdata/logs.log
vendored
Normal file
File diff suppressed because it is too large
Load diff
9216
dist/ba_root/mods/serverdata/logs.log2023-04-07 23%3A51%3A33.875558
vendored
Normal file
9216
dist/ba_root/mods/serverdata/logs.log2023-04-07 23%3A51%3A33.875558
vendored
Normal file
File diff suppressed because it is too large
Load diff
38
dist/ba_root/mods/serverdata/serverdata.py
vendored
Normal file
38
dist/ba_root/mods/serverdata/serverdata.py
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# 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
Normal file
4452
dist/ba_root/mods/serverdata/systemlogs.log
vendored
Normal file
File diff suppressed because it is too large
Load diff
180
dist/ba_root/mods/setting.json
vendored
Normal file
180
dist/ba_root/mods/setting.json
vendored
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
{
|
||||
"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
Normal file
50
dist/ba_root/mods/setting.py
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
"""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
Normal file
7
dist/ba_root/mods/spazmod/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""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
Normal file
44
dist/ba_root/mods/spazmod/hitmessage.py
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# -*- 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
Normal file
80
dist/ba_root/mods/spazmod/modifyspaz.py
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
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
Normal file
388
dist/ba_root/mods/spazmod/spaz_effects.py
vendored
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
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
Normal file
169
dist/ba_root/mods/spazmod/tag.py
vendored
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
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
Normal file
7
dist/ba_root/mods/stats/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"""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
Normal file
299
dist/ba_root/mods/stats/mystats.py
vendored
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
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
Normal file
18
dist/ba_root/mods/stats/stats.json
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"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
Normal file
18
dist/ba_root/mods/stats/stats.json.backup
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"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/stats2022-02-26 000000.json
vendored
Normal file
18
dist/ba_root/mods/stats/stats2022-02-26 000000.json
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"startDate": "26-02-2022",
|
||||
"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-03-10 12:46:46.070990"
|
||||
}
|
||||
}
|
||||
}
|
||||
31
dist/ba_root/mods/stats/stats2022-07-14 000000.json
vendored
Normal file
31
dist/ba_root/mods/stats/stats2022-07-14 000000.json
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"startDate": "14-07-2022",
|
||||
"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"
|
||||
},
|
||||
"pb-IF4RU2ECAg==": {
|
||||
"rank": 2,
|
||||
"name": "PC452402",
|
||||
"scores": 352,
|
||||
"total_damage": 0.0,
|
||||
"kills": 0,
|
||||
"deaths": 4,
|
||||
"games": 11,
|
||||
"kd": 0.0,
|
||||
"avg_score": 32.0,
|
||||
"last_seen": "2022-07-16 17:48:21.111588",
|
||||
"aid": "pb-IF4RU2ECAg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
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