123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- from django.conf import settings
- from django.shortcuts import render, redirect
- from django.contrib import messages
- from django.contrib.auth.decorators import login_required
- from django.db.models import F, Count, Sum, Avg
- from decimal import Decimal, InvalidOperation
- import random
- from .models import GameSession, HighScore, PlayerStats
- # Game configuration
- LOCATIONS = ['Downtown', 'Uptown', 'Airport', 'University', 'Suburbs', 'Docks']
- PRODUCTS = {
- 'Espresso': {'base': 10, 'variance': 5, 'event_mult': 3},
- 'Latte': {'base': 25, 'variance': 10, 'event_mult': 2},
- 'Cold Brew': {'base': 40, 'variance': 20, 'event_mult': 2.5},
- 'Jamaican': {'base': 100, 'variance': 50, 'event_mult': 3},
- 'Ethiopian': {'base': 200, 'variance': 100, 'event_mult': 4},
- 'Kopi Luwak': {'base': 500, 'variance': 300, 'event_mult': 5},
- }
- MAX_DAYS = getattr(settings, "MAX_DAYS", 30)
- INTEREST_RATE = getattr(settings, "INTEREST_RATE", Decimal('0.05'))
- def get_or_create_game(request):
- """Get or create game session"""
- if request.user.is_authenticated:
- # For logged-in users, get their active game or create new one
- game, created = GameSession.objects.get_or_create(
- user=request.user,
- is_active=True,
- defaults={'session_key': request.session.session_key}
- )
- if created:
- # Create or update player stats
- stats, _ = PlayerStats.objects.get_or_create(user=request.user)
- else:
- # For anonymous users, use session-based games
- session_key = request.session.session_key
- if not session_key:
- request.session.create()
- session_key = request.session.session_key
- game, created = GameSession.objects.get_or_create(
- session_key=session_key,
- user=None,
- is_active=True
- )
- return game
- def generate_prices(request=None):
- """Generate random prices for products"""
- prices = {}
- for product, config in PRODUCTS.items():
- base = config['base']
- variance = config['variance']
- price = base + random.randint(-variance, variance)
- prices[product] = max(1, price) # Minimum price of $1
- # Random events that affect prices
- event_message = None
- if random.random() < 0.1: # 10% chance of price event
- product = random.choice(list(PRODUCTS.keys()))
- if random.random() < 0.5:
- # Price surge
- prices[product] *= PRODUCTS[product]['event_mult']
- event_message = f"☕ {product} prices are SOARING due to shortage!"
- else:
- # Price crash
- prices[product] //= 2
- event_message = f"📉 {product} prices CRASHED due to oversupply!"
- return prices, event_message
- def home(request):
- """Main game view"""
- game = get_or_create_game(request)
- # Check if game is over
- if game.day > MAX_DAYS:
- return redirect('game_over')
- # Generate current prices
- prices = request.session.get('current_prices')
- if not prices or request.session.get('current_day') != game.day:
- prices, event_message = generate_prices()
- request.session['current_prices'] = prices
- request.session['current_day'] = game.day
- if event_message:
- messages.info(request, event_message)
- # Calculate inventory value and total items
- inventory = game.get_inventory()
- inventory_value = sum(
- inventory.get(product, 0) * prices[product]
- for product in PRODUCTS.keys()
- )
- total_items = sum(inventory.values())
- # Random events
- if random.random() < 0.05 and game.cash > 1000: # 5% chance
- stolen = random.randint(100, min(int(game.cash), 1000))
- game.cash -= stolen
- game.save()
- messages.warning(request, f"💸 You got mugged! Lost ${stolen}")
- # Prepare products with inventory for template
- products_data = []
- for product in PRODUCTS.keys():
- products_data.append({
- 'name': product,
- 'price': prices.get(product, 0),
- 'owned': inventory.get(product, 0)
- })
- context = {
- 'game': game,
- 'locations': LOCATIONS,
- 'products_data': products_data,
- 'inventory': inventory,
- 'inventory_value': inventory_value,
- 'total_items': total_items,
- 'capacity_left': game.capacity - total_items,
- 'net_worth': Decimal(game.cash) + Decimal(inventory_value) - Decimal(game.debt),
- 'interest': INTEREST_RATE * 100,
- }
- return render(request, 'game/home.html', context)
- def buy(request, product):
- """Handle buying products"""
- if request.method == 'POST':
- game = get_or_create_game(request)
- prices = request.session.get('current_prices', {})
- try:
- quantity = int(request.POST.get('quantity', 0))
- if quantity <= 0:
- raise ValueError("Invalid quantity")
- price = prices.get(product, 0)
- total_cost = Decimal(price * quantity)
- inventory = game.get_inventory()
- current_items = sum(inventory.values())
- if current_items + quantity > game.capacity:
- messages.error(request, f"Not enough capacity! Space for {game.capacity - current_items} items.")
- elif total_cost > game.cash:
- messages.error(request, f"Not enough cash! You need ${total_cost:.2f}")
- else:
- game.cash -= total_cost
- inventory[product] = inventory.get(product, 0) + quantity
- game.set_inventory(inventory)
- game.save()
- messages.success(request, f"Bought {quantity} {product} for ${total_cost:.2f}")
- except (ValueError, TypeError):
- messages.error(request, "Invalid quantity!")
- return redirect('home')
- def sell(request, product):
- """Handle selling products"""
- if request.method == 'POST':
- game = get_or_create_game(request)
- prices = request.session.get('current_prices', {})
- try:
- quantity = int(request.POST.get('quantity', 0))
- if quantity <= 0:
- raise ValueError("Invalid quantity")
- inventory = game.get_inventory()
- if inventory.get(product, 0) < quantity:
- messages.error(request, f"You don't have {quantity} {product}!")
- else:
- price = prices.get(product, 0)
- total_sale = Decimal(price * quantity)
- game.cash += total_sale
- inventory[product] -= quantity
- if inventory[product] == 0:
- del inventory[product]
- game.set_inventory(inventory)
- game.save()
- messages.success(request, f"Sold {quantity} {product} for ${total_sale:.2f}")
- except (ValueError, TypeError):
- messages.error(request, "Invalid quantity!")
- return redirect('home')
- def travel(request):
- """Travel to new location (advances day)"""
- if request.method == 'POST':
- game = get_or_create_game(request)
- new_location = request.POST.get('location')
- if new_location in LOCATIONS:
- game.location = new_location
- game.day += 1
- game.debt = game.debt * Decimal(1 + INTEREST_RATE) # Apply interest
- game.save()
- # Clear prices for new day
- request.session['current_prices'] = None
- messages.info(request, f"📍 Traveled to {new_location}. Day {game.day}/{MAX_DAYS}")
- # Random events during travel
- if random.random() < 0.1:
- bonus = random.randint(50, 500)
- game.cash += bonus
- game.save()
- messages.success(request, f"🎁 Found ${bonus} on the subway!")
- return redirect('home')
- def pay_debt(request):
- """Pay off debt"""
- if request.method == 'POST':
- game = get_or_create_game(request)
- try:
- amount = Decimal(request.POST.get('amount', 0))
- if amount <= 0:
- raise ValueError("Invalid amount")
- if amount > game.cash:
- messages.error(request, "You don't have that much cash!")
- elif amount > game.debt:
- messages.error(request, "You can't pay more than you owe!")
- else:
- game.cash -= amount
- game.debt -= amount
- game.save()
- messages.success(request, f"Paid ${amount:.2f} toward debt")
- except (ValueError, TypeError, InvalidOperation):
- messages.error(request, "Invalid amount!")
- return redirect('home')
- def new_game(request):
- """Start a new game"""
- game = get_or_create_game(request)
- # Save high score and mark game as completed if applicable
- if game.day > 1: # Only if a game was actually played
- inventory = game.get_inventory()
- prices = request.session.get('current_prices', {})
- if prices:
- inventory_value = sum(
- inventory.get(product, 0) * prices.get(product, 0)
- for product in PRODUCTS.keys()
- )
- else:
- inventory_value = 0
- net_worth = game.cash + inventory_value - game.debt
- game.final_score = net_worth
- game.completed = True
- game.is_active = False
- game.save()
- # Update player stats if logged in
- if request.user.is_authenticated:
- stats, _ = PlayerStats.objects.get_or_create(user=request.user)
- stats.games_played += 1
- if net_worth > 0:
- stats.games_won += 1
- if net_worth > stats.best_score:
- stats.best_score = net_worth
- stats.total_profit += net_worth
- stats.save()
- # Create new game
- if request.user.is_authenticated:
- new_game = GameSession.objects.create(
- user=request.user,
- session_key=request.session.session_key,
- is_active=True
- )
- else:
- new_game = GameSession.objects.create(
- session_key=request.session.session_key,
- is_active=True
- )
- # Clear session data
- request.session['current_prices'] = None
- request.session['current_day'] = 1
- messages.info(request, "🆕 New game started!")
- return redirect('home')
- def game_over(request):
- """Game over view"""
- game = get_or_create_game(request)
- inventory = game.get_inventory()
- prices, _ = generate_prices() # Don't need event message here
- if request.session.get('current_prices'):
- prices = request.session.get('current_prices')
- inventory_value = sum(
- inventory.get(product, 0) * prices[product]
- for product in PRODUCTS.keys()
- )
- final_score = game.cash + inventory_value - game.debt
- if request.method == 'POST':
- name = request.POST.get('name', 'Anonymous')
- # Save high score
- high_score = HighScore.objects.create(
- player_name=name,
- user=request.user if request.user.is_authenticated else None,
- score=final_score,
- days_played=min(game.day, MAX_DAYS)
- )
- # Mark game as completed
- game.final_score = final_score
- game.completed = True
- game.is_active = False
- game.save()
- # Update player stats if logged in
- if request.user.is_authenticated:
- stats, _ = PlayerStats.objects.get_or_create(user=request.user)
- stats.games_played += 1
- if final_score > 0:
- stats.games_won += 1
- if final_score > stats.best_score:
- stats.best_score = final_score
- stats.total_profit += final_score
- stats.save()
- return redirect('high_scores')
- context = {
- 'game': game,
- 'final_score': final_score,
- 'inventory_value': inventory_value,
- }
- return render(request, 'game/game_over.html', context)
- def high_scores(request):
- """Display high scores"""
- scores = HighScore.objects.all()[:10]
- user_best = None
- if request.user.is_authenticated:
- user_scores = HighScore.objects.filter(user=request.user)
- if user_scores.exists():
- user_best = user_scores.first()
- return render(request, 'game/high_scores.html', {
- 'scores': scores,
- 'user_best': user_best
- })
- @login_required
- def profile(request):
- """Display user profile and stats"""
- stats, created = PlayerStats.objects.get_or_create(user=request.user)
- # Get user's game history
- games = GameSession.objects.filter(
- user=request.user,
- completed=True
- ).order_by('-created_at')[:10]
- # Get user's high scores
- high_scores = HighScore.objects.filter(user=request.user).order_by('-score')[:5]
- # Calculate additional stats
- win_rate = (stats.games_won / stats.games_played * 100) if stats.games_played > 0 else 0
- avg_score = stats.total_profit / stats.games_played if stats.games_played > 0 else 0
- context = {
- 'stats': stats,
- 'games': games,
- 'high_scores': high_scores,
- 'win_rate': win_rate,
- 'avg_score': avg_score,
- }
- return render(request, 'game/profile.html', context)
- @login_required
- def my_games(request):
- """Display all user's games"""
- games = GameSession.objects.filter(user=request.user).order_by('-created_at')
- active_games = games.filter(is_active=True)
- completed_games = games.filter(completed=True)
- return render(request, 'game/my_games.html', {
- 'active_games': active_games,
- 'completed_games': completed_games,
- })
|