adding missing files
16
app/blueprints/ajax.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from flask import Blueprint, request, url_for, render_template, session,flash
|
||||
from app.services.appwrite_client import AppWriteClient
|
||||
|
||||
|
||||
ajax_bp = Blueprint("ajax", __name__, url_prefix="/ajax")
|
||||
|
||||
|
||||
@ajax_bp.route("/races", methods=["GET", "POST"])
|
||||
def races():
|
||||
race = request.args.get("race")
|
||||
return render_template(f"ajax/race_{race}.html")
|
||||
|
||||
@ajax_bp.route("/prof", methods=["GET", "POST"])
|
||||
def professions():
|
||||
prof = request.args.get("prof")
|
||||
return render_template(f"ajax/prof_{prof}.html")
|
||||
112
app/blueprints/auth.py
Normal file
@@ -0,0 +1,112 @@
|
||||
# app/auth/routes.py
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, flash, session,make_response
|
||||
|
||||
from app.services.appwrite_client import AppWriteClient
|
||||
|
||||
|
||||
auth_bp = Blueprint("auth", __name__, url_prefix="/auth")
|
||||
|
||||
|
||||
@auth_bp.route("/register", methods=["GET", "POST"])
|
||||
def register():
|
||||
if request.method == "POST":
|
||||
email = request.form.get("email", "").strip()
|
||||
password = request.form.get("password", "")
|
||||
name = (request.form.get("name") or "").strip() or None
|
||||
|
||||
if not email or not password:
|
||||
flash("Email and password are required.", "error")
|
||||
return redirect(url_for("auth.register"))
|
||||
|
||||
try:
|
||||
aw = AppWriteClient()
|
||||
aw.create_new_user(email,password,name)
|
||||
|
||||
login_valid, error = aw.log_user_in(email,password)
|
||||
if login_valid:
|
||||
flash("Account created and you are now logged in.", "success")
|
||||
return redirect(url_for("main.dashboard"))
|
||||
else:
|
||||
flash(str(error), "error")
|
||||
except Exception as e:
|
||||
flash(str(e), "error")
|
||||
return redirect(url_for("auth.register"))
|
||||
|
||||
return render_template("auth/register.html")
|
||||
|
||||
@auth_bp.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
if request.method == "POST":
|
||||
email = request.form.get("email", "").strip()
|
||||
password = request.form.get("password", "")
|
||||
if not email or not password:
|
||||
flash("Email and password are required.", "error")
|
||||
return redirect(url_for("auth.login"))
|
||||
|
||||
aw = AppWriteClient()
|
||||
login_valid, error = aw.log_user_in(email,password)
|
||||
try:
|
||||
if login_valid:
|
||||
username = session.get("user",{}).get("name","User")
|
||||
flash(f"Welcome Back {username}", "success")
|
||||
return redirect(url_for("main.dashboard"))
|
||||
else:
|
||||
flash(str(error), "error")
|
||||
except Exception as e:
|
||||
flash(str(e), "error")
|
||||
return redirect(url_for("auth.login"))
|
||||
|
||||
# Get method
|
||||
|
||||
return render_template("auth/login.html")
|
||||
|
||||
@auth_bp.route("/logout", methods=["GET", "POST"])
|
||||
def logout():
|
||||
aw = AppWriteClient()
|
||||
aw.log_user_out()
|
||||
session.clear()
|
||||
flash("Signed out.", "success")
|
||||
return redirect(url_for("auth.login"))
|
||||
|
||||
@auth_bp.route("/send", methods=["POST"])
|
||||
def send():
|
||||
"""
|
||||
Sends a verification email to the currently logged-in user.
|
||||
Appwrite will redirect the user back to /verify/callback with userId & secret.
|
||||
"""
|
||||
try:
|
||||
aw = AppWriteClient()
|
||||
aw.send_email_verification()
|
||||
flash("Verification email sent. Please check your inbox.", "info")
|
||||
except Exception as e:
|
||||
flash(f"Could not send verification email: {e}", "error")
|
||||
# Go back to where the user came from, or your dashboard
|
||||
return redirect(request.referrer or url_for("main.dashboard"))
|
||||
|
||||
@auth_bp.route("/callback", methods=["GET"])
|
||||
def callback():
|
||||
"""
|
||||
Completes verification after user clicks the email link.
|
||||
Requires the user to be logged in (Appwrite Account endpoints need a session).
|
||||
If not logged in, we stash the link params and send them to log in first.
|
||||
"""
|
||||
aw = AppWriteClient()
|
||||
user_id = request.args.get("userId")
|
||||
secret = request.args.get("secret")
|
||||
if not user_id or not secret:
|
||||
flash("Invalid verification link.", "error")
|
||||
return redirect(url_for("auth.login"))
|
||||
|
||||
try:
|
||||
# If we don't currently have an Appwrite session, ask them to log in, then resume.
|
||||
if not aw.verify_email(user_id,secret):
|
||||
session["pending_verification"] = {"userId": user_id, "secret": secret}
|
||||
flash("Please log in to complete email verification.", "warning")
|
||||
return redirect(url_for("auth.login"))
|
||||
|
||||
# We have a session; complete verification
|
||||
flash("Email verified! You're all set.", "success")
|
||||
return redirect(url_for("main.dashboard"))
|
||||
except Exception as e:
|
||||
flash(f"Verification failed: {e}", "error")
|
||||
return redirect(url_for("auth.login"))
|
||||
46
app/blueprints/char.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import os
|
||||
import json
|
||||
import random
|
||||
from flask import Blueprint, redirect, url_for, render_template, g, request, flash
|
||||
from typing import cast
|
||||
|
||||
from app.utils.session_user import SessionUser
|
||||
from app.services.appwrite_client import AppWriteClient
|
||||
from app.services.appwrite_db import AppwriteTables, Env
|
||||
from app.services.coc_api import CoCApi
|
||||
|
||||
char_bp = Blueprint("char", __name__, url_prefix="/char")
|
||||
|
||||
cocapi = CoCApi()
|
||||
|
||||
|
||||
def get_current_user() -> SessionUser:
|
||||
return cast(SessionUser, g.current_user)
|
||||
|
||||
|
||||
@char_bp.route("/create", methods=["GET", "POST"])
|
||||
def create_char():
|
||||
if request.method == "POST":
|
||||
name = request.form.get("character_name")
|
||||
race_id = request.form.get("race_dropdown")
|
||||
profession_id = request.form.get("profession_dropdown")
|
||||
origin_story = request.form.get("origin_story")
|
||||
uuid = cocapi.create_char(name=name,origin_story=origin_story,race_id=race_id,profession_id=profession_id)
|
||||
redirect(url_for("main.dashboard"))
|
||||
|
||||
ai_dumps_path = os.path.join("app","game_data","ai_dumps.json")
|
||||
with open(ai_dumps_path,"r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
origin_stories = data.get("origin_stories",[])
|
||||
if len(origin_stories) > 0:
|
||||
starter_text = random.choice(origin_stories)
|
||||
else:
|
||||
starter_text = "I was born in a small, secluded village on the edge of a vast and mysterious forest, where whispers of ancient magic still lingered in the air."
|
||||
|
||||
template_data = {
|
||||
"starter_text":starter_text
|
||||
}
|
||||
|
||||
return render_template("char/create_char.html", data=template_data)
|
||||
|
||||
27
app/blueprints/main.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import os
|
||||
from flask import Blueprint, redirect, url_for, render_template, g, session,flash
|
||||
from typing import cast
|
||||
|
||||
from app.utils.session_user import SessionUser
|
||||
from app.services.appwrite_client import AppWriteClient
|
||||
from app.services.appwrite_db import AppwriteTables
|
||||
from app.services.coc_api import CoCApi
|
||||
|
||||
main_bp = Blueprint("main", __name__, url_prefix="/main")
|
||||
cocapi = CoCApi()
|
||||
|
||||
|
||||
def get_current_user() -> SessionUser:
|
||||
return cast(SessionUser, g.current_user)
|
||||
|
||||
|
||||
@main_bp.route("/dashboard")
|
||||
def dashboard():
|
||||
db_tables = AppwriteTables()
|
||||
user = get_current_user()
|
||||
results = db_tables.get_characters_for_user_id(user.id)
|
||||
if len(results) == 0:
|
||||
return redirect(url_for("char.create_char"))
|
||||
else:
|
||||
char=results
|
||||
return render_template("main/dashboard.html", profile=g.current_user, jwt_info=char)
|
||||
10
app/blueprints/public.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from flask import Blueprint, redirect, url_for, render_template, session,flash
|
||||
from app.services.appwrite_client import AppWriteClient
|
||||
|
||||
|
||||
public_bp = Blueprint("public", __name__, url_prefix="/")
|
||||
|
||||
|
||||
@public_bp.route("/")
|
||||
def home():
|
||||
return render_template("public/home.html")
|
||||
19
app/game_data/ai_dumps.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"origin_stories":[
|
||||
"I was born in a small village on the edge of a vast and mysterious forest.",
|
||||
"The day I was born, the stars aligned in a peculiar pattern above our town.",
|
||||
"My earliest memories are of wandering through the ruins of an ancient city.",
|
||||
"In the depths of my childhood, I stumbled upon a hidden text that changed everything.",
|
||||
"I was raised by a family of nomads who taught me the ways of the wind and the sun.",
|
||||
"The village elder predicted my arrival on the day I was born, as if I were already known.",
|
||||
"I've always felt like there's something missing in my memories, like pieces are waiting to be filled in.",
|
||||
"As a child, I would often sneak into the local library at night, devouring forbidden knowledge from ancient tomes.",
|
||||
"My family's past is shrouded in mystery, but one thing is certain: we've always been on the move.",
|
||||
"I was found as a baby in the heart of a dense jungle, with no memory of who I am or where I came from.",
|
||||
"The village priestess foretold my destiny on the day of my birth, speaking words that only I could understand.",
|
||||
"Growing up, I would often experience strange and vivid dreams that felt more real than reality itself.",
|
||||
"I was born with a rare gift: the ability to communicate with animals in ways others cannot.",
|
||||
"The village elder's prophecies spoke of me as a bringer of change, but I'm still unsure what that means."
|
||||
]
|
||||
|
||||
}
|
||||
87
app/services/appwrite_db.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from __future__ import annotations
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
from typing import Final, Optional
|
||||
from flask import current_app
|
||||
|
||||
from appwrite.client import Client
|
||||
from appwrite.services.tables_db import TablesDB
|
||||
from appwrite.query import Query
|
||||
from appwrite.id import ID
|
||||
|
||||
from app.utils.logging import get_logger
|
||||
from app.utils.settings import get_settings, Environment
|
||||
|
||||
settings = get_settings()
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
class Env(StrEnum):
|
||||
PROD = "prod"
|
||||
DEV = "dev"
|
||||
|
||||
# --- Database schemas (strongly-typed namespaces) ----------------------------
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Database:
|
||||
"""Schema for a single database: each attribute is a table name (or ID)."""
|
||||
id: str
|
||||
characters: str
|
||||
inventory: str
|
||||
# add more tables here as you grow your schema
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Databases:
|
||||
"""Top-level namespace exposing prod/dev as attributes."""
|
||||
prod: Database
|
||||
dev: Database
|
||||
|
||||
DB: Final[Databases] = Databases(
|
||||
prod=Database(
|
||||
id="SETME", # actual DB / ID
|
||||
characters="SETME", # actual table / ID
|
||||
inventory="inventory",
|
||||
),
|
||||
dev=Database(
|
||||
id="69041f9600177b675485",
|
||||
characters="69050f830024afb0d253",
|
||||
inventory="inventory",
|
||||
),
|
||||
)
|
||||
|
||||
class AppwriteTables:
|
||||
|
||||
def __init__(self):
|
||||
print()
|
||||
self.client = (Client()
|
||||
.set_endpoint(settings.appwrite_endpoint)
|
||||
.set_project(settings.appwrite_project_id)
|
||||
.set_key(settings.appwrite_api_key)
|
||||
)
|
||||
self.tables_db = TablesDB(self.client)
|
||||
self.env = Env.DEV
|
||||
if settings.env == Environment.PROD:
|
||||
self.env = Env.PROD
|
||||
|
||||
@property
|
||||
def db(self) -> Database:
|
||||
# Gives autocompletion for .character, .users, etc.
|
||||
return DB.prod if self.env is Env.PROD else DB.dev
|
||||
|
||||
def get_characters_for_user_id(self, user_id: str) -> Optional[dict]:
|
||||
|
||||
try:
|
||||
result = self.tables_db.list_rows(
|
||||
self.db.id,
|
||||
self.db.characters,
|
||||
[
|
||||
Query.equal('player_id', [str(user_id)]),
|
||||
]
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Unable to list rows for char. User id: {user_id}")
|
||||
return {}
|
||||
|
||||
return result.get("rows",{})
|
||||
|
||||
232
app/services/coc_api.py
Normal file
@@ -0,0 +1,232 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import requests
|
||||
from requests import Response, Session
|
||||
from requests.adapters import HTTPAdapter
|
||||
from urllib3.util.retry import Retry
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from app.services.appwrite_client import AppWriteClient
|
||||
from app.utils.logging import get_logger
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
class CoCApi:
|
||||
"""
|
||||
Centralized API client for Code of Conquest.
|
||||
All HTTP interactions go through _request() for consistent behavior and logging.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
base_url: str = "http://localhost:8000",
|
||||
default_timeout: tuple[float, float] = (5.0, 10.0),
|
||||
max_retries: int = 3,
|
||||
) -> None:
|
||||
"""
|
||||
:param base_url: Base URL for the API (no trailing slash needed).
|
||||
:param default_timeout: (connect_timeout, read_timeout)
|
||||
:param max_retries: Number of retries for transient network/server errors.
|
||||
"""
|
||||
self.base_url = base_url.rstrip("/")
|
||||
self.default_timeout = default_timeout
|
||||
self._aw = AppWriteClient()
|
||||
|
||||
# Base headers for JSON APIs.
|
||||
self._base_headers: Dict[str, str] = {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "CoC-Client/1.0",
|
||||
}
|
||||
|
||||
# Pre-configured Session with retries.
|
||||
self._session = self._build_session(max_retries=max_retries)
|
||||
|
||||
# ---------- Public convenience methods ----------
|
||||
|
||||
def create_char(self, name:str, origin_story:str, race_id:str, profession_id:str) -> Dict[str, Any]:
|
||||
payload = {
|
||||
"name":name,
|
||||
"origin_story":origin_story,
|
||||
"race_id":race_id,
|
||||
"profession_id":profession_id
|
||||
}
|
||||
result = self.post("/char/new",payload=payload)
|
||||
player_uuid = result.get("result")
|
||||
return player_uuid
|
||||
|
||||
def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
||||
return self._request("GET", path, params=params)
|
||||
|
||||
def post(self, path: str, payload: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
||||
return self._request("POST", path, json_body=payload)
|
||||
|
||||
def patch(self, path: str, json_body: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
||||
return self._request("PATCH", path, json_body=json_body)
|
||||
|
||||
# ---------- Internal helpers ----------
|
||||
|
||||
def _build_session(self, max_retries: int) -> Session:
|
||||
"""
|
||||
Create a Session with sane retries for transient network/server failures.
|
||||
We retry idempotent methods and some 5xx responses.
|
||||
"""
|
||||
session = requests.Session()
|
||||
|
||||
retries = Retry(
|
||||
total=max_retries,
|
||||
connect=max_retries,
|
||||
read=max_retries,
|
||||
backoff_factor=0.5,
|
||||
status_forcelist=(502, 503, 504),
|
||||
allowed_methods=frozenset(["GET", "HEAD", "OPTIONS", "TRACE"]),
|
||||
raise_on_status=False,
|
||||
)
|
||||
|
||||
adapter = HTTPAdapter(max_retries=retries)
|
||||
session.mount("http://", adapter)
|
||||
session.mount("https://", adapter)
|
||||
return session
|
||||
|
||||
def _mint_jwt(self) -> str:
|
||||
"""Mint a JWT from AppWrite; empty string if unavailable (don’t block requests)."""
|
||||
try:
|
||||
token = self._aw.mint_jwt() # expected to return dict-like
|
||||
return token.get("jwt", "") if token else ""
|
||||
except Exception as e:
|
||||
logger.warning("Failed to mint JWT", extra={"error": str(e)})
|
||||
return ""
|
||||
|
||||
def _auth_headers(self) -> Dict[str, str]:
|
||||
"""
|
||||
Build per-call headers with Authorization if a JWT is available.
|
||||
Avoid mutating shared headers.
|
||||
"""
|
||||
headers = dict(self._base_headers)
|
||||
jwt = self._mint_jwt()
|
||||
if jwt:
|
||||
headers["Authorization"] = f"Bearer {jwt}"
|
||||
return headers
|
||||
|
||||
def _resolve_url(self, path_or_url: str) -> str:
|
||||
"""
|
||||
Accept either a full URL or a path (e.g., '/char/'). Join with base_url when needed.
|
||||
"""
|
||||
if path_or_url.lower().startswith(("http://", "https://")):
|
||||
return path_or_url
|
||||
return urljoin(self.base_url + "/", path_or_url.lstrip("/"))
|
||||
|
||||
def _safe_json(self, resp: Response) -> Dict[str, Any]:
|
||||
"""
|
||||
Attempt to parse JSON. If body is empty or not JSON, return {}.
|
||||
"""
|
||||
# 204 No Content or truly empty payloads
|
||||
if resp.status_code == 204 or not resp.content:
|
||||
return {}
|
||||
|
||||
try:
|
||||
return resp.json()
|
||||
except ValueError:
|
||||
# Non-JSON payload; log preview and return empty.
|
||||
preview = ""
|
||||
try:
|
||||
preview = resp.text[:400]
|
||||
except Exception:
|
||||
pass
|
||||
logger.warning(
|
||||
"Non-JSON response body",
|
||||
extra={
|
||||
"url": resp.request.url if resp.request else None,
|
||||
"status": resp.status_code,
|
||||
"body_preview": preview,
|
||||
},
|
||||
)
|
||||
return {}
|
||||
|
||||
def _request(
|
||||
self,
|
||||
method: str,
|
||||
path: str,
|
||||
*,
|
||||
params: Optional[Dict[str, Any]] = None,
|
||||
json_body: Optional[Dict[str, Any]] = None,
|
||||
timeout: Optional[tuple[float, float]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Central request executor. Never raises to the caller.
|
||||
Returns parsed JSON on success or {} on any error.
|
||||
"""
|
||||
url = self._resolve_url(path)
|
||||
headers = self._auth_headers()
|
||||
to = timeout or self.default_timeout
|
||||
|
||||
try:
|
||||
resp = self._session.request(
|
||||
method=method.upper(),
|
||||
url=url,
|
||||
headers=headers,
|
||||
params=params,
|
||||
json=json_body,
|
||||
timeout=to,
|
||||
)
|
||||
|
||||
# Log and return {} on non-2xx
|
||||
if not (200 <= resp.status_code < 300):
|
||||
# Truncate body in logs to avoid huge entries
|
||||
preview = ""
|
||||
try:
|
||||
preview = resp.text[:400]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
logger.warning(
|
||||
"HTTP request failed",
|
||||
extra={
|
||||
"method": method.upper(),
|
||||
"url": resp.request.url if resp.request else url,
|
||||
"status": resp.status_code,
|
||||
"params": params,
|
||||
"json_body": json_body,
|
||||
"body_preview": preview,
|
||||
},
|
||||
)
|
||||
return {}
|
||||
|
||||
# Success path: parse JSON safely
|
||||
return self._safe_json(resp)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
# Network/timeout/connection errors
|
||||
logger.warning(
|
||||
"Network error during HTTP request",
|
||||
extra={
|
||||
"method": method.upper(),
|
||||
"url": url,
|
||||
"params": params,
|
||||
"json_body": json_body,
|
||||
"error_type": type(e).__name__,
|
||||
"error": str(e),
|
||||
},
|
||||
exc_info=True,
|
||||
)
|
||||
return {}
|
||||
except Exception as e:
|
||||
# Absolute last-resort guardrail
|
||||
logger.error(
|
||||
"Unexpected error during HTTP request",
|
||||
extra={
|
||||
"method": method.upper(),
|
||||
"url": url,
|
||||
"params": params,
|
||||
"json_body": json_body,
|
||||
"error_type": type(e).__name__,
|
||||
"error": str(e),
|
||||
},
|
||||
exc_info=True,
|
||||
)
|
||||
return {}
|
||||
BIN
app/static/images/races/avaline.jpg
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
app/static/images/races/beastfolk.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
app/static/images/races/draconian.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
app/static/images/races/dwarf.jpg
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
app/static/images/races/elf.jpg
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
app/static/images/races/hellion.jpg
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
app/static/images/races/terran.jpg
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
app/static/images/races/vorgath.jpg
Normal file
|
After Width: | Height: | Size: 85 KiB |
18
app/templates/ajax/prof_archanist.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<h3>Archanist</h3>
|
||||
<p>
|
||||
The Archanists are masters of the arcane arts, wielding the fundamental forces of reality to achieve their goals.
|
||||
They possess a deep understanding of the underlying fabric of existence, allowing them to manipulate the threads
|
||||
of fate, bend time and space to their will, and summon the raw power of the cosmos. Through tireless study,
|
||||
experimentation, and mystical communion with the celestial forces, Archanists have developed a unique blend of
|
||||
magical prowess, strategic insight, and philosophical clarity. They are often sought as counselors, diplomats, and
|
||||
problem-solvers by those in need of guidance or solution to complex challenges.
|
||||
</p>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes:</h4>
|
||||
<ul>
|
||||
<li>Health: 8</li>
|
||||
<li>Magic Power: 14</li>
|
||||
<li>Primary Stat: INT</li>
|
||||
</ul>
|
||||
</div>
|
||||
19
app/templates/ajax/prof_assassin.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<h3>Assassins</h3>
|
||||
<p>
|
||||
The Assassins are stealthy and deadly agents, trained to stalk the shadows and strike without warning. They
|
||||
possess a unique blend of physical agility, mental focus, and cunning strategy, allowing them to move unseen,
|
||||
gather information, and eliminate targets with precision and silence. Through years of rigorous training in the
|
||||
art of espionage, deception, and murder, Assassins have honed their skills to the point where they can blend into
|
||||
the background, become one with the darkness, and disappear into the night like specters. They are often hired as
|
||||
mercenaries, bodyguards, or spies, but those who employ them do so at their own peril.
|
||||
|
||||
</p>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes:</h4>
|
||||
<ul>
|
||||
<li>Health: 10</li>
|
||||
<li>Magic Power: 10</li>
|
||||
<li>Primary Stat: DEX</li>
|
||||
</ul>
|
||||
</div>
|
||||
19
app/templates/ajax/prof_bloodborn.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<h3>Bloodborn</h3>
|
||||
<p>
|
||||
The Bloodborn are unbridled warriors, born of the darkest depths of human nature and forged in the fire of
|
||||
unforgiving violence. They possess a primal connection to their own rage and fury, allowing them to tap into a
|
||||
deep wellspring of strength, speed, and ferocity when faced with adversity. Through generations of brutal training
|
||||
and unrelenting combat, Bloodborn have learned to channel their inner turmoil into a maelstrom of aggression,
|
||||
unleashing devastating attacks that leave foes reeling in terror. They are often seen as outcasts, driven by their
|
||||
own unyielding passions and instincts, but those who dare oppose them do so at the risk of facing an unrelenting
|
||||
storm of bloodlust and fury.
|
||||
</p>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes:</h4>
|
||||
<ul>
|
||||
<li>Health: 14</li>
|
||||
<li>Magic Power: 5</li>
|
||||
<li>Primary Stat: STR</li>
|
||||
</ul>
|
||||
</div>
|
||||
17
app/templates/ajax/prof_cleric.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<h3>Clerics</h3>
|
||||
<p>
|
||||
The Clerics are devoted servants of a higher power, channeling the divine energies to heal, protect, and uplift
|
||||
those around them. They possess a deep understanding of the mysteries of life and death, allowing them to tend to
|
||||
wounds, calm fears, and bring solace to the afflicted. Through their sacred vows and mystical communion with the
|
||||
divine, Clerics have developed a unique blend of spiritual insight, empathetic compassion, and healing artistry.
|
||||
As agents of hope and redemption, they often walk among the sick, the dying, and the grieving, offering comfort,
|
||||
guidance, and the promise of a brighter tomorrow.
|
||||
</p>
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes:</h4>
|
||||
<ul>
|
||||
<li>Health: 8</li>
|
||||
<li>Magic Power: 14</li>
|
||||
<li>Primary Stat: WIS</li>
|
||||
</ul>
|
||||
</div>
|
||||
17
app/templates/ajax/prof_guardian.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<h3>Guardians</h3>
|
||||
<p>
|
||||
The Guardians are holy warriors, sworn to defend the innocent and uphold the principles of justice, honor, and
|
||||
righteousness. They possess a strong sense of duty, unshakeable conviction, and unwavering compassion, guiding
|
||||
them as they stride into battle with valor in their hearts. Through their sacred oath and spiritual connection to
|
||||
a higher power, Guardians have developed a unique blend of martial prowess, moral authority, and divine
|
||||
intervention. As champions of the faithful, they are often called upon to vanquish evil, protect the vulnerable,
|
||||
and defend the sacred realms against darkness and despair.
|
||||
</p>
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes:</h4>
|
||||
<ul>
|
||||
<li>Health: 14</li>
|
||||
<li>Magic Power: 5</li>
|
||||
<li>Primary Stat: STR</li>
|
||||
</ul>
|
||||
</div>
|
||||
17
app/templates/ajax/prof_hexist.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<h3>Hexists</h3>
|
||||
<p>
|
||||
The Hexists are masters of dark and malevolent magic, delighting in the suffering and terror they inspire in
|
||||
others. They possess a twisted understanding of the arcane forces, allowing them to manipulate reality itself to
|
||||
inflict cruel and capricious punishment upon their enemies. Through their mastery of dark arts and infernal pacts,
|
||||
Hexists have developed a unique blend of magical cunning, sadistic glee, and corrupted ambition. As agents of
|
||||
chaos and despair, they often seek to undermine the fabric of society, sow discord, and revel in the misery of
|
||||
others.
|
||||
</p>
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes:</h4>
|
||||
<ul>
|
||||
<li>Health: 8</li>
|
||||
<li>Magic Power: 14</li>
|
||||
<li>Primary Stat: INT</li>
|
||||
</ul>
|
||||
</div>
|
||||
16
app/templates/ajax/prof_ranger.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<h3>Rangers</h3>
|
||||
<p>
|
||||
The Rangers are skilled trackers, hunters, and guardians of the natural world. They possess a deep understanding
|
||||
of the land, its rhythms, and its creatures, allowing them to move unseen, stalk their prey with precision, and
|
||||
track down even the most elusive foes. Through their connection to the wild, Rangers have developed a unique blend
|
||||
of survival skills, stealth, and primal intuition. As champions of the wilderness, they often walk a fine line
|
||||
between hunter and protector, defending the innocent and vanquishing those who would desecrate the land.
|
||||
</p>
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes:</h4>
|
||||
<ul>
|
||||
<li>Health: 10</li>
|
||||
<li>Magic Power: 10</li>
|
||||
<li>Primary Stat: DEX</li>
|
||||
</ul>
|
||||
</div>
|
||||
17
app/templates/ajax/prof_warlock.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<h3>Warlock</h3>
|
||||
<p>
|
||||
The Warlocks are adepts of dark and forbidden magic, forging pacts with malevolent forces to wield power beyond
|
||||
mortal comprehension. They possess a mastery of arcane energies, allowing them to summon eldritch powers,
|
||||
manipulate reality, and bend the fabric of existence to their will. Through their pact with an otherworldly
|
||||
patron, Warlocks have developed a unique blend of magical prowess, psychological manipulation, and shadowy
|
||||
intrigue. As agents of chaos and darkness, they often walk a delicate balance between power and corruption, using
|
||||
their mastery of the arcane to further their own sinister agendas.
|
||||
</p>
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes:</h4>
|
||||
<ul>
|
||||
<li>Health: 8</li>
|
||||
<li>Magic Power: 14</li>
|
||||
<li>Primary Stat: WIS</li>
|
||||
</ul>
|
||||
</div>
|
||||
30
app/templates/ajax/race_avaline.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<h3>Avaline</h3>
|
||||
<p>
|
||||
The Avaline are a divine and majestic people, born from the celestial realm to serve as radiant warriors of the
|
||||
sky. Their physical form is one of elegance and power, with slender bodies, delicate wings, and eyes that shine
|
||||
like stars. With their connection to the divine, they possess exceptional martial prowess, wielding blades that
|
||||
seem to cut through the very fabric of reality. The Avaline are drawn to conflict, not for the sake of bloodshed,
|
||||
but to vanquish darkness and restore balance to a world in need. Their presence on the battlefield is a beacon of
|
||||
hope, inspiring allies and striking fear into the hearts of their enemies.
|
||||
|
||||
</p>
|
||||
|
||||
<div class="row align-items-center justify-content-center">
|
||||
<div class="col-12 col-md-6">
|
||||
<img src="{{ url_for('static', filename='images/races/avaline.jpg') }}" style="width: 300px;" alt="Race Image"/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes: + / - from 10</h4>
|
||||
<ul>
|
||||
<li>Strength (Str): +5</li>
|
||||
<li>Dexterity (Dex): 0</li>
|
||||
<li>Intelligence (Int): +3</li>
|
||||
<li>Wisdom (Wis): 1</li>
|
||||
<li>Luck (Luk): -2</li>
|
||||
<li>Charisma (Cha): -1</li>
|
||||
<li>Constitution (Con): 0</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
29
app/templates/ajax/race_beastfolk.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<h3>Beastfolk</h3>
|
||||
<p>
|
||||
Beastfolk are feral and resilient tribes that have adapted to life in the wilderness, developing a deep connection
|
||||
with the natural world and its creatures. They possess a unique physical form, blending human and animal
|
||||
characteristics, allowing them to move unseen, communicate through scent and sound, and track their prey with
|
||||
ease. Beastfolk are fiercely protective of their territory and those they care about, making them formidable
|
||||
allies or fierce opponents. Despite their primal appearance, they are not mindless beasts, but rather complex
|
||||
individuals with a rich culture and history.
|
||||
</p>
|
||||
|
||||
<div class="row align-items-center justify-content-center">
|
||||
<div class="col-12 col-md-6">
|
||||
<img src="{{ url_for('static', filename='images/races/beastfolk.jpg') }}" style="width: 300px;" alt="Race Image"/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes: + / - from 10</h4>
|
||||
<ul>
|
||||
<li>Strength (Str): +3</li>
|
||||
<li>Dexterity (Dex): +5</li>
|
||||
<li>Intelligence (Int): +1</li>
|
||||
<li>Wisdom (Wis): -2</li>
|
||||
<li>Luck (Luk): -1</li>
|
||||
<li>Charisma (Cha): 0</li>
|
||||
<li>Constitution (Con): +1</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
30
app/templates/ajax/race_draconian.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<h3>Draconian</h3>
|
||||
<p>
|
||||
The Draconians are a proud and ancient people, born from the union of dragons and terrans. Their physical form is a
|
||||
testament to their draconic heritage, with scales that shimmer like polished gemstones, wings that soar through
|
||||
the skies, and eyes that burn with inner fire. As half-dragon, they possess a unique blend of ferocity,
|
||||
intelligence, and charisma, making them formidable diplomats, warriors, and leaders. The Draconians walk the line
|
||||
between two worlds, bridging the gap between humans and dragons, and often serving as mediators and ambassadors
|
||||
between their respective cultures. Their presence is often accompanied by a hint of smoke and flame, signifying
|
||||
their connection to the primal forces of nature.
|
||||
|
||||
</p>
|
||||
<div class="row align-items-center justify-content-center">
|
||||
<div class="col-12 col-md-6">
|
||||
<img src="{{ url_for('static', filename='images/races/draconian.jpg') }}" style="width: 300px;" alt="Race Image"/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes: + / - from 10</h4>
|
||||
<ul>
|
||||
<li>Strength (Str): +1</li>
|
||||
<li>Dexterity (Dex): +3</li>
|
||||
<li>Intelligence (Int): 0</li>
|
||||
<li>Wisdom (Wis): -2</li>
|
||||
<li>Luck (Luk): 5</li>
|
||||
<li>Charisma (Cha): +1</li>
|
||||
<li>Constitution (Con): -1</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
30
app/templates/ajax/race_dwarf.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<h3>Dwarf</h3>
|
||||
<p>
|
||||
The Dwarves are a sturdy and proud people, born from the depths of the earth to forge a legacy of resilience and
|
||||
determination. Their physical form is a testament to their craft and industry, with stout bodies, strong limbs,
|
||||
and eyes that shine like polished iron. As skilled artisans and master craftsmen, they possess an unyielding
|
||||
dedication to their work, honing their skills in the depths of mountains and caverns. Dwarves are a people of
|
||||
tradition and heritage, standing guard over ancient secrets, hidden treasures, and forgotten knowledge. Their
|
||||
connection to the earth is deep and abiding, granting them a profound understanding of the natural world and its
|
||||
rhythms.
|
||||
|
||||
</p>
|
||||
<div class="row align-items-center justify-content-center">
|
||||
<div class="col-12 col-md-6">
|
||||
<img src="{{ url_for('static', filename='images/races/dwarf.jpg') }}" style="width: 300px;" alt="Race Image"/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes: + / - from 10</h4>
|
||||
<ul>
|
||||
<li>Strength (Str): +5</li>
|
||||
<li>Dexterity (Dex): -2</li>
|
||||
<li>Intelligence (Int): 0</li>
|
||||
<li>Wisdom (Wis): -1</li>
|
||||
<li>Luck (Luk): +1</li>
|
||||
<li>Charisma (Cha): +1</li>
|
||||
<li>Constitution (Con): +3</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
29
app/templates/ajax/race_elf.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<h3>Elves</h3>
|
||||
<p>
|
||||
Elves are an ancient and enigmatic people, known for their striking physical beauty, exceptional magical
|
||||
abilities, and unparalleled connection with nature. They possess a deep understanding of the natural world,
|
||||
allowing them to communicate with animals, manipulate plants, and wield the elements with precision. Elves are
|
||||
highly attuned to their surroundings, making them formidable hunters, skilled archers, and gifted warriors.
|
||||
Despite their refined features, elves are not immune to the harsh realities of life, and they have developed a
|
||||
rich culture that reflects their struggles against the forces of darkness.
|
||||
</p>
|
||||
|
||||
<div class="row align-items-center justify-content-center">
|
||||
<div class="col-12 col-md-6">
|
||||
<img src="{{ url_for('static', filename='images/races/elf.jpg') }}" style="width: 300px;" alt="Race Image"/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes: + / - from 10</h4>
|
||||
<ul>
|
||||
<li>Strength (Str): -2</li>
|
||||
<li>Dexterity (Dex): +5</li>
|
||||
<li>Intelligence (Int): +3</li>
|
||||
<li>Wisdom (Wis): 0</li>
|
||||
<li>Luck (Luk): 1</li>
|
||||
<li>Charisma (Cha): 1</li>
|
||||
<li>Constitution (Con): -1</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
32
app/templates/ajax/race_hellion.html
Normal file
@@ -0,0 +1,32 @@
|
||||
<h3>Hellions</h3>
|
||||
<p>
|
||||
The Hellions are a malevolent and enigmatic people, born from the dark recesses of the Shadow Realm to bring
|
||||
terror and chaos into the world. Their physical form is a twisted mockery of humanity, with bodies that seem to
|
||||
shift and writhe like living shadows, eyes that burn with an otherworldly green fire, and skin that seems to
|
||||
absorb the light around them. As half-demon, they possess a unique blend of dark magic, cunning, and charisma,
|
||||
making them formidable manipulators, spies, and assassins. Hellions are drawn to the darker aspects of life,
|
||||
reveling in the fear and suffering of others, and often serving as agents of chaos and destruction for their
|
||||
masters. Their presence is accompanied by an aura of malevolent energy, casting a pall of dread over those around
|
||||
them.
|
||||
|
||||
</p>
|
||||
|
||||
<div class="row align-items-center justify-content-center">
|
||||
<div class="col-12 col-md-6">
|
||||
<img src="{{ url_for('static', filename='images/races/hellion.jpg') }}" style="width: 300px;" alt="Race Image"/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes: + / - from 10</h4>
|
||||
<ul>
|
||||
<li>Strength (Str): +3</li>
|
||||
<li>Dexterity (Dex): +1</li>
|
||||
<li>Intelligence (Int): +1</li>
|
||||
<li>Wisdom (Wis): 0</li>
|
||||
<li>Luck (Luk): -2</li>
|
||||
<li>Charisma (Cha): +5</li>
|
||||
<li>Constitution (Con): -1</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
31
app/templates/ajax/race_terran.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<h3>Terrans</h3>
|
||||
<p>
|
||||
The Terrans are a sturdy and resilient people, born from the rich soil of the land to thrive in the everyday
|
||||
world. Their physical form is unremarkable, yet serviceable, with bodies that adapt to their surroundings, eyes
|
||||
that see clearly through the mundane, and hearts that beat with a deep connection to the earth. As ordinary
|
||||
individuals, they possess an extraordinary capacity for empathy, compassion, and determination, making them
|
||||
formidable mediators, leaders, and guardians of the common good. Terrans are not remarkable in any one way, yet
|
||||
their collective strength lies in their ability to work together, support each other, and build a better world
|
||||
through incremental progress and everyday heroism.
|
||||
|
||||
</p>
|
||||
|
||||
<div class="row align-items-center justify-content-center">
|
||||
<div class="col-12 col-md-6">
|
||||
<img src="{{ url_for('static', filename='images/races/terran.jpg') }}" style="width: 300px;" alt="Race Image"/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes: + / - from 10</h4>
|
||||
<ul>
|
||||
<li>Strength (Str): +1</li>
|
||||
<li>Dexterity (Dex): +3</li>
|
||||
<li>Intelligence (Int): 0</li>
|
||||
<li>Wisdom (Wis): -2</li>
|
||||
<li>Luck (Luk): +5</li>
|
||||
<li>Charisma (Cha): +1</li>
|
||||
<li>Constitution (Con): -1</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
33
app/templates/ajax/race_vorgath.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<h3>Vorgath</h3>
|
||||
<p>
|
||||
The Vorgath are a twisted and malevolent people, born from the darkest recesses of existence to bring unrelenting
|
||||
destruction into the world. Their physical form is an affront to nature, with bodies that seem to be perpetually
|
||||
corrupted, eyes that blaze with an otherworldly intensity, and skin that appears to writhe like living darkness.
|
||||
As abominations of the cosmos, they possess a unique blend of dark energy, unnatural resilience, and malevolent
|
||||
willpower, making them formidable enemies, unyielding in their pursuit of chaos and despair. Vorgath are driven by
|
||||
an insatiable hunger for destruction, reveling in the suffering of others, and often serving as agents of darkness
|
||||
and terror for their masters. Their presence is accompanied by an aura of unrelenting malevolence, striking fear
|
||||
into the hearts of all who behold them.
|
||||
|
||||
</p>
|
||||
|
||||
<div class="row align-items-center justify-content-center">
|
||||
<div class="col-12 col-md-6">
|
||||
<img src="{{ url_for('static', filename='images/races/vorgath.jpg') }}" style="width: 300px;" alt="Race Image"/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6" id="attributes">
|
||||
<h4>Attributes: + / - from 10</h4>
|
||||
<ul>
|
||||
<li>Strength (Str): +5</li>
|
||||
<li>Dexterity (Dex): +3</li>
|
||||
<li>Intelligence (Int): 1</li>
|
||||
<li>Wisdom (Wis): 0</li>
|
||||
<li>Luck (Luk): -1</li>
|
||||
<li>Charisma (Cha): -2</li>
|
||||
<li>Constitution (Con): +3</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
136
app/templates/char/create_char.html
Normal file
@@ -0,0 +1,136 @@
|
||||
{% extends "bases/main_base.html" %}
|
||||
|
||||
{% block title %}Admin Panel — Dashboard{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container-fluid vh-100">
|
||||
<div class="row vh-100 align-items-center justify-content-center">
|
||||
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="card w-400 mw-full mx-auto" style="padding: 20px;">
|
||||
|
||||
<h5 class="card-title text-center">Create Character</h5>
|
||||
|
||||
<form action="" method="POST">
|
||||
<div class="form-group">
|
||||
<label for="text-input" class="required">Name</label>
|
||||
<input type="text" class="form-control mb-4" id="character_name" name="character_name" placeholder="Name your Character" required >
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="race_dropdown">Choose Race</label>
|
||||
<select class="form-control mb-4" id="race_dropdown" name="race_dropdown" required >
|
||||
<option value="" disabled selected>Select a Race...</option>
|
||||
<option value="avaline">Avaline - Divine, Strong, Intelligent</option>
|
||||
<option value="beastfolk">Beastfolk - Half Beast, Half Terran</option>
|
||||
<option value="draconian">Draconian - Half Dragon, Half Terran</option>
|
||||
<option value="dwarf">Dwarf - Stout, Strong, Short</option>
|
||||
<option value="elf">Elf - Tall, Slender, Agile</option>
|
||||
<option value="hellion">Hellion - Dark, Shadowy Humanoid</option>
|
||||
<option value="terran">Terran - Some call them Human.</option>
|
||||
<option value="vorgath">Vorgath - Monstrous Evil Humanoid</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="profession_dropdown">Choose Profession</label>
|
||||
<select class="form-control mb-4" id="profession_dropdown" name="profession_dropdown" required >
|
||||
<option value="" disabled selected>Select a Profession...</option>
|
||||
<option value="archanist">Archanist - Magic</option>
|
||||
<option value="assassin">Assassin - Physical</option>
|
||||
<option value="bloodborn">Bloodborn - Physical</option>
|
||||
<option value="cleric">Cleric - Magic</option>
|
||||
<option value="guardian">Guardian - Physical</option>
|
||||
<option value="hexist">Hexist - Magic</option>
|
||||
<option value="ranger">Ranger - Physical</option>
|
||||
<option value="warlock">Warlock - Magic</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="origin_story">Origin Story</label>
|
||||
<textarea class="form-control mb-4" id="origin_story" name="origin_story" rows="10" cols="40" required >{{data.starter_text}}</textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="text_holder" class="col-12 col-md-6">
|
||||
<p>
|
||||
To start playing, we need to help you create your unique character.
|
||||
</p>
|
||||
|
||||
<p>The following fields are required:</p>
|
||||
<ul>
|
||||
<li>Name: This is your character's identity in the game.</li>
|
||||
<li>Race: Choose from our selection of options.</li>
|
||||
<li>Profession: Select from our list of profession (commonly called classes).</li>
|
||||
<li>Origin Story: This section is optional, but can provide additional context for your character.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// Reference to the dropdown and image
|
||||
const racedropdown = document.getElementById('race_dropdown');
|
||||
const profdropdown = document.getElementById('profession_dropdown');
|
||||
const contentArea = document.getElementById('text_holder');
|
||||
|
||||
racedropdown.addEventListener('change', async function () {
|
||||
const value = this.value;
|
||||
|
||||
const url = `/ajax/races?race=${value}`;
|
||||
|
||||
try {
|
||||
// Fetch HTML fragment from the server
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const html = await response.text();
|
||||
|
||||
// Replace existing content
|
||||
contentArea.innerHTML = html;
|
||||
} catch (err) {
|
||||
console.error("Error loading content:", err);
|
||||
contentArea.innerHTML = `
|
||||
<div class="alert alert-danger">
|
||||
Failed to load content. Please try again.
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
|
||||
profdropdown.addEventListener('change', async function () {
|
||||
const value = this.value;
|
||||
|
||||
const url = `/ajax/prof?prof=${value}`;
|
||||
|
||||
try {
|
||||
// Fetch HTML fragment from the server
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const html = await response.text();
|
||||
|
||||
// Replace existing content
|
||||
contentArea.innerHTML = html;
|
||||
} catch (err) {
|
||||
console.error("Error loading content:", err);
|
||||
contentArea.innerHTML = `
|
||||
<div class="alert alert-danger">
|
||||
Failed to load content. Please try again.
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
198
docs/Battle Mechanics.MD
Normal file
@@ -0,0 +1,198 @@
|
||||
# ⚔️ Code of Conquest — Game Mechanics Guide
|
||||
|
||||
Welcome to the **Code of Conquest Battle System** — a turn-based, JRPG-inspired combat framework that blends strategy, morality, and fate.
|
||||
Every action, from sword swings to spellcasting, is influenced by your attributes, alignment, and a touch of luck.
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Core Attributes
|
||||
|
||||
Each hero or enemy has six base attributes that define their natural strengths and weaknesses.
|
||||
|
||||
| Attribute | Description |
|
||||
|------------|--------------|
|
||||
| **Strength (STR)** | Governs physical power, melee attacks, and endurance. |
|
||||
| **Dexterity (DEX)** | Controls accuracy, evasion, and proficiency with ranged weapons. |
|
||||
| **Intelligence (INT)** | Determines magic power and spell accuracy. |
|
||||
| **Wisdom (WIS)** | Governs magical defense, perception, and resistance to debuffs. |
|
||||
| **Luck (LCK)** | Influences critical hits, evasion, loot, and unexpected fortune. |
|
||||
| **Charisma (CHA)** | Affects leadership, morale, and social interactions. |
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Derived Combat Stats
|
||||
|
||||
These values are automatically calculated from your core attributes, level, and equipment.
|
||||
|
||||
| Stat | Formula | Description |
|
||||
|------|----------|-------------|
|
||||
| **Attack (ATK)** | `(STR × 2) + WeaponBonus` | Physical damage output. |
|
||||
| **Magic Power (MAG)** | `(INT × 2) + StaffBonus` | Magical damage output. |
|
||||
| **Defense (DEF)** | `(STR × 0.5) + ArmorBonus` | Reduces incoming physical damage. |
|
||||
| **Magic Defense (MDEF)** | `(WIS × 0.5) + GearBonus` | Reduces incoming magical damage. |
|
||||
| **Accuracy (ACC)** | `(DEX × 2) + (LCK × 0.25)` | Likelihood of hitting your target. |
|
||||
| **Evasion (EVA)** | `(DEX × 1.5) + (LCK × 0.25)` | Likelihood of dodging attacks. |
|
||||
| **Critical Chance (CRIT%)** | `(LCK × 0.5)%` | Chance to land a critical hit. |
|
||||
| **Resistance (RES)** | `(WIS × 2) + (LCK × 0.5)` | Chance to resist magical or status effects. |
|
||||
|
||||
---
|
||||
|
||||
## ⚔️ Combat Overview
|
||||
|
||||
Combat is **turn-based** and **formula-driven**.
|
||||
Every action follows a consistent, deterministic flow — modified by attributes, skills, and luck.
|
||||
|
||||
### Step 1 — Accuracy Check
|
||||
Before any attack lands, its success is determined by comparing **Accuracy** and **Evasion**.
|
||||
|
||||
- **Physical attacks**
|
||||
```
|
||||
|
||||
HitChance = ACC / (ACC + Target.EVA)
|
||||
|
||||
```
|
||||
- **Magical attacks**
|
||||
```
|
||||
|
||||
SpellHitChance = (INT + LCK) / ((INT + LCK) + (Target.WIS + Target.LCK))
|
||||
|
||||
```
|
||||
|
||||
If the resulting value ≥ a random roll (0–1), the attack hits.
|
||||
|
||||
---
|
||||
|
||||
### Step 2 — Damage Calculation
|
||||
|
||||
Once a hit lands, the system determines how much damage is dealt.
|
||||
|
||||
#### Physical Damage
|
||||
```
|
||||
|
||||
BaseDamage = (ATK - (Target.DEF × 0.5))
|
||||
Variance = random(0.9, 1.1)
|
||||
CritModifier = 1.5 if roll ≤ CRIT% else 1.0
|
||||
Damage = BaseDamage × Variance × CritModifier
|
||||
|
||||
```
|
||||
|
||||
#### Magical Damage
|
||||
```
|
||||
|
||||
BaseDamage = (MAG - (Target.MDEF × 0.5))
|
||||
Variance = random(0.9, 1.1)
|
||||
Damage = BaseDamage × Variance
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💫 Alignment Influence
|
||||
|
||||
Every character and creature has an **Alignment Score** from **–100 (Evil)** to **+100 (Good)**.
|
||||
The difference between two combatants’ alignments affects damage output.
|
||||
|
||||
```
|
||||
|
||||
AlignmentDifference = abs(Attacker.Alignment - Target.Alignment)
|
||||
AlignmentBonus = (AlignmentDifference / 10) / 100
|
||||
|
||||
```
|
||||
|
||||
**Example:**
|
||||
A Paladin (+80) attacks a Necromancer (–70).
|
||||
Difference = 150 → +15% damage.
|
||||
|
||||
```
|
||||
|
||||
Damage = Damage × (1 + AlignmentBonus)
|
||||
|
||||
```
|
||||
|
||||
The greater the moral divide, the stronger the opposing strike.
|
||||
|
||||
---
|
||||
|
||||
## 🍀 Luck in Action
|
||||
|
||||
**Luck (LCK)** weaves subtle influence through nearly every mechanic:
|
||||
|
||||
| Mechanic | Luck Effect |
|
||||
|-----------|--------------|
|
||||
| Hit Accuracy | Boosts ACC slightly. |
|
||||
| Critical Hits | Directly increases CRIT%. |
|
||||
| Damage Rolls | Pushes random variance toward higher values. |
|
||||
| Loot & Rewards | Increases rare drop chances. |
|
||||
| Status Resistance | Adds to RES for debuff avoidance. |
|
||||
| Escape & Initiative | Slightly improves odds in initiative rolls. |
|
||||
|
||||
Even a low-power adventurer with high luck can turn the tides of battle.
|
||||
|
||||
---
|
||||
|
||||
## 📈 Level Scaling
|
||||
|
||||
As characters grow in level, their core attributes increase according to **class-based growth curves**.
|
||||
|
||||
| Class | STR | DEX | INT | WIS | LCK | CHA |
|
||||
|--------|------|------|------|------|------|------|
|
||||
| Guardian | High | Low | Low | Medium | Low | Medium |
|
||||
| Ranger | Medium | High | Low | Low | Medium | Medium |
|
||||
| Arcanist | Low | Medium | High | Medium | Medium | Low |
|
||||
| Cleric | Low | Low | Medium | High | Medium | High |
|
||||
|
||||
Level growth, combined with equipment bonuses and skills, naturally scales all derived stats.
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Skill Modifiers
|
||||
|
||||
Every ability or spell uses the base formulas but applies **unique multipliers** or special effects.
|
||||
|
||||
| Skill | Type | Effects |
|
||||
|--------|------|----------|
|
||||
| **Power Slash** | Melee | Damage × 1.5, Accuracy × 0.9 |
|
||||
| **Piercing Shot** | Ranged | Ignores 25% of target DEF |
|
||||
| **Fireball** | Magic | Damage × 1.2, chance to Burn (INT + LCK based) |
|
||||
| **Bless** | Support | Boosts ally ATK × 1.1 for 3 turns |
|
||||
| **Hex** | Curse | Reduces enemy WIS × 0.8 for 2 turns |
|
||||
|
||||
Skills can be enhanced, learned, or combined as your characters progress.
|
||||
|
||||
---
|
||||
|
||||
## 🧮 Example Combat Resolution
|
||||
|
||||
**Scenario:**
|
||||
A Level 10 Guardian with high STR attacks an evil creature using *Power Slash.*
|
||||
|
||||
```
|
||||
|
||||
HitChance = ACC / (ACC + Target.EVA)
|
||||
If hit:
|
||||
Damage = ((ATK - (Target.DEF × 0.5)) × 1.5)
|
||||
Damage *= random(0.9, 1.1)
|
||||
Damage *= (1 + AlignmentBonus)
|
||||
Damage *= CritModifier
|
||||
|
||||
```
|
||||
|
||||
**Result:**
|
||||
The system reports a hit, critical strike, or miss — then narrates the event through the AI Dungeon Master.
|
||||
|
||||
---
|
||||
|
||||
## 🔮 Design Philosophy
|
||||
|
||||
The **Code of Conquest Battle System** emphasizes:
|
||||
- Predictable, understandable outcomes driven by player stats.
|
||||
- Minimal dice-roll chaos — randomness exists, but never dominates.
|
||||
- Moral alignment and luck as storytelling mechanics, not just numbers.
|
||||
- Clear extensibility for future features like elemental affinities, buffs, and combo attacks.
|
||||
|
||||
Every fight is both a **test of numbers** and a **reflection of who your character truly is**.
|
||||
|
||||
---
|
||||
|
||||
*Last updated:* October 2025
|
||||
*System version:* 1.0 — “Balance of Fate”
|
||||
206
docs/Battle-Dev-Notes.MD
Normal file
@@ -0,0 +1,206 @@
|
||||
## 🧰 Developer Notes — System Architecture & Implementation
|
||||
|
||||
This section explains how the **Code of Conquest Battle System** is structured behind the scenes for developers and modders.
|
||||
|
||||
---
|
||||
|
||||
### 🧮 1. Formula Layers
|
||||
|
||||
Combat calculations occur in **three distinct layers**, each responsible for a specific part of the logic:
|
||||
|
||||
| Layer | Purpose | Example |
|
||||
|--------|----------|----------|
|
||||
| **Base Layer** | Core attributes and level-based growth. | STR, DEX, INT, etc. |
|
||||
| **Derived Layer** | Recalculates combat-ready stats from base values and equipment. | ATK, DEF, ACC, etc. |
|
||||
| **Resolution Layer** | Executes battle outcomes using deterministic formulas. | Damage, Crit, AlignmentBonus, etc. |
|
||||
|
||||
Each layer feeds cleanly into the next.
|
||||
This modular approach ensures stats can be recalculated at any time without breaking the combat state.
|
||||
|
||||
---
|
||||
|
||||
### ⚙️ 2. Suggested Class Structure
|
||||
|
||||
The system can be built around **data-driven classes**:
|
||||
|
||||
```python
|
||||
class Character:
|
||||
name: str
|
||||
level: int
|
||||
alignment: int
|
||||
attributes: Attributes # STR, DEX, INT, etc.
|
||||
equipment: Equipment
|
||||
derived_stats: DerivedStats
|
||||
|
||||
class Attributes:
|
||||
str: int
|
||||
dex: int
|
||||
int: int
|
||||
wis: int
|
||||
lck: int
|
||||
cha: int
|
||||
|
||||
class DerivedStats:
|
||||
atk: int
|
||||
mag: int
|
||||
def_: int
|
||||
mdef: int
|
||||
acc: float
|
||||
eva: float
|
||||
crit: float
|
||||
res: float
|
||||
````
|
||||
|
||||
All formulas (like ATK = STR × 2 + WeaponBonus) live inside the `DerivedStats.recalculate()` method.
|
||||
Skills and combat outcomes can then safely reference `character.derived_stats`.
|
||||
|
||||
---
|
||||
|
||||
### 📜 3. Skill Definitions
|
||||
|
||||
Skills should be stored as **external data files** (YAML or JSON) for easy modification and expansion.
|
||||
|
||||
Example:
|
||||
|
||||
```yaml
|
||||
# skills/power_slash.yaml
|
||||
id: power_slash
|
||||
name: Power Slash
|
||||
type: melee
|
||||
description: A strong physical strike with reduced accuracy.
|
||||
modifiers:
|
||||
damage_multiplier: 1.5
|
||||
accuracy_multiplier: 0.9
|
||||
crit_bonus: 0.0
|
||||
conditions:
|
||||
requires_weapon_type: sword
|
||||
```
|
||||
|
||||
When combat is resolved, the system loads the skill data dynamically:
|
||||
|
||||
```python
|
||||
damage *= skill.damage_multiplier
|
||||
accuracy *= skill.accuracy_multiplier
|
||||
```
|
||||
|
||||
This keeps gameplay logic **data-driven** and **easily expandable**.
|
||||
|
||||
---
|
||||
|
||||
### 🧠 4. Combat Resolution Flow
|
||||
|
||||
A full attack resolves in five modular steps:
|
||||
|
||||
1. **Precheck Phase** — confirm turn order, skill requirements, status effects.
|
||||
2. **Accuracy Phase** — roll for hit using `ACC` vs target `EVA`.
|
||||
3. **Damage Phase** — calculate raw damage using formulas.
|
||||
4. **Modifier Phase** — apply critical hits, alignment bonus, buffs/debuffs.
|
||||
5. **Finalize Phase** — subtract HP, trigger effects, narrate outcome.
|
||||
|
||||
Each step can be represented by its own method or even subclass (useful for custom battle systems later).
|
||||
|
||||
---
|
||||
|
||||
### ⚖️ 5. Alignment & Morality Hooks
|
||||
|
||||
Alignment is stored as a simple integer between –100 and +100.
|
||||
|
||||
When calculating damage:
|
||||
|
||||
```python
|
||||
alignment_difference = abs(attacker.alignment - target.alignment)
|
||||
alignment_bonus = (alignment_difference / 10) / 100
|
||||
damage *= 1 + alignment_bonus
|
||||
```
|
||||
|
||||
Later expansions might include:
|
||||
|
||||
* **Morality events** that shift alignment based on choices.
|
||||
* **Faction modifiers** (e.g., “Paladin vs Undead” adds extra scaling).
|
||||
* **Spells** that interact directly with alignment (“Smite Evil,” “Corrupt Heart”).
|
||||
|
||||
---
|
||||
|
||||
### 🍀 6. Luck Integration Strategy
|
||||
|
||||
Luck is treated as a *global adjustment factor* that nudges probability outcomes and variance rolls upward slightly.
|
||||
Instead of directly changing RNG, it *biases* existing rolls.
|
||||
|
||||
```python
|
||||
def apply_luck_bias(base_value: float, luck: int) -> float:
|
||||
bias = 1 + (luck * 0.002) # +0.2% per point of Luck
|
||||
return base_value * bias
|
||||
```
|
||||
|
||||
This keeps outcomes unpredictable yet fair — luckier characters simply lean toward better odds over time.
|
||||
|
||||
---
|
||||
|
||||
### 📈 7. Level Scaling Curves
|
||||
|
||||
Scaling should be **class-specific** and controlled via external tables:
|
||||
|
||||
```yaml
|
||||
# growth_curves/guardian.yaml
|
||||
str_growth: high
|
||||
dex_growth: low
|
||||
int_growth: low
|
||||
wis_growth: medium
|
||||
lck_growth: low
|
||||
cha_growth: medium
|
||||
```
|
||||
|
||||
When leveling up:
|
||||
|
||||
```python
|
||||
new_value = old_value + growth_table.get(attribute).calculate(level)
|
||||
```
|
||||
|
||||
You can easily balance or tweak these tables without touching core logic.
|
||||
|
||||
---
|
||||
|
||||
### 🧩 8. Extensibility Hooks
|
||||
|
||||
Future systems can layer naturally on top of this design:
|
||||
|
||||
* **Elemental Affinities:** Fire, Ice, Lightning resistances.
|
||||
* **Buff & Debuff States:** stored as timed modifiers.
|
||||
* **Equipment Enchantments:** simple additive multipliers.
|
||||
* **Status Effects:** stored as objects (e.g., `Status("Burning", duration=3, dot=5)`).
|
||||
|
||||
Each additional system only needs to plug into the Resolution Phase as a pre/post modifier.
|
||||
|
||||
---
|
||||
|
||||
### 🧱 9. Design Philosophy for Developers
|
||||
|
||||
1. **Transparency over randomness** — deterministic math first, variance second.
|
||||
2. **Data before code** — YAML or JSON defines skills, growth, and buffs.
|
||||
3. **Layered structure** — base → derived → resolution → output.
|
||||
4. **Alignment matters** — every moral choice has mechanical consequence.
|
||||
5. **Luck is universal** — a soft bias across all rolls, never a chaos driver.
|
||||
6. **Readable outputs** — easy for AI or human narrators to describe battle results.
|
||||
|
||||
---
|
||||
|
||||
### 🧾 10. Future Expansion Notes
|
||||
|
||||
* **Elemental Damage Types** (`fire`, `ice`, `holy`, `dark`).
|
||||
* **Combo Systems** (chain attacks between party members).
|
||||
* **Morale System** (CHA and WIS affecting group performance).
|
||||
* **Dynamic Battle Narration** (AI-driven storytelling from combat logs).
|
||||
* **Procedural Skill Fusion** (merge abilities to create new ones).
|
||||
|
||||
---
|
||||
|
||||
> **Implementation Tip:**
|
||||
> Keep every formula centralized in a single `Formulas` or `BattleMath` class.
|
||||
> This ensures balancing changes never require refactoring gameplay code.
|
||||
|
||||
---
|
||||
|
||||
*Developer Reference Version:* 1.0
|
||||
*Author:* Code of Conquest Systems Design Team
|
||||
*Document Last Updated:* October 2025
|
||||
|
||||
292
docs/hero_classes.md
Normal file
@@ -0,0 +1,292 @@
|
||||
Perfect — these class archetypes are strong foundations.
|
||||
Below is a **fully fleshed-out design document** that expands each of your 10 hero classes with:
|
||||
|
||||
1. A **generalized backstory** (broad enough for players to personalize).
|
||||
2. Two **skill trees** per class (with flavor and functional intent).
|
||||
3. For **spellcasting classes**, a **spell tree** by level (tiers of progression).
|
||||
|
||||
All designed to feel consistent across your Strength, Dexterity, Intelligence, Wisdom, and Charisma archetypes.
|
||||
|
||||
---
|
||||
|
||||
# 🛡️ Strength-Based Characters
|
||||
|
||||
## **GUARDIAN**
|
||||
|
||||
**Alignment:** Good
|
||||
**Description:**
|
||||
Guardians are stalwart defenders of light and justice. Whether serving as city protectors, temple champions, or wandering peacekeepers, their code of honor binds them to defend the weak. Each Guardian’s story is their own—a farmer turned hero, a knight fallen from grace, or a soldier seeking redemption.
|
||||
|
||||
**Skill Trees:**
|
||||
|
||||
* **Bulwark Tree (Defense Focus)**
|
||||
|
||||
* *Iron Will* – Reduces damage taken.
|
||||
* *Deflect* – Chance to block ranged or magic attacks.
|
||||
* *Last Stand* – Gain armor and regen HP when below 25%.
|
||||
* *Fortress* – Party-wide defense aura.
|
||||
* **Valor Tree (Offense Focus)**
|
||||
|
||||
* *Precision Strike* – Bonus damage with melee weapons.
|
||||
* *Riposte* – Counter-attack when blocking.
|
||||
* *Holy Challenge* – Taunts enemies, increasing their aggression toward you.
|
||||
* *Guardian’s Oath* – Boosts allies’ morale (buff to nearby allies).
|
||||
|
||||
---
|
||||
|
||||
## **BLOODBORN**
|
||||
|
||||
**Alignment:** Evil
|
||||
**Description:**
|
||||
Bloodborn are forged in the crucible of agony. They believe power is born from suffering, and that mercy weakens the spirit. Some were enslaved warriors, others mad experiment victims—now unleashed vengeance given form.
|
||||
|
||||
**Skill Trees:**
|
||||
|
||||
* **Crimson Path (Offense Focus)**
|
||||
|
||||
* *Rend Flesh* – Bleed enemies for lasting damage.
|
||||
* *Frenzy* – Gain attack speed as HP lowers.
|
||||
* *Bloodlust* – Restore HP per kill.
|
||||
* *Executioner’s Rite* – Huge critical hit chance when target <30% HP.
|
||||
* **Tortured Flesh (Endurance Focus)**
|
||||
|
||||
* *Pain Tolerance* – Reduces incoming damage when bleeding.
|
||||
* *Sanguine Shield* – Converts damage taken into temporary HP.
|
||||
* *Undying Rage* – Survive fatal blow with 1 HP once per combat.
|
||||
* *Blood Feast* – Heal massively after killing an enemy.
|
||||
|
||||
---
|
||||
|
||||
# 🏹 Dexterity-Based Characters
|
||||
|
||||
## **RANGER**
|
||||
|
||||
**Alignment:** Good or Neutral
|
||||
**Description:**
|
||||
Rangers are masters of the wilderness—trackers, scouts, and hunters. They thrive in solitude, guided by the rhythm of nature. Some defend forests from civilization’s encroachment; others serve as elite archers for noble causes.
|
||||
|
||||
**Skill Trees:**
|
||||
|
||||
* **Beastmaster Tree (Companion Focus)**
|
||||
|
||||
* *Call of the Wild* – Summon a loyal beast.
|
||||
* *Pack Instinct* – Boosts pet attack and defense.
|
||||
* *Shared Survival* – Heal your pet when you heal yourself.
|
||||
* *Alpha Bond* – Merge senses with your pet for heightened awareness.
|
||||
* **Sharpshooter Tree (Marksmanship Focus)**
|
||||
|
||||
* *Quickdraw* – Increased attack speed with bows.
|
||||
* *Piercing Arrow* – Arrows ignore partial armor.
|
||||
* *Rain of Arrows* – Multi-shot area attack.
|
||||
* *Eagle Eye* – Doubles critical hit chance when undetected.
|
||||
|
||||
---
|
||||
|
||||
## **ASSASSIN**
|
||||
|
||||
**Alignment:** Evil
|
||||
**Description:**
|
||||
Born from the underbelly of cities or raised by shadowy guilds, Assassins live for precision and silence. Every kill is an art form, and every target’s final breath is their masterpiece.
|
||||
|
||||
**Skill Trees:**
|
||||
|
||||
* **Shadow Arts Tree (Stealth Focus)**
|
||||
|
||||
* *Fade Step* – Become briefly invisible.
|
||||
* *Backstab* – Massive critical from stealth.
|
||||
* *Smoke Veil* – Escape combat, dropping enemy aggro.
|
||||
* *Phantom Edge* – Shadow clone attacks alongside you.
|
||||
* **Venomcraft Tree (Poison Focus)**
|
||||
|
||||
* *Toxin Coating* – Coat blades with poison.
|
||||
* *Virulent Strike* – Poisons spread between enemies.
|
||||
* *Death’s Kiss* – Bonus damage to poisoned foes.
|
||||
* *Noxious Cloud* – Area poison that slows and weakens.
|
||||
|
||||
---
|
||||
|
||||
# 📚 Intelligence-Based Characters
|
||||
|
||||
## **ARCANIST**
|
||||
|
||||
**Alignment:** Good or Neutral
|
||||
**Description:**
|
||||
Arcanists are scholars of the arcane, shaping elemental forces through study and sheer intellect. Each Arcanist’s past is different—some taught in grand academies, others self-taught hermits obsessed with understanding creation.
|
||||
|
||||
**Skill Trees:**
|
||||
|
||||
* **Elementalist Tree (Damage Focus)**
|
||||
|
||||
* *Fireball* – AoE fire attack.
|
||||
* *Frost Lance* – Slows targets.
|
||||
* *Thunderstrike* – Chance to stun.
|
||||
* *Elemental Mastery* – Boosts all elemental damage.
|
||||
* **Chronomancer Tree (Utility Focus)**
|
||||
|
||||
* *Haste* – Increase speed.
|
||||
* *Time Warp* – Rewind health and position briefly.
|
||||
* *Mana Surge* – Restore MP faster.
|
||||
* *Temporal Collapse* – Massive AoE time distortion (endgame).
|
||||
|
||||
**Spell Tree by Level:**
|
||||
|
||||
* **Novice (Lv 1–5)** – Spark, Frost Bite, Ember Bolt
|
||||
* **Adept (Lv 6–10)** – Fireball, Ice Shard, Lightning Arc
|
||||
* **Expert (Lv 11–15)** – Chain Lightning, Cone of Cold, Meteor Fall
|
||||
* **Master (Lv 16+)** – Elemental Storm, Time Stop, Arcane Nova
|
||||
|
||||
---
|
||||
|
||||
## **HEXIST**
|
||||
|
||||
**Alignment:** Evil
|
||||
**Description:**
|
||||
Hexists wield forbidden knowledge—grimoires of curses and decay. They manipulate life energy to cripple foes and empower themselves, believing pain and entropy are the universe’s true constants.
|
||||
|
||||
**Skill Trees:**
|
||||
|
||||
* **Malefic Arts Tree (Curse Focus)**
|
||||
|
||||
* *Wither* – Reduces target’s defense.
|
||||
* *Hex of Misery* – Deals damage over time and reduces healing.
|
||||
* *Soul Rot* – Spreads curses between enemies.
|
||||
* *Dark Empowerment* – Boosts damage per active curse.
|
||||
* **Necrotic Path Tree (Summoning Focus)**
|
||||
|
||||
* *Raise Dead* – Summon weak undead.
|
||||
* *Soul Leech* – Regain HP when minions attack.
|
||||
* *Grave Tide* – Summon undead horde temporarily.
|
||||
* *Lich Form* – Become undead temporarily for massive boosts.
|
||||
|
||||
**Spell Tree by Level:**
|
||||
|
||||
* **Novice (Lv 1–5)** – Rot Touch, Curse of Weakness
|
||||
* **Adept (Lv 6–10)** – Wither, Soul Leech
|
||||
* **Expert (Lv 11–15)** – Bone Spear, Plague Cloud
|
||||
* **Master (Lv 16+)** – Death Coil, Summon Lich, Unholy Blight
|
||||
|
||||
---
|
||||
|
||||
# ✨ Wisdom-Based Characters
|
||||
|
||||
## **CLERIC**
|
||||
|
||||
**Alignment:** Good
|
||||
**Description:**
|
||||
Clerics channel divine power through prayer and unwavering faith. Whether from temples, shrines, or hidden monasteries, their power comes from devotion to benevolent deities and their eternal struggle against darkness.
|
||||
|
||||
**Skill Trees:**
|
||||
|
||||
* **Sanctity Tree (Healing & Support)**
|
||||
|
||||
* *Healing Light* – Restore HP to ally.
|
||||
* *Cleanse* – Remove curses/debuffs.
|
||||
* *Guardian Angel* – Revive ally with portion of HP.
|
||||
* *Divine Radiance* – Heal over time aura.
|
||||
* **Retribution Tree (Battle Cleric)**
|
||||
|
||||
* *Smite* – Holy damage attack.
|
||||
* *Holy Ward* – Boost defense for allies.
|
||||
* *Blessed Weapon* – Adds radiant damage to melee attacks.
|
||||
* *Judgment* – Massive single-target divine strike.
|
||||
|
||||
**Spell Tree by Level:**
|
||||
|
||||
* **Novice (Lv 1–5)** – Light Heal, Smite, Bless
|
||||
* **Adept (Lv 6–10)** – Greater Heal, Holy Fire, Purify
|
||||
* **Expert (Lv 11–15)** – Sanctuary, Revive, Divine Shield
|
||||
* **Master (Lv 16+)** – Resurrection, Wrath of the Heavens
|
||||
|
||||
---
|
||||
|
||||
## **WARLOCK**
|
||||
|
||||
**Alignment:** Evil
|
||||
**Description:**
|
||||
Warlocks forge pacts with dark entities to channel forbidden power. Each contract comes with a price—often their soul or sanity—but grants terrifying might that rivals even the gods.
|
||||
|
||||
**Skill Trees:**
|
||||
|
||||
* **Pact Magic Tree (Damage Focus)**
|
||||
|
||||
* *Eldritch Blast* – Signature ranged spell.
|
||||
* *Corrupting Ray* – Reduces target’s resistances.
|
||||
* *Soul Drain* – Steal HP and MP.
|
||||
* *Abyssal Eruption* – Large AoE shadow explosion.
|
||||
* **Infernal Pact Tree (Summoning Focus)**
|
||||
|
||||
* *Summon Imp* – Small demon ally.
|
||||
* *Hellfire Aura* – Damages nearby enemies.
|
||||
* *Dark Bargain* – Sacrifice HP for mana.
|
||||
* *Infernal Legion* – Summon multiple demons.
|
||||
|
||||
**Spell Tree by Level:**
|
||||
|
||||
* **Novice (Lv 1–5)** – Dark Bolt, Drain Life
|
||||
* **Adept (Lv 6–10)** – Shadow Grasp, Corrupting Ray
|
||||
* **Expert (Lv 11–15)** – Infernal Gate, Soul Drain
|
||||
* **Master (Lv 16+)** – Abyssal Rift, Summon Demon Lord
|
||||
|
||||
---
|
||||
|
||||
# 💋 Charisma-Based Characters
|
||||
|
||||
## **LUSTWRAITH**
|
||||
|
||||
**Alignment:** Evil or Chaotic Neutral
|
||||
**Description:**
|
||||
Lustwraiths wield allure as their weapon. They charm, dominate, and collect humanoids as companions or slaves. Legends say their very touch can bind minds. Their origin is shrouded in desire and corruption—some were succubi, others mortals consumed by vanity and power.
|
||||
|
||||
**Skill Trees:**
|
||||
|
||||
* **Seduction Tree (Control Focus)**
|
||||
|
||||
* *Charm* – Temporarily turn enemies into allies.
|
||||
* *Enthrall* – Extend charm duration.
|
||||
* *Obsession* – Charmed allies deal more damage.
|
||||
* *Dominion* – Permanently enslave a weak-willed target.
|
||||
* **Soul Feast Tree (Empowerment Focus)**
|
||||
|
||||
* *Essence Drain* – Gain HP/MP from dominated followers.
|
||||
* *Mirror Desire* – Redirect damage to charmed ally.
|
||||
* *Ecstatic Pulse* – AoE charm with reduced duration.
|
||||
* *Heartbreaker* – Sacrifice a follower to regain full HP/MP.
|
||||
|
||||
---
|
||||
|
||||
## **TAMER**
|
||||
|
||||
**Alignment:** Neutral or Good
|
||||
**Description:**
|
||||
Tamers form bonds with beasts and magical creatures, commanding their loyalty through compassion, strength, or song. They serve as guardians of the wilds and mediators between man and monster.
|
||||
|
||||
**Skill Trees:**
|
||||
|
||||
* **Bondmaster Tree (Companion Focus)**
|
||||
|
||||
* *Beast Call* – Summon a captured creature.
|
||||
* *Empathic Link* – Share damage between you and your beast.
|
||||
* *Group Command* – Control multiple beasts.
|
||||
* *Soulbond* – Permanent synergy buff to your primary beast.
|
||||
* **Warden Tree (Nature Control)**
|
||||
|
||||
* *Thorn Lash* – Vine-based attack.
|
||||
* *Wild Regeneration* – Heal both you and nearby beasts.
|
||||
* *Territorial Howl* – Buffs allies, intimidates enemies.
|
||||
* *Verdant Storm* – AoE nature attack that roots foes.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Summary Table
|
||||
|
||||
| Attribute | Good Class | Evil Class | Primary Role | Spellcaster |
|
||||
| ------------ | ---------- | ---------- | --------------------------- | ----------------- |
|
||||
| Strength | Guardian | Bloodborn | Tank / Melee DPS | No |
|
||||
| Dexterity | Ranger | Assassin | Ranged / Stealth DPS | No |
|
||||
| Intelligence | Arcanist | Hexist | Elemental Mage / Curse Mage | Yes |
|
||||
| Wisdom | Cleric | Warlock | Healer / Dark Caster | Yes |
|
||||
| Charisma | Tamer | Lustwraith | Beast / Human Controller | No (Hybrid Magic) |
|
||||
|
||||
---
|
||||
|
||||
Would you like me to format this as a **YAML or JSON schema** next, so your game can load class metadata dynamically (e.g., backstory, skill_trees, spell_trees, alignment tags, etc.)? That’s ideal if you plan to store these in `/data/classes/` for procedural generation and easy balancing.
|
||||