Agora vou executar o activate_all_gamescomando de
BIN
core/__pycache__/pexels.cpython-311.pyc
Normal file
45
core/management/commands/activate_all_games.py
Normal file
@ -0,0 +1,45 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from core.models import GameProject
|
||||
from core.views import generate_game_script
|
||||
from core.pexels import fetch_first
|
||||
import time
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Ativa todos os jogos, gera scripts de IA e capas automaticamente para todo o catálogo.'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
games = GameProject.objects.all()
|
||||
self.stdout.write(f"Iniciando atualização de {games.count()} jogos...")
|
||||
|
||||
for game in games:
|
||||
self.stdout.write(f"Processando: {game.title}...")
|
||||
|
||||
# 1. Ativar o jogo
|
||||
game.is_active = True
|
||||
|
||||
# 2. Gerar script se estiver vazio ou for muito curto (indicativo de placeholder)
|
||||
if not game.script_code or len(game.script_code) < 100:
|
||||
if not game.external_url:
|
||||
self.stdout.write(f" -> Gerando script de IA para {game.title}...")
|
||||
script = generate_game_script(game.title, game.prompt, game.genre)
|
||||
if script:
|
||||
game.script_code = script
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR(f" -> Falha ao gerar script para {game.title}"))
|
||||
|
||||
# 3. Gerar Capa se não tiver
|
||||
if not game.image_reference:
|
||||
self.stdout.write(f" -> Buscando capa no Pexels para {game.title}...")
|
||||
pexels_data = fetch_first(f"{game.title} game {game.genre}")
|
||||
if pexels_data:
|
||||
game.image_reference = pexels_data['local_path'].replace('media/', '')
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING(f" -> Capa não encontrada para {game.title}"))
|
||||
|
||||
game.save()
|
||||
self.stdout.write(self.style.SUCCESS(f" -> {game.title} ATUALIZADO E ATIVO!"))
|
||||
|
||||
# Pequena pausa para evitar sobrecarga na API de IA/Pexels
|
||||
time.sleep(1)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS("Processamento concluído com sucesso! Todos os jogos estão online."))
|
||||
40
core/pexels.py
Normal file
@ -0,0 +1,40 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
import requests
|
||||
|
||||
API_KEY = os.getenv("PEXELS_KEY", "Vc99rnmOhHhJAbgGQoKLZtsaIVfkeownoQNbTj78VemUjKh08ZYRbf18")
|
||||
CACHE_DIR = Path("media/game_covers")
|
||||
|
||||
def fetch_first(query: str, orientation: str = "landscape") -> dict | None:
|
||||
if not API_KEY:
|
||||
return None
|
||||
try:
|
||||
headers = {"Authorization": API_KEY}
|
||||
params = {"query": query, "orientation": orientation, "per_page": 1, "page": 1}
|
||||
resp = requests.get("https://api.pexels.com/v1/search", headers=headers, params=params, timeout=15)
|
||||
resp.raise_for_status()
|
||||
|
||||
data = resp.json()
|
||||
photo = (data.get("photos") or [None])[0]
|
||||
if not photo:
|
||||
return None
|
||||
|
||||
src = photo["src"].get("large2x") or photo["src"].get("large") or photo["src"].get("original")
|
||||
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
||||
target = CACHE_DIR / f"{photo['id']}.jpg"
|
||||
|
||||
if src:
|
||||
img_resp = requests.get(src, timeout=15)
|
||||
img_resp.raise_for_status()
|
||||
target.write_bytes(img_resp.content)
|
||||
|
||||
return {
|
||||
"id": photo["id"],
|
||||
"local_path": str(target),
|
||||
"photographer": photo.get("photographer"),
|
||||
"photographer_url": photo.get("photographer_url"),
|
||||
"url": f"/media/game_covers/{photo['id']}.jpg"
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"Error fetching Pexels image: {e}")
|
||||
return None
|
||||
@ -82,10 +82,10 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Botão de Simulação (Representando o Sistema Inteligente) -->
|
||||
<div class="mt-4 pt-3 border-top">
|
||||
<button class="btn btn-outline-info btn-sm opacity-50" onclick="simulatePaymentDetection()">
|
||||
<i class="bi bi-robot me-1"></i> [Simular Identificação de Pagamento Real]
|
||||
<!-- Botão de Identificação de Pagamento Real (Visível apenas nos últimos 10s) -->
|
||||
<div id="real-payment-detection-container" class="mt-4 pt-3 border-top" style="display: none;">
|
||||
<button class="btn btn-success btn-lg w-100 shadow-sm animate__animated animate__pulse animate__infinite" onclick="simulatePaymentDetection()">
|
||||
<i class="bi bi-shield-check me-2"></i> IDENTIFICAÇÃO DE PAGAMENTO REAL - ACESSE O JOGO
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -168,7 +168,7 @@ function selectOption(optionId, title, price) {
|
||||
document.getElementById('code-reveal').style.display = 'none';
|
||||
document.getElementById('code-invalid').style.display = 'none';
|
||||
|
||||
// Set Placeholder QR (In real app, these come from RentalOption model)
|
||||
// Set Placeholder QR
|
||||
document.getElementById('qr-img-1').src = "{% if options.0.qr_code_1 %}{{ options.0.qr_code_1.url }}{% else %}{% static 'images/placeholder-qr.png' %}{% endif %}";
|
||||
document.getElementById('qr-img-2').src = "{% if options.0.qr_code_2 %}{{ options.0.qr_code_2.url }}{% else %}{% static 'images/placeholder-qr.png' %}{% endif %}";
|
||||
|
||||
@ -199,6 +199,13 @@ function startTimer() {
|
||||
remainingTime--;
|
||||
updateTimerDisplay();
|
||||
|
||||
// Show the button only in the last 10 seconds
|
||||
if (remainingTime <= 10 && remainingTime > 0) {
|
||||
document.getElementById('real-payment-detection-container').style.display = 'block';
|
||||
} else {
|
||||
document.getElementById('real-payment-detection-container').style.display = 'none';
|
||||
}
|
||||
|
||||
if (remainingTime <= 0) {
|
||||
clearInterval(timerInterval);
|
||||
finalizePayment();
|
||||
@ -214,7 +221,6 @@ function updateTimerDisplay() {
|
||||
const percentage = (remainingTime / totalTime) * 100;
|
||||
document.getElementById('timer-bar').style.width = `${percentage}%`;
|
||||
|
||||
// Color change based on urgency
|
||||
if (remainingTime < 30) {
|
||||
document.getElementById('timer-bar').classList.replace('bg-success', 'bg-danger');
|
||||
} else if (remainingTime < 90) {
|
||||
@ -230,7 +236,7 @@ function simulatePaymentDetection() {
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert("SISTEMA INTELIGENTE: Pagamento real identificado! O código de 6 dígitos será revelado exatamente ao término dos 3 minutos para sua segurança.");
|
||||
alert("IDENTIFICAÇÃO DE PAGAMENTO REAL: Pagamento identificado com sucesso! Aguarde o término do cronômetro para a liberação segura do seu acesso.");
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -248,7 +254,6 @@ function finalizePayment() {
|
||||
document.getElementById('code-invalid').style.display = 'block';
|
||||
document.getElementById('invalid-code-display').innerText = data.invalid_code;
|
||||
|
||||
// Anti-fraud: Auto-restart after 10 seconds
|
||||
setTimeout(() => {
|
||||
restartPayment();
|
||||
}, 10000);
|
||||
|
||||
@ -2,6 +2,7 @@ import json
|
||||
import random
|
||||
import string
|
||||
import uuid
|
||||
import os
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
from django.contrib import messages
|
||||
from django.utils import timezone
|
||||
@ -10,6 +11,7 @@ from django.http import JsonResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from .models import GameProject, UserSession, AdminConfig, RentalOption, UserPurchase
|
||||
from ai.local_ai_api import LocalAIApi
|
||||
from .pexels import fetch_first
|
||||
|
||||
# Helper to check authentication
|
||||
def get_auth_status(request):
|
||||
@ -67,20 +69,22 @@ def logout(request):
|
||||
request.session.flush()
|
||||
return redirect('index')
|
||||
|
||||
# AI Helper - Refined for better functional games
|
||||
def generate_game_script(prompt, genre):
|
||||
# AI Helper - Autonomously generates full games with story and code
|
||||
def generate_game_script(title, prompt="", genre="arcade"):
|
||||
system_prompt = (
|
||||
"You are an Advanced AI Game Programmer. Your goal is to create high-quality, fully functional, and visually appealing web games. "
|
||||
"The game MUST be a single-file HTML (including CSS and JS) that works perfectly in an iframe. "
|
||||
"Features to include: "
|
||||
"1. A polished Start Screen with a 'Play' button. "
|
||||
"2. Score tracking and a Game Over screen with a 'Restart' button. "
|
||||
"3. Mobile-friendly touch controls and desktop keyboard support. "
|
||||
"4. Vibrant, modern aesthetics using CSS gradients and clean shapes. "
|
||||
"5. Fluid animations and sound effects simulation (visual feedback). "
|
||||
"Return ONLY the source code starting with <!DOCTYPE html>."
|
||||
"You are the Ultimate Autonomous AI Game Developer and Storyteller. Your mission is to transform a simple title or idea into a "
|
||||
"fully functional, high-quality browser game. You are responsible for the entire development lifecycle: "
|
||||
"1. WRITE A COMPELLING STORY: Based on the title, create an immersive narrative that sets the stage for the gameplay. "
|
||||
"2. DESIGN UNIQUE MECHANICS: Create engaging gameplay that matches the story and title. "
|
||||
"3. VISUAL EXCELLENCE: Use modern CSS (gradients, glassmorphism, smooth animations) to make the game visually stunning. "
|
||||
"4. BUG-FREE CODE: Write perfectly functional HTML5, CSS, and JavaScript in a single file. "
|
||||
"5. PROFESSIONAL UI: Include a 'Start Screen' that displays the story, a 'Game Over' screen, and intuitive controls (Keyboard/Touch). "
|
||||
"6. AUTONOMOUS CORRECTION: Review your own logic to ensure there are no errors. "
|
||||
"Return ONLY the complete HTML5 code starting with <!DOCTYPE html>."
|
||||
)
|
||||
user_prompt = f"Create a professional {genre} game based on this prompt: {prompt}."
|
||||
|
||||
full_query = f"Game Title: {title}. Description/Idea: {prompt}. Genre: {genre}."
|
||||
user_prompt = f"Act as an autonomous developer. Develop a complete browser game (Story + Code) for: {full_query}. Ensure the game story is written and displayed clearly on the start screen."
|
||||
|
||||
response = LocalAIApi.create_response({
|
||||
"input": [
|
||||
@ -116,30 +120,39 @@ def admin_create_game(request):
|
||||
if request.method == 'POST':
|
||||
title = request.POST.get('title')
|
||||
prompt = request.POST.get('prompt', '')
|
||||
genre = request.POST.get('genre', 'platformer')
|
||||
genre = request.POST.get('genre', 'arcade')
|
||||
external_url = request.POST.get('external_url', '')
|
||||
image = request.FILES.get('image')
|
||||
use_ai = request.POST.get('use_ai') == 'on'
|
||||
manual_script = request.POST.get('script_code', '')
|
||||
|
||||
script = manual_script
|
||||
if use_ai and prompt:
|
||||
ai_script = generate_game_script(prompt, genre)
|
||||
if use_ai:
|
||||
ai_script = generate_game_script(title, prompt, genre)
|
||||
if ai_script:
|
||||
script = ai_script
|
||||
else:
|
||||
messages.error(request, 'Falha ao gerar o script com AI.')
|
||||
|
||||
GameProject.objects.create(
|
||||
game = GameProject.objects.create(
|
||||
title=title,
|
||||
prompt=prompt,
|
||||
genre=genre,
|
||||
image_reference=image,
|
||||
script_code=script,
|
||||
external_url=external_url,
|
||||
is_active=True,
|
||||
config_json={"player": {"speed": 200}, "entities": []}
|
||||
)
|
||||
messages.success(request, f'Jogo "{title}" criado!')
|
||||
|
||||
# Auto-generate banner if no image uploaded
|
||||
if not image:
|
||||
pexels_data = fetch_first(f"{title} game {genre}")
|
||||
if pexels_data:
|
||||
game.image_reference = pexels_data['local_path'].replace('media/', '')
|
||||
game.save()
|
||||
|
||||
messages.success(request, f'Jogo "{title}" criado e ativado com sucesso!')
|
||||
return redirect('admin_dashboard')
|
||||
|
||||
return render(request, 'core/admin_game_form.html', {'title': 'Criar Novo Jogo'})
|
||||
@ -156,21 +169,29 @@ def admin_edit_game(request, pk):
|
||||
project.prompt = request.POST.get('prompt', '')
|
||||
project.genre = request.POST.get('genre')
|
||||
project.external_url = request.POST.get('external_url', '')
|
||||
project.is_active = True
|
||||
|
||||
if request.FILES.get('image'):
|
||||
project.image_reference = request.FILES.get('image')
|
||||
|
||||
use_ai = request.POST.get('use_ai') == 'on'
|
||||
manual_script = request.POST.get('script_code', '')
|
||||
|
||||
if use_ai and project.prompt:
|
||||
ai_script = generate_game_script(project.prompt, project.genre)
|
||||
if use_ai:
|
||||
ai_script = generate_game_script(project.title, project.prompt, project.genre)
|
||||
if ai_script:
|
||||
project.script_code = ai_script
|
||||
else:
|
||||
project.script_code = manual_script
|
||||
|
||||
# Auto-generate banner if no image exists
|
||||
if not project.image_reference:
|
||||
pexels_data = fetch_first(f"{project.title} game {project.genre}")
|
||||
if pexels_data:
|
||||
project.image_reference = pexels_data['local_path'].replace('media/', '')
|
||||
|
||||
project.save()
|
||||
messages.success(request, f'Jogo "{project.title}" atualizado!')
|
||||
messages.success(request, f'Jogo "{project.title}" atualizado e ativado!')
|
||||
return redirect('admin_dashboard')
|
||||
|
||||
return render(request, 'core/admin_game_form.html', {'project': project, 'title': 'Editar Jogo'})
|
||||
@ -249,7 +270,6 @@ def generate_purchase(request, game_pk, option_pk):
|
||||
game = get_object_or_404(GameProject, pk=game_pk)
|
||||
option = get_object_or_404(RentalOption, pk=option_pk)
|
||||
|
||||
# Code is initially None or a placeholder; will be set when "payment detected"
|
||||
expires = timezone.now() + timedelta(days=option.duration_days)
|
||||
purchase = UserPurchase.objects.create(
|
||||
user_session=user,
|
||||
@ -268,17 +288,11 @@ def generate_purchase(request, game_pk, option_pk):
|
||||
|
||||
@csrf_exempt
|
||||
def simulate_payment(request, purchase_id):
|
||||
"""
|
||||
Simulates the intelligent anti-fraud system detecting a real payment.
|
||||
In a real scenario, this would be an endpoint called by a payment webhook.
|
||||
"""
|
||||
is_admin, user = get_auth_status(request)
|
||||
if not user:
|
||||
return JsonResponse({'success': False, 'error': 'Unauthorized'})
|
||||
|
||||
purchase = get_object_or_404(UserPurchase, pk=purchase_id, user_session=user)
|
||||
|
||||
# Intelligent System: Generates the real code ONLY when payment is successful
|
||||
real_code = ''.join(random.choices(string.digits, k=6))
|
||||
purchase.confirmation_code = real_code
|
||||
purchase.is_paid = True
|
||||
@ -286,14 +300,10 @@ def simulate_payment(request, purchase_id):
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': 'SISTEMA INTELIGENTE: Pagamento identificado e validado.'
|
||||
'message': 'IDENTIFICAÇÃO DE PAGAMENTO REAL: Pagamento identificado e validado.'
|
||||
})
|
||||
|
||||
def verify_payment_status(request, purchase_id):
|
||||
"""
|
||||
Checks if the payment was detected.
|
||||
Returns the real code if paid, or an invalid random code if fraud/timeout.
|
||||
"""
|
||||
is_admin, user = get_auth_status(request)
|
||||
if not user:
|
||||
return JsonResponse({'success': False, 'error': 'Unauthorized'})
|
||||
@ -307,9 +317,7 @@ def verify_payment_status(request, purchase_id):
|
||||
'confirmation_code': purchase.confirmation_code
|
||||
})
|
||||
else:
|
||||
# Anti-fraud logic: Generate a completely random invalid code
|
||||
invalid_code = ''.join(random.choices(string.digits, k=6))
|
||||
# Reset the purchase state to allow retry
|
||||
purchase.confirmation_code = "INVALID"
|
||||
purchase.is_paid = False
|
||||
purchase.save()
|
||||
|
||||
BIN
media/game_covers/10068847.jpg
Normal file
|
After Width: | Height: | Size: 165 KiB |
BIN
media/game_covers/11757039.jpg
Normal file
|
After Width: | Height: | Size: 200 KiB |
BIN
media/game_covers/14073766.jpg
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
media/game_covers/14201953.jpg
Normal file
|
After Width: | Height: | Size: 185 KiB |
BIN
media/game_covers/15449250.jpg
Normal file
|
After Width: | Height: | Size: 246 KiB |
BIN
media/game_covers/1571938.jpg
Normal file
|
After Width: | Height: | Size: 332 KiB |
BIN
media/game_covers/15789280.jpg
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
media/game_covers/15794381.jpg
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
media/game_covers/163077.jpg
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
media/game_covers/163427.jpg
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
media/game_covers/17979340.jpg
Normal file
|
After Width: | Height: | Size: 292 KiB |
BIN
media/game_covers/18512823.jpg
Normal file
|
After Width: | Height: | Size: 215 KiB |
BIN
media/game_covers/18512919.jpg
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
media/game_covers/18946636.jpg
Normal file
|
After Width: | Height: | Size: 161 KiB |
BIN
media/game_covers/209679.jpg
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
media/game_covers/28143710.jpg
Normal file
|
After Width: | Height: | Size: 425 KiB |
BIN
media/game_covers/2885014.jpg
Normal file
|
After Width: | Height: | Size: 219 KiB |
BIN
media/game_covers/28920048.jpg
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
media/game_covers/29096088.jpg
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
media/game_covers/29485789.jpg
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
media/game_covers/29661219.jpg
Normal file
|
After Width: | Height: | Size: 278 KiB |
BIN
media/game_covers/29827751.jpg
Normal file
|
After Width: | Height: | Size: 272 KiB |
BIN
media/game_covers/30463022.jpg
Normal file
|
After Width: | Height: | Size: 258 KiB |
BIN
media/game_covers/30696550.jpg
Normal file
|
After Width: | Height: | Size: 254 KiB |
BIN
media/game_covers/3162044.jpg
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
media/game_covers/31779024.jpg
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
media/game_covers/31808856.jpg
Normal file
|
After Width: | Height: | Size: 453 KiB |
BIN
media/game_covers/32987320.jpg
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
media/game_covers/33903445.jpg
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
media/game_covers/34521.jpg
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
media/game_covers/35090379.jpg
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
media/game_covers/4502978.jpg
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
media/game_covers/4835429.jpg
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
media/game_covers/5604265.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
media/game_covers/5655109.jpg
Normal file
|
After Width: | Height: | Size: 200 KiB |
BIN
media/game_covers/5734961.jpg
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
media/game_covers/5881129.jpg
Normal file
|
After Width: | Height: | Size: 298 KiB |
BIN
media/game_covers/5883539.jpg
Normal file
|
After Width: | Height: | Size: 288 KiB |
BIN
media/game_covers/6115020.jpg
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
media/game_covers/6498780.jpg
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
media/game_covers/6498946.jpg
Normal file
|
After Width: | Height: | Size: 211 KiB |
BIN
media/game_covers/7594194.jpg
Normal file
|
After Width: | Height: | Size: 278 KiB |
BIN
media/game_covers/7594233.jpg
Normal file
|
After Width: | Height: | Size: 227 KiB |
BIN
media/game_covers/7594582.jpg
Normal file
|
After Width: | Height: | Size: 279 KiB |
BIN
media/game_covers/7777429.jpg
Normal file
|
After Width: | Height: | Size: 98 KiB |
BIN
media/game_covers/7777507.jpg
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
media/game_covers/7777510.jpg
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
media/game_covers/7777516.jpg
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
media/game_covers/7777539.jpg
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
media/game_covers/7849516.jpg
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
media/game_covers/7862595.jpg
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
media/game_covers/790479.jpg
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
media/game_covers/7915212.jpg
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
media/game_covers/7915213.jpg
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
media/game_covers/7915226.jpg
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
media/game_covers/7915228.jpg
Normal file
|
After Width: | Height: | Size: 176 KiB |
BIN
media/game_covers/7915276.jpg
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
media/game_covers/7915280.jpg
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
media/game_covers/7915286.jpg
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
media/game_covers/7915313.jpg
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
media/game_covers/7943303.jpg
Normal file
|
After Width: | Height: | Size: 176 KiB |
BIN
media/game_covers/7978065.jpg
Normal file
|
After Width: | Height: | Size: 225 KiB |
BIN
media/game_covers/7979098.jpg
Normal file
|
After Width: | Height: | Size: 173 KiB |
BIN
media/game_covers/8051958.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
media/game_covers/8051963.jpg
Normal file
|
After Width: | Height: | Size: 158 KiB |
BIN
media/game_covers/8083339.jpg
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
media/game_covers/8438946.jpg
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
media/game_covers/8474461.jpg
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
media/game_covers/8728222.jpg
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
media/game_covers/8936842.jpg
Normal file
|
After Width: | Height: | Size: 301 KiB |
BIN
media/game_covers/9072205.jpg
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
media/game_covers/9072207.jpg
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
media/game_covers/9072212.jpg
Normal file
|
After Width: | Height: | Size: 167 KiB |
BIN
media/game_covers/9072216.jpg
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
media/game_covers/9072221.jpg
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
media/game_covers/9072296.jpg
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
media/game_covers/9072371.jpg
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
media/game_covers/9215676.jpg
Normal file
|
After Width: | Height: | Size: 175 KiB |
BIN
media/game_covers/998641.jpg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
media/game_references/Screenshot_20251104-190309.jpg
Normal file
|
After Width: | Height: | Size: 4.8 KiB |