Added dart_glitch_ever field

This commit is contained in:
Walter 2025-02-18 03:01:03 +01:00
parent 15e046fab4
commit 5ee47ecdcb
8 changed files with 128 additions and 73 deletions

View File

@ -2,6 +2,7 @@ import json
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from django.shortcuts import render, HttpResponse
from django.http import HttpResponseBadRequest, JsonResponse
from django.views.generic import TemplateView
@ -43,20 +44,24 @@ class LoadLevel(CSRFexemptTemplateView):
class RandomLevel(CSRFexemptTemplateView):
def post(self, request, *args, **kwargs):
level: Level = Level.objects.order_by("?").first()
flashVars = level.get_flash_vars(seperator="&")
if request.user.is_authenticated:
bloonsa_util.tag_player(request=request)
player: Player = Player.objects.get(user=request.user)
level: Level = Level.objects.order_by("?").filter(~Q(played_by=player)).first()
if not level:
level: Level = Level.objects.order_by("?").first()
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)
else:
level: Level = Level.objects.order_by("?").first()
flashVars = level.get_flash_vars(seperator="&")
return HttpResponse(flashVars)
def get(self, request, *args, **kwargs):
@ -79,25 +84,45 @@ class CompleteLevel(CSRFexemptTemplateView):
level: Level = Level.objects.get(level_id=level_id)
player: Player = Player.objects.get(user=request.user)
prevScore = LevelScore.objects.filter(player=player,
level=level).first()
if prevScore is None \
or pops > prevScore.pops \
or pops == prevScore.pops and darts_left + int(dart_glitch) > prevScore.darts_left:
score = LevelScore.objects.filter(player=player,
level=level).first()
clear = pops >= level.target
if score is None:
score = LevelScore.objects.create(level=level,
clear=True,
clear=clear,
darts_left=darts_left,
dart_glitch=dart_glitch,
dart_glitch_ever=dart_glitch,
pops=pops)
if prevScore:
prevScore.delete()
player.bloonsa_level_scores.add(score)
score.save()
player.save()
bloonsa_util.log(player=player,
action=actions.bloonsa_submit_score,
note=level.level_id)
# Check if score is an improvement
elif any((
pops > score.pops,
pops == score.pops and darts_left + int(dart_glitch) > score.darts_left,
clear and not score.clear
)):
# Has dart glitch ever been done in a score?
dart_glitch_ever = bool(dart_glitch or score.dart_glitch_ever or score.dart_glitch)
score.update(commit=True,
clear=clear,
darts_left=darts_left,
dart_glitch=dart_glitch,
dart_glitch_ever=dart_glitch_ever,
pops=pops)
# Check if we just did a dart glitch for fun
elif dart_glitch:
score.update(commit=True,
dart_glitch_ever=True)
bloonsa_util.log(player=player,
action=actions.bloonsa_submit_score,
note=level.level_id)
# Sending empty content means error in as3
return HttpResponse(content="GG", status=200)
@ -137,22 +162,13 @@ class GetStatusData(CSRFexemptTemplateView):
level_id = int(json.loads(request.body).get("level_id"))
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)
level_plays = level.play_count
level_wins = level.win_count
level_win_percentage = 0.00 if level_wins == 0 else level_wins / level_plays
dart_glitch_count = level.dart_glitch_count
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,
@ -161,19 +177,31 @@ class GetStatusData(CSRFexemptTemplateView):
"level_legacy_rating": level.rating,
"level_darts": level.darts,
"level_target": level.target,
"level_cleared": level_cleared,
"level_rating": level.stars,
"level_plays": level_plays,
"level_wins": level_wins,
"level_win_percentage": level_win_percentage,
"dart_glitch_count": dart_glitch_count,
}
if score:
if request.user.is_authenticated:
player: Player = Player.objects.get(user=request.user)
score: LevelScore = LevelScore.objects.filter(player=player,
level=level).first()
level_cleared = player.has_beaten_bloonsa_level(level=level)
jdata.update({
"score_darts_left": score.darts_left,
"score_dart_glitch": score.dart_glitch,
"score_pops": score.pops,
"bloonsa_levels_played": player.bloonsa_levels_played_count,
"bloonsa_levels_beaten": player.bloonsa_levels_beaten_count,
"bloonsa_total_levels": total_levels,
"level_cleared": level_cleared,
})
if score:
jdata.update({
"score_darts_left": score.darts_left,
"score_dart_glitch": score.dart_glitch,
"score_dart_glitch_ever": score.dart_glitch_ever,
"score_pops": score.pops,
})
return JsonResponse(jdata)

