diff --git a/app/bloonsa_api/views.py b/app/bloonsa_api/views.py
index ed07304..203a490 100644
--- a/app/bloonsa_api/views.py
+++ b/app/bloonsa_api/views.py
@@ -28,7 +28,7 @@ class LoadLevel(CSRFexemptTemplateView):
if request.user.is_authenticated:
bloonsa_util.tag_player(request=request)
player = Player.objects.get(user=request.user)
- player.bloonsa_levelsPlayed.add(level)
+ player.bloonsa_levels_played.add(level)
bloonsa_util.log(player=player,
action=actions.bloonsa_load_level_by_id,
note=level.levelId)
@@ -45,7 +45,7 @@ class RandomLevel(CSRFexemptTemplateView):
if request.user.is_authenticated:
bloonsa_util.tag_player(request=request)
player = Player.objects.get(user=request.user)
- player.bloonsa_levelsPlayed.add(level)
+ level.played_by.add(player)
bloonsa_util.log(player=player,
action=actions.bloonsa_load_random_level,
note=level.levelId)
@@ -95,21 +95,20 @@ class RateLevel(CSRFexemptTemplateView):
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_levelRatings__level=level).first()
- if ratingObject:
- ratingObject.rating = rating
- ratingObject.save()
-
- else:
- rating = LevelRating.objects.create(level=level,
- rating=rating)
- player.bloonsa_levelRatings.add(rating)
- rating.save()
- player.save()
-
+ 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,
+ rating=rating)
+ player.bloonsa_level_ratings.add(rating)
+ rating.save()
+ player.save()
return HttpResponse(content="OK", status=200)
return HttpResponse(status=400)
diff --git a/app/bloonsa_game/migrations/0015_levelrating_player_levelscore_player.py b/app/bloonsa_game/migrations/0015_levelrating_player_levelscore_player.py
new file mode 100644
index 0000000..daa77e6
--- /dev/null
+++ b/app/bloonsa_game/migrations/0015_levelrating_player_levelscore_player.py
@@ -0,0 +1,25 @@
+# Generated by Django 5.1.6 on 2025-02-14 03:01
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('bloonsa_game', '0014_alter_levelscore_level'),
+ ('users', '0021_remove_player_bloonsa_levelratings_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='levelrating',
+ name='player',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='bloonsa_level_ratings', to='users.player'),
+ ),
+ migrations.AddField(
+ model_name='levelscore',
+ name='player',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='bloonsa_level_scores', to='users.player'),
+ ),
+ ]
diff --git a/app/bloonsa_game/migrations/0016_level_played_by.py b/app/bloonsa_game/migrations/0016_level_played_by.py
new file mode 100644
index 0000000..3b34d91
--- /dev/null
+++ b/app/bloonsa_game/migrations/0016_level_played_by.py
@@ -0,0 +1,19 @@
+# Generated by Django 5.1.6 on 2025-02-14 03:04
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('bloonsa_game', '0015_levelrating_player_levelscore_player'),
+ ('users', '0021_remove_player_bloonsa_levelratings_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='level',
+ name='played_by',
+ field=models.ManyToManyField(blank=True, related_name='bloonsa_levels_played', to='users.player'),
+ ),
+ ]
diff --git a/app/bloonsa_game/migrations/0017_alter_levelrating_rating.py b/app/bloonsa_game/migrations/0017_alter_levelrating_rating.py
new file mode 100644
index 0000000..7c7fa24
--- /dev/null
+++ b/app/bloonsa_game/migrations/0017_alter_levelrating_rating.py
@@ -0,0 +1,19 @@
+# Generated by Django 5.1.6 on 2025-02-14 03:16
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('bloonsa_game', '0016_level_played_by'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='levelrating',
+ name='rating',
+ field=models.SmallIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(10)]),
+ ),
+ ]
diff --git a/app/bloonsa_game/models.py b/app/bloonsa_game/models.py
index 88bf9b3..2cc4e24 100644
--- a/app/bloonsa_game/models.py
+++ b/app/bloonsa_game/models.py
@@ -1,5 +1,9 @@
+from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import models
+from users.models import Player
+
+
class Author(models.Model):
name = models.CharField(max_length=255)
authorId = models.IntegerField()
@@ -18,6 +22,8 @@ class Level(models.Model):
rating = models.FloatField(null=True, blank=True)
data = models.CharField(max_length=288)
+ played_by = models.ManyToManyField(Player, blank=True, related_name="bloonsa_levels_played")
+
def __str__(self):
return f"{self.author} - {self.title}"
@@ -38,16 +44,19 @@ class Level(models.Model):
class LevelRating(models.Model):
+ player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="bloonsa_level_ratings", null=True)
level = models.ForeignKey(Level, on_delete=models.CASCADE)
- rating = models.SmallIntegerField()
+ rating = models.SmallIntegerField(validators=[MinValueValidator(1),
+ MaxValueValidator(10)])
def __str__(self):
- return (f"{self.player.first().user.username}"
+ return (f"{self.player.user.username}"
f" - [<{int(self.rating) * '★'}> - {self.level.title}]")
# There should only be 1 score per player
# Highest popcount wins
class LevelScore(models.Model):
+ player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="bloonsa_level_scores", null=True)
level = models.ForeignKey(Level, on_delete=models.CASCADE)
clear = models.BooleanField(default=True) # This is for if we ever submit scores for failed attempts
darts_left = models.PositiveSmallIntegerField(null=True)
@@ -55,5 +64,5 @@ class LevelScore(models.Model):
def __str__(self):
clearState = "✅" if self.clear else "❌"
- return (f"{self.player.first().user.username}'s {clearState} @ {self.level.title}"
+ return (f"{self.player.user.username}'s {clearState} @ {self.level.title}"
f"[🎈{self.pops} | 🎯{self.darts_left}]")
\ No newline at end of file
diff --git a/app/bloonsa_game/static/bloonsa_game/misc/bloons_unlimited.swf b/app/bloonsa_game/static/bloonsa_game/misc/bloons_unlimited.swf
index 0e7387b..bc156f3 100644
Binary files a/app/bloonsa_game/static/bloonsa_game/misc/bloons_unlimited.swf and b/app/bloonsa_game/static/bloonsa_game/misc/bloons_unlimited.swf differ
diff --git a/app/bloonsa_game/templates/bloonsa_game/base.html b/app/bloonsa_game/templates/bloonsa_game/base.html
index 78d5da8..b43dcb7 100644
--- a/app/bloonsa_game/templates/bloonsa_game/base.html
+++ b/app/bloonsa_game/templates/bloonsa_game/base.html
@@ -26,13 +26,15 @@
+ "quality": "high",
+ "splashScreen": false,
+ "menu": false,
+ "polyfills": false,
+ };
{% if head %}
diff --git a/app/bloonsa_game/templates/bloonsa_game/game.html b/app/bloonsa_game/templates/bloonsa_game/game.html
index 590d29a..6bc694d 100644
--- a/app/bloonsa_game/templates/bloonsa_game/game.html
+++ b/app/bloonsa_game/templates/bloonsa_game/game.html
@@ -15,12 +15,13 @@
width="640" height="480" title="" allowScriptAccess=true>
+