zapsaber/zapsaber.py

116 lines
3.4 KiB
Python
Raw Normal View History

2022-11-24 20:35:08 +00:00
import asyncio
import json
import logging
from pyshock import PiShock
2022-11-25 06:15:41 +00:00
from datapuller import LiveData
2022-11-24 20:35:08 +00:00
logging.basicConfig(level=logging.INFO)
2022-11-24 23:07:25 +00:00
logger = logging.getLogger(__name__)
2022-11-24 20:35:08 +00:00
2022-11-25 06:15:41 +00:00
class ZapSaber:
"""
ZapSaber contains methods for reacting to DataPuller events.
"""
2022-11-24 20:35:08 +00:00
combo_A = 100
combo_B = 300
2022-11-24 23:07:25 +00:00
combo_A_break = ['vibrate', 0.5, 20]
combo_B_break = ['shock', 0.3, 5]
fail = ['shock', 1, 10]
2022-11-24 20:35:08 +00:00
2022-11-24 23:07:25 +00:00
playing = False
combo_level = None
map_failed = False
2022-11-24 20:35:08 +00:00
2022-11-24 23:07:25 +00:00
def __init__(self, config, shocker):
2022-11-24 20:35:08 +00:00
self.combo_A = config['comboA']
self.combo_B = config['comboB']
self.combo_A_break = config['comboABreak']
self.combo_B_break = config['comboBBreak']
self.fail = config['fail']
2022-11-24 23:07:25 +00:00
self.shocker = shocker
def update_combo(self, message):
2022-11-25 06:15:41 +00:00
"""
update_combo receives a message when the score chages. It first checks if the map
has been failed, and if so, sends the `fail` event. Next, it checks whether the
current combo is greater than `combo_A` or `combo_B` and sets the combo level
accordingly.
"""
2022-11-24 23:07:25 +00:00
combo = message['Combo']
health = message['PlayerHealth']
2022-11-25 06:15:41 +00:00
# Determine whether or not the player has failed.
2022-11-24 23:07:25 +00:00
if not self.map_failed and health == 0:
logger.info('Failed the map!')
self.shocker.act(*self.fail)
self.map_failed = True
self.combo_level = None
2022-11-25 06:15:41 +00:00
# Set appropriate combo levels.
2022-11-24 23:07:25 +00:00
if combo > self.combo_A:
self.combo_level = 'A'
if combo > self.combo_B:
self.combo_level = 'B'
def break_combo(self, message):
2022-11-25 06:15:41 +00:00
"""
break_combo receives a message when a note is missed. If the combo broken is
greater than one of the combo levels (as determined by `update_combo`), then
the appropriate message is sent to PiShock.
"""
2022-11-24 23:07:25 +00:00
if self.combo_level == 'A':
logger.info('Broke combo A!')
self.shocker.act(*self.combo_A_break)
if self.combo_level == 'B':
logger.info('Broke combo B!')
self.shocker.act(*self.combo_B_break)
2022-11-25 06:15:41 +00:00
# Since the combo has been broken, unset the combo level.
2022-11-24 23:07:25 +00:00
self.combo_level = None
def tick(self, message):
2022-11-25 06:15:41 +00:00
"""
tick runs every time the map timer elapses. This is where whether or not the
player is actively playing is determined.
"""
logger.debug('Tick')
2022-11-24 23:07:25 +00:00
if self.playing and message['TimeElapsed'] == 0:
logger.info('Stopping map...')
2022-11-25 06:15:41 +00:00
self.playing = False
2022-11-24 23:07:25 +00:00
self.map_failed = False
elif not self.playing and message['TimeElapsed'] != 0:
logger.info('Starting map...')
self.playing = True
2022-11-24 20:35:08 +00:00
2022-11-24 23:07:25 +00:00
async def main(config):
logger.info('Initializing...')
2022-11-25 06:15:41 +00:00
# Set up the shocker.
2022-11-25 06:20:35 +00:00
shocker = PiShock(config['username'], config['apiKey'], config['code'],
name='ZapSaber')
2022-11-25 06:15:41 +00:00
shocker.beep()
events = ZapSaber(config, shocker)
2022-11-24 20:35:08 +00:00
2022-11-25 06:15:41 +00:00
# Set up the BSDataPuller listeners.
2022-11-24 20:35:08 +00:00
livedata = LiveData()
2022-11-24 23:07:25 +00:00
livedata.on('ScoreChange', events.update_combo)
livedata.on('NoteMissed', events.break_combo)
livedata.on('TimerElapsed', events.tick)
2022-11-24 20:35:08 +00:00
2022-11-25 06:15:41 +00:00
# Start listening.
2022-11-24 20:35:08 +00:00
await livedata.listen()
if __name__ == '__main__':
with open('config.json') as config_file:
config = json.load(config_file)
2022-11-24 23:07:25 +00:00
asyncio.run(main(config))