Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef44030a6c |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -20,7 +20,8 @@ from django.conf import settings
|
|||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("admin/", admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
path('accounts/', include('django.contrib.auth.urls')),
|
||||||
path("", include("core.urls")),
|
path("", include("core.urls")),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
63
core/migrations/0001_initial.py
Normal file
63
core/migrations/0001_initial.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2026-03-05 10:42
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Item',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=100)),
|
||||||
|
('description', models.TextField()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Quest',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=100)),
|
||||||
|
('description', models.TextField()),
|
||||||
|
('is_active', models.BooleanField(default=False)),
|
||||||
|
('is_completed', models.BooleanField(default=False)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Scene',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('title', models.CharField(max_length=200)),
|
||||||
|
('content', models.TextField()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PlayerProfile',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('level', models.IntegerField(default=1)),
|
||||||
|
('inventory', models.ManyToManyField(blank=True, to='core.item')),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
('quests', models.ManyToManyField(blank=True, to='core.quest')),
|
||||||
|
('current_scene', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.scene')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Choice',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('text', models.CharField(max_length=200)),
|
||||||
|
('next_scene', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='incoming_choices', to='core.scene')),
|
||||||
|
('scene', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='choices', to='core.scene')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -1,3 +1,43 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
# Create your models here.
|
class Item(models.Model):
|
||||||
|
name = models.CharField(max_length=100)
|
||||||
|
description = models.TextField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Quest(models.Model):
|
||||||
|
title = models.CharField(max_length=100)
|
||||||
|
description = models.TextField()
|
||||||
|
is_active = models.BooleanField(default=False)
|
||||||
|
is_completed = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
class Scene(models.Model):
|
||||||
|
title = models.CharField(max_length=200)
|
||||||
|
content = models.TextField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
class Choice(models.Model):
|
||||||
|
scene = models.ForeignKey(Scene, related_name='choices', on_delete=models.CASCADE)
|
||||||
|
text = models.CharField(max_length=200)
|
||||||
|
next_scene = models.ForeignKey(Scene, related_name='incoming_choices', on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.text
|
||||||
|
|
||||||
|
class PlayerProfile(models.Model):
|
||||||
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
|
current_scene = models.ForeignKey(Scene, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
|
inventory = models.ManyToManyField(Item, blank=True)
|
||||||
|
quests = models.ManyToManyField(Quest, blank=True)
|
||||||
|
level = models.IntegerField(default=1)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.user.username
|
||||||
@ -3,23 +3,33 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>{% block title %}Knowledge Base{% endblock %}</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
{% if project_description %}
|
<title>{% block title %}Witcher RPG{% endblock %}</title>
|
||||||
<meta name="description" content="{{ project_description }}">
|
|
||||||
<meta property="og:description" content="{{ project_description }}">
|
|
||||||
<meta property="twitter:description" content="{{ project_description }}">
|
|
||||||
{% endif %}
|
|
||||||
{% if project_image_url %}
|
|
||||||
<meta property="og:image" content="{{ project_image_url }}">
|
|
||||||
<meta property="twitter:image" content="{{ project_image_url }}">
|
|
||||||
{% endif %}
|
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
<!-- Google Fonts for Cinzel and Inter -->
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;700&family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v=1.0">
|
||||||
{% block head %}{% endblock %}
|
{% block head %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body style="background-color: #0F1310; color: #E0E0E0; font-family: 'Inter', sans-serif; margin: 0; padding: 0;">
|
||||||
{% block content %}{% endblock %}
|
|
||||||
|
<header style="padding: 20px; border-bottom: 2px solid #333;">
|
||||||
|
<h1 style="font-family: 'Cinzel', serif; color: #B68D4C; margin: 0;">Witcher RPG</h1>
|
||||||
|
<nav>
|
||||||
|
<a href="{% url 'core:home' %}" style="color: #D4B26A; margin-right: 15px;">Home</a>
|
||||||
|
<a href="{% url 'core:current_scene' %}" style="color: #D4B26A; margin-right: 15px;">Play</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main style="padding: 20px;">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer style="padding: 20px; text-align: center; color: #88938C; font-size: 0.8em; margin-top: 50px;">
|
||||||
|
© 2026 Dark Fantasy RPG Engine
|
||||||
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
36
core/templates/core/scene_detail.html
Normal file
36
core/templates/core/scene_detail.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card">
|
||||||
|
<h1>{{ scene.title }}</h1>
|
||||||
|
<p>{{ scene.content }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
{% for choice in scene.choices.all %}
|
||||||
|
<a href="{% url 'core:choose_action' choice.id %}" class="btn btn-accent" style="display: block; margin-bottom: 10px;">{{ choice.text }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card" style="background-color: #1C2320; border: 1px solid #333; padding: 15px;">
|
||||||
|
<h2 style="color: #B68D4C; font-size: 1.2em;">Character Status</h2>
|
||||||
|
<p>Level: {{ player.level }}</p>
|
||||||
|
<h3 style="color: #B68D4C; font-size: 1.1em;">Quests</h3>
|
||||||
|
<ul>
|
||||||
|
{% for quest in player.quests.all %}
|
||||||
|
<li>{{ quest.title }} - {{ quest.is_completed|yesno:"Completed,Active" }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<h3 style="color: #B68D4C; font-size: 1.1em;">Inventory</h3>
|
||||||
|
<ul>
|
||||||
|
{% for item in player.inventory.all %}
|
||||||
|
<li>{{ item.name }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
34
core/templates/registration/login.html
Normal file
34
core/templates/registration/login.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}Sign In | Witcher RPG{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<section style="max-width: 480px; margin: 40px auto; background: #1C2320; border: 1px solid #2F3A34; border-radius: 12px; padding: 28px; box-shadow: 0 12px 30px rgba(0,0,0,0.35);">
|
||||||
|
<h2 style="font-family: 'Cinzel', serif; color: #D4B26A; margin-top: 0;">Sign in to continue</h2>
|
||||||
|
<p style="color: #A8B1AA; margin-bottom: 24px;">Enter your credentials to resume your journey.</p>
|
||||||
|
|
||||||
|
{% if form.non_field_errors %}
|
||||||
|
<div style="background: rgba(190, 64, 64, 0.15); border: 1px solid #8C3B3B; color: #F2B8B5; padding: 12px; border-radius: 8px; margin-bottom: 16px;">
|
||||||
|
{{ form.non_field_errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" style="display: grid; gap: 16px;">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<label style="display: grid; gap: 6px; color: #D4B26A;">
|
||||||
|
<span>Username</span>
|
||||||
|
{{ form.username }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label style="display: grid; gap: 6px; color: #D4B26A;">
|
||||||
|
<span>Password</span>
|
||||||
|
{{ form.password }}
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button type="submit" style="background: linear-gradient(135deg, #B68D4C, #D4B26A); color: #0F1310; border: none; padding: 12px 18px; border-radius: 10px; font-weight: 700; cursor: pointer;">
|
||||||
|
Sign In
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
@ -1,7 +1,11 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
from .views import home
|
app_name = 'core'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", home, name="home"),
|
path('', views.home, name='home'),
|
||||||
|
path('scene/<int:scene_id>/', views.scene_view, name='scene_detail'),
|
||||||
|
path('scene/', views.scene_view, name='current_scene'),
|
||||||
|
path('choose/<int:choice_id>/', views.choose_action, name='choose_action'),
|
||||||
]
|
]
|
||||||
@ -1,25 +1,36 @@
|
|||||||
import os
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
import platform
|
from .models import Scene, Choice, PlayerProfile
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
from django import get_version as django_version
|
|
||||||
from django.shortcuts import render
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
|
|
||||||
def home(request):
|
def home(request):
|
||||||
"""Render the landing screen with loader and environment details."""
|
return render(request, "core/index.html")
|
||||||
host_name = request.get_host().lower()
|
|
||||||
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
|
||||||
now = timezone.now()
|
|
||||||
|
|
||||||
context = {
|
@login_required
|
||||||
"project_name": "New Style",
|
def scene_view(request, scene_id=None):
|
||||||
"agent_brand": agent_brand,
|
player = request.user.playerprofile
|
||||||
"django_version": django_version(),
|
if scene_id:
|
||||||
"python_version": platform.python_version(),
|
scene = get_object_or_404(Scene, id=scene_id)
|
||||||
"current_time": now,
|
player.current_scene = scene
|
||||||
"host_name": host_name,
|
player.save()
|
||||||
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
|
else:
|
||||||
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
|
scene = player.current_scene
|
||||||
}
|
if not scene:
|
||||||
return render(request, "core/index.html", context)
|
first_scene = Scene.objects.first()
|
||||||
|
if first_scene:
|
||||||
|
player.current_scene = first_scene
|
||||||
|
player.save()
|
||||||
|
scene = first_scene
|
||||||
|
else:
|
||||||
|
return render(request, "core/empty.html")
|
||||||
|
|
||||||
|
return render(request, "core/scene_detail.html", {"scene": scene, "player": player})
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def choose_action(request, choice_id):
|
||||||
|
choice = get_object_or_404(Choice, id=choice_id)
|
||||||
|
player = request.user.playerprofile
|
||||||
|
if choice.next_scene:
|
||||||
|
player.current_scene = choice.next_scene
|
||||||
|
player.save()
|
||||||
|
return redirect('core:scene_detail', scene_id=choice.next_scene.id)
|
||||||
|
return redirect('core:current_scene')
|
||||||
@ -1,4 +1,26 @@
|
|||||||
/* Custom styles for the application */
|
|
||||||
body {
|
body {
|
||||||
font-family: system-ui, -apple-system, sans-serif;
|
background-color: #0F1310;
|
||||||
|
color: #E0E0E0;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3 {
|
||||||
|
font-family: 'Cinzel', serif;
|
||||||
|
color: #B68D4C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-accent {
|
||||||
|
background-color: #B68D4C;
|
||||||
|
color: #0F1310;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: #1C2320;
|
||||||
|
border: 1px solid #333;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user