View File

@ -0,0 +1,18 @@
# Generated by Django 5.1.6 on 2025-02-17 23:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('bloonsa_game', '0022_alter_levelrating_level'),
]
operations = [
migrations.AddField(
model_name='levelscore',
name='dart_glitch_ever',
field=models.BooleanField(default=False),
),
]

View File

@ -4,8 +4,17 @@ from django.db.models import Avg
from users.models import Player
class ModelWithUpdate(models.Model):
class Meta:
abstract = True
class Author(models.Model):
def update(self, commit=False, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
if commit:
self.save()
class Author(ModelWithUpdate):
name = models.CharField(max_length=255)
author_id = models.IntegerField()
@ -19,7 +28,7 @@ class Level(models.Model):
darts = models.SmallIntegerField()
target = models.SmallIntegerField()
plays = models.IntegerField()
beats = models.IntegerField() # TODO can be removed?
beats = models.IntegerField()
rating = models.FloatField(null=True, blank=True)
data = models.CharField(max_length=288)
@ -35,7 +44,7 @@ class Level(models.Model):
@property
def dart_glitch_count(self):
return LevelScore.objects.filter(level=self, dart_glitch=True).count()
return LevelScore.objects.filter(level=self, dart_glitch_ever=True).count()
@property
def stars(self):
@ -44,7 +53,7 @@ class Level(models.Model):
def __str__(self):
return f"{self.author} - {self.title}"
return f"{self.level_id}: {self.author.name} - {self.title}"
def get_flash_vars(self, seperator):
flashVars = {
@ -61,7 +70,7 @@ class Level(models.Model):
return flashVarsStr
class LevelRating(models.Model):
class LevelRating(ModelWithUpdate):
player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="bloonsa_level_ratings", null=True)
level = models.ForeignKey(Level, on_delete=models.CASCADE, related_name="bloonsa_level_ratings")
rating = models.SmallIntegerField(validators=[MinValueValidator(1),
@ -74,16 +83,18 @@ class LevelRating(models.Model):
# There should only be 1 score per player
# Highest popcount wins
class LevelScore(models.Model):
class LevelScore(ModelWithUpdate):
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()
dart_glitch = models.BooleanField(default=False) # This is triggered when the player lingers their last dart
dart_glitch_ever = models.BooleanField(default=False) # If they ever performed dart glitch in the past
pops = models.PositiveSmallIntegerField()
def __str__(self):
clearState = "" if self.clear else ""
scoreView = f"🎈{self.pops} | 🎯{self.darts_left} {'' if self.dart_glitch else ''}"
clear_state = "" if self.clear else ""
dart_glitch_state = "" if self.dart_glitch else "🖋" if self.dart_glitch_ever else ""
score_view = f"🎈{self.pops} | 🎯{self.darts_left} {dart_glitch_state}"
return (f"{self.player.user.username if self.player else ''}'s "
f"{clearState} @ {self.level.title}: {scoreView}")
f"{clear_state} @ {self.level}: {score_view}")

View File

@ -0,0 +1,5 @@
#bloonsa-game {
width: 640px;
height: 480px;
background-color: #20B0FF;
}

View File

@ -87,19 +87,25 @@
}
.filter-red {
filter: invert(61%) sepia(72%) saturate(7291%) hue-rotate(338deg) brightness(108%) contrast(101%); // red
filter: invert(61%) sepia(72%) saturate(7291%) hue-rotate(338deg) brightness(108%) contrast(101%);
}
.filter-grey {
filter: invert(17%) sepia(2%) saturate(72%) hue-rotate(66deg) brightness(94%) contrast(89%); // grey
filter: invert(17%) sepia(2%) saturate(72%) hue-rotate(66deg) brightness(94%) contrast(89%);
}
.filter-black {
filter: invert(0%) sepia(96%) saturate(15%) hue-rotate(246deg) brightness(105%) contrast(105%); // black
filter: invert(0%) sepia(96%) saturate(15%) hue-rotate(246deg) brightness(105%) contrast(105%);
}
.filter-softblue {
filter: invert(53%) sepia(93%) saturate(488%) hue-rotate(182deg) brightness(103%) contrast(103%);
}
.filter-orange {
filter: invert(75%) sepia(23%) saturate(6404%) hue-rotate(355deg) brightness(98%) contrast(107%) !important; // orange
filter: invert(75%) sepia(23%) saturate(6404%) hue-rotate(355deg) brightness(98%) contrast(107%) !important;
}
.filter-purple {
filter: invert(14%) sepia(92%) saturate(7496%) hue-rotate(299deg) brightness(91%) contrast(106%); !important;
}
.filter-pink {
filter: invert(65%) sepia(38%) saturate(4716%) hue-rotate(268deg) brightness(102%) contrast(102%);
}
.invisible {

View File

@ -4,6 +4,9 @@ function bloonsa_new_level_started(level_id) {
}
function bloonsa_level_completed(level_id) {
if (document.getElementById("login_box") != null) {
return
}
console.log("call to bloonsa_level_completed");
bloonsa_update_data(level_id)
}
@ -13,8 +16,14 @@ function bloonsa_rated_level_success(level_id) {
bloonsa_update_data(level_id)
}
// TODO only update neccessary parts
// eg: new_level_started -> level info box
// level_completed -> all
// level_rated -> level info box
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);
@ -70,6 +79,10 @@ function bloonsa_update_html(r) {
document.getElementById("level-glitch-number").textContent = r.dart_glitch_count;
// Personal best
if (document.getElementById("login_box") != null) {
document.getElementById("level-pb").remove();
return
}
var darts_img = document.getElementById("level-pb-darts-img");
if ("score_pops" in r) {
document.getElementById("level-pb-pops-number").textContent = r.score_pops;
@ -77,7 +90,7 @@ function bloonsa_update_html(r) {
document.getElementById("level-pb-darts-number").textContent = r.score_darts_left;
document.getElementById("level-pb-darts-glitch").textContent = r.score_dart_glitch ? "+1": "";
if (r.score_dart_glitch) {
if (r.score_dart_glitch | r.score_dart_glitch_ever) {
darts_img.classList.add("filter-orange");
} else {
darts_img.classList.remove("filter-orange");

View File

@ -20,6 +20,7 @@
};</script>
<script src="{% static 'bloonsa_game/misc/ruffle/ruffle.js' %}"></script>
<script src="{% static 'bloonsa_game/js/flash_handler.js' %}"></script>
<link rel="stylesheet" type="text/css" media="screen" href="{% static 'bloonsa_game/css/game.css' %}"/>
{% endblock head %}
{% block content %}
@ -32,9 +33,9 @@
const container = document.getElementById("bloonsa-game");
container.appendChild(player);
player.load({
url: "{% static 'bloonsa_game/misc/bloons_unlimited.swf' %}?{{ flashVars |safe }}",
url: "{% static 'bloonsa_game/misc/bloons_unlimited.swf' %}?{{ flashVars | safe }}",
allowScriptAccess: true,
backgroundColor: "#000",
// backgroundColor: "#000",
});
player.style.width = "640px";
player.style.height = "480px";

View File

@ -1,27 +0,0 @@
{% extends 'game/base.html' %}
{% load static %}
{% block content %}
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="{% static 'game/misc/swflash.cab' %}#version=7,0,19,0"
width="640" height="480" title="Confusing">
<param name="movie" value="{% static 'game/misc/bloons_unlimited.swf' %}">
<param name="quality" value="high">
<param name="FlashVars"
value="&amp;{{flashVars}}">
<embed src="{% static 'game/misc/bloons_unlimited.swf' %}"
quality="high"
pluginspage="http://www.macromedia.com/go/getflashplayer"
type="application/x-shockwave-flash"
width="640" height="480" flashvars="&amp;{{flashVars}}">
</object>
<div class="wide">
Move the mouse to aim the arrow, and press and hold the left mouse button to select the power of your throw. Pop the minimum number of bloons to pass each level. You can rate the levels you play by clicking the 'rate level' button in the game screen. You can also go to another random level by clicking 'Go random' at any time.</div> <div class="wide centered">
Copyright Kaiparasoft 2007, all rights reserved<br/>
<a href="https://web.archive.org/web/20140327034910/http://www.ninjakiwi.com/">Ninjakiwi</a>
- <a href="/web/20140327034910/http://www.bloonsworld.com/symfony/terms">Terms of Use</a>
- <a href="/web/20140327034910/http://www.bloonsworld.com/symfony/contact_us">Contact Us</a>
</div>
{% endblock content %}