diff --git a/app/bloonsa_api/views.py b/app/bloonsa_api/views.py index ba0ac7d..4237695 100644 --- a/app/bloonsa_api/views.py +++ b/app/bloonsa_api/views.py @@ -49,7 +49,10 @@ class RandomLevel(CSRFexemptTemplateView): if request.user.is_authenticated: bloonsa_util.tag_player(request=request) player: Player = Player.objects.get(user=request.user) - level.played_by.add(player) + + if not player.bloonsa_levels_played.filter(pk=level.pk).exists(): + level.played_by.add(player) + level.save() bloonsa_util.log(player=player, action=actions.bloonsa_load_random_level, note=level.level_id) @@ -69,18 +72,21 @@ class CompleteLevel(CSRFexemptTemplateView): bloonsa_util.tag_player(request=request) level_id = int(request.POST.get("level_id")) darts_left = int(request.POST.get("darts_left")) + dart_glitch = bool(request.POST.get("dart_glitch")) pops = int(request.POST.get("pops")) level: Level = Level.objects.get(level_id=level_id) player: Player = Player.objects.get(user=request.user) - prevScore = player.bloonsa_level_scores.first() + prevScore = LevelScore.objects.filter(player=player, + level=level).first() if prevScore is None \ 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, + dart_glitch=dart_glitch, pops=pops) if prevScore: player.bloonsa_level_scores.remove(prevScore) @@ -123,14 +129,15 @@ class RateLevel(CSRFexemptTemplateView): 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) + level: Level = Level.objects.get(level_id=level_id) + player: Player = Player.objects.get(user=request.user) + score: LevelScore = LevelScore.objects.filter(player=player, + level=level).first() total_levels = Level.objects.count() level_cleared = player.has_beaten_bloonsa_level(level=level) @@ -148,4 +155,11 @@ class GetStatusData(CSRFexemptTemplateView): "level_rating": level.rating, "level_cleared": level_cleared, } + if score: + jdata.update({ + "score_darts_left": score.darts_left, + "score_dart_glitch": score.dart_glitch, + "score_pops": score.pops, + }) + return JsonResponse(jdata) \ No newline at end of file diff --git a/app/bloonsa_game/admin.py b/app/bloonsa_game/admin.py index 1cbc983..0405576 100644 --- a/app/bloonsa_game/admin.py +++ b/app/bloonsa_game/admin.py @@ -1,7 +1,14 @@ from django.contrib import admin -from .models import Level, Author, LevelScore +from .models import Level, Author, LevelScore, LevelRating + +@admin.register(LevelRating) +class LevelRatingAdmin(admin.ModelAdmin): + exclude = ("level",) + +@admin.register(LevelScore) +class LevelScoreAdmin(admin.ModelAdmin): + exclude = ("level",) admin.site.register(Level) -admin.site.register(Author) -admin.site.register(LevelScore) \ No newline at end of file +admin.site.register(Author) \ No newline at end of file diff --git a/app/bloonsa_game/migrations/0019_levelscore_dart_glitch.py b/app/bloonsa_game/migrations/0019_levelscore_dart_glitch.py new file mode 100644 index 0000000..a805397 --- /dev/null +++ b/app/bloonsa_game/migrations/0019_levelscore_dart_glitch.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.6 on 2025-02-14 18:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('bloonsa_game', '0018_rename_authorid_author_author_id_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='levelscore', + name='dart_glitch', + field=models.BooleanField(default=False), + ), + ] diff --git a/app/bloonsa_game/migrations/0020_alter_levelscore_darts_left_alter_levelscore_player_and_more.py b/app/bloonsa_game/migrations/0020_alter_levelscore_darts_left_alter_levelscore_player_and_more.py new file mode 100644 index 0000000..afbd797 --- /dev/null +++ b/app/bloonsa_game/migrations/0020_alter_levelscore_darts_left_alter_levelscore_player_and_more.py @@ -0,0 +1,32 @@ +# Generated by Django 5.1.6 on 2025-02-14 20:46 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('bloonsa_game', '0019_levelscore_dart_glitch'), + ('users', '0023_alter_player_user'), + ] + + operations = [ + migrations.AlterField( + model_name='levelscore', + name='darts_left', + field=models.PositiveSmallIntegerField(default=0), + preserve_default=False, + ), + migrations.AlterField( + model_name='levelscore', + name='player', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bloonsa_level_scores', to='users.player'), + ), + migrations.AlterField( + model_name='levelscore', + name='pops', + field=models.PositiveSmallIntegerField(default=0), + preserve_default=False, + ), + ] diff --git a/app/bloonsa_game/models.py b/app/bloonsa_game/models.py index b2d75b6..8feaa80 100644 --- a/app/bloonsa_game/models.py +++ b/app/bloonsa_game/models.py @@ -53,19 +53,20 @@ class LevelRating(models.Model): MaxValueValidator(10)]) def __str__(self): - return (f"{self.player.user.username}" + return (f"{self.player.user.username if self.player else ''}" 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) + player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="bloonsa_level_scores") 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) - pops = models.PositiveSmallIntegerField(null=True) + darts_left = models.PositiveSmallIntegerField() + dart_glitch = models.BooleanField(default=False) # This is triggered when the player lingers their last dart + pops = models.PositiveSmallIntegerField() def __str__(self): clearState = "✅" if self.clear else "❌" - return (f"{self.player.user.username}'s {clearState} @ {self.level.title}: " + return (f"{self.player.user.username if self.player else ''}'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/css/append.css b/app/bloonsa_game/static/bloonsa_game/css/append.css index a0d7234..5a530b1 100644 --- a/app/bloonsa_game/static/bloonsa_game/css/append.css +++ b/app/bloonsa_game/static/bloonsa_game/css/append.css @@ -28,23 +28,23 @@ white-space: nowrap; } -#level-title, -#level-id, -#level-author, -#level-author-id -#level-clear-state { +#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 { +#level_id, +#level_author_id { color: #59b1ff; } -#current-level-container { +#current_level_container { height: 110px; } @@ -61,6 +61,6 @@ font-size: 24px; } -#level-clear-state { +#level_clear_state { } \ No newline at end of file diff --git a/app/bloonsa_game/static/bloonsa_game/js/flash_handler.js b/app/bloonsa_game/static/bloonsa_game/js/flash_handler.js index 2ce2274..70e7ece 100644 --- a/app/bloonsa_game/static/bloonsa_game/js/flash_handler.js +++ b/app/bloonsa_game/static/bloonsa_game/js/flash_handler.js @@ -30,12 +30,14 @@ function bloonsa_update_data(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})`; + 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("level_clear_state").textContent = `✅`; + } else { + document.getElementById("level_clear_state").textContent = ``; } document.getElementById("levels_played").textContent = `${r.bloonsa_levels_played}`; document.getElementById("levels_beaten").textContent = `${r.bloonsa_levels_beaten}`; 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 487ff00..8a76161 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 cd3e669..4c08920 100644 --- a/app/bloonsa_game/templates/bloonsa_game/base.html +++ b/app/bloonsa_game/templates/bloonsa_game/base.html @@ -68,10 +68,10 @@
{% block content %}{% endblock content %} -
+

