You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
241 lines
9.6 KiB
Python
241 lines
9.6 KiB
Python
from collections import OrderedDict
|
|
from io import BytesIO
|
|
import qrcode
|
|
|
|
from django.db import transaction
|
|
from django.http import HttpResponse
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
from django.views.decorators.http import require_safe, require_http_methods
|
|
from django.urls import reverse
|
|
|
|
from .forms import NewGameForm, JoinGameForm, PlayerFormSet
|
|
from .models import Game, Player, Fear
|
|
|
|
|
|
def lookup_access_code(func):
|
|
def with_game(request, access_code, *args, **kwargs):
|
|
game = get_object_or_404(Game, access_code=access_code.lower())
|
|
return func(request, game, *args, **kwargs)
|
|
|
|
return with_game
|
|
|
|
|
|
@require_safe
|
|
def index(request):
|
|
return render(request, 'index.html')
|
|
|
|
|
|
@require_http_methods(["HEAD", "GET", "POST"])
|
|
def enter_code(request):
|
|
if request.method == 'POST':
|
|
form = JoinGameForm(request.POST)
|
|
if form.is_valid():
|
|
game = form.cleaned_data['game']
|
|
return redirect('game', access_code=game.access_code)
|
|
else:
|
|
form = JoinGameForm()
|
|
|
|
return render(request, 'enter_code.html', {'form': form})
|
|
|
|
|
|
@require_http_methods(["HEAD", "GET", "POST"])
|
|
def new_game(request):
|
|
if request.method == 'POST':
|
|
form = NewGameForm(request.POST)
|
|
formset = PlayerFormSet(request.POST)
|
|
if form.is_valid() and formset.is_valid():
|
|
player_forms = list(filter(lambda p: p.cleaned_data.get('name'),
|
|
formset.forms))
|
|
num_players = len(player_forms)
|
|
game = Game()
|
|
game.combined_growth_spirit =\
|
|
form.cleaned_data.get('combined_growth_spirit')
|
|
game.england_build = form.cleaned_data.get('england_build')
|
|
game.fear_per_card =\
|
|
form.cleaned_data.get('fear_per_player') * num_players
|
|
game.save()
|
|
|
|
for player_form in player_forms:
|
|
player = Player()
|
|
player.order = int(player_form.prefix[5:]) + 1
|
|
player.spirit = player_form.cleaned_data.get('spirit')
|
|
player.name = player_form.cleaned_data.get('name')
|
|
player.game = game
|
|
player.save()
|
|
|
|
game.advance_phase()
|
|
|
|
return redirect('game', access_code=game.access_code)
|
|
else:
|
|
form = NewGameForm()
|
|
|
|
initial_player_data = [{'spirit': i}
|
|
for i in enumerate(Player.SPIRIT_NAMES)]
|
|
formset = PlayerFormSet(initial=initial_player_data)
|
|
for player_form in formset:
|
|
player_form.player_id = int(player_form.prefix[5:]) + 1
|
|
|
|
return render(request, 'new_game.html',
|
|
{'form': form, 'formset': formset})
|
|
|
|
|
|
@require_safe
|
|
def qr_code(request, access_code):
|
|
join_url = reverse('game', kwargs={'access_code': access_code.lower()})
|
|
join_url = request.build_absolute_uri(join_url)
|
|
img = qrcode.make(join_url)
|
|
output = BytesIO()
|
|
img.save(output, "PNG")
|
|
return HttpResponse(output.getvalue(), content_type='image/png')
|
|
|
|
|
|
@transaction.atomic
|
|
@lookup_access_code
|
|
@require_http_methods(["HEAD", "GET", "POST"])
|
|
def game(request, game):
|
|
players = OrderedDict()
|
|
for player in game.player_set.order_by('order').all():
|
|
player.total_fear = 0
|
|
player.changed_to_ready = False
|
|
player.fear = OrderedDict()
|
|
players[player.order] = player
|
|
|
|
errors = []
|
|
fear_kind_dict = {
|
|
'fear': 'pure_fear',
|
|
'towns': 'towns_destroyed',
|
|
'cities': 'cities_destroyed',
|
|
}
|
|
current_phase = game.get_current_phase()
|
|
if request.method == 'POST':
|
|
correct_phase =\
|
|
current_phase.game_turn == int(request.POST['game_turn'])\
|
|
and current_phase.game_phase == int(request.POST['game_phase'])
|
|
advance_phase = 'advance' in request.POST
|
|
revert_phase = 'revert' in request.POST
|
|
for key, value in request.POST.items():
|
|
sections = key.split('-')
|
|
if key in ['csrfmiddlewaretoken',
|
|
'update', 'advance', 'revert',
|
|
'game_turn', 'game_phase', 'phase_name']\
|
|
or sections[-1] == 'orig':
|
|
pass
|
|
elif sections[0] == 'player':
|
|
orig_value = request.POST.get(key + '-orig', None)
|
|
player_ord = int(sections[1])
|
|
player = players[player_ord]
|
|
if sections[2] == 'visible':
|
|
player.visible = True
|
|
elif sections[2] == 'ready':
|
|
if correct_phase and value != orig_value:
|
|
if value == 'true':
|
|
player.ready = True
|
|
player.changed_to_ready = True
|
|
else:
|
|
player.ready = False
|
|
player.save()
|
|
elif sections[2] == 'effect':
|
|
effect_num = int(sections[3])
|
|
effect_kind = sections[4]
|
|
if orig_value != value:
|
|
amount = int(value)
|
|
orig = int(orig_value)
|
|
if amount != orig:
|
|
if player.ready and not player.changed_to_ready:
|
|
errors.append(
|
|
f"{player.name} " +
|
|
f"({player.get_spirit_name()}) was " +
|
|
"marked as ready by another user, so " +
|
|
"your attempt to change their effect " +
|
|
f"#{effect_num} {effect_kind} " +
|
|
f"from {orig} to {amount} " +
|
|
"was not processed.")
|
|
elif not correct_phase or advance_phase:
|
|
old_phase = request.POST['phase_name']
|
|
errors.append(
|
|
f"{player.name} " +
|
|
f"({player.get_spirit_name()})'s " +
|
|
f"#{effect_num} {effect_kind} " +
|
|
"could not be changed from " +
|
|
f"{orig} to {amount} " +
|
|
f"because {old_phase} has already ended.")
|
|
else:
|
|
fear_row, created = Fear.objects.get_or_create(
|
|
phase=current_phase,
|
|
player=player,
|
|
effect=effect_num)
|
|
prop_name = fear_kind_dict[effect_kind]
|
|
current_value = getattr(fear_row, prop_name)
|
|
if current_value == orig:
|
|
setattr(fear_row, prop_name, amount)
|
|
fear_row.save()
|
|
else:
|
|
errors.append(
|
|
f"{player.name} " +
|
|
f"({player.get_spirit_name()})'s " +
|
|
"effect " +
|
|
f"#{effect_num} {effect_kind} " +
|
|
"was changed by another user to " +
|
|
f"{current_value}, so " +
|
|
f"your attempt to change it from " +
|
|
f"{orig} to {amount} " +
|
|
"was not processed.")
|
|
else:
|
|
raise Exception(key + ": " + value)
|
|
else:
|
|
raise Exception(key + ": " + value)
|
|
if advance_phase:
|
|
players_ready = all(player.ready for player in players.values())
|
|
if not players_ready:
|
|
errors.append("Cannot advance to next phase until " +
|
|
"all players are done with the current phase.")
|
|
else:
|
|
current_phase = game.advance_phase()
|
|
for player in players.values():
|
|
player.ready = False
|
|
elif revert_phase:
|
|
current_phase = game.revert_phase()
|
|
for player in players.values():
|
|
player.ready = True
|
|
|
|
for fear in current_phase.fear_set.order_by('effect').all():
|
|
players[fear.player.order].fear[fear.effect] = {
|
|
'pure_fear': fear.pure_fear,
|
|
'towns': fear.towns_destroyed,
|
|
'cities': fear.cities_destroyed,
|
|
}
|
|
players[fear.player.order].total_fear +=\
|
|
fear.pure_fear + fear.towns_destroyed + 2*fear.cities_destroyed
|
|
for player in players.values():
|
|
info = player.fear
|
|
if not info:
|
|
new_effect = 0
|
|
else:
|
|
new_effect = max(info.keys()) + 1
|
|
info[new_effect] = {
|
|
'pure_fear': 0,
|
|
'towns': 0,
|
|
'cities': 0,
|
|
}
|
|
return render(request, 'game.html', {
|
|
'access_code': game.access_code,
|
|
'turn': game.game_turn,
|
|
'phase': game.get_current_phase_name(),
|
|
'phase_id': game.game_phase,
|
|
'available_fear_cards': game.num_available_fear_cards(),
|
|
'fear_to_next_card': game.get_fear_to_next_card(),
|
|
'fear_this_phase': current_phase.fear_this_phase(),
|
|
'total_fear': game.get_current_total_fear(),
|
|
'players': players,
|
|
'all_ready': all(player.ready for player in players.values()),
|
|
'range': range(21),
|
|
'errors': errors,
|
|
})
|
|
|
|
|
|
@lookup_access_code
|
|
@require_safe
|
|
def status(request, game):
|
|
# TODO status json
|
|
pass
|