Added dart_glitch_ever field
This commit is contained in:
parent
15e046fab4
commit
5ee47ecdcb
@ -2,6 +2,7 @@ import json
|
|||||||
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.db.models import Q
|
||||||
from django.shortcuts import render, HttpResponse
|
from django.shortcuts import render, HttpResponse
|
||||||
from django.http import HttpResponseBadRequest, JsonResponse
|
from django.http import HttpResponseBadRequest, JsonResponse
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
@ -43,20 +44,24 @@ class LoadLevel(CSRFexemptTemplateView):
|
|||||||
|
|
||||||
class RandomLevel(CSRFexemptTemplateView):
|
class RandomLevel(CSRFexemptTemplateView):
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
level: Level = Level.objects.order_by("?").first()
|
|
||||||
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 = Player.objects.get(user=request.user)
|
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():
|
if not player.bloonsa_levels_played.filter(pk=level.pk).exists():
|
||||||
level.played_by.add(player)
|
level.played_by.add(player)
|
||||||
level.save()
|
level.save()
|
||||||
bloonsa_util.log(player=player,
|
bloonsa_util.log(player=player,
|
||||||
action=actions.bloonsa_load_random_level,
|
action=actions.bloonsa_load_random_level,
|
||||||
note=level.level_id)
|
note=level.level_id)
|
||||||
|
else:
|
||||||
|
level: Level = Level.objects.order_by("?").first()
|
||||||
|
|
||||||
|
flashVars = level.get_flash_vars(seperator="&")
|
||||||
return HttpResponse(flashVars)
|
return HttpResponse(flashVars)
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
@ -79,25 +84,45 @@ class CompleteLevel(CSRFexemptTemplateView):
|
|||||||
level: Level = Level.objects.get(level_id=level_id)
|
level: Level = Level.objects.get(level_id=level_id)
|
||||||
player: Player = Player.objects.get(user=request.user)
|
player: Player = Player.objects.get(user=request.user)
|
||||||
|
|
||||||
prevScore = LevelScore.objects.filter(player=player,
|
score = LevelScore.objects.filter(player=player,
|
||||||
level=level).first()
|
level=level).first()
|
||||||
if prevScore is None \
|
clear = pops >= level.target
|
||||||
or pops > prevScore.pops \
|
|
||||||
or pops == prevScore.pops and darts_left + int(dart_glitch) > prevScore.darts_left:
|
if score is None:
|
||||||
score = LevelScore.objects.create(level=level,
|
score = LevelScore.objects.create(level=level,
|
||||||
clear=True,
|
clear=clear,
|
||||||
darts_left=darts_left,
|
darts_left=darts_left,
|
||||||
dart_glitch=dart_glitch,
|
dart_glitch=dart_glitch,
|
||||||
|
dart_glitch_ever=dart_glitch,
|
||||||
pops=pops)
|
pops=pops)
|
||||||
if prevScore:
|
|
||||||
prevScore.delete()
|
|
||||||
|
|
||||||
player.bloonsa_level_scores.add(score)
|
player.bloonsa_level_scores.add(score)
|
||||||
score.save()
|
score.save()
|
||||||
player.save()
|
player.save()
|
||||||
bloonsa_util.log(player=player,
|
|
||||||
action=actions.bloonsa_submit_score,
|
# Check if score is an improvement
|
||||||
note=level.level_id)
|
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
|
# Sending empty content means error in as3
|
||||||
return HttpResponse(content="GG", status=200)
|
return HttpResponse(content="GG", status=200)
|
||||||
@ -137,22 +162,13 @@ class GetStatusData(CSRFexemptTemplateView):
|
|||||||
level_id = int(json.loads(request.body).get("level_id"))
|
level_id = int(json.loads(request.body).get("level_id"))
|
||||||
|
|
||||||
level: Level = Level.objects.get(level_id=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()
|
total_levels = Level.objects.count()
|
||||||
level_cleared = player.has_beaten_bloonsa_level(level=level)
|
|
||||||
level_plays = level.play_count
|
level_plays = level.play_count
|
||||||
level_wins = level.win_count
|
level_wins = level.win_count
|
||||||
level_win_percentage = 0.00 if level_wins == 0 else level_wins / level_plays
|
level_win_percentage = 0.00 if level_wins == 0 else level_wins / level_plays
|
||||||
dart_glitch_count = level.dart_glitch_count
|
dart_glitch_count = level.dart_glitch_count
|
||||||
|
|
||||||
jdata = {
|
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_name": level.author.name,
|
||||||
"level_author_id": level.author.id,
|
"level_author_id": level.author.id,
|
||||||
"level_title": level.title,
|
"level_title": level.title,
|
||||||
@ -161,19 +177,31 @@ class GetStatusData(CSRFexemptTemplateView):
|
|||||||
"level_legacy_rating": level.rating,
|
"level_legacy_rating": level.rating,
|
||||||
"level_darts": level.darts,
|
"level_darts": level.darts,
|
||||||
"level_target": level.target,
|
"level_target": level.target,
|
||||||
"level_cleared": level_cleared,
|
|
||||||
"level_rating": level.stars,
|
"level_rating": level.stars,
|
||||||
"level_plays": level_plays,
|
"level_plays": level_plays,
|
||||||
"level_wins": level_wins,
|
"level_wins": level_wins,
|
||||||
"level_win_percentage": level_win_percentage,
|
"level_win_percentage": level_win_percentage,
|
||||||
"dart_glitch_count": dart_glitch_count,
|
"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({
|
jdata.update({
|
||||||
"score_darts_left": score.darts_left,
|
"bloonsa_levels_played": player.bloonsa_levels_played_count,
|
||||||
"score_dart_glitch": score.dart_glitch,
|
"bloonsa_levels_beaten": player.bloonsa_levels_beaten_count,
|
||||||
"score_pops": score.pops,
|
"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)
|
return JsonResponse(jdata)
|
||||||
@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -4,8 +4,17 @@ from django.db.models import Avg
|
|||||||
|
|
||||||
from users.models import Player
|
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)
|
name = models.CharField(max_length=255)
|
||||||
author_id = models.IntegerField()
|
author_id = models.IntegerField()
|
||||||
|
|
||||||
@ -19,7 +28,7 @@ class Level(models.Model):
|
|||||||
darts = models.SmallIntegerField()
|
darts = models.SmallIntegerField()
|
||||||
target = models.SmallIntegerField()
|
target = models.SmallIntegerField()
|
||||||
plays = models.IntegerField()
|
plays = models.IntegerField()
|
||||||
beats = models.IntegerField() # TODO can be removed?
|
beats = models.IntegerField()
|
||||||
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)
|
||||||
|
|
||||||
@ -35,7 +44,7 @@ class Level(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def dart_glitch_count(self):
|
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
|
@property
|
||||||
def stars(self):
|
def stars(self):
|
||||||
@ -44,7 +53,7 @@ class Level(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
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):
|
def get_flash_vars(self, seperator):
|
||||||
flashVars = {
|
flashVars = {
|
||||||
@ -61,7 +70,7 @@ class Level(models.Model):
|
|||||||
return flashVarsStr
|
return flashVarsStr
|
||||||
|
|
||||||
|
|
||||||
class LevelRating(models.Model):
|
class LevelRating(ModelWithUpdate):
|
||||||
player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="bloonsa_level_ratings", null=True)
|
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")
|
level = models.ForeignKey(Level, on_delete=models.CASCADE, related_name="bloonsa_level_ratings")
|
||||||
rating = models.SmallIntegerField(validators=[MinValueValidator(1),
|
rating = models.SmallIntegerField(validators=[MinValueValidator(1),
|
||||||
@ -74,16 +83,18 @@ class LevelRating(models.Model):
|
|||||||
|
|
||||||
# There should only be 1 score per player
|
# There should only be 1 score per player
|
||||||
# Highest popcount wins
|
# 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)
|
player = models.ForeignKey(Player, on_delete=models.CASCADE, related_name="bloonsa_level_scores", null=True)
|
||||||
level = models.ForeignKey(Level, on_delete=models.CASCADE)
|
level = models.ForeignKey(Level, on_delete=models.CASCADE)
|
||||||
clear = models.BooleanField(default=True) # This is for if we ever submit scores for failed attempts
|
clear = models.BooleanField(default=True) # This is for if we ever submit scores for failed attempts
|
||||||
darts_left = models.PositiveSmallIntegerField()
|
darts_left = models.PositiveSmallIntegerField()
|
||||||
dart_glitch = models.BooleanField(default=False) # This is triggered when the player lingers their last dart
|
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()
|
pops = models.PositiveSmallIntegerField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
clearState = "✅" if self.clear else "❌"
|
clear_state = "✅" if self.clear else "❌"
|
||||||
scoreView = f"🎈{self.pops} | 🎯{self.darts_left} {'✏' if self.dart_glitch 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 "
|
return (f"{self.player.user.username if self.player else ''}'s "
|
||||||
f"{clearState} @ {self.level.title}: {scoreView}")
|
f"{clear_state} @ {self.level}: {score_view}")
|
||||||
5
app/bloonsa_game/static/bloonsa_game/css/game.css
Normal file
5
app/bloonsa_game/static/bloonsa_game/css/game.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#bloonsa-game {
|
||||||
|
width: 640px;
|
||||||
|
height: 480px;
|
||||||
|
background-color: #20B0FF;
|
||||||
|
}
|
||||||
@ -87,19 +87,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.filter-red {
|
.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-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-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-softblue {
|
||||||
filter: invert(53%) sepia(93%) saturate(488%) hue-rotate(182deg) brightness(103%) contrast(103%);
|
filter: invert(53%) sepia(93%) saturate(488%) hue-rotate(182deg) brightness(103%) contrast(103%);
|
||||||
}
|
}
|
||||||
.filter-orange {
|
.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 {
|
.invisible {
|
||||||
|
|||||||
@ -4,6 +4,9 @@ function bloonsa_new_level_started(level_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function bloonsa_level_completed(level_id) {
|
function bloonsa_level_completed(level_id) {
|
||||||
|
if (document.getElementById("login_box") != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
console.log("call to bloonsa_level_completed");
|
console.log("call to bloonsa_level_completed");
|
||||||
bloonsa_update_data(level_id)
|
bloonsa_update_data(level_id)
|
||||||
}
|
}
|
||||||
@ -13,8 +16,14 @@ function bloonsa_rated_level_success(level_id) {
|
|||||||
bloonsa_update_data(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) {
|
function bloonsa_update_data(level_id) {
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
console.log("call to update_data");
|
console.log("call to update_data");
|
||||||
xhr.open("POST", "/bloonsa_api/get_status_data", true);
|
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;
|
document.getElementById("level-glitch-number").textContent = r.dart_glitch_count;
|
||||||
|
|
||||||
// Personal best
|
// Personal best
|
||||||
|
if (document.getElementById("login_box") != null) {
|
||||||
|
document.getElementById("level-pb").remove();
|
||||||
|
return
|
||||||
|
}
|
||||||
var darts_img = document.getElementById("level-pb-darts-img");
|
var darts_img = document.getElementById("level-pb-darts-img");
|
||||||
if ("score_pops" in r) {
|
if ("score_pops" in r) {
|
||||||
document.getElementById("level-pb-pops-number").textContent = r.score_pops;
|
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-number").textContent = r.score_darts_left;
|
||||||
document.getElementById("level-pb-darts-glitch").textContent = r.score_dart_glitch ? "+1": "";
|
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");
|
darts_img.classList.add("filter-orange");
|
||||||
} else {
|
} else {
|
||||||
darts_img.classList.remove("filter-orange");
|
darts_img.classList.remove("filter-orange");
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
};</script>
|
};</script>
|
||||||
<script src="{% static 'bloonsa_game/misc/ruffle/ruffle.js' %}"></script>
|
<script src="{% static 'bloonsa_game/misc/ruffle/ruffle.js' %}"></script>
|
||||||
<script src="{% static 'bloonsa_game/js/flash_handler.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 %}
|
{% endblock head %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@ -32,9 +33,9 @@
|
|||||||
const container = document.getElementById("bloonsa-game");
|
const container = document.getElementById("bloonsa-game");
|
||||||
container.appendChild(player);
|
container.appendChild(player);
|
||||||
player.load({
|
player.load({
|
||||||
url: "{% static 'bloonsa_game/misc/bloons_unlimited.swf' %}?{{ flashVars |safe }}",
|
url: "{% static 'bloonsa_game/misc/bloons_unlimited.swf' %}?{{ flashVars | safe }}",
|
||||||
allowScriptAccess: true,
|
allowScriptAccess: true,
|
||||||
backgroundColor: "#000",
|
// backgroundColor: "#000",
|
||||||
});
|
});
|
||||||
player.style.width = "640px";
|
player.style.width = "640px";
|
||||||
player.style.height = "480px";
|
player.style.height = "480px";
|
||||||
|
|||||||
@ -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="&{{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="&{{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 %}
|
|
||||||
Loading…
Reference in New Issue
Block a user