-

-


+

+


diff --git a/app/bloonsa_game/templates/bloonsa_game/game.html b/app/bloonsa_game/templates/bloonsa_game/game.html index 6bc694d..a524554 100644 --- a/app/bloonsa_game/templates/bloonsa_game/game.html +++ b/app/bloonsa_game/templates/bloonsa_game/game.html @@ -6,7 +6,6 @@ {% endblock title %} {% block head %} - {% endblock head %} {% block content %} diff --git a/app/users/admin.py b/app/users/admin.py index 527d331..bcb6cc0 100644 --- a/app/users/admin.py +++ b/app/users/admin.py @@ -2,17 +2,5 @@ from django.contrib import admin from django.contrib.auth.admin import UserAdmin from .models import Player, Log -from bloonsa_game.models import LevelRating -@admin.register(Player) -class PlayerAdmin(admin.ModelAdmin): - # list_display = ("id", "user") - # These lag too much - # exclude = ("bloonsa_levels_played", "bloonsa_levels_beaten") - - def get_played_levels(self, obj): - return obj.bloonsa_levels_played.all() - - -admin.site.register(LevelRating) admin.site.register(Log) diff --git a/app/users/migrations/0023_alter_player_user.py b/app/users/migrations/0023_alter_player_user.py new file mode 100644 index 0000000..0271cf2 --- /dev/null +++ b/app/users/migrations/0023_alter_player_user.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.6 on 2025-02-14 20:46 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0022_rename_creationdate_player_creation_date_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='player', + name='user', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='player', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/app/users/models.py b/app/users/models.py index e97f8d8..f7269f8 100644 --- a/app/users/models.py +++ b/app/users/models.py @@ -3,7 +3,7 @@ from django.utils import timezone from django.contrib.auth.models import User class Player(models.Model): - user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True, related_name="player") + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="player") # Logging creation_ip = models.GenericIPAddressField() latest_ip = models.GenericIPAddressField() @@ -26,7 +26,7 @@ class Player(models.Model): return self.bloonsa_level_scores.filter(clear=True).count() def has_beaten_bloonsa_level(self, level): - return bool(self.bloonsa_level_scores.filter(clear=True, level=level).first()) + return bool(self.bloonsa_level_scores.filter(clear=True, level=level).exists()) def __str__(self): statesDict = { diff --git a/dev/designs/designs.free b/dev/designs/designs.free new file mode 100644 index 0000000..3945a69 Binary files /dev/null and b/dev/designs/designs.free differ