Progress on js exec stuff
This commit is contained in:
parent
53a4734cae
commit
919d4acf6f
@ -1,6 +1,8 @@
|
|||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
from bloonsa_api.views import LoadLevel, RandomLevel, CompleteLevel, RateLevel
|
from bloonsa_api.views import (LoadLevel, RandomLevel,
|
||||||
|
CompleteLevel, RateLevel,
|
||||||
|
GetStatusData)
|
||||||
|
|
||||||
app_name = "bloonsa_api"
|
app_name = "bloonsa_api"
|
||||||
|
|
||||||
@ -8,5 +10,7 @@ urlpatterns = [
|
|||||||
path("load_level", LoadLevel.as_view(), name="api-load_level"),
|
path("load_level", LoadLevel.as_view(), name="api-load_level"),
|
||||||
path("get_random_level", RandomLevel.as_view(), name="api-get_random_level"),
|
path("get_random_level", RandomLevel.as_view(), name="api-get_random_level"),
|
||||||
path("complete_level", CompleteLevel.as_view(), name="api-complete_level"),
|
path("complete_level", CompleteLevel.as_view(), name="api-complete_level"),
|
||||||
path("rate_level", RateLevel.as_view(), name="api-rate_level")
|
path("rate_level", RateLevel.as_view(), name="api-rate_level"),
|
||||||
|
|
||||||
|
path("get_status_data", GetStatusData.as_view(), name="api-get_status_data")
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.shortcuts import render, HttpResponse
|
from django.shortcuts import render, HttpResponse
|
||||||
from django.http import HttpResponseBadRequest
|
from django.http import HttpResponseBadRequest, JsonResponse
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
@ -21,94 +25,127 @@ class LoadLevel(CSRFexemptTemplateView):
|
|||||||
if level_id is None or not level_id.isdigit():
|
if level_id is None or not level_id.isdigit():
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
level = Level.objects.filter(levelId=int(level_id)).first()
|
level: Level = Level.objects.filter(level_id=int(level_id)).first()
|
||||||
if level is None:
|
if level is None:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
bloonsa_util.tag_player(request=request)
|
bloonsa_util.tag_player(request=request)
|
||||||
player = Player.objects.get(user=request.user)
|
player: Player = Player.objects.get(user=request.user)
|
||||||
player.bloonsa_levels_played.add(level)
|
player.bloonsa_levels_played.add(level)
|
||||||
bloonsa_util.log(player=player,
|
bloonsa_util.log(player=player,
|
||||||
action=actions.bloonsa_load_level_by_id,
|
action=actions.bloonsa_load_level_by_id,
|
||||||
note=level.levelId)
|
note=level.level_id)
|
||||||
|
|
||||||
flashVars = level.getFlashVars(seperator="&")
|
flashVars = level.get_flash_vars(seperator="&")
|
||||||
return HttpResponse(flashVars)
|
return HttpResponse(flashVars)
|
||||||
|
|
||||||
|
|
||||||
class RandomLevel(CSRFexemptTemplateView):
|
class RandomLevel(CSRFexemptTemplateView):
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
level = Level.objects.order_by("?").first()
|
level: Level = Level.objects.order_by("?").first()
|
||||||
flashVars = level.getFlashVars(seperator="&")
|
flashVars = level.get_flash_vars(seperator="&")
|
||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
bloonsa_util.tag_player(request=request)
|
bloonsa_util.tag_player(request=request)
|
||||||
player = Player.objects.get(user=request.user)
|
player: Player = Player.objects.get(user=request.user)
|
||||||
level.played_by.add(player)
|
level.played_by.add(player)
|
||||||
bloonsa_util.log(player=player,
|
bloonsa_util.log(player=player,
|
||||||
action=actions.bloonsa_load_random_level,
|
action=actions.bloonsa_load_random_level,
|
||||||
note=level.levelId)
|
note=level.level_id)
|
||||||
|
|
||||||
return HttpResponse(flashVars)
|
return HttpResponse(flashVars)
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
return self.post(request=request, *args, **kwargs)
|
return self.post(request=request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CompleteLevel(CSRFexemptTemplateView):
|
class CompleteLevel(CSRFexemptTemplateView):
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
if request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
bloonsa_util.tag_player(request=request)
|
return HttpResponse(content="GG", status=200)
|
||||||
level_id = int(request.POST.get("level_id"))
|
|
||||||
darts_left = int(request.POST.get("darts_left"))
|
|
||||||
pops = int(request.POST.get("pops"))
|
|
||||||
|
|
||||||
level = Level.objects.get(levelId=level_id)
|
bloonsa_util.tag_player(request=request)
|
||||||
player = Player.objects.get(user=request.user)
|
level_id = int(request.POST.get("level_id"))
|
||||||
|
darts_left = int(request.POST.get("darts_left"))
|
||||||
|
pops = int(request.POST.get("pops"))
|
||||||
|
|
||||||
prevScore = player.bloonsa_level_scores.first()
|
level: Level = Level.objects.get(level_id=level_id)
|
||||||
if prevScore is None \
|
player: Player = Player.objects.get(user=request.user)
|
||||||
or pops > prevScore.pops \
|
|
||||||
or pops == prevScore.pops and darts_left > prevScore.darts_left:
|
|
||||||
score = LevelScore.objects.create(level=level,
|
|
||||||
clear=True,
|
|
||||||
darts_left=darts_left,
|
|
||||||
pops=pops)
|
|
||||||
if prevScore:
|
|
||||||
player.bloonsa_level_scores.remove(prevScore)
|
|
||||||
|
|
||||||
player.bloonsa_level_scores.add(score)
|
prevScore = player.bloonsa_level_scores.first()
|
||||||
score.save()
|
if prevScore is None \
|
||||||
player.save()
|
or pops > prevScore.pops \
|
||||||
bloonsa_util.log(player=player,
|
or pops == prevScore.pops and darts_left > prevScore.darts_left:
|
||||||
action=actions.bloonsa_submit_score,
|
score = LevelScore.objects.create(level=level,
|
||||||
note=level.levelId)
|
clear=True,
|
||||||
|
darts_left=darts_left,
|
||||||
|
pops=pops)
|
||||||
|
if prevScore:
|
||||||
|
player.bloonsa_level_scores.remove(prevScore)
|
||||||
|
|
||||||
|
player.bloonsa_level_scores.add(score)
|
||||||
|
score.save()
|
||||||
|
player.save()
|
||||||
|
bloonsa_util.log(player=player,
|
||||||
|
action=actions.bloonsa_submit_score,
|
||||||
|
note=level.level_id)
|
||||||
|
|
||||||
# Sending empty content means error in as3
|
# Sending empty content means error in as3
|
||||||
return HttpResponse(content="GG", status=200)
|
return HttpResponse(content="GG", status=200)
|
||||||
|
|
||||||
class RateLevel(CSRFexemptTemplateView):
|
class RateLevel(CSRFexemptTemplateView):
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
if request.user.is_authenticated:
|
|
||||||
bloonsa_util.tag_player(request=request)
|
|
||||||
rating = int(request.POST.get("rating"))
|
|
||||||
level_id = int(request.POST.get("level_id"))
|
|
||||||
level = Level.objects.get(levelId=level_id)
|
|
||||||
player = Player.objects.get(user=request.user)
|
|
||||||
ratingObject = Player.objects.filter(bloonsa_level_ratings__level=level).first()
|
|
||||||
bloonsa_util.log(player=player,
|
|
||||||
action=actions.bloonsa_rate_level,
|
|
||||||
note=level.levelId)
|
|
||||||
if ratingObject:
|
|
||||||
ratingObject.rating = rating
|
|
||||||
ratingObject.save()
|
|
||||||
return HttpResponse(content="OK", status=200)
|
|
||||||
|
|
||||||
rating = LevelRating.objects.create(level=level,
|
def post(self, request, *args, **kwargs):
|
||||||
rating=rating)
|
if not request.user.is_authenticated:
|
||||||
player.bloonsa_level_ratings.add(rating)
|
|
||||||
rating.save()
|
|
||||||
player.save()
|
|
||||||
return HttpResponse(content="OK", status=200)
|
return HttpResponse(content="OK", status=200)
|
||||||
|
|
||||||
return HttpResponse(status=400)
|
bloonsa_util.tag_player(request=request)
|
||||||
|
rating = int(request.POST.get("rating"))
|
||||||
|
level_id = int(request.POST.get("level_id"))
|
||||||
|
level: Level = Level.objects.get(level_id=level_id)
|
||||||
|
player: Player = Player.objects.get(user=request.user)
|
||||||
|
ratingObject = player.bloonsa_level_ratings.filter(level=level).first()
|
||||||
|
bloonsa_util.log(player=player,
|
||||||
|
action=actions.bloonsa_rate_level,
|
||||||
|
note=level.level_id)
|
||||||
|
if ratingObject:
|
||||||
|
ratingObject.rating = rating
|
||||||
|
ratingObject.save()
|
||||||
|
return HttpResponse(content="OK", status=200)
|
||||||
|
|
||||||
|
rating = LevelRating.objects.create(level=level,
|
||||||
|
rating=rating)
|
||||||
|
player.bloonsa_level_ratings.add(rating)
|
||||||
|
rating.save()
|
||||||
|
player.save()
|
||||||
|
return HttpResponse(content="OK", status=200)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class GetStatusData(CSRFexemptTemplateView):
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
bloonsa_util.tag_player(request=request)
|
||||||
|
level_id = int(json.loads(request.body).get("level_id"))
|
||||||
|
level = Level.objects.get(level_id=level_id)
|
||||||
|
player = Player.objects.get(user=request.user)
|
||||||
|
total_levels = Level.objects.count()
|
||||||
|
level_cleared = bool(player.bloonsa_level_scores.filter())
|
||||||
|
|
||||||
|
jdata = {
|
||||||
|
"bloonsa_levels_played": player.bloonsa_levels_played_count,
|
||||||
|
"bloonsa_levels_beaten": player.bloonsa_levels_beaten_count,
|
||||||
|
"bloonsa_total_levels": total_levels,
|
||||||
|
|
||||||
|
"level_author_name": level.author.name,
|
||||||
|
"level_author_id": level.author.id,
|
||||||
|
"level_title": level.title,
|
||||||
|
"level_id": level.level_id,
|
||||||
|
"level_legacy_plays": level.plays,
|
||||||
|
"level_plays": level.play_count,
|
||||||
|
"level_rating": level.rating,
|
||||||
|
"level_cleared": level_cleared,
|
||||||
|
}
|
||||||
|
return JsonResponse(jdata)
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-14 13:44
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('bloonsa_game', '0017_alter_levelrating_rating'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='author',
|
||||||
|
old_name='authorId',
|
||||||
|
new_name='author_id',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='level',
|
||||||
|
old_name='levelId',
|
||||||
|
new_name='level_id',
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -6,36 +6,39 @@ from users.models import Player
|
|||||||
|
|
||||||
class Author(models.Model):
|
class Author(models.Model):
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
authorId = models.IntegerField()
|
author_id = models.IntegerField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.authorId} - {self.name}"
|
return f"{self.author_id} - {self.name}"
|
||||||
|
|
||||||
class Level(models.Model):
|
class Level(models.Model):
|
||||||
author = models.ForeignKey(Author, on_delete=models.CASCADE)
|
author = models.ForeignKey(Author, on_delete=models.CASCADE)
|
||||||
title = models.CharField(max_length=16)
|
title = models.CharField(max_length=16)
|
||||||
levelId = models.IntegerField()
|
level_id = models.IntegerField()
|
||||||
darts = models.SmallIntegerField()
|
darts = models.SmallIntegerField()
|
||||||
target = models.SmallIntegerField()
|
target = models.SmallIntegerField()
|
||||||
plays = models.IntegerField()
|
plays = models.IntegerField()
|
||||||
beats = models.IntegerField()
|
beats = models.IntegerField() # TODO can be removed?
|
||||||
rating = models.FloatField(null=True, blank=True)
|
rating = models.FloatField(null=True, blank=True)
|
||||||
data = models.CharField(max_length=288)
|
data = models.CharField(max_length=288)
|
||||||
|
|
||||||
played_by = models.ManyToManyField(Player, blank=True, related_name="bloonsa_levels_played")
|
played_by = models.ManyToManyField(Player, blank=True, related_name="bloonsa_levels_played")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def play_count(self):
|
||||||
|
return self.played_by.count()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.author} - {self.title}"
|
return f"{self.author} - {self.title}"
|
||||||
|
|
||||||
|
def get_flash_vars(self, seperator):
|
||||||
def getFlashVars(self, seperator):
|
|
||||||
flashVars = {
|
flashVars = {
|
||||||
"levelNum": self.levelId,
|
"levelNum": self.level_id,
|
||||||
"title": self.title,
|
"title": self.title,
|
||||||
"darts": self.darts,
|
"darts": self.darts,
|
||||||
"target": self.target,
|
"target": self.target,
|
||||||
"data": self.data,
|
"data": self.data,
|
||||||
"authorID": self.author.authorId,
|
"authorID": self.author.author_id,
|
||||||
"rating": self.rating if self.rating else 0.0,
|
"rating": self.rating if self.rating else 0.0,
|
||||||
}
|
}
|
||||||
flashVarsStr = f"{seperator}".join([f"{k}={v}"
|
flashVarsStr = f"{seperator}".join([f"{k}={v}"
|
||||||
|
|||||||
@ -27,3 +27,40 @@
|
|||||||
.wide.centered {
|
.wide.centered {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#level-title,
|
||||||
|
#level-id,
|
||||||
|
#level-author,
|
||||||
|
#level-author-id
|
||||||
|
#level-clear-state {
|
||||||
|
white-space: break-spaces;
|
||||||
|
float: left;
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#level-id,
|
||||||
|
#level-author-id {
|
||||||
|
color: #59b1ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#current-level-container {
|
||||||
|
height: 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#levels_played, #levels_beaten, #total_levels, .profile_text {
|
||||||
|
white-space: break-spaces;
|
||||||
|
float: left;
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logout_button {
|
||||||
|
float: left;
|
||||||
|
display: block;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#level-clear-state {
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,7 +1,46 @@
|
|||||||
function bloonsa_new_level_started(id) {
|
function bloonsa_new_level_started(level_id) {
|
||||||
console.log("new level started! id=" + id)
|
console.log("call to new_level_started");
|
||||||
|
bloonsa_update_data(level_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function bloonsa_level_completed(id) {
|
function bloonsa_level_completed(level_id) {
|
||||||
console.log("level completed! id=" + id)
|
bloonsa_update_data(level_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function bloonsa_update_data(level_id) {
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
console.log("call to update_data");
|
||||||
|
xhr.open("POST", "/bloonsa_api/get_status_data", true);
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
|
xhr.onreadystatechange = function () {
|
||||||
|
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||||
|
try {
|
||||||
|
var r = JSON.parse(xhr.response);
|
||||||
|
|
||||||
|
bloonsa_update_html(r);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
xhr.send(JSON.stringify({
|
||||||
|
level_id: level_id
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function bloonsa_update_html(r) {
|
||||||
|
document.getElementById("level-title").textContent = r.level_title;
|
||||||
|
document.getElementById("level-id").textContent = `(${r.level_id})`;
|
||||||
|
document.getElementById("level-author").textContent = `by ${r.level_author_name}`;
|
||||||
|
document.getElementById("level-author-id").textContent = `(${r.level_author_id})`;
|
||||||
|
if (r.level_cleared) {
|
||||||
|
document.getElementById("level-clear-state").textContent = `✅`;
|
||||||
|
}
|
||||||
|
document.getElementById("levels_played").textContent = `${r.bloonsa_levels_played}`;
|
||||||
|
document.getElementById("levels_beaten").textContent = `${r.bloonsa_levels_beaten}`;
|
||||||
|
document.getElementById("total_levels").textContent = `${r.bloonsa_total_levels}`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Binary file not shown.
@ -68,11 +68,10 @@
|
|||||||
<div id="content">
|
<div id="content">
|
||||||
{% block content %}{% endblock content %}
|
{% block content %}{% endblock content %}
|
||||||
|
|
||||||
<div class="wide left">
|
<div id="current-level-container" class="wide left">
|
||||||
<p>
|
<p>
|
||||||
<h1 class="level-title-container"></h1>
|
<h1 id="level-title"></h1><h1 id="level-id"></h1><h1 id="level-clear-state"></h1><h1></h1>
|
||||||
<h3 class="level-author-container"></h3><br>
|
<h3 id="level-author"></h3><h3 id="level-author-id"></h3><br>
|
||||||
<p class="level-id-container"></p>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
{% load bloonsa_game_tags %}
|
{% load bloonsa_game_tags %}
|
||||||
<div id="profile_box">
|
<div id="profile_box">
|
||||||
<h3>{{ user }}</h3>
|
<h3 id="username">{{ user }}</h3>
|
||||||
<p>Levels played: {{ player.levels_played }}<br>
|
<div class="profile_text">Levels played: </div>
|
||||||
Levels beaten: {{ player.levels_beaten }} / {{ total_levels }}</p>
|
<div id="levels_played">{{ player.bloonsa_levels_played_count }}</div><br>
|
||||||
<h3><a href="/users/logout">Logout</a></h3>
|
<div class="profile_text">Levels beaten: </div>
|
||||||
|
<div id="levels_beaten">{{ player.bloonsa_levels_beaten_count }}</div>
|
||||||
|
<div class="profile_text"> / </div><div id="total_levels">{{ total_levels }}</div><br>
|
||||||
|
<a href="/users/logout" id="logout_button"><b>Logout</b></a>
|
||||||
</div>
|
</div>
|
||||||
@ -34,7 +34,7 @@ class GameView(TemplateView):
|
|||||||
return render(request, "bloonsa_game/level.html", context={
|
return render(request, "bloonsa_game/level.html", context={
|
||||||
"player": player,
|
"player": player,
|
||||||
"total_levels": total_levels,
|
"total_levels": total_levels,
|
||||||
"flashVars": level.getFlashVars(seperator="&"),
|
"flashVars": level.get_flash_vars(seperator="&"),
|
||||||
"levelTitle": level.title,
|
"levelTitle": level.title,
|
||||||
"levelAuthor": level.author,
|
"levelAuthor": level.author,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -14,16 +14,16 @@ def run():
|
|||||||
cursor.execute("SELECT * FROM Levels")
|
cursor.execute("SELECT * FROM Levels")
|
||||||
|
|
||||||
for (levelNum, title, authorname, authorID, numPlays, numCompleted, rating, target, darts, data) in tqdm(cursor.fetchall()):
|
for (levelNum, title, authorname, authorID, numPlays, numCompleted, rating, target, darts, data) in tqdm(cursor.fetchall()):
|
||||||
authorObj = Author.objects.filter(authorId=authorID).first()
|
authorObj = Author.objects.filter(author_id=authorID).first()
|
||||||
if not authorObj:
|
if not authorObj:
|
||||||
authorObj = Author.objects.create(authorId=authorID,
|
authorObj = Author.objects.create(author_id=authorID,
|
||||||
name=authorname)
|
name=authorname)
|
||||||
authorObj.save()
|
authorObj.save()
|
||||||
|
|
||||||
levelObj = Level.objects.filter(levelId=levelNum).first()
|
levelObj = Level.objects.filter(level_id=levelNum).first()
|
||||||
if not levelObj:
|
if not levelObj:
|
||||||
levelObj = Level.objects.create(author=authorObj,
|
levelObj = Level.objects.create(author=authorObj,
|
||||||
levelId=levelNum,
|
level_id=levelNum,
|
||||||
title=title,
|
title=title,
|
||||||
plays=numPlays,
|
plays=numPlays,
|
||||||
beats=numCompleted,
|
beats=numCompleted,
|
||||||
|
|||||||
@ -18,17 +18,15 @@ class Player(models.Model):
|
|||||||
|
|
||||||
# If bloonsa_levels_played gets replaced then this needs to be updated
|
# If bloonsa_levels_played gets replaced then this needs to be updated
|
||||||
@property
|
@property
|
||||||
def levels_played(self):
|
def bloonsa_levels_played_count(self):
|
||||||
return self.bloonsa_levels_played.count()
|
return self.bloonsa_levels_played.count()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def levels_beaten(self):
|
def bloonsa_levels_beaten_count(self):
|
||||||
return self.bloonsa_level_scores.filter(clear=True).count()
|
return self.bloonsa_level_scores.filter(clear=True).count()
|
||||||
|
|
||||||
@property
|
def has_beaten_level(self, level):
|
||||||
def total_levels(self):
|
bool(self.bloonsa_level_scores.filter(clear=True, level=level).first())
|
||||||
# TODO
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
statesDict = {
|
statesDict = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user