adding missing files

This commit is contained in:
2025-11-03 21:43:13 -06:00
parent efdf3570c5
commit 0443d1553f
36 changed files with 1765 additions and 0 deletions

16
app/blueprints/ajax.py Normal file
View 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
View 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
View 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
View 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
View 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")

View 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."
]
}

View 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
View 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 (dont 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 {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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
View 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 (01), 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
View 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
View 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 Guardians 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.
* *Guardians 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.
* *Executioners 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 civilizations 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 targets 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.
* *Deaths 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 Arcanists 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 15)** Spark, Frost Bite, Ember Bolt
* **Adept (Lv 610)** Fireball, Ice Shard, Lightning Arc
* **Expert (Lv 1115)** 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 universes true constants.
**Skill Trees:**
* **Malefic Arts Tree (Curse Focus)**
* *Wither* Reduces targets 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 15)** Rot Touch, Curse of Weakness
* **Adept (Lv 610)** Wither, Soul Leech
* **Expert (Lv 1115)** 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 15)** Light Heal, Smite, Bless
* **Adept (Lv 610)** Greater Heal, Holy Fire, Purify
* **Expert (Lv 1115)** 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 targets 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 15)** Dark Bolt, Drain Life
* **Adept (Lv 610)** Shadow Grasp, Corrupting Ray
* **Expert (Lv 1115)** 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.)? Thats ideal if you plan to store these in `/data/classes/` for procedural generation and easy balancing.