Compare commits
4 Commits
b92159d4de
...
28ab47e5e4
Author | SHA1 | Date | |
---|---|---|---|
28ab47e5e4 | |||
11f661fd8a | |||
b5c689915f | |||
f8f963ce51 |
|
@ -6,6 +6,7 @@
|
|||
<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>
|
||||
{% block header_script %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
{% block content %}
|
||||
<header>
|
||||
<div class="phase">{{ phase }} phase of turn #{{ turn }}</div>
|
||||
<div class="phase"><span class="status-phase">{{ status.phase }}</span> phase of turn #<span class="status-turn">{{ status.turn }}</span></div>
|
||||
<div class="fear_summary">
|
||||
<span class="available_fear_cards">{{ available_fear_cards }}</span>
|
||||
<span class="available_fear_cards">{{ status.available_fear_cards }}</span>
|
||||
😱🎴;
|
||||
<span class="fear_to_next_card">{{ fear_to_next_card }}</span>
|
||||
<span class="fear_to_next_card">{{ status.fear_to_next_card }}</span>
|
||||
😱 to next 😱🎴;
|
||||
<span class="fear_this_phase">{{ fear_this_phase }}</span>
|
||||
<span class="fear_this_phase">{{ status.fear_this_phase }}</span>
|
||||
😱 this phase
|
||||
</div>
|
||||
<div class="access-code">
|
||||
|
@ -24,15 +24,15 @@
|
|||
</div>
|
||||
<form action="#" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="game_turn" value="{{ turn }}">
|
||||
<input type="hidden" name="game_phase" value="{{ phase_id }}">
|
||||
<input type="hidden" name="phase_name" value="{{ phase }} phase of turn #{{ turn }}">
|
||||
<input type="hidden" name="game_turn" value="{{ status.turn }}">
|
||||
<input type="hidden" name="game_phase" value="{{ status.phase_id }}">
|
||||
<input type="hidden" name="phase_name" value="{{ status.phase }} phase of turn #{{ status.turn }}">
|
||||
<div class="players">
|
||||
{% for order, player in players.items %}
|
||||
<div class="player player-{{ player.order }}">
|
||||
<label for="player-{{ player.order }}-visible">
|
||||
<div class="player-summary">
|
||||
{% if player.ready %}🏁{% else %}⏳{% endif %}
|
||||
<span class="player-{{ player.order }}-ready">{% if player.ready %}🏁{% else %}⏳{% endif %}</span>
|
||||
(<span class="player-total-fear player-{{ player.order }}-total-fear">{{ player.total_fear }}</span> 😱)
|
||||
{{ player.name }} ({{ player.get_spirit_name }})
|
||||
</div>
|
||||
|
@ -56,9 +56,9 @@
|
|||
id="player-{{ player.order }}-ready"
|
||||
value="true"
|
||||
{% if player.ready %} checked{% endif %}/>Done with
|
||||
<span class="phase">{{ phase }} phase of turn #{{ turn }}</span>
|
||||
<span class="phase"><span class="status-phase">{{ status.phase }}</span> phase of turn #<span class="status-turn">{{ status.turn }}</span></span>
|
||||
</label>
|
||||
<div class="player-effects">
|
||||
<div class="player-effects" id="player-{{ player.order }}-effects">
|
||||
{% for effect_num, effect in player.fear.items %}
|
||||
<div class="effect effect-{{ effect_num }}">
|
||||
Effect #{{ effect_num|add:1 }}
|
||||
|
@ -90,10 +90,300 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<input type="submit" name="update" value="Update fear">
|
||||
{% if all_ready %}
|
||||
<input type="submit" name="advance" value="Advance to next phase">
|
||||
{% endif %}
|
||||
<input type="submit" name="revert" value="Revert to previous phase">
|
||||
<input type="submit" name="update" class="update-button" value="Update fear">
|
||||
<input type="submit" name="advance" class="advance-button" value="Advance to next phase"{% if not status.all_ready %} disabled{% endif %}>
|
||||
<input type="submit" name="revert" class="revert-button" value="Revert to previous phase">
|
||||
</form>
|
||||
{% block game_refresh %}
|
||||
{% if not results_only %}
|
||||
<div class="button-container">
|
||||
<a href="{% url 'game' access_code=access_code %}" class="button" id="button-refresh">Refresh</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
{% block header_script %}
|
||||
{% if not results_only %}
|
||||
<template id="effect-template">
|
||||
<div class="effect">
|
||||
Effect #<span class="effect-num">?</span>
|
||||
|
||||
<input type="hidden" class="fear-orig" value="0">
|
||||
<select class="fear">
|
||||
{% for val in range %}
|
||||
<option value="{{ val }}"{% if val == 0 %} selected="selected"{% endif %}>{{ val }} 😱</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<input type="hidden" class="towns-orig" value="0">
|
||||
<select class="towns">
|
||||
{% for val in range %}
|
||||
<option value="{{ val }}"{% if val == 0 %} selected="selected"{% endif %}>{{ val }} 🏠</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<input type="hidden" class="cities-orig" value="0">
|
||||
<select class="cities">
|
||||
{% for val in range %}
|
||||
<option value="{{ val }}"{% if val == 0 %} selected="selected"{% endif %}>{{ val }} 🏙️</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
var statusObj = JSON.parse("{{ status_json|escapejs }}");
|
||||
function ensureEffectRowExists(playerOrd, effectNum) {
|
||||
if(document.querySelector(".player-" + playerOrd + " .effect-" + effectNum)) {
|
||||
return;
|
||||
}
|
||||
if(effectNum > 0) {
|
||||
ensureEffectRowExists(playerOrd, effectNum-1);
|
||||
}
|
||||
|
||||
let effDiv = document.getElementById("player-" + playerOrd + "-effects");
|
||||
let effectTemplate = document.querySelector("#effect-template");
|
||||
let clone = effectTemplate.content.cloneNode(true);
|
||||
clone.querySelector("div.effect").classList.add("effect-" + effectNum);
|
||||
clone.querySelector(".effect-num").innerText = effectNum + 1;
|
||||
let prefix = "player-" + playerOrd + "-effect-" + effectNum + "-";
|
||||
["fear", "towns", "cities"].forEach(suffix => {
|
||||
clone.querySelector("." + suffix).name = prefix + suffix;
|
||||
clone.querySelector("." + suffix + "-orig").name = prefix + suffix + "-orig";
|
||||
});
|
||||
for(let el of clone.querySelectorAll('select')) {
|
||||
el.addEventListener("change", formElementChanged);
|
||||
}
|
||||
effDiv.appendChild(clone);
|
||||
}
|
||||
function addEmptyEffects(clearAll) {
|
||||
for(let effDiv of document.getElementsByClassName("player-effects")) {
|
||||
if(clearAll) {
|
||||
while(effDiv.lastElementChild) {
|
||||
effDiv.removeChild(effDiv.lastElementChild);
|
||||
}
|
||||
}
|
||||
|
||||
var newEffectNum = null;
|
||||
if(effDiv.childElementCount == 0) {
|
||||
newEffectNum = 0;
|
||||
} else {
|
||||
let lastEffectDiv = effDiv.children[effDiv.children.length-1];
|
||||
var lastEffectNum = null;
|
||||
lastEffectDiv.classList.forEach(cls => {
|
||||
if(cls.startsWith("effect-")) {
|
||||
lastEffectNum = parseInt(cls.substring(7));
|
||||
}
|
||||
});
|
||||
lastEffectDiv.querySelectorAll("select").forEach(child => {
|
||||
if(child.value != "0") {
|
||||
newEffectNum = lastEffectNum + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
if(newEffectNum != null) {
|
||||
let playerOrd = effDiv.id.split("-")[1];
|
||||
ensureEffectRowExists(playerOrd, newEffectNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleNewStatus(oldStatus, newStatus) {
|
||||
{% block game_handle_new_status %}
|
||||
if(oldStatus.hash == newStatus.hash) return true;
|
||||
if(oldStatus.phase_id != newStatus.phase_id
|
||||
|| oldStatus.turn != newStatus.turn) {
|
||||
addEmptyEffects(true);
|
||||
}
|
||||
|
||||
let form = document.forms[0];
|
||||
for(let key in newStatus) {
|
||||
if(key == "hash" || key == "phase_id" || key == "total_fear") {
|
||||
// ignored
|
||||
} else if(key == "all_ready") {
|
||||
let all_ready = newStatus[key]
|
||||
for(let el of document.getElementsByClassName("advance-button")) {
|
||||
el.disabled = !all_ready;
|
||||
}
|
||||
} else if(key == "players") {
|
||||
let players = newStatus[key];
|
||||
for(let ord in players) {
|
||||
let prefix = "player-" + ord + "-";
|
||||
let player = players[ord];
|
||||
for(let pkey in player) {
|
||||
if(pkey == "ready") {
|
||||
let ready = player[pkey];
|
||||
for(let el of document.getElementsByClassName(prefix + "ready")) {
|
||||
el.innerText = ready ? "🏁" : "⏳";
|
||||
}
|
||||
document.getElementById(prefix + "ready-orig").value = ready;
|
||||
document.getElementById(prefix + "ready").checked = ready;
|
||||
|
||||
for(let el of document.querySelectorAll(".player-" + ord + " select")) {
|
||||
el.disabled = ready;
|
||||
}
|
||||
} else if(pkey == "total_fear") {
|
||||
let total_fear = player[pkey];
|
||||
for(let el of document.getElementsByClassName(prefix + "total-fear")) {
|
||||
el.innerText = total_fear;
|
||||
}
|
||||
} else if(pkey == "fear") {
|
||||
let fear = player[pkey];
|
||||
for(let effect_num in fear) {
|
||||
ensureEffectRowExists(ord, effect_num);
|
||||
let fprefix = prefix + "effect-" + effect_num + "-";
|
||||
let effect = fear[effect_num];
|
||||
for(let ekey in effect) {
|
||||
let value = effect[ekey];
|
||||
let eclass = ekey == "pure_fear" ? "fear" : ekey;
|
||||
let eprefix = fprefix + eclass;
|
||||
form.elements[eprefix].value = value;
|
||||
form.elements[eprefix + "-orig"].value = value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert("Unknown player status key: " + pkey + "=" + player[pkey]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
addEmptyEffects(false);
|
||||
} else {
|
||||
var matching_elements = document.getElementsByClassName("status-"
|
||||
+ key);
|
||||
if(matching_elements.length == 0) {
|
||||
matching_elements = document.getElementsByClassName(key);
|
||||
}
|
||||
if(matching_elements.length == 0) {
|
||||
alert("Unknown status key: " + key + "=" + newStatus[key]);
|
||||
return false;
|
||||
}
|
||||
|
||||
let value = newStatus[key];
|
||||
for(let el of matching_elements) {
|
||||
el.innerText = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
{% endblock %}
|
||||
}
|
||||
var activeRequests = new Set();
|
||||
function formElementChanged(e) {
|
||||
let form = document.forms[0];
|
||||
let el = e.target;
|
||||
let name = el.name;
|
||||
let origName = name + "-orig";
|
||||
let origEl = form.elements[origName];
|
||||
|
||||
let newValue = el.type == "checkbox" ? el.checked : el.value;
|
||||
let oldValue = origEl.value;
|
||||
|
||||
el.disabled = true;
|
||||
if(el.name.endsWith("ready") && newValue) {
|
||||
let ord = name.split("-")[1];
|
||||
// If marking a player ready, disable all selects immediately.
|
||||
for(let el of document.querySelectorAll(".player-" + ord + " select")) {
|
||||
el.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
activeRequests.add(el);
|
||||
statusObj.hash = '';
|
||||
|
||||
// From https://stackoverflow.com/a/5588435
|
||||
function getCookie(name) {
|
||||
var cookieValue = null;
|
||||
if (document.cookie && document.cookie != '') {
|
||||
var cookies = document.cookie.split(';');
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
var cookie = cookies[i];
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) == (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
fetch("{% url 'update_game' access_code=access_code %}",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
"X-CSRFToken": getCookie("csrftoken"),
|
||||
},
|
||||
body: name + "=" + newValue + "&" + origName + "=" + oldValue
|
||||
+ "&game_turn=" + statusObj["turn"]
|
||||
+ "&game_phase=" + statusObj["phase_id"]
|
||||
+ "&phase_name=" + statusObj["phase"]
|
||||
+ "&csrfmiddlewaretoken=" + form.elements["csrfmiddlewaretoken"].value,
|
||||
}).then(response => response.json()
|
||||
).then(data => {
|
||||
if("success" in data && data["success"]) {
|
||||
form.elements[origName].value = newValue;
|
||||
} else {
|
||||
var updatedValue = oldValue;
|
||||
if("value" in data) {
|
||||
let updatedValue = data['value'];
|
||||
origEl.value = updatedValue;
|
||||
}
|
||||
if(el.type == "checkbox") {
|
||||
el.checked = updatedValue == "true";
|
||||
} else {
|
||||
el.value = updatedValue;
|
||||
}
|
||||
|
||||
if("errors" in data) {
|
||||
alert(data["errors"]);
|
||||
} else {
|
||||
alert("Unknown error: " + data);
|
||||
}
|
||||
}
|
||||
el.disabled = false;
|
||||
activeRequests.delete(el);
|
||||
});
|
||||
}
|
||||
window.addEventListener("DOMContentLoaded", e => {
|
||||
for(let updateButton of document.getElementsByClassName("update-button")) {
|
||||
updateButton.disabled = true;
|
||||
updateButton.style.display = "none";
|
||||
}
|
||||
let form = document.forms[0];
|
||||
for(let el of form.elements) {
|
||||
if(el.type == 'checkbox' && !el.name.endsWith("visible")) {
|
||||
el.addEventListener("change", formElementChanged);
|
||||
}
|
||||
}
|
||||
|
||||
for(let el of document.querySelectorAll('select')) {
|
||||
el.addEventListener("change", formElementChanged);
|
||||
}
|
||||
|
||||
setInterval(function() {
|
||||
if(activeRequests.size != 0) return;
|
||||
fetch(new Request("{% url 'status' access_code=access_code %}"
|
||||
+ (statusObj.hash != ""
|
||||
? statusObj.hash + "/"
|
||||
: "")))
|
||||
.then(response => {
|
||||
if(response.status === 304) {
|
||||
// TODO Just skip the next step?
|
||||
return statusObj;
|
||||
} else {
|
||||
return response.json();
|
||||
}
|
||||
})
|
||||
.then(data => {
|
||||
if(activeRequests.size != 0) return;
|
||||
if(!handleNewStatus(statusObj, data)) {
|
||||
document.getElementById("button-refresh").click();
|
||||
} else {
|
||||
statusObj = data;
|
||||
}
|
||||
});
|
||||
}, 5000);
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -24,7 +24,10 @@ urlpatterns = [
|
|||
path('new/', views.new_game, name='new_game'),
|
||||
url(r'^(?P<access_code>[a-zA-Z]{6})/', include([
|
||||
path('', views.game, name='game'),
|
||||
path('update/', views.update_game, name='update_game'),
|
||||
path('qr/', views.qr_code, name='qr_code'),
|
||||
path('status/', views.status, name='status'),
|
||||
url('^status/(?P<hashcode>[a-z0-9]{64})/',
|
||||
views.status, name='status'),
|
||||
])),
|
||||
]
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
from collections import OrderedDict
|
||||
from http import HTTPStatus
|
||||
from io import BytesIO
|
||||
import hashlib
|
||||
import json
|
||||
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.views.decorators.http import require_safe, require_http_methods,\
|
||||
require_POST
|
||||
from django.urls import reverse
|
||||
|
||||
from .forms import NewGameForm, JoinGameForm, PlayerFormSet
|
||||
|
@ -89,16 +93,87 @@ def qr_code(request, access_code):
|
|||
return HttpResponse(output.getvalue(), content_type='image/png')
|
||||
|
||||
|
||||
def load_players(game):
|
||||
players = OrderedDict()
|
||||
for player in game.player_set.order_by('order').all():
|
||||
player.total_fear = 0
|
||||
player.fear = OrderedDict()
|
||||
players[player.order] = player
|
||||
|
||||
return players
|
||||
|
||||
|
||||
def get_players_with_fear(game, current_phase=None, players=None):
|
||||
if players is None:
|
||||
players = load_players(game)
|
||||
if current_phase is None:
|
||||
current_phase = game.get_current_phase()
|
||||
|
||||
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
|
||||
|
||||
return players
|
||||
|
||||
|
||||
def game_status_object(game, current_phase=None, players_with_fear=None):
|
||||
if current_phase is None:
|
||||
current_phase = game.get_current_phase()
|
||||
if players_with_fear is None:
|
||||
players_with_fear = get_players_with_fear(game, current_phase)
|
||||
|
||||
players = OrderedDict()
|
||||
for order, player in players_with_fear.items():
|
||||
players[order] = {
|
||||
'ready': player.ready,
|
||||
'fear': player.fear,
|
||||
'total_fear': player.total_fear
|
||||
}
|
||||
|
||||
status_obj = {
|
||||
'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()),
|
||||
}
|
||||
h = hashlib.sha256()
|
||||
h.update(json.dumps(status_obj).encode('utf-8'))
|
||||
status_hash = h.hexdigest()
|
||||
status_obj['hash'] = status_hash
|
||||
|
||||
return status_obj
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
@lookup_access_code
|
||||
@require_POST
|
||||
def update_game(request, game):
|
||||
return handle_game_request(request, game, update=True)
|
||||
|
||||
|
||||
@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
|
||||
return handle_game_request(request, game, update=False)
|
||||
|
||||
|
||||
def handle_game_request(request, game, update):
|
||||
# TODO Should check if game is over and redirect to summary table view.
|
||||
|
||||
players = load_players(game)
|
||||
for player in players.values():
|
||||
player.changed_to_ready = False
|
||||
player.fear = OrderedDict()
|
||||
players[player.order] = player
|
||||
|
||||
errors = []
|
||||
fear_kind_dict = {
|
||||
|
@ -106,14 +181,17 @@ def game(request, game):
|
|||
'towns': 'towns_destroyed',
|
||||
'cities': 'cities_destroyed',
|
||||
}
|
||||
current_value = None
|
||||
current_phase = game.get_current_phase()
|
||||
if request.method == 'POST':
|
||||
post_data = request.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():
|
||||
current_phase.game_turn == int(post_data['game_turn'])\
|
||||
and current_phase.game_phase == int(post_data['game_phase'])
|
||||
advance_phase = 'advance' in post_data
|
||||
revert_phase = 'revert' in post_data
|
||||
for key, value in post_data.items():
|
||||
sections = key.split('-')
|
||||
if key in ['csrfmiddlewaretoken',
|
||||
'update', 'advance', 'revert',
|
||||
|
@ -121,7 +199,7 @@ def game(request, game):
|
|||
or sections[-1] == 'orig':
|
||||
pass
|
||||
elif sections[0] == 'player':
|
||||
orig_value = request.POST.get(key + '-orig', None)
|
||||
orig_value = post_data.get(key + '-orig', None)
|
||||
player_ord = int(sections[1])
|
||||
player = players[player_ord]
|
||||
if sections[2] == 'visible':
|
||||
|
@ -151,7 +229,7 @@ def game(request, game):
|
|||
f"from {orig} to {amount} " +
|
||||
"was not processed.")
|
||||
elif not correct_phase or advance_phase:
|
||||
old_phase = request.POST['phase_name']
|
||||
old_phase = post_data['phase_name']
|
||||
errors.append(
|
||||
f"{player.name} " +
|
||||
f"({player.get_spirit_name()})'s " +
|
||||
|
@ -198,14 +276,22 @@ def game(request, game):
|
|||
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
|
||||
if update:
|
||||
if errors:
|
||||
res = {
|
||||
'success': False,
|
||||
'errors': '\n'.join(errors)
|
||||
}
|
||||
if current_value is not None:
|
||||
res['value'] = current_value
|
||||
else:
|
||||
res = {'success': True}
|
||||
return HttpResponse(json.dumps(res))
|
||||
|
||||
players = get_players_with_fear(game, current_phase, players)
|
||||
status_obj = game_status_object(game, current_phase, players)
|
||||
status_string = json.dumps(status_obj)
|
||||
|
||||
for player in players.values():
|
||||
info = player.fear
|
||||
if not info:
|
||||
|
@ -217,17 +303,12 @@ def game(request, game):
|
|||
'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(),
|
||||
'status': status_obj,
|
||||
'status_json': status_string,
|
||||
'players': players,
|
||||
'all_ready': all(player.ready for player in players.values()),
|
||||
'range': range(21),
|
||||
'errors': errors,
|
||||
})
|
||||
|
@ -235,6 +316,10 @@ def game(request, game):
|
|||
|
||||
@lookup_access_code
|
||||
@require_safe
|
||||
def status(request, game):
|
||||
# TODO status json
|
||||
pass
|
||||
def status(request, game, hashcode=None):
|
||||
status_obj = game_status_object(game)
|
||||
if hashcode == status_obj['hash']:
|
||||
return HttpResponse(status=HTTPStatus.NOT_MODIFIED)
|
||||
else:
|
||||
status_string = json.dumps(status_obj)
|
||||
return HttpResponse(status_string)
|
||||
|
|
Loading…
Reference in New Issue
Block a user