Initial models and forms done; working on views.
vecāks
984b80abf7
revīzija
b8b686d4ed
@ -1,3 +0,0 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
@ -1,5 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class FearTrackerConfig(AppConfig):
|
||||
name = 'fear_tracker'
|
@ -0,0 +1,55 @@
|
||||
from django import forms
|
||||
|
||||
from .models import Game, Player
|
||||
|
||||
|
||||
class PlayerForm(forms.Form):
|
||||
name = forms.CharField(max_length=80, label="Name", required=False)
|
||||
spirit = forms.TypedChoiceField(label="Spirit",
|
||||
choices=enumerate(Player.SPIRIT_NAMES),
|
||||
empty_value=None, coerce=int)
|
||||
|
||||
|
||||
class BasePlayerFormSet(forms.BaseFormSet):
|
||||
def clean(self):
|
||||
if any(self.errors):
|
||||
return
|
||||
names = set()
|
||||
for form in self.forms:
|
||||
if self.can_delete and self._should_delete_form(form):
|
||||
continue
|
||||
name = form.cleaned_data.get('name')
|
||||
if name:
|
||||
if name in names:
|
||||
raise forms.ValidationError(
|
||||
"Players must have distinct names.")
|
||||
names.add(name)
|
||||
if not names:
|
||||
raise forms.ValidationError("Must have at least one player.")
|
||||
|
||||
|
||||
PlayerFormSet = forms.formset_factory(PlayerForm, formset=BasePlayerFormSet)
|
||||
|
||||
|
||||
class NewGameForm(forms.Form):
|
||||
combined_growth_spirit = forms.BooleanField(
|
||||
required=False, initial=True,
|
||||
label="Combine Growth and Spirit phases into a single phase")
|
||||
england_build = forms.BooleanField(
|
||||
required=False, initial=False,
|
||||
label="High Immigration (extra build phase for England level 3+)")
|
||||
fear_per_player = forms.IntegerField(
|
||||
label="Fear per player", initial=4, min_value=1, max_value=99)
|
||||
|
||||
|
||||
class JoinGameForm(forms.Form):
|
||||
game = forms.CharField(label='Access code',
|
||||
max_length=Game.ACCESS_CODE_LENGTH)
|
||||
|
||||
def clean_game(self):
|
||||
data = self.cleaned_data['game']
|
||||
|
||||
try:
|
||||
return Game.objects.get(access_code=data.lower())
|
||||
except Game.DoesNotExist:
|
||||
raise forms.ValidationError("Invalid access code.")
|
@ -1,3 +1,252 @@
|
||||
import random
|
||||
import string
|
||||
|
||||
from django.db import models
|
||||
from django.db.models import Sum
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
def generate_code(length):
|
||||
return "".join([random.choice(string.ascii_lowercase)
|
||||
for i in range(length)])
|
||||
|
||||
|
||||
class Game(models.Model):
|
||||
ACCESS_CODE_LENGTH = 6
|
||||
access_code = models.CharField(db_index=True, unique=True,
|
||||
max_length=ACCESS_CODE_LENGTH)
|
||||
game_turn = models.IntegerField(default=0)
|
||||
GAME_PHASE_LOBBY = 0
|
||||
GAME_PHASE_GROWTH = 1
|
||||
GAME_PHASE_SPIRIT = 2
|
||||
GAME_PHASE_GROWTH_SPIRIT = 3
|
||||
GAME_PHASE_FAST = 4
|
||||
GAME_PHASE_BLIGHTED_ISLAND = 5
|
||||
GAME_PHASE_FEAR = 6
|
||||
GAME_PHASE_FEAR_CARDS = [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
|
||||
GAME_PHASE_ENGLAND_BUILD = 100
|
||||
GAME_PHASE_RAVAGE = 101
|
||||
GAME_PHASE_BUILD = 102
|
||||
GAME_PHASE_EXPLORE = 103
|
||||
GAME_PHASE_SLOW = 104
|
||||
GAME_PHASE_END = 120
|
||||
game_phase = models.IntegerField(default=GAME_PHASE_LOBBY)
|
||||
created = models.DateTimeField()
|
||||
ended = models.DateTimeField(null=True, default=None)
|
||||
combined_growth_spirit = models.BooleanField(default=False)
|
||||
england_build = models.BooleanField(default=False)
|
||||
fear_per_card = models.IntegerField()
|
||||
|
||||
# from http://stackoverflow.com/a/11821832
|
||||
def save(self, *args, **kwargs):
|
||||
# object is being created, thus no primary key field yet
|
||||
if not self.pk:
|
||||
# Make sure access_code is unique before using it.
|
||||
access_code = generate_code(Game.ACCESS_CODE_LENGTH)
|
||||
while Game.objects.filter(access_code=access_code).exists():
|
||||
access_code = generate_code(Game.ACCESS_CODE_LENGTH)
|
||||
self.access_code = access_code
|
||||
self.created = timezone.now()
|
||||
if self.ended is None and self.game_phase == Game.GAME_PHASE_END:
|
||||
self.ended = timezone.now()
|
||||
super(Game, self).save(*args, **kwargs)
|
||||
|
||||
def next_turn_and_phase(self):
|
||||
if self.game_phase in [Game.GAME_PHASE_LOBBY, Game.GAME_PHASE_SLOW]:
|
||||
turn = self.game_turn+1
|
||||
if self.combined_growth_spirit:
|
||||
return (turn, Game.GAME_PHASE_GROWTH_SPIRIT)
|
||||
else:
|
||||
return (turn, Game.GAME_PHASE_GROWTH)
|
||||
|
||||
def next_phase():
|
||||
if self.game_phase == Game.GAME_PHASE_SPIRIT:
|
||||
return Game.GAME_PHASE_FAST
|
||||
elif self.game_phase == Game.GAME_PHASE_BLIGHTED_ISLAND:
|
||||
if self.num_available_fear_cards() == 0:
|
||||
return Game.GAME_PHASE_FEAR
|
||||
else:
|
||||
return Game.GAME_PHASE_FEAR_CARDS[0]
|
||||
elif self.game_phase == Game.GAME_PHASE_FEAR or\
|
||||
self.game_phase in Game.GAME_PHASE_FEAR_CARDS:
|
||||
num_fear = self.fear_cards_in_current_fear_phase()
|
||||
fear_so_far = self.game_phase - Game.GAME_PHASE_FEAR
|
||||
if num_fear < fear_so_far:
|
||||
return self.game_phase + 1
|
||||
else:
|
||||
if self.england_build:
|
||||
return Game.GAME_PHASE_ENGLAND_BUILD
|
||||
else:
|
||||
return Game.GAME_PHASE_RAVAGE
|
||||
else:
|
||||
return self.game_phase + 1
|
||||
|
||||
return (self.game_turn, next_phase())
|
||||
|
||||
def get_current_phase(self):
|
||||
return self.phase_set.filter(game_turn=self.game_turn,
|
||||
game_phase=self.game_phase).first()
|
||||
|
||||
def get_current_total_fear(self):
|
||||
current_phase = self.get_current_phase()
|
||||
if current_phase:
|
||||
return current_phase.starting_fear +\
|
||||
current_phase.fear_this_phase()
|
||||
else:
|
||||
return 0
|
||||
|
||||
def get_fear_to_next_card(self):
|
||||
total_fear = self.get_current_total_fear()
|
||||
leftover_fear = total_fear // self.fear_per_card
|
||||
return self.fear_per_card - leftover_fear
|
||||
|
||||
def _fear_phase(self, previous):
|
||||
if self.game_phase >= Game.GAME_PHASE_FEAR and not previous:
|
||||
turn = self.game_turn
|
||||
else:
|
||||
turn = self.game_turn - 1
|
||||
return self.phase_set.filter(
|
||||
game_turn=turn,
|
||||
game_phase__in=[Game.GAME_PHASE_FEAR,
|
||||
Game.GAME_PHASE_FEAR_CARDS[0]]).first()
|
||||
|
||||
def _fear_cards_as_of_fear_phase(self, previous):
|
||||
fear_phase = self._fear_phase(previous=previous)
|
||||
if fear_phase:
|
||||
return fear_phase.initial_fear // self.fear_per_card
|
||||
else:
|
||||
return 0
|
||||
|
||||
def fear_cards_in_current_fear_phase(self):
|
||||
previous_fear_cards = self._fear_cards_as_of_fear_phase(previous=True)
|
||||
current_fear_cards = self._fear_cards_as_of_fear_phase(previous=False)
|
||||
return current_fear_cards - previous_fear_cards
|
||||
|
||||
def num_available_fear_cards(self):
|
||||
recent_fear_fear_cards = self._fear_cards_as_of_fear_phase(
|
||||
previous=False)
|
||||
current_fear = self.get_current_total_fear()
|
||||
current_fear_cards = current_fear // self.fear_per_card
|
||||
|
||||
return current_fear_cards - recent_fear_fear_cards
|
||||
|
||||
def advance_phase(self):
|
||||
current_phase = self.get_current_phase()
|
||||
current_time = timezone.now()
|
||||
if current_phase:
|
||||
current_phase.ended = current_time
|
||||
current_phase.save()
|
||||
initial_fear = current_phase.starting_fear +\
|
||||
current_phase.fear_this_phase()
|
||||
else:
|
||||
initial_fear = 0
|
||||
(next_turn, next_phase) = self.next_turn_and_phase()
|
||||
self.game_turn = next_turn
|
||||
self.game_phase = next_phase
|
||||
self.save()
|
||||
|
||||
next_phase = self.get_current_phase()
|
||||
if next_phase is None:
|
||||
Phase.objects.create(
|
||||
game=self,
|
||||
game_turn=next_turn,
|
||||
game_phase=next_phase,
|
||||
initial_fear=initial_fear,
|
||||
started=current_time)
|
||||
else:
|
||||
next_phase.started = current_time
|
||||
next_phase.initial_fear = initial_fear
|
||||
next_phase.ended = None
|
||||
next_phase.save()
|
||||
|
||||
@staticmethod
|
||||
def get_name_for_phase_id(phase):
|
||||
if phase == Game.GAME_PHASE_LOBBY:
|
||||
return "Game Setup"
|
||||
elif phase == Game.GAME_PHASE_GROWTH:
|
||||
return "Growth"
|
||||
elif phase == Game.GAME_PHASE_SPIRIT:
|
||||
return "Spirit"
|
||||
elif phase == Game.GAME_PHASE_GROWTH_SPIRIT:
|
||||
return "Growth/Spirit"
|
||||
elif phase == Game.GAME_PHASE_FAST:
|
||||
return "Fast Actions"
|
||||
elif phase == Game.GAME_PHASE_BLIGHTED_ISLAND:
|
||||
return "Blighted Island"
|
||||
elif phase == Game.GAME_PHASE_FEAR:
|
||||
return "Fear (no fear cards)"
|
||||
elif phase in Game.GAME_PHASE_FEAR_CARDS:
|
||||
return "Fear Card #" + str(phase-Game.GAME_PHASE_FEAR)
|
||||
elif phase == Game.GAME_PHASE_ENGLAND_BUILD:
|
||||
return "England Extra Build"
|
||||
elif phase == Game.GAME_PHASE_RAVAGE:
|
||||
return "Ravage"
|
||||
elif phase == Game.GAME_PHASE_BUILD:
|
||||
return "Build"
|
||||
elif phase == Game.GAME_PHASE_EXPLORE:
|
||||
return "Explore"
|
||||
elif phase == Game.GAME_PHASE_SLOW:
|
||||
return "Slow Actions"
|
||||
elif phase == Game.GAME_PHASE_END:
|
||||
return "Game Over"
|
||||
|
||||
def get_current_phase_name(self):
|
||||
res = Game.get_name_for_phase_id(self.game_phase)
|
||||
if self.game_phase in Game.GAME_PHASE_FEAR_CARDS:
|
||||
res += ' (of %d)' % self.fear_cards_in_current_fear_phase()
|
||||
return res
|
||||
|
||||
|
||||
class Player(models.Model):
|
||||
game = models.ForeignKey(Game, on_delete=models.CASCADE, db_index=True)
|
||||
name = models.CharField(max_length=80)
|
||||
SPIRIT_NAMES = [
|
||||
"Lightning's Swift Strike",
|
||||
"River Surges in Sunlight",
|
||||
"Vital Strength of the Earth",
|
||||
"Shadows Flicker Like Flame",
|
||||
"Thunderspeaker",
|
||||
"A Spread of Rampant Green",
|
||||
"Ocean's Hungry Grasp",
|
||||
"Bringer of Dreams and Nightmares",
|
||||
"Keeper of the Forbidden Wilds",
|
||||
"Sharp Fangs Behind the Leaves",
|
||||
"Serpent Slumbering Beneath the Island",
|
||||
"Heart of the Wildfire",
|
||||
]
|
||||
spirit = models.IntegerField()
|
||||
order = models.IntegerField()
|
||||
ready = models.BooleanField(default=False)
|
||||
|
||||
unique_together = (("game", "name"), ("game", "order"))
|
||||
|
||||
def get_spirit_name(self):
|
||||
return Player.SPIRIT_NAMES[self.spirit]
|
||||
|
||||
|
||||
class Phase(models.Model):
|
||||
game = models.ForeignKey(Game, on_delete=models.CASCADE, db_index=True)
|
||||
game_turn = models.IntegerField()
|
||||
game_phase = models.IntegerField()
|
||||
started = models.DateTimeField()
|
||||
ended = models.DateTimeField(null=True, default=None)
|
||||
# fear total at the start of this phase
|
||||
starting_fear = models.IntegerField(default=0)
|
||||
|
||||
unique_together = (("game", "game_turn", "game_phase"))
|
||||
|
||||
def fear_this_phase(self):
|
||||
return self.fear_set.aggregate(
|
||||
fear=Sum('pure_fear')
|
||||
+ Sum('towns_destroyed')
|
||||
+ 2*Sum('cities_destroyed'))['fear'] or 0
|
||||
|
||||
|
||||
# Create your models here.
|
||||
class Fear(models.Model):
|
||||
phase = models.ForeignKey(Phase, on_delete=models.CASCADE, db_index=True)
|
||||
player = models.ForeignKey(Player, on_delete=models.CASCADE, db_index=True)
|
||||
effect = models.IntegerField(default=0)
|
||||
# "pure" = not from destroying a town or city
|
||||
pure_fear = models.IntegerField()
|
||||
towns_destroyed = models.IntegerField()
|
||||
cities_destroyed = models.IntegerField()
|
||||
|
@ -0,0 +1,67 @@
|
||||
footer hr {
|
||||
border-width: 0;
|
||||
border-top: 1px solid lightgray;
|
||||
}
|
||||
|
||||
footer p {
|
||||
text-align: center;
|
||||
font-size: 0.7em;
|
||||
margin: 0.4em;
|
||||
color: darkgrey;
|
||||
}
|
||||
|
||||
footer a, footer a:visited {
|
||||
color: darkgray;
|
||||
}
|
||||
|
||||
.players-form p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.player-1 {
|
||||
background: #f0ba57;
|
||||
}
|
||||
|
||||
.player-2 {
|
||||
background: #73fcf6;
|
||||
}
|
||||
|
||||
.player-3 {
|
||||
background: #bc9c8b;
|
||||
}
|
||||
|
||||
.player-4 {
|
||||
background: #77a0a6;
|
||||
}
|
||||
|
||||
.player-5 {
|
||||
background: #fff172;
|
||||
}
|
||||
|
||||
.player-6 {
|
||||
background: #3adb85;
|
||||
}
|
||||
|
||||
.player-7 {
|
||||
background: #99c8e1;
|
||||
}
|
||||
|
||||
.player-8 {
|
||||
background: #f73347;
|
||||
}
|
||||
|
||||
.player-9 {
|
||||
background: #cac186;
|
||||
}
|
||||
|
||||
.player-10 {
|
||||
background: #8ca684;
|
||||
}
|
||||
|
||||
.player-11 {
|
||||
background: #f068a9;
|
||||
}
|
||||
|
||||
.player-12 {
|
||||
background: #ed822b;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link rel="stylesheet" href="{% static 'css/styles.css' %}" type="text/css">
|
||||
<title>Spirit Island Fear Tracker</title>
|
||||
</head>
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
<footer>
|
||||
<hr>
|
||||
<p>
|
||||
<a href="https://boardgamegeek.com/boardgame/162886/spirit-island">Spirit
|
||||
Island</a>
|
||||
was designed by
|
||||
<a href="https://boardgamegeek.com/boardgamedesigner/16615/r-eric-reuss">Eric Reuss</a>
|
||||
and published by
|
||||
<a href="https://www.greaterthangames.com/">Greater Than Games</a>
|
||||
</p>
|
||||
<p>
|
||||
Spirit Island Fear Tracker was built by
|
||||
<a href="https://aweirdimagination.net/~perelman/">Daniel Perelman</a>
|
||||
|
|
||||
<a href="https://git.aweirdimagination.net/perelman/fear-tracker">source
|
||||
code</a> licensed under
|
||||
<a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPLv3+</a>
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Join Game</h2>
|
||||
|
||||
<form class="join-game-form" action="{% url 'enter_code' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
|
||||
<div class="button-container">
|
||||
<input type="submit" class="button button-join" value="Join"></input>
|
||||
</div>
|
||||
<div class="button-container">
|
||||
<a href="{% url 'index' %}" class="button button-main-menu">Back</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
@ -0,0 +1,40 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<header>
|
||||
<div class="phase">{{ phase }} phase of turn #{{ turn }}</div>
|
||||
<div class="fear_summary">
|
||||
<span class="available_fear_cards">{{ available_fear_cards }}</span>
|
||||
fear cards available;
|
||||
<span class="fear_to_next_card">{{ fear_to_next_card }}</span>
|
||||
fear to next fear card;
|
||||
players have generated
|
||||
<span class="fear_this_phase">{{ fear_this_phase }}</span>
|
||||
fear this phase
|
||||
</div>
|
||||
<div class="access-code">
|
||||
<span class="header">Access Code:</span> <a href="{% url 'qr_code' access_code=access_code %}" target="_blank">{{ access_code }}</a>
|
||||
</div>
|
||||
</header>
|
||||
<div class="players">
|
||||
{% for order, player in players.items %}
|
||||
<div class="player player-{{ player.order }}">
|
||||
<div class="player-summary">
|
||||
{% if player.ready %}(ready){% else %}(waiting...){% endif %}
|
||||
(<span class="player-total-fear player-{{ player.order }}-total-fear">{{ player.total_fear }}</span> fear)
|
||||
{{ player.name }} ({{ player.get_spirit_name }})
|
||||
</div>
|
||||
<div class="player-effects">
|
||||
{% for effect_num, effect in player.fear.items %}
|
||||
<div class="effect effect-{{ effect_num }}">
|
||||
Effect #{{ effect_num|add:1 }}
|
||||
{{ effect.pure_fear }} fear
|
||||
{{ effect.towns }} towns
|
||||
{{ effect.cities }} cities
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="button-container">
|
||||
<a href="{% url 'new_game' %}" class="button button-new-game">New Game</a>
|
||||
<a href="{% url 'enter_code' %}" class="button button-join-game">Join Game</a>
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,27 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h2>New Game</h2>
|
||||
<form class="new-game-form" action="#" method="post">
|
||||
{% csrf_token %}
|
||||
{{ formset.non_form_errors }}
|
||||
<table class="new-game-form">
|
||||
{{ form.as_table }}
|
||||
</table>
|
||||
{{ formset.management_form }}
|
||||
<table class="players-form">
|
||||
{% for form in formset %}
|
||||
<tr class="player-{{ form.player_id }}">
|
||||
<th>Player #{{ form.player_id }}</th>
|
||||
<td>{{ form.as_p }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<div class="button-container">
|
||||
<input type="submit" class="button button-create" value="Create Game"></input>
|
||||
</div>
|
||||
<div class="button-container">
|
||||
<a href="{% url 'index' %}" class="button button-main-menu">Back</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
@ -0,0 +1,30 @@
|
||||
"""fear_tracker URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/2.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf.urls import include, url
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.index, name='index'),
|
||||
path('join/', views.enter_code, name='enter_code'),
|
||||
path('new/', views.new_game, name='new_game'),
|
||||
url(r'^(?P<access_code>[a-zA-Z]{6})/', include([
|
||||
path('', views.game, name='game'),
|
||||
path('qr/', views.qr_code, name='qr_code'),
|
||||
path('status/', views.status, name='status'),
|
||||
])),
|
||||
]
|
@ -1,3 +1,133 @@
|
||||
from django.shortcuts import render
|
||||
from collections import OrderedDict
|
||||
from io import BytesIO
|
||||
import qrcode
|
||||
|
||||
# Create your views here.
|
||||
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
|
||||
|
||||
|
||||
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')
|
||||
|
||||
|
||||
@lookup_access_code
|
||||
@require_safe
|
||||
def game(request, game):
|
||||
players = OrderedDict()
|
||||
for player in game.player_set.order_by('order').all():
|
||||
player.total_fear = 0
|
||||
player.fear = OrderedDict()
|
||||
players[player.order] = player
|
||||
for fear in game.get_current_phase().fear_set.order_by('effect').all():
|
||||
player[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(),
|
||||
'available_fear_cards': game.num_available_fear_cards(),
|
||||
'fear_to_next_card': game.get_fear_to_next_card(),
|
||||
'fear_this_phase': game.get_current_phase().fear_this_phase(),
|
||||
'players': players,
|
||||
})
|
||||
|
||||
|
||||
@lookup_access_code
|
||||
@require_safe
|
||||
def status(request, game):
|
||||
# TODO status json
|
||||
pass
|
||||
|
Notiek ielāde…
Atsaukties uz šo jaunā problēmā