initial
This commit is contained in:
285
backend/services/game_service.py
Normal file
285
backend/services/game_service.py
Normal file
@@ -0,0 +1,285 @@
|
||||
from backend.models import db, Game, Score
|
||||
from flask_socketio import emit
|
||||
|
||||
|
||||
def get_game_state(game):
|
||||
"""Get current game state with all necessary information"""
|
||||
current_question = game.get_current_question()
|
||||
|
||||
state = {
|
||||
'game_id': game.id,
|
||||
'game_name': game.name,
|
||||
'current_question_index': game.current_question_index,
|
||||
'total_questions': len(game.game_questions),
|
||||
'is_active': game.is_active,
|
||||
'teams': [team.to_dict() for team in game.teams]
|
||||
}
|
||||
|
||||
if current_question:
|
||||
state['current_question'] = current_question.to_dict(include_answer=False)
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def get_admin_game_state(game):
|
||||
"""Get game state with answer (for admin only)"""
|
||||
state = get_game_state(game)
|
||||
current_question = game.get_current_question()
|
||||
|
||||
if current_question:
|
||||
state['current_question'] = current_question.to_dict(include_answer=True)
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def start_game(game, socketio_instance):
|
||||
"""Start/activate a game"""
|
||||
# Deactivate any other active games
|
||||
active_games = Game.query.filter_by(is_active=True).all()
|
||||
for g in active_games:
|
||||
if g.id != game.id:
|
||||
g.is_active = False
|
||||
|
||||
game.is_active = True
|
||||
game.current_question_index = 0
|
||||
db.session.commit()
|
||||
|
||||
# Emit game_started event
|
||||
socketio_instance.emit('game_started', {
|
||||
'game_id': game.id,
|
||||
'game_name': game.name,
|
||||
'total_questions': len(game.game_questions)
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
|
||||
socketio_instance.emit('game_started', {
|
||||
'game_id': game.id,
|
||||
'game_name': game.name,
|
||||
'total_questions': len(game.game_questions)
|
||||
}, room=f'game_{game.id}_admin')
|
||||
|
||||
# Emit first question
|
||||
broadcast_question_change(game, socketio_instance)
|
||||
|
||||
|
||||
def next_question(game, socketio_instance):
|
||||
"""Move to next question"""
|
||||
if game.current_question_index < len(game.game_questions) - 1:
|
||||
game.current_question_index += 1
|
||||
db.session.commit()
|
||||
broadcast_question_change(game, socketio_instance)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def previous_question(game, socketio_instance):
|
||||
"""Move to previous question"""
|
||||
if game.current_question_index > 0:
|
||||
game.current_question_index -= 1
|
||||
db.session.commit()
|
||||
broadcast_question_change(game, socketio_instance)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def broadcast_question_change(game, socketio_instance):
|
||||
"""Broadcast question change to all connected clients"""
|
||||
current_question = game.get_current_question()
|
||||
|
||||
if not current_question:
|
||||
return
|
||||
|
||||
# Emit to contestant room (without answer)
|
||||
socketio_instance.emit('question_changed', {
|
||||
'question_index': game.current_question_index,
|
||||
'question': current_question.to_dict(include_answer=False),
|
||||
'total_questions': len(game.game_questions)
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
|
||||
# Emit to admin room (with answer)
|
||||
socketio_instance.emit('question_with_answer', {
|
||||
'question_index': game.current_question_index,
|
||||
'question': current_question.to_dict(include_answer=True),
|
||||
'total_questions': len(game.game_questions)
|
||||
}, room=f'game_{game.id}_admin')
|
||||
|
||||
|
||||
def award_points(game, team, points, socketio_instance):
|
||||
"""Award points to a team for the current question"""
|
||||
# Check if score already exists for this team and question
|
||||
existing_score = Score.query.filter_by(
|
||||
team_id=team.id,
|
||||
question_index=game.current_question_index
|
||||
).first()
|
||||
|
||||
if existing_score:
|
||||
# Add to existing score
|
||||
existing_score.points += points
|
||||
else:
|
||||
# Create new score
|
||||
score = Score(
|
||||
team_id=team.id,
|
||||
game_id=game.id,
|
||||
question_index=game.current_question_index,
|
||||
points=points
|
||||
)
|
||||
db.session.add(score)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Get all team scores with full data including lifelines
|
||||
all_scores = [t.to_dict() for t in game.teams]
|
||||
|
||||
# Broadcast score update
|
||||
score_data = {
|
||||
'team_id': team.id,
|
||||
'team_name': team.name,
|
||||
'new_score': team.total_score,
|
||||
'points_awarded': points,
|
||||
'all_scores': all_scores
|
||||
}
|
||||
|
||||
socketio_instance.emit('score_updated', score_data, room=f'game_{game.id}_contestant')
|
||||
socketio_instance.emit('score_updated', score_data, room=f'game_{game.id}_admin')
|
||||
|
||||
|
||||
def toggle_answer_visibility(game, show_answer, socketio_instance):
|
||||
"""Toggle answer visibility on contestant screen"""
|
||||
current_question = game.get_current_question()
|
||||
|
||||
if not current_question:
|
||||
return
|
||||
|
||||
answer_data = {
|
||||
'show_answer': show_answer
|
||||
}
|
||||
|
||||
if show_answer:
|
||||
answer_data['answer'] = current_question.answer
|
||||
|
||||
# Broadcast to contestant room only
|
||||
socketio_instance.emit('answer_visibility_changed', answer_data, room=f'game_{game.id}_contestant')
|
||||
|
||||
|
||||
def toggle_timer_pause(game, paused, socketio_instance):
|
||||
"""Pause or resume the timer"""
|
||||
# Broadcast timer pause state to both rooms
|
||||
socketio_instance.emit('timer_paused', {
|
||||
'paused': paused
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
|
||||
socketio_instance.emit('timer_paused', {
|
||||
'paused': paused
|
||||
}, room=f'game_{game.id}_admin')
|
||||
|
||||
|
||||
def reset_timer(game, socketio_instance):
|
||||
"""Reset the timer to 30 seconds"""
|
||||
# Broadcast timer reset to both rooms
|
||||
socketio_instance.emit('timer_reset', {
|
||||
'seconds': 30
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
|
||||
socketio_instance.emit('timer_reset', {
|
||||
'seconds': 30
|
||||
}, room=f'game_{game.id}_admin')
|
||||
|
||||
|
||||
def end_game(game, socketio_instance):
|
||||
"""End/deactivate a game"""
|
||||
game.is_active = False
|
||||
db.session.commit()
|
||||
|
||||
# Emit game_ended event to all rooms
|
||||
socketio_instance.emit('game_ended', {
|
||||
'game_id': game.id,
|
||||
'game_name': game.name
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
|
||||
socketio_instance.emit('game_ended', {
|
||||
'game_id': game.id,
|
||||
'game_name': game.name
|
||||
}, room=f'game_{game.id}_admin')
|
||||
|
||||
|
||||
def restart_game(game, socketio_instance):
|
||||
"""Restart a game (clear scores and reset to waiting state)"""
|
||||
# Clear all scores for this game
|
||||
Score.query.filter_by(game_id=game.id).delete()
|
||||
|
||||
# Reset game state
|
||||
game.is_active = False
|
||||
game.current_question_index = 0
|
||||
|
||||
# Reset phone-a-friend lifelines for all teams
|
||||
for team in game.teams:
|
||||
team.phone_a_friend_count = 5
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Emit game_ended event to reset contestant view
|
||||
socketio_instance.emit('game_ended', {
|
||||
'game_id': game.id,
|
||||
'game_name': game.name
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
|
||||
# Emit score update to show cleared scores and reset lifelines
|
||||
socketio_instance.emit('score_updated', {
|
||||
'team_id': None,
|
||||
'team_name': None,
|
||||
'new_score': 0,
|
||||
'points_awarded': 0,
|
||||
'all_scores': [t.to_dict() for t in game.teams]
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
|
||||
socketio_instance.emit('score_updated', {
|
||||
'team_id': None,
|
||||
'team_name': None,
|
||||
'new_score': 0,
|
||||
'points_awarded': 0,
|
||||
'all_scores': [t.to_dict() for t in game.teams]
|
||||
}, room=f'game_{game.id}_admin')
|
||||
|
||||
|
||||
def broadcast_lifeline_update(game, team, socketio_instance):
|
||||
"""Broadcast phone-a-friend lifeline update"""
|
||||
# Get all team scores with updated lifeline counts
|
||||
all_scores = [t.to_dict() for t in game.teams]
|
||||
|
||||
lifeline_data = {
|
||||
'team_id': team.id,
|
||||
'team_name': team.name,
|
||||
'phone_a_friend_count': team.phone_a_friend_count,
|
||||
'all_scores': all_scores
|
||||
}
|
||||
|
||||
socketio_instance.emit('lifeline_updated', lifeline_data, room=f'game_{game.id}_contestant')
|
||||
socketio_instance.emit('lifeline_updated', lifeline_data, room=f'game_{game.id}_admin')
|
||||
|
||||
|
||||
def broadcast_audio_play(game, socketio_instance):
|
||||
"""Broadcast audio play command to contestants"""
|
||||
socketio_instance.emit('audio_play', {
|
||||
'game_id': game.id
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
|
||||
|
||||
def broadcast_audio_pause(game, socketio_instance):
|
||||
"""Broadcast audio pause command to contestants"""
|
||||
socketio_instance.emit('audio_pause', {
|
||||
'game_id': game.id
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
|
||||
|
||||
def broadcast_audio_stop(game, socketio_instance):
|
||||
"""Broadcast audio stop command to contestants"""
|
||||
socketio_instance.emit('audio_stop', {
|
||||
'game_id': game.id
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
|
||||
|
||||
def broadcast_audio_seek(game, position, socketio_instance):
|
||||
"""Broadcast audio seek command to contestants"""
|
||||
socketio_instance.emit('audio_seek', {
|
||||
'game_id': game.id,
|
||||
'position': position
|
||||
}, room=f'game_{game.id}_contestant')
|
||||
Reference in New Issue
Block a user