Compare commits
16 Commits
b78c979647
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| cbc045acfe | |||
| a39b63a38b | |||
| 3c4daf35fd | |||
| 4948681b9c | |||
| 94f33d78b3 | |||
| 05bf907283 | |||
| 1c9bfcf80f | |||
| 26488f581f | |||
| 4280825bf9 | |||
| 2087dd8d20 | |||
|
|
a06b58c1da | ||
|
|
ae780e1bbc | ||
|
|
c5dbd0606a | ||
|
|
4f474dbcf3 | ||
|
|
69a1d87169 | ||
|
|
359702c6ca |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -48,4 +48,4 @@ prompt
|
|||||||
|
|
||||||
server.log
|
server.log
|
||||||
# Skills directory
|
# Skills directory
|
||||||
/skills/
|
/.zscripts/
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# 将 stderr 重定向到 stdout,避免 execute_command 因为 stderr 输出而报错
|
|
||||||
exec 2>&1
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# 获取脚本所在目录(.zscripts 目录,即 workspace-agent/.zscripts)
|
|
||||||
# 使用 $0 获取脚本路径(兼容 sh 和 bash)
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
|
|
||||||
# Next.js 项目路径
|
|
||||||
NEXTJS_PROJECT_DIR="/home/z/my-project"
|
|
||||||
|
|
||||||
# 检查 Next.js 项目目录是否存在
|
|
||||||
if [ ! -d "$NEXTJS_PROJECT_DIR" ]; then
|
|
||||||
echo "❌ 错误: Next.js 项目目录不存在: $NEXTJS_PROJECT_DIR"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "🚀 开始构建 Next.js 应用和 mini-services..."
|
|
||||||
echo "📁 Next.js 项目路径: $NEXTJS_PROJECT_DIR"
|
|
||||||
|
|
||||||
# 切换到 Next.js 项目目录
|
|
||||||
cd "$NEXTJS_PROJECT_DIR" || exit 1
|
|
||||||
|
|
||||||
# 设置环境变量
|
|
||||||
export NEXT_TELEMETRY_DISABLED=1
|
|
||||||
|
|
||||||
BUILD_DIR="/tmp/build_fullstack_$BUILD_ID"
|
|
||||||
echo "📁 清理并创建构建目录: $BUILD_DIR"
|
|
||||||
mkdir -p "$BUILD_DIR"
|
|
||||||
|
|
||||||
# 安装依赖
|
|
||||||
echo "📦 安装依赖..."
|
|
||||||
bun install
|
|
||||||
|
|
||||||
# 构建 Next.js 应用
|
|
||||||
echo "🔨 构建 Next.js 应用..."
|
|
||||||
bun run build
|
|
||||||
|
|
||||||
# 构建 mini-services
|
|
||||||
# 检查 Next.js 项目目录下是否有 mini-services 目录
|
|
||||||
if [ -d "$NEXTJS_PROJECT_DIR/mini-services" ]; then
|
|
||||||
echo "🔨 构建 mini-services..."
|
|
||||||
# 使用 workspace-agent 目录下的 mini-services 脚本
|
|
||||||
sh "$SCRIPT_DIR/mini-services-install.sh"
|
|
||||||
sh "$SCRIPT_DIR/mini-services-build.sh"
|
|
||||||
|
|
||||||
# 复制 mini-services-start.sh 到 mini-services-dist 目录
|
|
||||||
echo " - 复制 mini-services-start.sh 到 $BUILD_DIR"
|
|
||||||
cp "$SCRIPT_DIR/mini-services-start.sh" "$BUILD_DIR/mini-services-start.sh"
|
|
||||||
chmod +x "$BUILD_DIR/mini-services-start.sh"
|
|
||||||
else
|
|
||||||
echo "ℹ️ mini-services 目录不存在,跳过"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 将所有构建产物复制到临时构建目录
|
|
||||||
echo "📦 收集构建产物到 $BUILD_DIR..."
|
|
||||||
|
|
||||||
# 复制 Next.js standalone 构建输出
|
|
||||||
if [ -d ".next/standalone" ]; then
|
|
||||||
echo " - 复制 .next/standalone"
|
|
||||||
cp -r .next/standalone "$BUILD_DIR/next-service-dist/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 复制 Next.js 静态文件
|
|
||||||
if [ -d ".next/static" ]; then
|
|
||||||
echo " - 复制 .next/static"
|
|
||||||
mkdir -p "$BUILD_DIR/next-service-dist/.next"
|
|
||||||
cp -r .next/static "$BUILD_DIR/next-service-dist/.next/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 复制 public 目录
|
|
||||||
if [ -d "public" ]; then
|
|
||||||
echo " - 复制 public"
|
|
||||||
cp -r public "$BUILD_DIR/next-service-dist/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 最后再迁移数据库到 BUILD_DIR/db
|
|
||||||
if [ "$(ls -A ./db 2>/dev/null)" ]; then
|
|
||||||
echo "🗄️ 检测到数据库文件,运行数据库迁移..."
|
|
||||||
DATABASE_URL=file:$BUILD_DIR/db/custom.db bun run db:push
|
|
||||||
echo "✅ 数据库迁移完成"
|
|
||||||
ls -lah $BUILD_DIR/db
|
|
||||||
else
|
|
||||||
echo "ℹ️ db 目录为空,跳过数据库迁移"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 复制 Caddyfile(如果存在)
|
|
||||||
if [ -f "Caddyfile" ]; then
|
|
||||||
echo " - 复制 Caddyfile"
|
|
||||||
cp Caddyfile "$BUILD_DIR/"
|
|
||||||
else
|
|
||||||
echo "ℹ️ Caddyfile 不存在,跳过"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 复制 start.sh 脚本
|
|
||||||
echo " - 复制 start.sh 到 $BUILD_DIR"
|
|
||||||
cp "$SCRIPT_DIR/start.sh" "$BUILD_DIR/start.sh"
|
|
||||||
chmod +x "$BUILD_DIR/start.sh"
|
|
||||||
|
|
||||||
# 打包到 $BUILD_DIR.tar.gz
|
|
||||||
PACKAGE_FILE="${BUILD_DIR}.tar.gz"
|
|
||||||
echo ""
|
|
||||||
echo "📦 打包构建产物到 $PACKAGE_FILE..."
|
|
||||||
cd "$BUILD_DIR" || exit 1
|
|
||||||
tar -czf "$PACKAGE_FILE" .
|
|
||||||
cd - > /dev/null || exit 1
|
|
||||||
|
|
||||||
# # 清理临时目录
|
|
||||||
# rm -rf "$BUILD_DIR"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "✅ 构建完成!所有产物已打包到 $PACKAGE_FILE"
|
|
||||||
echo "📊 打包文件大小:"
|
|
||||||
ls -lh "$PACKAGE_FILE"
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
|||||||
563
|
|
||||||
154
.zscripts/dev.sh
154
.zscripts/dev.sh
@@ -1,154 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# 获取脚本所在目录(.zscripts)
|
|
||||||
# 使用 $0 获取脚本路径(与 build.sh 保持一致)
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
||||||
|
|
||||||
log_step_start() {
|
|
||||||
local step_name="$1"
|
|
||||||
echo "=========================================="
|
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting: $step_name"
|
|
||||||
echo "=========================================="
|
|
||||||
export STEP_START_TIME
|
|
||||||
STEP_START_TIME=$(date +%s)
|
|
||||||
}
|
|
||||||
|
|
||||||
log_step_end() {
|
|
||||||
local step_name="${1:-Unknown step}"
|
|
||||||
local end_time
|
|
||||||
end_time=$(date +%s)
|
|
||||||
local duration=$((end_time - STEP_START_TIME))
|
|
||||||
echo "=========================================="
|
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Completed: $step_name"
|
|
||||||
echo "[LOG] Step: $step_name | Duration: ${duration}s"
|
|
||||||
echo "=========================================="
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
start_mini_services() {
|
|
||||||
local mini_services_dir="$PROJECT_DIR/mini-services"
|
|
||||||
local started_count=0
|
|
||||||
|
|
||||||
log_step_start "Starting mini-services"
|
|
||||||
if [ ! -d "$mini_services_dir" ]; then
|
|
||||||
echo "Mini-services directory not found, skipping..."
|
|
||||||
log_step_end "Starting mini-services"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Found mini-services directory, scanning for sub-services..."
|
|
||||||
|
|
||||||
for service_dir in "$mini_services_dir"/*; do
|
|
||||||
if [ ! -d "$service_dir" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
local service_name
|
|
||||||
service_name=$(basename "$service_dir")
|
|
||||||
echo "Checking service: $service_name"
|
|
||||||
|
|
||||||
if [ ! -f "$service_dir/package.json" ]; then
|
|
||||||
echo "[$service_name] No package.json found, skipping..."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! grep -q '"dev"' "$service_dir/package.json"; then
|
|
||||||
echo "[$service_name] No dev script found, skipping..."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Starting $service_name in background..."
|
|
||||||
(
|
|
||||||
cd "$service_dir"
|
|
||||||
echo "[$service_name] Installing dependencies..."
|
|
||||||
bun install
|
|
||||||
echo "[$service_name] Running bun run dev..."
|
|
||||||
exec bun run dev
|
|
||||||
) >"$PROJECT_DIR/.zscripts/mini-service-${service_name}.log" 2>&1 &
|
|
||||||
|
|
||||||
local service_pid=$!
|
|
||||||
echo "[$service_name] Started in background (PID: $service_pid)"
|
|
||||||
echo "[$service_name] Log: $PROJECT_DIR/.zscripts/mini-service-${service_name}.log"
|
|
||||||
disown "$service_pid" 2>/dev/null || true
|
|
||||||
started_count=$((started_count + 1))
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Mini-services startup completed. Started $started_count service(s)."
|
|
||||||
log_step_end "Starting mini-services"
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_service() {
|
|
||||||
local host="$1"
|
|
||||||
local port="$2"
|
|
||||||
local service_name="$3"
|
|
||||||
local max_attempts="${4:-60}"
|
|
||||||
local attempt=1
|
|
||||||
|
|
||||||
echo "Waiting for $service_name to be ready on $host:$port..."
|
|
||||||
|
|
||||||
while [ "$attempt" -le "$max_attempts" ]; do
|
|
||||||
if curl -s --connect-timeout 2 --max-time 5 "http://$host:$port" >/dev/null 2>&1; then
|
|
||||||
echo "$service_name is ready!"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Attempt $attempt/$max_attempts: $service_name not ready yet, waiting..."
|
|
||||||
sleep 1
|
|
||||||
attempt=$((attempt + 1))
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "ERROR: $service_name failed to start within $max_attempts seconds"
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
if [ -n "${DEV_PID:-}" ] && kill -0 "$DEV_PID" >/dev/null 2>&1; then
|
|
||||||
echo "Stopping Next.js dev server (PID: $DEV_PID)..."
|
|
||||||
kill "$DEV_PID" >/dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
trap cleanup EXIT INT TERM
|
|
||||||
|
|
||||||
cd "$PROJECT_DIR"
|
|
||||||
|
|
||||||
if ! command -v bun >/dev/null 2>&1; then
|
|
||||||
echo "ERROR: bun is not installed or not in PATH"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_step_start "bun install"
|
|
||||||
echo "[BUN] Installing dependencies..."
|
|
||||||
bun install
|
|
||||||
log_step_end "bun install"
|
|
||||||
|
|
||||||
log_step_start "bun run db:push"
|
|
||||||
echo "[BUN] Setting up database..."
|
|
||||||
bun run db:push
|
|
||||||
log_step_end "bun run db:push"
|
|
||||||
|
|
||||||
log_step_start "Starting Next.js dev server"
|
|
||||||
echo "[BUN] Starting development server..."
|
|
||||||
bun run dev &
|
|
||||||
DEV_PID=$!
|
|
||||||
log_step_end "Starting Next.js dev server"
|
|
||||||
|
|
||||||
log_step_start "Waiting for Next.js dev server"
|
|
||||||
wait_for_service "localhost" "3000" "Next.js dev server"
|
|
||||||
log_step_end "Waiting for Next.js dev server"
|
|
||||||
|
|
||||||
log_step_start "Health check"
|
|
||||||
echo "[BUN] Performing health check..."
|
|
||||||
curl -fsS localhost:3000 >/dev/null
|
|
||||||
echo "[BUN] Health check passed"
|
|
||||||
log_step_end "Health check"
|
|
||||||
|
|
||||||
start_mini_services
|
|
||||||
|
|
||||||
echo "Next.js dev server is running in background (PID: $DEV_PID)."
|
|
||||||
echo "Use 'kill $DEV_PID' to stop it."
|
|
||||||
disown "$DEV_PID" 2>/dev/null || true
|
|
||||||
unset DEV_PID
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# 配置项
|
|
||||||
ROOT_DIR="/home/z/my-project/mini-services"
|
|
||||||
DIST_DIR="/tmp/build_fullstack_$BUILD_ID/mini-services-dist"
|
|
||||||
|
|
||||||
main() {
|
|
||||||
echo "🚀 开始批量构建..."
|
|
||||||
|
|
||||||
# 检查 rootdir 是否存在
|
|
||||||
if [ ! -d "$ROOT_DIR" ]; then
|
|
||||||
echo "ℹ️ 目录 $ROOT_DIR 不存在,跳过构建"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 创建输出目录(如果不存在)
|
|
||||||
mkdir -p "$DIST_DIR"
|
|
||||||
|
|
||||||
# 统计变量
|
|
||||||
success_count=0
|
|
||||||
fail_count=0
|
|
||||||
|
|
||||||
# 遍历 mini-services 目录下的所有文件夹
|
|
||||||
for dir in "$ROOT_DIR"/*; do
|
|
||||||
# 检查是否是目录且包含 package.json
|
|
||||||
if [ -d "$dir" ] && [ -f "$dir/package.json" ]; then
|
|
||||||
project_name=$(basename "$dir")
|
|
||||||
|
|
||||||
# 智能查找入口文件 (按优先级查找)
|
|
||||||
entry_path=""
|
|
||||||
for entry in "src/index.ts" "index.ts" "src/index.js" "index.js"; do
|
|
||||||
if [ -f "$dir/$entry" ]; then
|
|
||||||
entry_path="$dir/$entry"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "$entry_path" ]; then
|
|
||||||
echo "⚠️ 跳过 $project_name: 未找到入口文件 (index.ts/js)"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "📦 正在构建: $project_name..."
|
|
||||||
|
|
||||||
# 使用 bun build CLI 构建
|
|
||||||
output_file="$DIST_DIR/mini-service-$project_name.js"
|
|
||||||
|
|
||||||
if bun build "$entry_path" \
|
|
||||||
--outfile "$output_file" \
|
|
||||||
--target bun \
|
|
||||||
--minify; then
|
|
||||||
echo "✅ $project_name 构建成功 -> $output_file"
|
|
||||||
success_count=$((success_count + 1))
|
|
||||||
else
|
|
||||||
echo "❌ $project_name 构建失败"
|
|
||||||
fail_count=$((fail_count + 1))
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -f ./.zscripts/mini-services-start.sh ]; then
|
|
||||||
cp ./.zscripts/mini-services-start.sh "$DIST_DIR/mini-services-start.sh"
|
|
||||||
chmod +x "$DIST_DIR/mini-services-start.sh"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "🎉 所有任务完成!"
|
|
||||||
if [ $success_count -gt 0 ] || [ $fail_count -gt 0 ]; then
|
|
||||||
echo "✅ 成功: $success_count 个"
|
|
||||||
if [ $fail_count -gt 0 ]; then
|
|
||||||
echo "❌ 失败: $fail_count 个"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# 配置项
|
|
||||||
ROOT_DIR="/home/z/my-project/mini-services"
|
|
||||||
|
|
||||||
main() {
|
|
||||||
echo "🚀 开始批量安装依赖..."
|
|
||||||
|
|
||||||
# 检查 rootdir 是否存在
|
|
||||||
if [ ! -d "$ROOT_DIR" ]; then
|
|
||||||
echo "ℹ️ 目录 $ROOT_DIR 不存在,跳过安装"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 统计变量
|
|
||||||
success_count=0
|
|
||||||
fail_count=0
|
|
||||||
failed_projects=""
|
|
||||||
|
|
||||||
# 遍历 mini-services 目录下的所有文件夹
|
|
||||||
for dir in "$ROOT_DIR"/*; do
|
|
||||||
# 检查是否是目录且包含 package.json
|
|
||||||
if [ -d "$dir" ] && [ -f "$dir/package.json" ]; then
|
|
||||||
project_name=$(basename "$dir")
|
|
||||||
echo ""
|
|
||||||
echo "📦 正在安装依赖: $project_name..."
|
|
||||||
|
|
||||||
# 进入项目目录并执行 bun install
|
|
||||||
if (cd "$dir" && bun install); then
|
|
||||||
echo "✅ $project_name 依赖安装成功"
|
|
||||||
success_count=$((success_count + 1))
|
|
||||||
else
|
|
||||||
echo "❌ $project_name 依赖安装失败"
|
|
||||||
fail_count=$((fail_count + 1))
|
|
||||||
if [ -z "$failed_projects" ]; then
|
|
||||||
failed_projects="$project_name"
|
|
||||||
else
|
|
||||||
failed_projects="$failed_projects $project_name"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# 汇总结果
|
|
||||||
echo ""
|
|
||||||
echo "=================================================="
|
|
||||||
if [ $success_count -gt 0 ] || [ $fail_count -gt 0 ]; then
|
|
||||||
echo "🎉 安装完成!"
|
|
||||||
echo "✅ 成功: $success_count 个"
|
|
||||||
if [ $fail_count -gt 0 ]; then
|
|
||||||
echo "❌ 失败: $fail_count 个"
|
|
||||||
echo ""
|
|
||||||
echo "失败的项目:"
|
|
||||||
for project in $failed_projects; do
|
|
||||||
echo " - $project"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "ℹ️ 未找到任何包含 package.json 的项目"
|
|
||||||
fi
|
|
||||||
echo "=================================================="
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# 配置项
|
|
||||||
DIST_DIR="./mini-services-dist"
|
|
||||||
|
|
||||||
# 存储所有子进程的 PID
|
|
||||||
pids=""
|
|
||||||
|
|
||||||
# 清理函数:优雅关闭所有服务
|
|
||||||
cleanup() {
|
|
||||||
echo ""
|
|
||||||
echo "🛑 正在关闭所有服务..."
|
|
||||||
|
|
||||||
# 发送 SIGTERM 信号给所有子进程
|
|
||||||
for pid in $pids; do
|
|
||||||
if kill -0 "$pid" 2>/dev/null; then
|
|
||||||
service_name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown")
|
|
||||||
echo " 关闭进程 $pid ($service_name)..."
|
|
||||||
kill -TERM "$pid" 2>/dev/null
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# 等待所有进程退出(最多等待 5 秒)
|
|
||||||
sleep 1
|
|
||||||
for pid in $pids; do
|
|
||||||
if kill -0 "$pid" 2>/dev/null; then
|
|
||||||
# 如果还在运行,等待最多 4 秒
|
|
||||||
timeout=4
|
|
||||||
while [ $timeout -gt 0 ] && kill -0 "$pid" 2>/dev/null; do
|
|
||||||
sleep 1
|
|
||||||
timeout=$((timeout - 1))
|
|
||||||
done
|
|
||||||
# 如果仍然在运行,强制关闭
|
|
||||||
if kill -0 "$pid" 2>/dev/null; then
|
|
||||||
echo " 强制关闭进程 $pid..."
|
|
||||||
kill -KILL "$pid" 2>/dev/null
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "✅ 所有服务已关闭"
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
echo "🚀 开始启动所有 mini services..."
|
|
||||||
|
|
||||||
# 检查 dist 目录是否存在
|
|
||||||
if [ ! -d "$DIST_DIR" ]; then
|
|
||||||
echo "ℹ️ 目录 $DIST_DIR 不存在"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 查找所有 mini-service-*.js 文件
|
|
||||||
service_files=""
|
|
||||||
for file in "$DIST_DIR"/mini-service-*.js; do
|
|
||||||
if [ -f "$file" ]; then
|
|
||||||
if [ -z "$service_files" ]; then
|
|
||||||
service_files="$file"
|
|
||||||
else
|
|
||||||
service_files="$service_files $file"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# 计算服务文件数量
|
|
||||||
service_count=0
|
|
||||||
for file in $service_files; do
|
|
||||||
service_count=$((service_count + 1))
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ $service_count -eq 0 ]; then
|
|
||||||
echo "ℹ️ 未找到任何 mini service 文件"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "📦 找到 $service_count 个服务,开始启动..."
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 启动每个服务
|
|
||||||
for file in $service_files; do
|
|
||||||
service_name=$(basename "$file" .js | sed 's/mini-service-//')
|
|
||||||
echo "▶️ 启动服务: $service_name..."
|
|
||||||
|
|
||||||
# 使用 bun 运行服务(后台运行)
|
|
||||||
bun "$file" &
|
|
||||||
pid=$!
|
|
||||||
if [ -z "$pids" ]; then
|
|
||||||
pids="$pid"
|
|
||||||
else
|
|
||||||
pids="$pids $pid"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 等待一小段时间检查进程是否成功启动
|
|
||||||
sleep 0.5
|
|
||||||
if ! kill -0 "$pid" 2>/dev/null; then
|
|
||||||
echo "❌ $service_name 启动失败"
|
|
||||||
# 从字符串中移除失败的 PID
|
|
||||||
pids=$(echo "$pids" | sed "s/\b$pid\b//" | sed 's/ */ /g' | sed 's/^ *//' | sed 's/ *$//')
|
|
||||||
else
|
|
||||||
echo "✅ $service_name 已启动 (PID: $pid)"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# 计算运行中的服务数量
|
|
||||||
running_count=0
|
|
||||||
for pid in $pids; do
|
|
||||||
if kill -0 "$pid" 2>/dev/null; then
|
|
||||||
running_count=$((running_count + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "🎉 所有服务已启动!共 $running_count 个服务正在运行"
|
|
||||||
echo ""
|
|
||||||
echo "💡 按 Ctrl+C 停止所有服务"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 等待所有后台进程
|
|
||||||
wait
|
|
||||||
}
|
|
||||||
|
|
||||||
main
|
|
||||||
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# 获取脚本所在目录
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
BUILD_DIR="$SCRIPT_DIR"
|
|
||||||
|
|
||||||
# 存储所有子进程的 PID
|
|
||||||
pids=""
|
|
||||||
|
|
||||||
# 清理函数:优雅关闭所有服务
|
|
||||||
cleanup() {
|
|
||||||
echo ""
|
|
||||||
echo "🛑 正在关闭所有服务..."
|
|
||||||
|
|
||||||
# 发送 SIGTERM 信号给所有子进程
|
|
||||||
for pid in $pids; do
|
|
||||||
if kill -0 "$pid" 2>/dev/null; then
|
|
||||||
service_name=$(ps -p "$pid" -o comm= 2>/dev/null || echo "unknown")
|
|
||||||
echo " 关闭进程 $pid ($service_name)..."
|
|
||||||
kill -TERM "$pid" 2>/dev/null
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# 等待所有进程退出(最多等待 5 秒)
|
|
||||||
sleep 1
|
|
||||||
for pid in $pids; do
|
|
||||||
if kill -0 "$pid" 2>/dev/null; then
|
|
||||||
# 如果还在运行,等待最多 4 秒
|
|
||||||
timeout=4
|
|
||||||
while [ $timeout -gt 0 ] && kill -0 "$pid" 2>/dev/null; do
|
|
||||||
sleep 1
|
|
||||||
timeout=$((timeout - 1))
|
|
||||||
done
|
|
||||||
# 如果仍然在运行,强制关闭
|
|
||||||
if kill -0 "$pid" 2>/dev/null; then
|
|
||||||
echo " 强制关闭进程 $pid..."
|
|
||||||
kill -KILL "$pid" 2>/dev/null
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "✅ 所有服务已关闭"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "🚀 开始启动所有服务..."
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# 切换到构建目录
|
|
||||||
cd "$BUILD_DIR" || exit 1
|
|
||||||
|
|
||||||
ls -lah
|
|
||||||
|
|
||||||
# 初始化数据库(如果存在)
|
|
||||||
if [ -d "./next-service-dist/db" ] && [ "$(ls -A ./next-service-dist/db 2>/dev/null)" ] && [ -d "/db" ]; then
|
|
||||||
echo "🗄️ 初始化数据库从 ./next-service-dist/db 到 /db..."
|
|
||||||
cp -r ./next-service-dist/db/* /db/ 2>/dev/null || echo " ⚠️ 无法复制到 /db,跳过数据库初始化"
|
|
||||||
echo "✅ 数据库初始化完成"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 启动 Next.js 服务器
|
|
||||||
if [ -f "./next-service-dist/server.js" ]; then
|
|
||||||
echo "🚀 启动 Next.js 服务器..."
|
|
||||||
cd next-service-dist/ || exit 1
|
|
||||||
|
|
||||||
# 设置环境变量
|
|
||||||
export NODE_ENV=production
|
|
||||||
export PORT=${PORT:-3000}
|
|
||||||
export HOSTNAME=${HOSTNAME:-0.0.0.0}
|
|
||||||
|
|
||||||
# 后台启动 Next.js
|
|
||||||
bun server.js &
|
|
||||||
NEXT_PID=$!
|
|
||||||
pids="$NEXT_PID"
|
|
||||||
|
|
||||||
# 等待一小段时间检查进程是否成功启动
|
|
||||||
sleep 1
|
|
||||||
if ! kill -0 "$NEXT_PID" 2>/dev/null; then
|
|
||||||
echo "❌ Next.js 服务器启动失败"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "✅ Next.js 服务器已启动 (PID: $NEXT_PID, Port: $PORT)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd ../
|
|
||||||
else
|
|
||||||
echo "⚠️ 未找到 Next.js 服务器文件: ./next-service-dist/server.js"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 启动 mini-services
|
|
||||||
if [ -f "./mini-services-start.sh" ]; then
|
|
||||||
echo "🚀 启动 mini-services..."
|
|
||||||
|
|
||||||
# 运行启动脚本(从根目录运行,脚本内部会处理 mini-services-dist 目录)
|
|
||||||
sh ./mini-services-start.sh &
|
|
||||||
MINI_PID=$!
|
|
||||||
pids="$pids $MINI_PID"
|
|
||||||
|
|
||||||
# 等待一小段时间检查进程是否成功启动
|
|
||||||
sleep 1
|
|
||||||
if ! kill -0 "$MINI_PID" 2>/dev/null; then
|
|
||||||
echo "⚠️ mini-services 可能启动失败,但继续运行..."
|
|
||||||
else
|
|
||||||
echo "✅ mini-services 已启动 (PID: $MINI_PID)"
|
|
||||||
fi
|
|
||||||
elif [ -d "./mini-services-dist" ]; then
|
|
||||||
echo "⚠️ 未找到 mini-services 启动脚本,但目录存在"
|
|
||||||
else
|
|
||||||
echo "ℹ️ mini-services 目录不存在,跳过"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 启动 Caddy(如果存在 Caddyfile)
|
|
||||||
echo "🚀 启动 Caddy..."
|
|
||||||
|
|
||||||
# Caddy 作为前台进程运行(主进程)
|
|
||||||
echo "✅ Caddy 已启动(前台运行)"
|
|
||||||
echo ""
|
|
||||||
echo "🎉 所有服务已启动!"
|
|
||||||
echo ""
|
|
||||||
echo "💡 按 Ctrl+C 停止所有服务"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Caddy 作为主进程运行
|
|
||||||
exec caddy run --config Caddyfile --adapter caddyfile
|
|
||||||
927
docs/GAME_BRIEFING.md
Normal file
927
docs/GAME_BRIEFING.md
Normal file
@@ -0,0 +1,927 @@
|
|||||||
|
# Mana-Loop: Comprehensive Game Briefing Document
|
||||||
|
|
||||||
|
**Document Version:** 1.0
|
||||||
|
**Generated:** Game Systems Analysis
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Executive Summary](#executive-summary)
|
||||||
|
2. [Core Game Loop](#core-game-loop)
|
||||||
|
3. [Mana System](#mana-system)
|
||||||
|
4. [Time & Incursion System](#time--incursion-system)
|
||||||
|
5. [Spire & Floor System](#spire--floor-system)
|
||||||
|
6. [Combat System](#combat-system)
|
||||||
|
7. [Guardian & Pact System](#guardian--pact-system)
|
||||||
|
8. [Attunement System](#attunement-system)
|
||||||
|
9. [Skill System](#skill-system)
|
||||||
|
10. [Equipment & Enchantment System](#equipment--enchantment-system)
|
||||||
|
11. [Golemancy System](#golemancy-system)
|
||||||
|
12. [Prestige/Loop System](#prestigeloop-system)
|
||||||
|
13. [Achievement System](#achievement-system)
|
||||||
|
14. [Formulas & Calculations](#formulas--calculations)
|
||||||
|
15. [System Interactions](#system-interactions)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
**Mana-Loop** is a browser-based incremental/idle game with a 30-day time loop mechanic. Players gather mana, study skills, climb a 100-floor spire, defeat guardians, sign pacts, enchant equipment, and prestige for permanent progression.
|
||||||
|
|
||||||
|
**Key Differentiators:**
|
||||||
|
- 3-class Attunement system (Enchanter, Invoker, Fabricator)
|
||||||
|
- Equipment-based spell system (spells come from enchanted gear)
|
||||||
|
- 5-tier skill evolution with milestone upgrade choices
|
||||||
|
- Time pressure through incursion mechanic
|
||||||
|
- Guardian pacts provide permanent multipliers
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Game Loop
|
||||||
|
|
||||||
|
### Primary Loop (Within Each Run)
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ TIME LOOP (30 Days) │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ ┌─────────┐ ┌──────────┐ ┌───────────┐ ┌───────┐ │
|
||||||
|
│ │ GATHER │───▶│ STUDY │───▶│ CLIMB │───▶│ CRAFT │ │
|
||||||
|
│ │ MANA │ │ SKILLS │ │ SPIRE │ │ GEAR │ │
|
||||||
|
│ └─────────┘ └──────────┘ └───────────┘ └───────┘ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ ▼ ▼ ▼ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ DEFEAT GUARDIANS → SIGN PACTS │ │
|
||||||
|
│ └─────────────────────────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ DAY 30: LOOP ENDS → GAIN INSIGHT │ │
|
||||||
|
│ └─────────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Game Actions
|
||||||
|
|
||||||
|
| Action | Effect | Time Cost |
|
||||||
|
|--------|--------|-----------|
|
||||||
|
| **Meditate** | Regen mana with meditation bonus multiplier | Passive |
|
||||||
|
| **Climb** | Progress through spire floors, cast spells | Active combat |
|
||||||
|
| **Study** | Learn skills/spells, requires mana cost | Hours per level |
|
||||||
|
| **Craft** | Create/enchant equipment | Hours per stage |
|
||||||
|
| **Convert** | Auto-convert raw mana to elements | Per tick |
|
||||||
|
| **Design** | Create enchantment designs | Hours |
|
||||||
|
| **Prepare** | Ready equipment for enchanting | Hours + mana |
|
||||||
|
| **Enchant** | Apply enchantment to equipment | Hours + mana |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Mana System
|
||||||
|
|
||||||
|
### Mana Types Hierarchy
|
||||||
|
|
||||||
|
```
|
||||||
|
Raw Mana (Base Resource)
|
||||||
|
│
|
||||||
|
├──▶ Base Elements (7) ─────────────────────────────────────┐
|
||||||
|
│ Fire, Water, Air, Earth, Light, Dark, Death │
|
||||||
|
│ │
|
||||||
|
├──▶ Utility Element (1) ───────────────────────────────────┤
|
||||||
|
│ Transference (Enchanter attunement) │
|
||||||
|
│ │
|
||||||
|
├──▶ Compound Elements (3) ── Created from 2 base ──────────┤
|
||||||
|
│ Metal = Fire + Earth │
|
||||||
|
│ Sand = Earth + Water │
|
||||||
|
│ Lightning = Fire + Air │
|
||||||
|
│ │
|
||||||
|
└──▶ Exotic Elements (3) ── Created from advanced recipes ──┤
|
||||||
|
Crystal = Sand + Sand + Light │
|
||||||
|
Stellar = Fire + Fire + Light │
|
||||||
|
Void = Dark + Dark + Death │
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mana Formulas
|
||||||
|
|
||||||
|
**Maximum Raw Mana:**
|
||||||
|
```
|
||||||
|
maxMana = (100 + (manaWellLevel × 100) + (prestigeManaWell × 500) + equipmentBonuses) × maxManaMultiplier
|
||||||
|
```
|
||||||
|
|
||||||
|
**Maximum Elemental Mana:**
|
||||||
|
```
|
||||||
|
elementMax = (10 + (elemAttuneLevel × 50) + (prestigeElemAttune × 25)) × elementCapMultiplier
|
||||||
|
```
|
||||||
|
|
||||||
|
**Base Regeneration (per hour):**
|
||||||
|
```
|
||||||
|
baseRegen = 2 + (manaFlowLevel × 1) + (manaSpringLevel × 2) + (prestigeManaFlow × 0.5)
|
||||||
|
effectiveRegen = baseRegen × (1 - incursionStrength) × meditationMultiplier
|
||||||
|
```
|
||||||
|
|
||||||
|
**Meditation Bonus:**
|
||||||
|
```
|
||||||
|
Base: 1 + min(hours/4, 0.5) = up to 1.5x after 4 hours
|
||||||
|
With Meditation Focus skill: 2.5x after 4 hours
|
||||||
|
With Deep Trance skill: 3.0x after 6 hours
|
||||||
|
With Void Meditation skill: 5.0x after 8 hours
|
||||||
|
```
|
||||||
|
|
||||||
|
**Attunement Mana Conversion:**
|
||||||
|
- Enchanter: Raw → Transference at 0.2/hour base (scales with level)
|
||||||
|
- Fabricator: Raw → Earth at 0.25/hour base (scales with level)
|
||||||
|
|
||||||
|
### Mana Conversion Cost
|
||||||
|
- **100 Raw Mana = 1 Elemental Mana** (for base elements)
|
||||||
|
- Compound/Exotic elements are crafted, not converted
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Time & Incursion System
|
||||||
|
|
||||||
|
### Time Constants
|
||||||
|
|
||||||
|
| Constant | Value | Description |
|
||||||
|
|----------|-------|-------------|
|
||||||
|
| `TICK_MS` | 200ms | Real time per game tick |
|
||||||
|
| `HOURS_PER_TICK` | 0.04 | Game hours per tick |
|
||||||
|
| `MAX_DAY` | 30 | Days per loop |
|
||||||
|
| `INCURSION_START_DAY` | 20 | When incursion begins |
|
||||||
|
|
||||||
|
### Time Progression
|
||||||
|
- 1 real second = 5 game hours (at 5 ticks/second)
|
||||||
|
- 1 game day = 24 game hours = 4.8 real seconds
|
||||||
|
- Full 30-day loop ≈ 2.4 real minutes
|
||||||
|
|
||||||
|
### Incursion Mechanic
|
||||||
|
|
||||||
|
**Incursion Strength Formula:**
|
||||||
|
```
|
||||||
|
if (day < 20): incursionStrength = 0
|
||||||
|
else: incursionStrength = min(0.95, (totalHours / maxHours) × 0.95)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Effects:**
|
||||||
|
- Reduces mana regeneration by `(1 - incursionStrength)`
|
||||||
|
- Starts at 0% on Day 20, reaches 95% by Day 30
|
||||||
|
- Creates urgency to complete objectives before loop ends
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Spire & Floor System
|
||||||
|
|
||||||
|
### Floor Generation
|
||||||
|
|
||||||
|
**Floor Element Cycle:**
|
||||||
|
```javascript
|
||||||
|
FLOOR_ELEM_CYCLE = ["fire", "water", "air", "earth", "light", "dark", "death"]
|
||||||
|
element = FLOOR_ELEM_CYCLE[(floor - 1) % 7]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Floor HP Formula:**
|
||||||
|
```
|
||||||
|
normalFloorHP = floor(100 + floor × 50 + floor^1.7)
|
||||||
|
guardianFloorHP = GUARDIANS[floor].hp // Fixed values
|
||||||
|
```
|
||||||
|
|
||||||
|
**Guardian Floors:** 10, 20, 30, 40, 50, 60, 80, 90, 100
|
||||||
|
|
||||||
|
### Room Types
|
||||||
|
|
||||||
|
| Room Type | Chance | Description |
|
||||||
|
|-----------|--------|-------------|
|
||||||
|
| **Combat** | Default | Single enemy, normal combat |
|
||||||
|
| **Guardian** | Fixed | Boss floor, always on guardian floors |
|
||||||
|
| **Swarm** | 15% | 3-6 enemies with 40% HP each |
|
||||||
|
| **Speed** | 10% | Enemy with dodge chance (25% base + 0.5%/floor) |
|
||||||
|
| **Puzzle** | 20% on puzzle floors | Progress-based, faster with relevant attunement |
|
||||||
|
|
||||||
|
### Swarm Room Configuration
|
||||||
|
```javascript
|
||||||
|
SWARM_CONFIG = {
|
||||||
|
minEnemies: 3,
|
||||||
|
maxEnemies: 6,
|
||||||
|
hpMultiplier: 0.4, // Each enemy has 40% of normal HP
|
||||||
|
armorBase: 0,
|
||||||
|
armorPerFloor: 0.01 // +1% armor per 10 floors
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Speed Room Configuration
|
||||||
|
```javascript
|
||||||
|
SPEED_ROOM_CONFIG = {
|
||||||
|
baseDodgeChance: 0.25, // 25% base
|
||||||
|
dodgePerFloor: 0.005, // +0.5% per floor
|
||||||
|
maxDodge: 0.50 // Cap at 50%
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Armor Scaling
|
||||||
|
```javascript
|
||||||
|
FLOOR_ARMOR_CONFIG = {
|
||||||
|
baseChance: 0, // No armor before floor 10
|
||||||
|
chancePerFloor: 0.01, // +1% chance per floor after 10
|
||||||
|
maxArmorChance: 0.5, // Max 50% of floors have armor
|
||||||
|
minArmor: 0.05, // Min 5% damage reduction
|
||||||
|
maxArmor: 0.25 // Max 25% on non-guardians
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Combat System
|
||||||
|
|
||||||
|
### Spell Casting Mechanics
|
||||||
|
|
||||||
|
**Cast Progress:**
|
||||||
|
```
|
||||||
|
progressPerTick = HOURS_PER_TICK × spellCastSpeed × totalAttackSpeed
|
||||||
|
```
|
||||||
|
|
||||||
|
**Spell Cast Speed:**
|
||||||
|
- Each spell has a `castSpeed` value (casts per hour)
|
||||||
|
- Higher = faster casting
|
||||||
|
- Lightning spells get +30% effective cast speed
|
||||||
|
|
||||||
|
**Damage Calculation:**
|
||||||
|
```
|
||||||
|
baseDamage = spellDamage + (combatTrainLevel × 5)
|
||||||
|
pctBonus = 1 + (arcaneFuryLevel × 0.1)
|
||||||
|
elemMastery = 1 + (elementalMasteryLevel × 0.15)
|
||||||
|
pactMultiplier = product of all signed pact multipliers
|
||||||
|
elementalBonus = getElementalBonus(spellElement, floorElement)
|
||||||
|
|
||||||
|
finalDamage = baseDamage × pctBonus × pactMultiplier × elemMastery × elementalBonus
|
||||||
|
```
|
||||||
|
|
||||||
|
### Elemental Effectiveness
|
||||||
|
|
||||||
|
| Condition | Multiplier |
|
||||||
|
|-----------|------------|
|
||||||
|
| Spell same element as floor | 1.25x (25% bonus) |
|
||||||
|
| Spell is opposite of floor element | 1.50x (Super Effective) |
|
||||||
|
| Spell's opposite matches floor | 0.75x (Not Very Effective) |
|
||||||
|
| Raw mana spells | 1.00x (No bonus) |
|
||||||
|
|
||||||
|
**Element Opposites:**
|
||||||
|
```
|
||||||
|
Fire ↔ Water
|
||||||
|
Air ↔ Earth
|
||||||
|
Light ↔ Dark
|
||||||
|
Lightning ↔ (none, but has armor pierce)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Armor & Damage Reduction
|
||||||
|
|
||||||
|
**Effective Damage:**
|
||||||
|
```
|
||||||
|
effectiveArmor = max(0, enemyArmor - armorPierce)
|
||||||
|
damageDealt = damage × (1 - effectiveArmor)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Armor Pierce Sources:**
|
||||||
|
- Lightning spells: 20-50% pierce
|
||||||
|
- Metal spells: 25-60% pierce
|
||||||
|
- Golems: 15-60% pierce
|
||||||
|
|
||||||
|
### Critical Hits
|
||||||
|
|
||||||
|
```
|
||||||
|
critChance = precisionLevel × 0.05
|
||||||
|
critMultiplier = 1.5 (base)
|
||||||
|
```
|
||||||
|
|
||||||
|
### AOE Mechanics
|
||||||
|
|
||||||
|
```
|
||||||
|
aoeDamage = baseDamage × (1 - 0.1 × (numTargets - 1))
|
||||||
|
// 10% less damage per additional target
|
||||||
|
```
|
||||||
|
|
||||||
|
### Special Combat Effects
|
||||||
|
|
||||||
|
| Effect | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| **Burn** | Damage over time |
|
||||||
|
| **Freeze** | Prevents dodge (100% freeze = no dodge) |
|
||||||
|
| **Stun** | Temporary disable |
|
||||||
|
| **Pierce** | Ignores percentage of armor |
|
||||||
|
| **Chain** | Hits multiple targets |
|
||||||
|
| **AOE** | Area damage |
|
||||||
|
| **Buff** | Damage multiplier |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Guardian & Pact System
|
||||||
|
|
||||||
|
### Guardian Floors & Stats
|
||||||
|
|
||||||
|
| Floor | Guardian | Element | HP | Pact Mult | Armor | Unique Perk |
|
||||||
|
|-------|----------|---------|-----|-----------|-------|-------------|
|
||||||
|
| 10 | Ignis Prime | Fire | 5,000 | 1.5x | 10% | Fire spells cast 10% faster |
|
||||||
|
| 20 | Aqua Regia | Water | 15,000 | 1.75x | 15% | Water spells +15% damage |
|
||||||
|
| 30 | Ventus Rex | Air | 30,000 | 2.0x | 18% | Air spells 15% crit chance |
|
||||||
|
| 40 | Terra Firma | Earth | 50,000 | 2.25x | 25% | Earth spells +25% vs guardians |
|
||||||
|
| 50 | Lux Aeterna | Light | 80,000 | 2.5x | 20% | Light spells reveal weaknesses (+20% dmg) |
|
||||||
|
| 60 | Umbra Mortis | Dark | 120,000 | 2.75x | 22% | Dark spells +25% vs armored |
|
||||||
|
| 80 | Mors Ultima | Death | 250,000 | 3.25x | 25% | Death spells execute <20% HP |
|
||||||
|
| 90 | Primordialis | Void | 400,000 | 4.0x | 30% | Void spells ignore 30% resistance |
|
||||||
|
| 100 | The Awakened One | Stellar | 1,000,000 | 5.0x | 35% | All spells +50% dmg, 25% faster |
|
||||||
|
|
||||||
|
### Guardian Boons
|
||||||
|
|
||||||
|
When a pact is signed, the guardian grants permanent boons:
|
||||||
|
|
||||||
|
| Boon Type | Effect | Example |
|
||||||
|
|-----------|--------|---------|
|
||||||
|
| `maxMana` | +Max raw mana | +50 to +500 |
|
||||||
|
| `manaRegen` | +Regen/hour | +0.5 to +2 |
|
||||||
|
| `castingSpeed` | +% cast speed | +5% |
|
||||||
|
| `elementalDamage` | +% element damage | +5% to +20% |
|
||||||
|
| `rawDamage` | +% all damage | +10% |
|
||||||
|
| `critChance` | +% crit chance | N/A |
|
||||||
|
| `critDamage` | +% crit multiplier | +15% |
|
||||||
|
| `insightGain` | +% insight | +10% to +25% |
|
||||||
|
|
||||||
|
### Pact Ritual
|
||||||
|
|
||||||
|
**Pact Costs:**
|
||||||
|
```javascript
|
||||||
|
pactCost: 500 to 150,000 (raw mana)
|
||||||
|
pactTime: 2 to 24 (hours for ritual)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Victory Condition:**
|
||||||
|
- Defeat floor 100 guardian AND sign the pact
|
||||||
|
- Awards 3x normal insight
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Attunement System
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
Attunements are class-like specializations that grant unique capabilities and skill access.
|
||||||
|
|
||||||
|
### The Three Attunements
|
||||||
|
|
||||||
|
#### 1. Enchanter (Right Hand) ✅ Fully Implemented
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Slot** | Right Hand |
|
||||||
|
| **Primary Mana** | Transference |
|
||||||
|
| **Raw Regen** | +0.5/hour base |
|
||||||
|
| **Conversion** | 0.2 raw→transference/hour |
|
||||||
|
| **Unlock** | Starting attunement |
|
||||||
|
|
||||||
|
**Capabilities:**
|
||||||
|
- Enchanting equipment
|
||||||
|
- Disenchanting for mana recovery
|
||||||
|
|
||||||
|
**Skill Categories Unlocked:**
|
||||||
|
- `enchant` - Enchanting efficiency
|
||||||
|
- `effectResearch` - Unlock enchantment effects
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 2. Invoker (Chest) ⚠️ Partially Implemented
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Slot** | Chest |
|
||||||
|
| **Primary Mana** | None (gains from pacts) |
|
||||||
|
| **Raw Regen** | +0.3/hour base |
|
||||||
|
| **Conversion** | None |
|
||||||
|
| **Unlock** | Defeat first guardian |
|
||||||
|
|
||||||
|
**Capabilities:**
|
||||||
|
- Form pacts with guardians
|
||||||
|
- Access guardian powers
|
||||||
|
- Elemental mastery through pacts
|
||||||
|
|
||||||
|
**Skill Categories Unlocked:**
|
||||||
|
- `invocation` - ⚠️ No skills defined
|
||||||
|
- `pact` - ⚠️ No skills defined
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### 3. Fabricator (Left Hand) ✅ Implemented
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|----------|-------|
|
||||||
|
| **Slot** | Left Hand |
|
||||||
|
| **Primary Mana** | Earth |
|
||||||
|
| **Raw Regen** | +0.4/hour base |
|
||||||
|
| **Conversion** | 0.25 raw→earth/hour |
|
||||||
|
| **Unlock** | Prove crafting worth |
|
||||||
|
|
||||||
|
**Capabilities:**
|
||||||
|
- Golem crafting
|
||||||
|
- Gear crafting
|
||||||
|
- Earth shaping
|
||||||
|
|
||||||
|
**Skill Categories Unlocked:**
|
||||||
|
- `fabrication` - Crafting efficiency
|
||||||
|
- `golemancy` - Golem control
|
||||||
|
|
||||||
|
### Attunement Leveling
|
||||||
|
|
||||||
|
**XP Formula:**
|
||||||
|
```
|
||||||
|
Level 2: 1,000 XP
|
||||||
|
Level 3: 2,500 XP
|
||||||
|
Level 4: 5,000 XP
|
||||||
|
Level 5: 10,000 XP
|
||||||
|
Each level: 2× previous (approximately)
|
||||||
|
Max Level: 10
|
||||||
|
```
|
||||||
|
|
||||||
|
**Level Scaling:**
|
||||||
|
```
|
||||||
|
regenMultiplier = 1.5^(level - 1)
|
||||||
|
conversionRate = baseRate × 1.5^(level - 1)
|
||||||
|
```
|
||||||
|
|
||||||
|
**XP Sources:**
|
||||||
|
- Enchanting: 1 XP per 10 capacity used
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Skill System
|
||||||
|
|
||||||
|
### Skill Categories
|
||||||
|
|
||||||
|
| Category | Attunement | Description |
|
||||||
|
|----------|------------|-------------|
|
||||||
|
| `mana` | Core | Max mana, regen, element cap |
|
||||||
|
| `study` | Core | Study speed, cost reduction |
|
||||||
|
| `research` | Core | Click bonuses, advanced meditation |
|
||||||
|
| `ascension` | Core | Insight, guardian damage |
|
||||||
|
| `enchant` | Enchanter | Enchanting efficiency |
|
||||||
|
| `effectResearch` | Enchanter | Unlock enchantment effects |
|
||||||
|
| `invocation` | Invoker | ⚠️ Not implemented |
|
||||||
|
| `pact` | Invoker | ⚠️ Not implemented |
|
||||||
|
| `fabrication` | Fabricator | Crafting speed |
|
||||||
|
| `golemancy` | Fabricator | Golem control |
|
||||||
|
| `craft` | Legacy | Basic crafting |
|
||||||
|
|
||||||
|
### Core Skills
|
||||||
|
|
||||||
|
| Skill | Max | Effect | Study Time |
|
||||||
|
|-------|-----|--------|------------|
|
||||||
|
| Mana Well | 10 | +100 max mana/level | 4h |
|
||||||
|
| Mana Flow | 10 | +1 regen/level | 5h |
|
||||||
|
| Elem. Attunement | 10 | +50 element cap/level | 4h |
|
||||||
|
| Mana Overflow | 5 | +25% click mana/level | 6h |
|
||||||
|
| Quick Learner | 10 | +10% study speed/level | 4h |
|
||||||
|
| Focused Mind | 10 | -5% study cost/level | 5h |
|
||||||
|
| Meditation Focus | 1 | 2.5x regen after 4h | 6h |
|
||||||
|
|
||||||
|
### Skill Tier Evolution
|
||||||
|
|
||||||
|
**5-Tier System:**
|
||||||
|
```
|
||||||
|
Tier 1: Base skill (multiplier ×1)
|
||||||
|
Tier 2: Enhanced (multiplier ×10)
|
||||||
|
Tier 3: Master (multiplier ×100)
|
||||||
|
Tier 4: Legendary (multiplier ×1,000)
|
||||||
|
Tier 5: Mythic (multiplier ×10,000)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Tier Up:**
|
||||||
|
- Requires max level (10) in current tier
|
||||||
|
- Unlocks new skill ID (e.g., `manaWell_t2`)
|
||||||
|
|
||||||
|
### Milestone Upgrades
|
||||||
|
|
||||||
|
**At Level 5 and Level 10:**
|
||||||
|
- Choose 2 upgrades from 4+ options
|
||||||
|
- Upgrades can be stat multipliers, bonuses, or special effects
|
||||||
|
- Some upgrades have upgrade paths
|
||||||
|
|
||||||
|
**Example Mana Well Tier 1 Upgrades:**
|
||||||
|
| Level | Upgrade | Effect |
|
||||||
|
|-------|---------|--------|
|
||||||
|
| 5 | Expanded Capacity | +25% max mana |
|
||||||
|
| 5 | Natural Spring | +0.5 regen |
|
||||||
|
| 5 | Mana Threshold | +30% max, -10% regen |
|
||||||
|
| 10 | Deep Reservoir | +50% max (replaces Expanded) |
|
||||||
|
| 10 | Deep Wellspring | +50% meditation efficiency |
|
||||||
|
|
||||||
|
### Enchanter Skills
|
||||||
|
|
||||||
|
| Skill | Max | Requirement | Effect |
|
||||||
|
|-------|-----|-------------|--------|
|
||||||
|
| Enchanting | 10 | Enchanter 1 | Unlocks enchantment design |
|
||||||
|
| Efficient Enchant | 5 | Enchanter 2, Enchanting 3 | -5% capacity cost/level |
|
||||||
|
| Disenchanting | 3 | Enchanter 1, Enchanting 2 | +20% mana recovery/level |
|
||||||
|
| Enchant Speed | 5 | Enchanter 1, Enchanting 2 | -10% time/level |
|
||||||
|
| Scroll Crafting | 3 | Enchanter 3, Enchanting 5 | ⚠️ Not implemented |
|
||||||
|
| Essence Refining | 5 | Enchanter 2, Enchanting 4 | +10% effect power/level |
|
||||||
|
|
||||||
|
### Golemancy Skills
|
||||||
|
|
||||||
|
| Skill | Max | Requirement | Effect |
|
||||||
|
|-------|-----|-------------|--------|
|
||||||
|
| Golem Mastery | 5 | Fabricator 2 | +10% golem damage/level |
|
||||||
|
| Golem Efficiency | 5 | Fabricator 2 | +5% attack speed/level |
|
||||||
|
| Golem Longevity | 3 | Fabricator 3 | +1 floor duration/level |
|
||||||
|
| Golem Siphon | 3 | Fabricator 3 | -10% maintenance/level |
|
||||||
|
| Advanced Golemancy | 1 | Fabricator 5, Mastery 3 | Unlock hybrid golems |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Equipment & Enchantment System
|
||||||
|
|
||||||
|
### Equipment Slots
|
||||||
|
|
||||||
|
```
|
||||||
|
mainHand - Staves, Wands, Swords
|
||||||
|
offHand - Shields, Catalysts
|
||||||
|
head - Hoods, Hats, Helms
|
||||||
|
body - Robes, Armor
|
||||||
|
hands - Gloves, Gauntlets
|
||||||
|
feet - Boots, Shoes
|
||||||
|
accessory1 - Rings, Amulets
|
||||||
|
accessory2 - Rings, Amulets
|
||||||
|
```
|
||||||
|
|
||||||
|
### Equipment Categories
|
||||||
|
|
||||||
|
| Category | Slots | Description |
|
||||||
|
|----------|-------|-------------|
|
||||||
|
| Caster | Main Hand | Staves and wands for spellcasting |
|
||||||
|
| Sword | Main Hand | Magic swords with weapon enchants |
|
||||||
|
| Catalyst | Main Hand | Amplifies magical effects |
|
||||||
|
| Shield | Off Hand | Defensive equipment |
|
||||||
|
| Head/Body/Hands/Feet | Respective | Armor pieces |
|
||||||
|
| Accessory | Accessory1/2 | Rings and amulets |
|
||||||
|
|
||||||
|
### Equipment Instance Structure
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface EquipmentInstance {
|
||||||
|
instanceId: string; // Unique ID
|
||||||
|
typeId: string; // Reference to EquipmentType
|
||||||
|
name: string;
|
||||||
|
enchantments: AppliedEnchantment[];
|
||||||
|
usedCapacity: number; // Current capacity used
|
||||||
|
totalCapacity: number; // Max capacity
|
||||||
|
rarity: 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary' | 'mythic';
|
||||||
|
quality: number; // 0-100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enchantment Process (3 Stages)
|
||||||
|
|
||||||
|
#### Stage 1: Design
|
||||||
|
- Create enchantment design
|
||||||
|
- Select effects from unlocked pool
|
||||||
|
- Calculate capacity cost
|
||||||
|
- Time: Base 1h + 0.5h per effect stack
|
||||||
|
|
||||||
|
**Capacity Cost Formula:**
|
||||||
|
```
|
||||||
|
totalCost = Σ(effect.baseCost × (1 + 0.2 × stackIndex) × (1 - efficiencyBonus))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Stage 2: Prepare
|
||||||
|
- Prepare equipment for enchanting
|
||||||
|
- Mana cost: capacity × 10
|
||||||
|
- Time: 2h + 1h per 50 capacity
|
||||||
|
|
||||||
|
#### Stage 3: Apply
|
||||||
|
- Apply enchantment design
|
||||||
|
- Mana per hour: 20 + 5 per effect stack
|
||||||
|
- Time: 2h + 1h per effect stack
|
||||||
|
- Grants Enchanter XP: 1 XP per 10 capacity
|
||||||
|
|
||||||
|
### Enchantment Effect Categories
|
||||||
|
|
||||||
|
| Category | Description | Equipment Types |
|
||||||
|
|----------|-------------|-----------------|
|
||||||
|
| **Spell** | Grants spell ability | Casters only |
|
||||||
|
| **Mana** | Max/regen/click bonuses | Casters, Catalysts, Head, Body, Accessories |
|
||||||
|
| **Combat** | Damage, crit, speed | Casters, Hands |
|
||||||
|
| **Utility** | Study, meditation, insight | Most equipment |
|
||||||
|
| **Special** | Unique effects | Various |
|
||||||
|
| **Elemental** | Weapon enchantments | Swords, Casters |
|
||||||
|
|
||||||
|
### Starting Equipment
|
||||||
|
|
||||||
|
| Slot | Item | Enchantment | Capacity |
|
||||||
|
|------|------|-------------|----------|
|
||||||
|
| Main Hand | Basic Staff | Mana Bolt spell | 50/50 |
|
||||||
|
| Body | Civilian Shirt | None | 0/30 |
|
||||||
|
| Feet | Civilian Shoes | None | 0/15 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Golemancy System
|
||||||
|
|
||||||
|
### Golem Slots
|
||||||
|
|
||||||
|
```
|
||||||
|
Fabricator Level 2: 1 slot
|
||||||
|
Fabricator Level 4: 2 slots
|
||||||
|
Fabricator Level 6: 3 slots
|
||||||
|
Fabricator Level 8: 4 slots
|
||||||
|
Fabricator Level 10: 5 slots
|
||||||
|
|
||||||
|
slots = floor(fabricatorLevel / 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Golem Types
|
||||||
|
|
||||||
|
#### Base Golems
|
||||||
|
|
||||||
|
| Golem | Element | Damage | Speed | Armor Pierce | Unlock |
|
||||||
|
|-------|---------|--------|-------|--------------|--------|
|
||||||
|
| Earth Golem | Earth | 8 | 1.5/h | 15% | Fabricator 2 |
|
||||||
|
|
||||||
|
#### Elemental Variants
|
||||||
|
|
||||||
|
| Golem | Element | Damage | Speed | Pierce | Unlock |
|
||||||
|
|-------|---------|--------|-------|--------|--------|
|
||||||
|
| Steel Golem | Metal | 12 | 1.2/h | 35% | Metal mana unlocked |
|
||||||
|
| Crystal Golem | Crystal | 18 | 1.0/h | 25% | Crystal mana unlocked |
|
||||||
|
| Sand Golem | Sand | 6 | 2.0/h | 10% | Sand mana unlocked |
|
||||||
|
|
||||||
|
#### Advanced Hybrid Golems (Enchanter 5 + Fabricator 5)
|
||||||
|
|
||||||
|
| Golem | Elements | Damage | Speed | Pierce | Special |
|
||||||
|
|-------|----------|--------|-------|--------|---------|
|
||||||
|
| Lava Golem | Earth + Fire | 15 | 1.0/h | 20% | AOE 2 |
|
||||||
|
| Galvanic Golem | Metal + Lightning | 10 | 3.5/h | 45% | Fast |
|
||||||
|
| Obsidian Golem | Earth + Dark | 25 | 0.8/h | 50% | High damage |
|
||||||
|
| Prism Golem | Crystal + Light | 20 | 1.5/h | 35% | AOE 3 |
|
||||||
|
| Quicksilver Golem | Metal + Water | 8 | 4.0/h | 30% | Very fast |
|
||||||
|
| Voidstone Golem | Earth + Void | 40 | 0.6/h | 60% | Ultimate |
|
||||||
|
|
||||||
|
### Golem Costs
|
||||||
|
|
||||||
|
**Summon Cost (one-time per floor):**
|
||||||
|
```
|
||||||
|
Earth Golem: 10 Earth
|
||||||
|
Steel Golem: 8 Metal + 5 Earth
|
||||||
|
Crystal Golem: 6 Crystal + 3 Earth
|
||||||
|
```
|
||||||
|
|
||||||
|
**Maintenance Cost (per tick):**
|
||||||
|
```
|
||||||
|
Earth Golem: 0.5 Earth/hour
|
||||||
|
Steel Golem: 0.6 Metal + 0.2 Earth/hour
|
||||||
|
```
|
||||||
|
|
||||||
|
### Golem Duration
|
||||||
|
|
||||||
|
```
|
||||||
|
baseDuration = 1 floor
|
||||||
|
with Golem Longevity: +1 floor per level (max 4 floors)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Golem Combat
|
||||||
|
|
||||||
|
**Attack Progress:**
|
||||||
|
```
|
||||||
|
progressPerTick = HOURS_PER_TICK × attackSpeed × efficiencyBonus
|
||||||
|
```
|
||||||
|
|
||||||
|
**Damage:**
|
||||||
|
```
|
||||||
|
damage = baseDamage × (1 + golemMastery × 0.1)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prestige/Loop System
|
||||||
|
|
||||||
|
### Loop End Conditions
|
||||||
|
|
||||||
|
| Condition | Result |
|
||||||
|
|-----------|--------|
|
||||||
|
| Day 30 reached | Loop ends, gain insight |
|
||||||
|
| Floor 100 + Pact 100 signed | Victory! 3× insight |
|
||||||
|
|
||||||
|
### Insight Formula
|
||||||
|
|
||||||
|
```
|
||||||
|
baseInsight = floor(maxFloorReached × 15 + totalManaGathered / 500 + signedPacts.length × 150)
|
||||||
|
finalInsight = floor(baseInsight × (1 + insightAmpLevel × 0.25) × skillBonus)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prestige Upgrades
|
||||||
|
|
||||||
|
| Upgrade | Max | Cost | Effect |
|
||||||
|
|---------|-----|------|--------|
|
||||||
|
| Mana Well | 5 | 500 | +500 starting max mana |
|
||||||
|
| Mana Flow | 10 | 750 | +0.5 permanent regen |
|
||||||
|
| Deep Memory | 5 | 1000 | +1 memory slot |
|
||||||
|
| Insight Amp | 4 | 1500 | +25% insight gain |
|
||||||
|
| Spire Key | 5 | 4000 | Start at floor +2 |
|
||||||
|
| Temporal Echo | 5 | 3000 | +10% mana generation |
|
||||||
|
| Steady Hand | 5 | 1200 | -15% durability loss |
|
||||||
|
| Ancient Knowledge | 5 | 2000 | Start with blueprint |
|
||||||
|
| Elemental Attune | 10 | 600 | +25 element cap |
|
||||||
|
| Spell Memory | 3 | 2500 | Start with random spell |
|
||||||
|
| Guardian Pact | 5 | 3500 | +10% pact multiplier |
|
||||||
|
| Quick Start | 3 | 400 | +100 starting mana |
|
||||||
|
| Elem. Start | 3 | 800 | +5 each unlocked element |
|
||||||
|
|
||||||
|
### Memory System
|
||||||
|
|
||||||
|
- **Base slots:** 3
|
||||||
|
- **Additional:** +1 per Deep Memory level
|
||||||
|
- **Memories:** Spells or skills preserved across loops
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Achievement System
|
||||||
|
|
||||||
|
### Achievement Categories
|
||||||
|
|
||||||
|
| Category | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `mana` | Mana gathering milestones |
|
||||||
|
| `combat` | Combat achievements |
|
||||||
|
| `progression` | Floor/guardian progression |
|
||||||
|
| `crafting` | Equipment and enchanting |
|
||||||
|
| `prestige` | Loop and insight milestones |
|
||||||
|
|
||||||
|
### Achievement Rewards
|
||||||
|
|
||||||
|
| Reward Type | Effect |
|
||||||
|
|-------------|--------|
|
||||||
|
| `insight` | One-time insight bonus |
|
||||||
|
| `manaBonus` | Permanent max mana |
|
||||||
|
| `damageBonus` | Permanent damage increase |
|
||||||
|
| `regenBonus` | Permanent regen increase |
|
||||||
|
| `title` | Cosmetic title |
|
||||||
|
| `unlockEffect` | Unlocks enchantment effect |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Formulas & Calculations
|
||||||
|
|
||||||
|
### Damage Calculation (Complete)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function calcDamage(state, spellId, floorElement) {
|
||||||
|
const spell = SPELLS_DEF[spellId];
|
||||||
|
|
||||||
|
// Base damage
|
||||||
|
let damage = spell.dmg + (state.skills.combatTrain || 0) * 5;
|
||||||
|
|
||||||
|
// Percentage multipliers
|
||||||
|
damage *= 1 + (state.skills.arcaneFury || 0) * 0.1;
|
||||||
|
|
||||||
|
// Elemental mastery
|
||||||
|
damage *= 1 + (state.skills.elementalMastery || 0) * 0.15;
|
||||||
|
|
||||||
|
// Guardian bane (vs guardians only)
|
||||||
|
if (isGuardian) {
|
||||||
|
damage *= 1 + (state.skills.guardianBane || 0) * 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pact multiplier
|
||||||
|
damage *= state.signedPacts.reduce((m, f) => m * GUARDIANS[f].pact, 1);
|
||||||
|
|
||||||
|
// Elemental effectiveness
|
||||||
|
damage *= getElementalBonus(spell.elem, floorElement);
|
||||||
|
|
||||||
|
// Critical hit
|
||||||
|
const critChance = (state.skills.precision || 0) * 0.05;
|
||||||
|
if (Math.random() < critChance) {
|
||||||
|
damage *= 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equipment effects
|
||||||
|
damage *= effects.baseDamageMultiplier;
|
||||||
|
damage += effects.baseDamageBonus;
|
||||||
|
|
||||||
|
// Armor reduction
|
||||||
|
const effectiveArmor = Math.max(0, enemyArmor - armorPierce);
|
||||||
|
damage *= (1 - effectiveArmor);
|
||||||
|
|
||||||
|
return Math.floor(damage);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Study Time Calculation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
effectiveStudyTime = baseStudyTime × tier / studySpeedMultiplier
|
||||||
|
|
||||||
|
studySpeedMultiplier = 1 + (quickLearnerLevel × 0.1) + equipmentBonus
|
||||||
|
```
|
||||||
|
|
||||||
|
### Study Cost Calculation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
effectiveCost = floor(baseCost × (currentLevel + 1) × tier × costMultiplier)
|
||||||
|
|
||||||
|
costMultiplier = 1 - (focusedMindLevel × 0.05)
|
||||||
|
```
|
||||||
|
|
||||||
|
### DPS Calculation
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
dps = (damage × castSpeed × attackSpeedMultiplier) / hour
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## System Interactions
|
||||||
|
|
||||||
|
### Primary Interaction Map
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ CORE SYSTEM INTERACTIONS │
|
||||||
|
├─────────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ MANA │────────▶│ SKILLS │────────▶│ COMBAT │ │
|
||||||
|
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ ▼ ▼ ▼ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │ATTUNEMENT│────────▶│ENCHANTING│────────▶│ SPIRE │ │
|
||||||
|
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ ▼ ▼ ▼ │
|
||||||
|
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||||
|
│ │GOLEMANCY │ │EQUIPMENT │────────▶│ GUARDIAN │ │
|
||||||
|
│ └──────────┘ └──────────┘ └──────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌──────────┐ │
|
||||||
|
│ │ PACT │ │
|
||||||
|
│ └──────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌──────────┐ │
|
||||||
|
│ │ PRESTIGE │ │
|
||||||
|
│ └──────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key System Dependencies
|
||||||
|
|
||||||
|
| System | Depends On | Unlocks/Enables |
|
||||||
|
|--------|------------|-----------------|
|
||||||
|
| **Skills** | Mana (cost) | All combat/crafting bonuses |
|
||||||
|
| **Enchanting** | Enchanter attunement, Skills | Equipment spells, bonuses |
|
||||||
|
| **Golemancy** | Fabricator attunement, Earth mana | Additional combat damage |
|
||||||
|
| **Pacts** | Guardian defeat | Permanent multipliers, boons |
|
||||||
|
| **Prestige** | Loop completion | Permanent upgrades, memories |
|
||||||
|
| **Equipment** | Crafting/Blueprints | Spell access, stat bonuses |
|
||||||
|
|
||||||
|
### Progression Gates
|
||||||
|
|
||||||
|
1. **Early Game (Floors 1-10):**
|
||||||
|
- Mana gathering and regen
|
||||||
|
- Basic skills (mana, study categories)
|
||||||
|
- Starting equipment
|
||||||
|
|
||||||
|
2. **Mid Game (Floors 10-40):**
|
||||||
|
- First guardian pacts
|
||||||
|
- Attunement unlocking
|
||||||
|
- Equipment enchanting
|
||||||
|
- Golemancy (Fabricator)
|
||||||
|
|
||||||
|
3. **Late Game (Floors 40-80):**
|
||||||
|
- Compound/exotic elements
|
||||||
|
- Hybrid golems
|
||||||
|
- Skill tier evolution
|
||||||
|
- Advanced enchantments
|
||||||
|
|
||||||
|
4. **End Game (Floors 80-100):**
|
||||||
|
- Void/Stellar/Crystal spells
|
||||||
|
- Ultimate golems
|
||||||
|
- Victory preparation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Appendix: Known Issues
|
||||||
|
|
||||||
|
### Missing Implementations
|
||||||
|
|
||||||
|
1. **Invocation Skills** - Category defined but no skills
|
||||||
|
2. **Pact Skills** - Category defined but no skills
|
||||||
|
3. **Scroll Crafting** - Skill exists, no system
|
||||||
|
4. **Field Repair** - Skill exists, no repair system
|
||||||
|
|
||||||
|
### Balance Concerns
|
||||||
|
|
||||||
|
1. Exotic elements require extreme mana investment
|
||||||
|
2. Incursion creates hard time pressure without counterplay
|
||||||
|
3. Equipment progression limited after starting gear
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*End of Game Briefing Document*
|
||||||
719
docs/skills.md
719
docs/skills.md
@@ -1,55 +1,33 @@
|
|||||||
# Mana Loop - Skill System Documentation
|
# Mana Loop - Complete Skill System Documentation
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
1. [Overview](#overview)
|
||||||
|
2. [Core Mechanics](#core-mechanics)
|
||||||
|
3. [Skill Categories](#skill-categories)
|
||||||
|
4. [All Skills Reference](#all-skills-reference)
|
||||||
|
5. [Upgrade Trees](#upgrade-trees)
|
||||||
|
6. [Tier System](#tier-system)
|
||||||
|
7. [Banned Content](#banned-content)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The skill system in Mana Loop allows players to specialize their character through a deep progression system. Skills are organized by attunement, with each attunement providing access to specific skill categories.
|
The skill system in Mana Loop provides deep character customization through a branching upgrade tree system. Skills are organized by attunement, with each attunement granting access to specific skill categories.
|
||||||
|
|
||||||
|
### Skill Level Types
|
||||||
|
|
||||||
|
| Max Level | Description | Example Skills |
|
||||||
|
|-----------|-------------|----------------|
|
||||||
|
| 10 | Standard skills with full upgrade trees | Mana Well, Mana Flow, Enchanting |
|
||||||
|
| 5 | Specialized skills with limited upgrades | Efficient Enchant, Golem Mastery |
|
||||||
|
| 3 | Focused skills with no upgrades | Knowledge Retention, Golem Longevity |
|
||||||
|
| 1 | Effect research skills (unlock only) | All research skills |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Core Mechanics
|
## Core Mechanics
|
||||||
|
|
||||||
### Skill Levels
|
|
||||||
|
|
||||||
- Most skills level from **1 to 10**
|
|
||||||
- Some skills cap at **level 5** (specialized skills)
|
|
||||||
- Research skills are **level 1 only** (unlock effects)
|
|
||||||
|
|
||||||
### Tier Up System
|
|
||||||
|
|
||||||
When a skill reaches max level, it can **tier up**:
|
|
||||||
- **Tier 2, Level 1 = Tier 1, Level 10** in power
|
|
||||||
- Each tier multiplies the skill's base effect by 10x
|
|
||||||
- Maximum of 5 tiers per skill
|
|
||||||
- Tiering up requires attunement level prerequisites
|
|
||||||
|
|
||||||
### Milestone Upgrades (The Perk Tree)
|
|
||||||
|
|
||||||
At **every 5 levels** (5 and 10), you choose **1 upgrade/perk** from an upgrade tree:
|
|
||||||
|
|
||||||
- Starts with 3 base choices per milestone
|
|
||||||
- Each choice leads to branching paths
|
|
||||||
- You can always choose from previously available options
|
|
||||||
- Upgrades can be **upgraded again** at future milestones
|
|
||||||
- Upgrades meaningfully impact how the skill works
|
|
||||||
|
|
||||||
#### Example: Mana Well Upgrade Tree
|
|
||||||
|
|
||||||
```
|
|
||||||
Level 5 Choices:
|
|
||||||
├── Expanded Capacity (+25% max mana)
|
|
||||||
│ └── Level 10 upgrade: Deep Reservoir (+50% max mana)
|
|
||||||
│ └── Future: Can sacrifice capacity for regen
|
|
||||||
├── Natural Spring (+0.5 regen)
|
|
||||||
│ └── Level 10 upgrade: Flowing Spring (+1 regen)
|
|
||||||
│ └── Future: Regen scales with max mana
|
|
||||||
└── Mana Threshold (+20% max mana, -10% regen)
|
|
||||||
└── Level 10 upgrade: Mana Conversion (Convert capacity to click bonus)
|
|
||||||
└── Future: Threshold mechanics
|
|
||||||
|
|
||||||
Level 10 Choices (additional):
|
|
||||||
├── Mana Echo (10% chance double mana from clicks)
|
|
||||||
├── Emergency Reserve (Keep 10% mana on loop reset)
|
|
||||||
└── Deep Wellspring (+50% meditation efficiency)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Study System
|
### Study System
|
||||||
|
|
||||||
Leveling skills requires:
|
Leveling skills requires:
|
||||||
@@ -57,134 +35,426 @@ Leveling skills requires:
|
|||||||
2. **Study time** - Hours required to complete
|
2. **Study time** - Hours required to complete
|
||||||
3. **Active studying** - Must be in "study" action mode
|
3. **Active studying** - Must be in "study" action mode
|
||||||
|
|
||||||
Study costs scale with:
|
#### Study Cost Formula
|
||||||
- Current level (higher levels = more expensive)
|
```
|
||||||
- Skill tier (higher tiers = more expensive)
|
cost = baseCost × (currentLevel + 1) × tier × costMultiplier
|
||||||
- Focused Mind skill (reduces cost)
|
```
|
||||||
|
|
||||||
### Attunement Requirements
|
#### Study Time Formula
|
||||||
|
```
|
||||||
|
time = baseStudyTime × tier / studySpeedMultiplier
|
||||||
|
```
|
||||||
|
|
||||||
Skills have attunement requirements for:
|
### Milestone Upgrades
|
||||||
1. **Access** - Which attunement unlocks the skill category
|
|
||||||
2. **Tier Up** - Required attunement levels to advance tiers
|
|
||||||
|
|
||||||
| Requirement Type | Example |
|
At **levels 5 and 10**, you choose **1 upgrade** from an upgrade tree:
|
||||||
|-----------------|---------|
|
- Each skill has its own unique upgrade tree
|
||||||
| Single Attunement | Enchanting skills require Enchanter |
|
- Trees have branching paths with prerequisites
|
||||||
| Multiple Attunements | Hybrid skills require both attunements leveled |
|
- Choices are permanent for that tier
|
||||||
| Any Attunement | Core skills just need any attunement active |
|
- Upgrades persist when tiering up
|
||||||
| No Requirement | Basic skills always available |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Skill Categories
|
## Skill Categories
|
||||||
|
|
||||||
### Core Skills (No Attunement Required)
|
### Core Categories (No Attunement Required)
|
||||||
|
|
||||||
Always available to all players.
|
| Category | Icon | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| Mana | 💧 | Mana pool and regeneration |
|
||||||
|
| Study | 📚 | Learning speed and efficiency |
|
||||||
|
| Research | 🔮 | Permanent bonuses |
|
||||||
|
| Ascension | ⭐ | Loop-persisting benefits |
|
||||||
|
|
||||||
#### Mana Category
|
### Attunement Categories
|
||||||
| Skill | Max Level | Effect | Description |
|
|
||||||
|-------|-----------|--------|-------------|
|
|
||||||
| Mana Well | 10 | +100 max mana per level | Increases your mana reservoir |
|
|
||||||
| Mana Flow | 10 | +1 regen/hour per level | Faster mana regeneration |
|
|
||||||
| Elemental Attunement | 10 | +50 element cap per level | Store more elemental mana |
|
|
||||||
|
|
||||||
#### Study Category
|
| Category | Icon | Attunement | Description |
|
||||||
| Skill | Max Level | Effect | Description |
|
|----------|------|------------|-------------|
|
||||||
|-------|-----------|--------|-------------|
|
| Enchanting | ✨ | Enchanter | Enchantment design and efficiency |
|
||||||
| Quick Learner | 10 | +10% study speed per level | Learn faster |
|
| Effect Research | 🔬 | Enchanter | Unlock spell enchantments |
|
||||||
| Focused Mind | 10 | -5% study cost per level | Learn cheaper |
|
| Invocation | 💜 | Invoker | Pact-based abilities |
|
||||||
| Knowledge Retention | 3 | +20% progress saved on cancel | Don't lose study progress |
|
| Pact Mastery | 🤝 | Invoker | Guardian pact bonuses |
|
||||||
| Meditation Focus | 1 | Up to 2.5x regen after 4hr meditating | Enhanced meditation |
|
| Fabrication | ⚒️ | Fabricator | Crafting and construction |
|
||||||
|
| Golemancy | 🗿 | Fabricator | Golem summoning and control |
|
||||||
#### Research Category (Long Study Times)
|
|
||||||
| Skill | Max Level | Effect | Description |
|
|
||||||
|-------|-----------|--------|-------------|
|
|
||||||
| Mana Tap | 1 | +1 mana/click | Better clicking |
|
|
||||||
| Mana Surge | 1 | +3 mana/click | Requires Mana Tap |
|
|
||||||
| Mana Spring | 1 | +2 mana regen | Passive bonus |
|
|
||||||
| Deep Trance | 1 | 6hr meditation = 3x regen | Requires Meditation |
|
|
||||||
| Void Meditation | 1 | 8hr meditation = 5x regen | Requires Deep Trance |
|
|
||||||
|
|
||||||
### Enchanter Skills (Requires Enchanter Attunement)
|
|
||||||
|
|
||||||
#### Enchanting Category
|
|
||||||
| Skill | Max Level | Effect | Attunement Req |
|
|
||||||
|-------|-----------|--------|----------------|
|
|
||||||
| Enchanting | 10 | Unlocks enchantment design | Enchanter 1 |
|
|
||||||
| Efficient Enchant | 5 | -5% capacity cost per level | Enchanter 2 |
|
|
||||||
| Disenchanting | 3 | +20% mana recovery per level | Enchanter 1 |
|
|
||||||
| Enchant Speed | 5 | -10% enchant time per level | Enchanter 1 |
|
|
||||||
| Essence Refining | 5 | +10% effect power per level | Enchanter 2 |
|
|
||||||
|
|
||||||
#### Effect Research Category
|
|
||||||
Research skills unlock enchantment effects. All are level 1 only and require Enchanter attunement.
|
|
||||||
|
|
||||||
**Tier 1 Research (Basic Spells)**
|
|
||||||
- Mana Spell Research → Mana Strike enchantment
|
|
||||||
- Fire Spell Research → Ember Shot, Fireball enchantments
|
|
||||||
- Water Spell Research → Water Jet, Ice Shard enchantments
|
|
||||||
- Air Spell Research → Gust, Wind Slash enchantments
|
|
||||||
- Earth Spell Research → Stone Bullet, Rock Spike enchantments
|
|
||||||
- Light Spell Research → Light Lance, Radiance enchantments
|
|
||||||
- Dark Spell Research → Shadow Bolt, Dark Pulse enchantments
|
|
||||||
- Death Research → Drain enchantment
|
|
||||||
|
|
||||||
**Tier 2 Research (Advanced Spells)** - Requires Enchanter 3
|
|
||||||
- Advanced Fire/Water/Air/Earth Research
|
|
||||||
- Advanced Light/Dark Research
|
|
||||||
|
|
||||||
**Tier 3 Research (Master Spells)** - Requires Enchanter 5
|
|
||||||
- Master Fire/Water/Earth Research
|
|
||||||
|
|
||||||
**Compound Mana Research** - Requires parent element research
|
|
||||||
- Metal Spell Research (Fire + Earth)
|
|
||||||
- Sand Spell Research (Earth + Water)
|
|
||||||
- Lightning Spell Research (Fire + Air)
|
|
||||||
- Plus Advanced/Master variants
|
|
||||||
|
|
||||||
**Utility Research**
|
|
||||||
- Transference Spell Research
|
|
||||||
- Damage Effect Research
|
|
||||||
- Mana Effect Research
|
|
||||||
- Utility Effect Research
|
|
||||||
|
|
||||||
### Invoker Skills (Requires Invoker Attunement)
|
|
||||||
|
|
||||||
#### Pact Category
|
|
||||||
| Skill | Max Level | Effect | Attunement Req |
|
|
||||||
|-------|-----------|--------|----------------|
|
|
||||||
| Pact Mastery | 5 | +10% pact bonus per level | Invoker 1 |
|
|
||||||
| Guardian Insight | 3 | See guardian weaknesses | Invoker 2 |
|
|
||||||
| Pact Efficiency | 3 | -10% pact ritual cost | Invoker 2 |
|
|
||||||
|
|
||||||
### Fabricator Skills (Requires Fabricator Attunement)
|
|
||||||
|
|
||||||
#### Golemancy Category
|
|
||||||
| Skill | Max Level | Effect | Attunement Req |
|
|
||||||
|-------|-----------|--------|----------------|
|
|
||||||
| Golem Mastery | 5 | +10% golem damage per level | Fabricator 2 |
|
|
||||||
| Golem Efficiency | 5 | +5% attack speed per level | Fabricator 2 |
|
|
||||||
| Golem Longevity | 3 | +1 floor duration per level | Fabricator 3 |
|
|
||||||
| Golem Siphon | 3 | -10% maintenance per level | Fabricator 3 |
|
|
||||||
| Advanced Golemancy | 1 | Unlock hybrid golem recipes | Fabricator 5 |
|
|
||||||
| Golem Resonance | 1 | +1 golem slot at max level | Fabricator 8 |
|
|
||||||
|
|
||||||
### Ascension Skills (Require Any Attunement Level 5+)
|
|
||||||
|
|
||||||
| Skill | Max Level | Effect | Requirement |
|
|
||||||
|-------|-----------|--------|-------------|
|
|
||||||
| Insight Harvest | 5 | +10% insight per level | Any attunement 5 |
|
|
||||||
| Temporal Memory | 3 | Keep 1 spell per level across loops | Any attunement 5 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Tier Up Requirements
|
## All Skills Reference
|
||||||
|
|
||||||
Each tier requires specific attunement levels:
|
### Mana Skills (Core)
|
||||||
|
|
||||||
### Core Skills (Mana, Study)
|
| Skill | Max | Effect | Base Cost | Study Time |
|
||||||
|
|-------|-----|--------|-----------|------------|
|
||||||
|
| Mana Well | 10 | +100 max mana/level | 100 | 4h |
|
||||||
|
| Mana Flow | 10 | +1 regen/hour/level | 150 | 5h |
|
||||||
|
| Elemental Attunement | 10 | +50 element cap/level | 200 | 4h |
|
||||||
|
| Mana Overflow | 5 | +25% click mana/level | 400 | 6h |
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
- Mana Overflow: Mana Well 3
|
||||||
|
|
||||||
|
### Study Skills (Core)
|
||||||
|
|
||||||
|
| Skill | Max | Effect | Base Cost | Study Time |
|
||||||
|
|-------|-----|--------|-----------|------------|
|
||||||
|
| Quick Learner | 10 | +10% study speed/level | 250 | 4h |
|
||||||
|
| Focused Mind | 10 | -5% study cost/level | 300 | 5h |
|
||||||
|
| Meditation Focus | 1 | Up to 2.5x regen after 4hrs | 400 | 6h |
|
||||||
|
| Knowledge Retention | 3 | +20% progress saved on cancel/level | 350 | 5h |
|
||||||
|
|
||||||
|
### Research Skills (Core)
|
||||||
|
|
||||||
|
| Skill | Max | Effect | Base Cost | Study Time |
|
||||||
|
|-------|-----|--------|-----------|------------|
|
||||||
|
| Mana Tap | 1 | +1 mana/click | 300 | 12h |
|
||||||
|
| Mana Surge | 1 | +3 mana/click | 800 | 36h |
|
||||||
|
| Mana Spring | 1 | +2 mana regen | 600 | 24h |
|
||||||
|
| Deep Trance | 1 | 6hr meditation = 3x regen | 900 | 48h |
|
||||||
|
| Void Meditation | 1 | 8hr meditation = 5x regen | 1500 | 72h |
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
- Mana Surge: Mana Tap 1
|
||||||
|
- Deep Trance: Meditation 1
|
||||||
|
- Void Meditation: Deep Trance 1
|
||||||
|
|
||||||
|
### Ascension Skills (Any Attunement)
|
||||||
|
|
||||||
|
| Skill | Max | Effect | Base Cost | Study Time | Attunement Req |
|
||||||
|
|-------|-----|--------|-----------|------------|----------------|
|
||||||
|
| Insight Harvest | 5 | +10% insight/level | 1000 | 20h | Any level 5+ |
|
||||||
|
| Temporal Memory | 3 | Keep 1 spell/level across loops | 2000 | 36h | Any level 5+ |
|
||||||
|
| Guardian Bane | 3 | +20% dmg vs guardians/level | 1500 | 30h | Invoker 1 |
|
||||||
|
|
||||||
|
### Enchanting Skills (Enchanter)
|
||||||
|
|
||||||
|
| Skill | Max | Effect | Base Cost | Study Time | Attunement Req |
|
||||||
|
|-------|-----|--------|-----------|------------|----------------|
|
||||||
|
| Enchanting | 10 | Unlocks enchantment design | 200 | 5h | Enchanter 1 |
|
||||||
|
| Efficient Enchant | 5 | -5% capacity cost/level | 350 | 6h | Enchanter 2 |
|
||||||
|
| Disenchanting | 3 | +20% mana recovery/level | 400 | 6h | Enchanter 1 |
|
||||||
|
| Enchant Speed | 5 | -10% enchant time/level | 300 | 4h | Enchanter 1 |
|
||||||
|
| Scroll Crafting | 3 | Store enchantment designs | 500 | 8h | Enchanter 3 |
|
||||||
|
| Essence Refining | 5 | +10% effect power/level | 450 | 7h | Enchanter 2 |
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
- Efficient Enchant: Enchanting 3
|
||||||
|
- Disenchanting: Enchanting 2
|
||||||
|
- Enchant Speed: Enchanting 2
|
||||||
|
- Scroll Crafting: Enchanting 5
|
||||||
|
- Essence Refining: Enchanting 4
|
||||||
|
|
||||||
|
### Golemancy Skills (Fabricator)
|
||||||
|
|
||||||
|
| Skill | Max | Effect | Base Cost | Study Time | Attunement Req |
|
||||||
|
|-------|-----|--------|-----------|------------|----------------|
|
||||||
|
| Golem Mastery | 5 | +10% golem damage/level | 300 | 6h | Fabricator 2 |
|
||||||
|
| Golem Efficiency | 5 | +5% attack speed/level | 350 | 6h | Fabricator 2 |
|
||||||
|
| Golem Longevity | 3 | +1 floor duration/level | 500 | 8h | Fabricator 3 |
|
||||||
|
| Golem Siphon | 3 | -10% maintenance/level | 400 | 8h | Fabricator 3 |
|
||||||
|
| Advanced Golemancy | 1 | Unlock hybrid recipes | 800 | 16h | Fabricator 5 |
|
||||||
|
| Golem Resonance | 1 | +1 golem slot | 1200 | 24h | Fabricator 8 |
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
- Advanced Golemancy: Golem Mastery 3
|
||||||
|
- Golem Resonance: Golem Mastery 5
|
||||||
|
|
||||||
|
### Effect Research Skills (Enchanter)
|
||||||
|
|
||||||
|
All effect research skills are **max level 1** and unlock specific enchantment effects.
|
||||||
|
|
||||||
|
#### Tier 1 Research (Basic Spells)
|
||||||
|
| Skill | Unlocks | Study Time |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| Mana Spell Research | Mana Strike enchantment | 4h |
|
||||||
|
| Fire Spell Research | Ember Shot, Fireball | 6h |
|
||||||
|
| Water Spell Research | Water Jet, Ice Shard | 6h |
|
||||||
|
| Air Spell Research | Gust, Wind Slash | 6h |
|
||||||
|
| Earth Spell Research | Stone Bullet, Rock Spike | 6h |
|
||||||
|
| Light Spell Research | Light Lance, Radiance | 8h |
|
||||||
|
| Dark Spell Research | Shadow Bolt, Dark Pulse | 8h |
|
||||||
|
| Death Research | Drain enchantment | 8h |
|
||||||
|
|
||||||
|
#### Tier 2 Research (Advanced Spells)
|
||||||
|
Requires Enchanter 3+ and parent element research.
|
||||||
|
|
||||||
|
| Skill | Unlocks | Study Time |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| Advanced Fire Research | Inferno, Flame Wave | 12h |
|
||||||
|
| Advanced Water Research | Tidal Wave, Ice Storm | 12h |
|
||||||
|
| Advanced Air Research | Hurricane, Wind Blade | 12h |
|
||||||
|
| Advanced Earth Research | Earthquake, Stone Barrage | 12h |
|
||||||
|
| Advanced Light Research | Solar Flare, Divine Smite | 14h |
|
||||||
|
| Advanced Dark Research | Void Rift, Shadow Storm | 14h |
|
||||||
|
|
||||||
|
#### Tier 3 Research (Master Spells)
|
||||||
|
Requires Enchanter 5+ and advanced research.
|
||||||
|
|
||||||
|
| Skill | Unlocks | Study Time |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| Master Fire Research | Pyroclasm | 24h |
|
||||||
|
| Master Water Research | Tsunami | 24h |
|
||||||
|
| Master Earth Research | Meteor Strike | 26h |
|
||||||
|
|
||||||
|
#### Compound Element Research
|
||||||
|
Requires parent element research + Enchanter 3+.
|
||||||
|
|
||||||
|
| Skill | Unlocks | Study Time |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| Metal Spell Research | Metal Shard, Iron Fist | 6h |
|
||||||
|
| Sand Spell Research | Sand Blast, Sandstorm | 6h |
|
||||||
|
| Lightning Spell Research | Spark, Lightning Bolt | 6h |
|
||||||
|
| Advanced Metal Research | Steel Tempest | 12h |
|
||||||
|
| Advanced Sand Research | Desert Wind | 12h |
|
||||||
|
| Advanced Lightning Research | Chain Lightning, Storm Call | 12h |
|
||||||
|
| Master Metal Research | Furnace Blast | 26h |
|
||||||
|
| Master Sand Research | Dune Collapse | 26h |
|
||||||
|
| Master Lightning Research | Thunder Strike | 26h |
|
||||||
|
|
||||||
|
#### Utility Research
|
||||||
|
|
||||||
|
| Skill | Unlocks | Study Time |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| Transference Spell Research | Transfer Strike, Mana Rip | 5h |
|
||||||
|
| Advanced Transference Research | Essence Drain | 12h |
|
||||||
|
| Master Transference Research | Soul Transfer | 26h |
|
||||||
|
|
||||||
|
#### Effect Research
|
||||||
|
|
||||||
|
| Skill | Unlocks | Study Time |
|
||||||
|
|-------|---------|------------|
|
||||||
|
| Damage Effect Research | Minor/Moderate Power, Amplification | 5h |
|
||||||
|
| Combat Effect Research | Sharp Edge, Swift Casting | 6h |
|
||||||
|
| Mana Effect Research | Mana Reserve, Trickle, Mana Tap | 4h |
|
||||||
|
| Advanced Mana Research | Mana Reservoir, Stream, River | 8h |
|
||||||
|
| Utility Effect Research | Meditative Focus, Quick Study | 6h |
|
||||||
|
| Special Effect Research | Echo Chamber, Siphoning, Bane | 10h |
|
||||||
|
| Overpower Research | Overpower effect | 12h |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Upgrade Trees
|
||||||
|
|
||||||
|
### Mana Well Upgrade Tree
|
||||||
|
|
||||||
|
#### Tier 1 Upgrades
|
||||||
|
|
||||||
|
**Level 5 Choices:**
|
||||||
|
```
|
||||||
|
├── Expanded Capacity (+25% max mana)
|
||||||
|
│ └── Level 10: Deep Reservoir (+50% max mana) [replaces]
|
||||||
|
│
|
||||||
|
├── Natural Spring (+0.5 regen/hour)
|
||||||
|
│ └── Level 10: Flowing Spring (+1.5 regen) [replaces]
|
||||||
|
│
|
||||||
|
├── Mana Threshold (+30% max mana, -10% regen)
|
||||||
|
│ └── Level 10: Mana Conversion (5% max → click bonus)
|
||||||
|
│
|
||||||
|
└── Desperate Wells (+50% regen when below 25% mana)
|
||||||
|
└── Level 10: Panic Reserve (+100% regen below 10%)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Level 10 Additional Choices:**
|
||||||
|
- Mana Echo (10% chance double mana from clicks)
|
||||||
|
- Emergency Reserve (Keep 10% mana on loop reset)
|
||||||
|
- Deep Wellspring (+50% meditation efficiency)
|
||||||
|
|
||||||
|
#### Tier 2 Upgrades (Deep Reservoir)
|
||||||
|
- Abyssal Depth (+50% max mana)
|
||||||
|
- Ancient Well (+500 starting mana per loop)
|
||||||
|
- Mana Condense (+1% max per 1000 gathered)
|
||||||
|
- Deep Reserve (+0.5 regen per 100 max mana)
|
||||||
|
- Ocean of Mana (+1000 max mana)
|
||||||
|
- Mana Tide (Regen pulses ±50%)
|
||||||
|
- Void Storage (Store 150% max temporarily)
|
||||||
|
- Mana Core (0.5% max mana as regen)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Mana Flow Upgrade Tree
|
||||||
|
|
||||||
|
#### Tier 1 Upgrades
|
||||||
|
|
||||||
|
**Level 5 Choices:**
|
||||||
|
```
|
||||||
|
├── Rapid Flow (+25% regen speed)
|
||||||
|
│ └── Level 10: Mana Torrent (+50% regen above 75% mana)
|
||||||
|
│
|
||||||
|
├── Steady Stream (Immune to incursion penalty)
|
||||||
|
│ └── Level 10: Eternal Flow (Immune to all penalties)
|
||||||
|
│
|
||||||
|
├── Mana Cascade (+0.1 regen per 100 max mana)
|
||||||
|
│ └── Level 10: Mana Waterfall (+0.25 per 100 max) [replaces]
|
||||||
|
│
|
||||||
|
└── Mana Overflow (Raw mana can exceed max by 20%)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Level 10 Additional Choices:**
|
||||||
|
- Ambient Absorption (+1 permanent regen)
|
||||||
|
- Flow Surge (Clicks boost regen for 1 hour)
|
||||||
|
- Flow Mastery (+10% mana from all sources)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Elemental Attunement Upgrade Tree
|
||||||
|
|
||||||
|
#### Tier 1 Upgrades
|
||||||
|
|
||||||
|
**Level 5 Choices:**
|
||||||
|
```
|
||||||
|
├── Expanded Attunement (+25% element cap)
|
||||||
|
│ └── Level 10: Element Master (+50% element cap) [replaces]
|
||||||
|
│
|
||||||
|
├── Elemental Surge (+15% elemental spell damage)
|
||||||
|
│ └── Level 10: Elemental Power (+30% damage) [replaces]
|
||||||
|
│
|
||||||
|
└── Elemental Affinity (New elements start with 10 capacity)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Level 10 Additional Choices:**
|
||||||
|
- Elemental Resonance (Spell use restores element)
|
||||||
|
- Exotic Mastery (+20% exotic element damage)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Quick Learner Upgrade Tree
|
||||||
|
|
||||||
|
#### Tier 1 Upgrades
|
||||||
|
|
||||||
|
**Level 5 Choices:**
|
||||||
|
```
|
||||||
|
├── Deep Focus (+25% study speed)
|
||||||
|
│ └── Level 10: Deep Concentration (+50% speed) [replaces]
|
||||||
|
│
|
||||||
|
├── Quick Grasp (5% chance double study progress)
|
||||||
|
│ └── Level 10: Knowledge Echo (15% instant complete)
|
||||||
|
│
|
||||||
|
├── Parallel Study (Study 2 things at 50% speed each)
|
||||||
|
│
|
||||||
|
└── Quick Mastery (-20% time for final 3 levels)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Level 10 Additional Choices:**
|
||||||
|
- Study Momentum (+5% speed per hour, max 50%)
|
||||||
|
- Knowledge Transfer (New skills start at 10% progress)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Focused Mind Upgrade Tree
|
||||||
|
|
||||||
|
#### Tier 1 Upgrades
|
||||||
|
|
||||||
|
**Level 5 Choices:**
|
||||||
|
```
|
||||||
|
├── Mind Efficiency (+25% cost reduction)
|
||||||
|
│ └── Level 10: Efficient Learning (-15% study cost) [replaces]
|
||||||
|
│
|
||||||
|
├── Mental Clarity (+10% speed when mana > 75%)
|
||||||
|
│ └── Level 10: Study Rush (First hour 2x speed)
|
||||||
|
│
|
||||||
|
└── Study Refund (25% mana back on completion)
|
||||||
|
└── Level 10: Deep Understanding (+10% skill bonuses)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Level 10 Additional Choices:**
|
||||||
|
- Chain Study (-5% cost per maxed skill)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Enchanting Upgrade Tree
|
||||||
|
|
||||||
|
#### Tier 1 Upgrades
|
||||||
|
|
||||||
|
**Level 5 Choices:**
|
||||||
|
```
|
||||||
|
├── Enchantment Capacity (+20% equipment capacity)
|
||||||
|
├── Swift Enchanting (-15% design time)
|
||||||
|
│
|
||||||
|
└── Quality Control (+10% effect power)
|
||||||
|
└── Level 10: Perfect Refinement (+25% power) [replaces]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Level 10 Additional Choices:**
|
||||||
|
- Enchantment Mastery (2 designs in progress)
|
||||||
|
- Mana Preservation (25% chance free enchant)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Golem Mastery Upgrade Tree
|
||||||
|
|
||||||
|
#### Tier 1 Upgrades
|
||||||
|
|
||||||
|
**Level 5 Choices:**
|
||||||
|
```
|
||||||
|
├── Golem Power (+25% golem damage)
|
||||||
|
├── Golem Durability (+1 floor duration)
|
||||||
|
│
|
||||||
|
└── Efficient Summons (-20% summon cost)
|
||||||
|
└── Level 10: Golem Siphon (-30% maintenance)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Level 10 Additional Choices:**
|
||||||
|
- Golem Fury (+50% attack speed for first 2 floors)
|
||||||
|
- Golem Resonance (Golems share 10% damage)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Other Skill Upgrade Trees
|
||||||
|
|
||||||
|
#### Mana Overflow (Max 5)
|
||||||
|
- **Level 5:** Click Surge (+50% click mana above 90% mana)
|
||||||
|
- **Tier 2 Level 5:** Mana Flood (+75% click mana above 75% mana)
|
||||||
|
|
||||||
|
#### Efficient Enchant (Max 5)
|
||||||
|
- **Level 5:** Thrifty Enchanter (+10% free enchant chance)
|
||||||
|
- **Tier 2 Level 5:** Optimized Enchanting (+25% free chance)
|
||||||
|
|
||||||
|
#### Enchant Speed (Max 5)
|
||||||
|
- **Level 5:** Hasty Enchanter (+25% speed for repeat designs)
|
||||||
|
- **Tier 2 Level 5:** Instant Designs (10% instant completion)
|
||||||
|
|
||||||
|
#### Essence Refining (Max 5)
|
||||||
|
- **Level 5:** Pure Essence (+25% power for tier 1 enchants)
|
||||||
|
- **Tier 2 Level 5:** Perfect Essence (+50% all enchant power)
|
||||||
|
|
||||||
|
#### Efficient Crafting (Max 5)
|
||||||
|
- **Level 5:** Batch Crafting (2 items at 75% speed each)
|
||||||
|
- **Tier 2 Level 5:** Mass Production (3 items at full speed)
|
||||||
|
|
||||||
|
#### Field Repair (Max 5)
|
||||||
|
- **Level 5:** Scavenge (Recover 10% materials from broken items)
|
||||||
|
- **Tier 2 Level 5:** Reclaim (Recover 25% materials)
|
||||||
|
|
||||||
|
#### Golem Efficiency (Max 5)
|
||||||
|
- **Level 5:** Rapid Strikes (+25% speed for first 3 floors)
|
||||||
|
- **Tier 2 Level 5:** Blitz Attack (+50% speed for first 5 floors)
|
||||||
|
|
||||||
|
#### Insight Harvest (Max 5)
|
||||||
|
- **Level 5:** Insight Bounty (+25% insight from guardians)
|
||||||
|
- **Tier 2 Level 5:** Greater Harvest (+50% insight from all sources)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tier System
|
||||||
|
|
||||||
|
### How Tiers Work
|
||||||
|
|
||||||
|
1. **Reach max level** (10 for most skills, 5 for specialized)
|
||||||
|
2. **Meet attunement requirements**
|
||||||
|
3. **Tier up** - Skill resets to level 1 with 10x power multiplier
|
||||||
|
|
||||||
|
### Tier Power Scaling
|
||||||
|
|
||||||
|
| Tier | Multiplier | Level 1 Power = |
|
||||||
|
|------|------------|-----------------|
|
||||||
|
| 1 | 1x | Base |
|
||||||
|
| 2 | 10x | Tier 1 Level 10 |
|
||||||
|
| 3 | 100x | Tier 2 Level 10 |
|
||||||
|
| 4 | 1000x | Tier 3 Level 10 |
|
||||||
|
| 5 | 10000x | Tier 4 Level 10 |
|
||||||
|
|
||||||
|
### Tier Up Requirements
|
||||||
|
|
||||||
|
#### Core Skills (Mana, Study)
|
||||||
| Tier | Requirement |
|
| Tier | Requirement |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| 1→2 | Any attunement level 3 |
|
| 1→2 | Any attunement level 3 |
|
||||||
@@ -192,7 +462,7 @@ Each tier requires specific attunement levels:
|
|||||||
| 3→4 | Any attunement level 7 |
|
| 3→4 | Any attunement level 7 |
|
||||||
| 4→5 | Any attunement level 10 |
|
| 4→5 | Any attunement level 10 |
|
||||||
|
|
||||||
### Enchanter Skills
|
#### Enchanter Skills
|
||||||
| Tier | Requirement |
|
| Tier | Requirement |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| 1→2 | Enchanter level 3 |
|
| 1→2 | Enchanter level 3 |
|
||||||
@@ -200,7 +470,7 @@ Each tier requires specific attunement levels:
|
|||||||
| 3→4 | Enchanter level 7 |
|
| 3→4 | Enchanter level 7 |
|
||||||
| 4→5 | Enchanter level 10 |
|
| 4→5 | Enchanter level 10 |
|
||||||
|
|
||||||
### Fabricator Skills (Golemancy)
|
#### Fabricator Skills (Golemancy)
|
||||||
| Tier | Requirement |
|
| Tier | Requirement |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| 1→2 | Fabricator level 3 |
|
| 1→2 | Fabricator level 3 |
|
||||||
@@ -208,116 +478,49 @@ Each tier requires specific attunement levels:
|
|||||||
| 3→4 | Fabricator level 7 |
|
| 3→4 | Fabricator level 7 |
|
||||||
| 4→5 | Fabricator level 10 |
|
| 4→5 | Fabricator level 10 |
|
||||||
|
|
||||||
### Hybrid Skills (Multiple Attunements)
|
|
||||||
| Tier | Requirement |
|
|
||||||
|------|-------------|
|
|
||||||
| 1→2 | Both attunements level 3 |
|
|
||||||
| 2→3 | Both attunements level 5 |
|
|
||||||
| etc. | Both attunements leveled together |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Upgrade Tree Design Principles
|
## Banned Content
|
||||||
|
|
||||||
### Branching Paths
|
The following effects/mechanics are **NOT allowed** in skill upgrades:
|
||||||
Each skill's upgrade tree should:
|
|
||||||
1. Offer meaningful choices that change playstyle
|
|
||||||
2. Allow players to specialize or generalize
|
|
||||||
3. Provide trade-offs (gain X, lose Y)
|
|
||||||
4. Scale with future milestone choices
|
|
||||||
|
|
||||||
### Upgrade Categories
|
|
||||||
|
|
||||||
**Multiplier Upgrades**: Increase the base effect
|
|
||||||
- Example: "+25% max mana" → "+50% max mana"
|
|
||||||
|
|
||||||
**Bonus Upgrades**: Add flat bonuses
|
|
||||||
- Example: "+0.5 regen" → "+1 regen"
|
|
||||||
|
|
||||||
**Special Mechanics**: Unique behaviors
|
|
||||||
- Example: "Mana Cascade" (+0.1 regen per 100 max mana)
|
|
||||||
- Example: "Steady Stream" (immune to incursion regen penalty)
|
|
||||||
|
|
||||||
**Trade-offs**: Risk/reward
|
|
||||||
- Example: "+20% max mana, -10% regen"
|
|
||||||
- Example: "+50% damage when below 50% mana"
|
|
||||||
|
|
||||||
### Upgrade Persistence
|
|
||||||
- **Milestone upgrades persist through tier-ups**
|
|
||||||
- Tier 2 starts with all Tier 1's chosen upgrades
|
|
||||||
- New tier offers new upgrade paths
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Banned Effects
|
|
||||||
|
|
||||||
The following effects are **NOT allowed** in skill upgrades:
|
|
||||||
|
|
||||||
| Banned Effect | Reason |
|
| Banned Effect | Reason |
|
||||||
|---------------|--------|
|
|---------------|--------|
|
||||||
| Lifesteal | Player cannot take damage |
|
| Lifesteal | Player cannot take damage |
|
||||||
| Healing | Player cannot take damage |
|
| Healing (for player) | Player cannot take damage |
|
||||||
| Life/Blood/Wood/Mental/Force mana | Removed elements |
|
| Life/Blood/Wood/Mental/Force mana | Removed elements |
|
||||||
| Execution effects | Instant kills bypass gameplay |
|
| Execution effects | Bypasses gameplay mechanics |
|
||||||
| Instant finishing | Skip mechanics |
|
| Instant finishing | Skips mechanics |
|
||||||
| Direct spell damage bonuses | Spells only via weapons |
|
| Direct spell damage bonuses | Spells only via weapons |
|
||||||
|
| Familiar system | Replaced by golemancy |
|
||||||
|
|
||||||
|
### Design Philosophy
|
||||||
|
|
||||||
|
1. **Player cannot take damage** - Only floors/enemies have HP
|
||||||
|
2. **No healing needed** - Player health doesn't exist
|
||||||
|
3. **Weapons matter** - Player attacks through enchanted weapons
|
||||||
|
4. **Golems fight** - Fabricator's constructs do the combat
|
||||||
|
5. **Enchantments empower** - Enchanter enhances equipment
|
||||||
|
6. **Pacts grant power** - Invoker makes deals with guardians
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Removed Skills
|
## Example Progression
|
||||||
|
|
||||||
The following skills have been removed because the player cannot cast offensive spells directly:
|
### Mana Well Complete Journey
|
||||||
|
|
||||||
| Removed Skill | Reason |
|
1. **Level 1-4:** +400 max mana (100 per level)
|
||||||
|---------------|--------|
|
2. **Level 5:** Choose "Expanded Capacity" (+25% max)
|
||||||
| Combat Training | No direct spell casting |
|
- Total: 500 base + 125 bonus = 625 max mana
|
||||||
| Arcane Fury | Combat damage bonus |
|
3. **Level 6-9:** +400 more max mana
|
||||||
| Precision | Crit chance for spells |
|
4. **Level 10:** Choose "Deep Reservoir" (replaces to +50%)
|
||||||
| Quick Cast | Spell cast speed |
|
- Total: 1000 base + 500 bonus = 1500 max mana
|
||||||
| Elemental Mastery | Elemental damage bonus |
|
5. **Tier Up to Tier 2:** Mana Well becomes "Deep Reservoir"
|
||||||
| Spell Echo | Spell mechanics |
|
6. **Tier 2 Level 1:** 100 × 10 = 1000 base (same as T1 L10)
|
||||||
| Guardian Bane | Direct damage to guardians |
|
7. **Tier 2 Level 5:** Choose "Abyssal Depth" (+50% max)
|
||||||
|
8. **Continue progression...**
|
||||||
|
|
||||||
---
|
### Total Power at Tier 2 Level 5:
|
||||||
|
- Base: 500 × 10 = 5000 max mana
|
||||||
## Implementation Notes
|
- Upgrades: +50% from Tier 1 +50% from Tier 2 = +100%
|
||||||
|
- Total: 5000 × 2 = **10,000 max mana**
|
||||||
### Study Cost Formula
|
|
||||||
```
|
|
||||||
cost = baseCost * (currentLevel + 1) * tier * costMultiplier
|
|
||||||
```
|
|
||||||
|
|
||||||
### Study Time Formula
|
|
||||||
```
|
|
||||||
time = baseStudyTime * tier / studySpeedMultiplier
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tier Multiplier
|
|
||||||
```
|
|
||||||
tierMultiplier = 10^(tier - 1)
|
|
||||||
```
|
|
||||||
- Tier 1: 1x
|
|
||||||
- Tier 2: 10x
|
|
||||||
- Tier 3: 100x
|
|
||||||
- Tier 4: 1000x
|
|
||||||
- Tier 5: 10000x
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Example: Complete Skill Progression
|
|
||||||
|
|
||||||
### Mana Well Journey
|
|
||||||
|
|
||||||
1. **Study Mana Well** → Level 1 (+100 max mana)
|
|
||||||
2. **Continue studying** → Level 2-4 (+400 more max mana)
|
|
||||||
3. **Level 5 Milestone** → Choose "Expanded Capacity" (+25% max mana)
|
|
||||||
4. **Continue studying** → Level 6-9 (+400 more max mana)
|
|
||||||
5. **Level 10 Milestone** → Choose "Deep Reservoir" (upgrades Expanded Capacity to +50%)
|
|
||||||
6. **Tier Up** → Mana Well becomes "Deep Reservoir" (Tier 2)
|
|
||||||
7. **Continue at Tier 2** → Level 1-5 with 10x multiplier
|
|
||||||
8. **New upgrades available** at Tier 2 milestones
|
|
||||||
|
|
||||||
Total effect at Tier 2, Level 5:
|
|
||||||
- Base: 500 * 10 = 5000 max mana
|
|
||||||
- +50% from upgrades: +2500 max mana
|
|
||||||
- Total: 7500 max mana from one skill!
|
|
||||||
|
|||||||
@@ -221,7 +221,8 @@ describe('computeRegen', () => {
|
|||||||
};
|
};
|
||||||
const effects = { regenBonus: 0, regenMultiplier: 1, permanentRegenBonus: 0 };
|
const effects = { regenBonus: 0, regenMultiplier: 1, permanentRegenBonus: 0 };
|
||||||
const result = computeRegen(state, effects);
|
const result = computeRegen(state, effects);
|
||||||
expect(result).toBe(2); // Base regen
|
// Base regen is 2 (this test provides effects, so no attunement bonus)
|
||||||
|
expect(result).toBe(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
347
src/lib/game/__tests__/skill-system.test.ts
Normal file
347
src/lib/game/__tests__/skill-system.test.ts
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import {
|
||||||
|
SKILL_EVOLUTION_PATHS,
|
||||||
|
getBaseSkillId,
|
||||||
|
generateTierSkillDef,
|
||||||
|
getUpgradesForSkillAtMilestone,
|
||||||
|
getNextTierSkill,
|
||||||
|
getTierMultiplier,
|
||||||
|
canTierUp,
|
||||||
|
getAvailableUpgrades,
|
||||||
|
} from '../skill-evolution';
|
||||||
|
import { SKILLS_DEF } from '../constants';
|
||||||
|
|
||||||
|
describe('Skill Evolution Paths', () => {
|
||||||
|
it('should have evolution paths for all skills with max > 1', () => {
|
||||||
|
const skillsWithMaxGt1 = Object.entries(SKILLS_DEF)
|
||||||
|
.filter(([_, def]) => def.max > 1)
|
||||||
|
.map(([id]) => id);
|
||||||
|
|
||||||
|
for (const skillId of skillsWithMaxGt1) {
|
||||||
|
expect(SKILL_EVOLUTION_PATHS[skillId], `Missing evolution path for ${skillId}`).toBeDefined();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have at least one tier for each evolution path', () => {
|
||||||
|
for (const [skillId, path] of Object.entries(SKILL_EVOLUTION_PATHS)) {
|
||||||
|
expect(path.tiers.length, `${skillId} should have at least one tier`).toBeGreaterThanOrEqual(1);
|
||||||
|
expect(path.baseSkillId, `${skillId} baseSkillId should match`).toBe(skillId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have correct tier multipliers (10x per tier)', () => {
|
||||||
|
for (const [skillId, path] of Object.entries(SKILL_EVOLUTION_PATHS)) {
|
||||||
|
for (const tier of path.tiers) {
|
||||||
|
const expectedMultiplier = Math.pow(10, tier.tier - 1);
|
||||||
|
expect(tier.multiplier, `${skillId} tier ${tier.tier} multiplier`).toBe(expectedMultiplier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have valid skill IDs for each tier', () => {
|
||||||
|
for (const [skillId, path] of Object.entries(SKILL_EVOLUTION_PATHS)) {
|
||||||
|
for (const tier of path.tiers) {
|
||||||
|
if (tier.tier === 1) {
|
||||||
|
expect(tier.skillId, `${skillId} tier 1 skillId`).toBe(skillId);
|
||||||
|
} else {
|
||||||
|
expect(tier.skillId, `${skillId} tier ${tier.tier} skillId`).toContain('_t');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getBaseSkillId', () => {
|
||||||
|
it('should return the same ID for base skills', () => {
|
||||||
|
expect(getBaseSkillId('manaWell')).toBe('manaWell');
|
||||||
|
expect(getBaseSkillId('manaFlow')).toBe('manaFlow');
|
||||||
|
expect(getBaseSkillId('enchanting')).toBe('enchanting');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should extract base ID from tiered skills', () => {
|
||||||
|
expect(getBaseSkillId('manaWell_t2')).toBe('manaWell');
|
||||||
|
expect(getBaseSkillId('manaFlow_t3')).toBe('manaFlow');
|
||||||
|
expect(getBaseSkillId('enchanting_t5')).toBe('enchanting');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateTierSkillDef', () => {
|
||||||
|
it('should return null for non-existent skills', () => {
|
||||||
|
expect(generateTierSkillDef('nonexistent', 1)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null for non-existent tiers', () => {
|
||||||
|
// Most skills don't have tier 10
|
||||||
|
expect(generateTierSkillDef('manaWell', 10)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct tier definition', () => {
|
||||||
|
const tier1 = generateTierSkillDef('manaWell', 1);
|
||||||
|
expect(tier1).not.toBeNull();
|
||||||
|
expect(tier1?.name).toBe('Mana Well');
|
||||||
|
expect(tier1?.tier).toBe(1);
|
||||||
|
expect(tier1?.multiplier).toBe(1);
|
||||||
|
|
||||||
|
const tier2 = generateTierSkillDef('manaWell', 2);
|
||||||
|
expect(tier2).not.toBeNull();
|
||||||
|
expect(tier2?.name).toBe('Deep Reservoir');
|
||||||
|
expect(tier2?.tier).toBe(2);
|
||||||
|
expect(tier2?.multiplier).toBe(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getUpgradesForSkillAtMilestone', () => {
|
||||||
|
it('should return empty array for non-existent skills', () => {
|
||||||
|
const upgrades = getUpgradesForSkillAtMilestone('nonexistent', 5, {});
|
||||||
|
expect(upgrades).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return upgrades for manaWell at milestone 5', () => {
|
||||||
|
const upgrades = getUpgradesForSkillAtMilestone('manaWell', 5, { manaWell: 1 });
|
||||||
|
expect(upgrades.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// All should be milestone 5
|
||||||
|
for (const upgrade of upgrades) {
|
||||||
|
expect(upgrade.milestone).toBe(5);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return upgrades for manaWell at milestone 10', () => {
|
||||||
|
const upgrades = getUpgradesForSkillAtMilestone('manaWell', 10, { manaWell: 1 });
|
||||||
|
expect(upgrades.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// All should be milestone 10
|
||||||
|
for (const upgrade of upgrades) {
|
||||||
|
expect(upgrade.milestone).toBe(10);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return tier 2 upgrades when at tier 2', () => {
|
||||||
|
const upgrades = getUpgradesForSkillAtMilestone('manaWell', 5, { manaWell: 2 });
|
||||||
|
expect(upgrades.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
|
// Should have tier 2 specific upgrades
|
||||||
|
const upgradeIds = upgrades.map(u => u.id);
|
||||||
|
expect(upgradeIds.some(id => id.startsWith('mw_t2'))).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getNextTierSkill', () => {
|
||||||
|
it('should return null for non-existent skills', () => {
|
||||||
|
expect(getNextTierSkill('nonexistent')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return next tier for tier 1 skills', () => {
|
||||||
|
expect(getNextTierSkill('manaWell')).toBe('manaWell_t2');
|
||||||
|
expect(getNextTierSkill('manaFlow')).toBe('manaFlow_t2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return next tier for higher tier skills', () => {
|
||||||
|
expect(getNextTierSkill('manaWell_t2')).toBe('manaWell_t3');
|
||||||
|
expect(getNextTierSkill('manaWell_t3')).toBe('manaWell_t4');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null for max tier skills', () => {
|
||||||
|
// manaWell has 5 tiers
|
||||||
|
expect(getNextTierSkill('manaWell_t5')).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTierMultiplier', () => {
|
||||||
|
it('should return 1 for tier 1 skills', () => {
|
||||||
|
expect(getTierMultiplier('manaWell')).toBe(1);
|
||||||
|
expect(getTierMultiplier('manaFlow')).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return 10 for tier 2 skills', () => {
|
||||||
|
expect(getTierMultiplier('manaWell_t2')).toBe(10);
|
||||||
|
expect(getTierMultiplier('manaFlow_t2')).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct multiplier for higher tiers', () => {
|
||||||
|
expect(getTierMultiplier('manaWell_t3')).toBe(100);
|
||||||
|
expect(getTierMultiplier('manaWell_t4')).toBe(1000);
|
||||||
|
expect(getTierMultiplier('manaWell_t5')).toBe(10000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('canTierUp', () => {
|
||||||
|
const mockAttunements = {
|
||||||
|
enchanter: { level: 10, active: true },
|
||||||
|
fabricator: { level: 10, active: true },
|
||||||
|
invoker: { level: 10, active: true },
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should return false for non-existent skills', () => {
|
||||||
|
const result = canTierUp('nonexistent', 10, {}, mockAttunements);
|
||||||
|
expect(result.canTierUp).toBe(false);
|
||||||
|
expect(result.reason).toBe('No evolution path');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if not at max level', () => {
|
||||||
|
const result = canTierUp('manaWell', 5, { manaWell: 1 }, mockAttunements);
|
||||||
|
expect(result.canTierUp).toBe(false);
|
||||||
|
expect(result.reason).toBe('Need level 10 to tier up');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true when at max level with attunement', () => {
|
||||||
|
const result = canTierUp('manaWell', 10, { manaWell: 1 }, mockAttunements);
|
||||||
|
expect(result.canTierUp).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if already at max tier', () => {
|
||||||
|
const result = canTierUp('manaWell_t5', 10, { manaWell: 5 }, mockAttunements);
|
||||||
|
expect(result.canTierUp).toBe(false);
|
||||||
|
expect(result.reason).toBe('Already at max tier');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getAvailableUpgrades', () => {
|
||||||
|
const manaWellTier1Tree = SKILL_EVOLUTION_PATHS.manaWell.tiers[0].upgrades;
|
||||||
|
|
||||||
|
it('should return only upgrades for specified milestone', () => {
|
||||||
|
const available = getAvailableUpgrades(
|
||||||
|
manaWellTier1Tree as any[],
|
||||||
|
[],
|
||||||
|
5,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const upgrade of available) {
|
||||||
|
expect(upgrade.milestone).toBe(5);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should exclude already chosen upgrades', () => {
|
||||||
|
const available = getAvailableUpgrades(
|
||||||
|
manaWellTier1Tree as any[],
|
||||||
|
['mw_t1_l5_capacity'],
|
||||||
|
5,
|
||||||
|
['mw_t1_l5_capacity']
|
||||||
|
);
|
||||||
|
|
||||||
|
const ids = available.map(u => u.id);
|
||||||
|
expect(ids).not.toContain('mw_t1_l5_capacity');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Skill Definitions', () => {
|
||||||
|
it('should have valid max levels for all skills', () => {
|
||||||
|
for (const [skillId, def] of Object.entries(SKILLS_DEF)) {
|
||||||
|
expect(def.max, `${skillId} max level`).toBeGreaterThanOrEqual(1);
|
||||||
|
expect(def.max, `${skillId} max level`).toBeLessThanOrEqual(10);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have study time defined for all skills', () => {
|
||||||
|
for (const [skillId, def] of Object.entries(SKILLS_DEF)) {
|
||||||
|
expect(def.studyTime, `${skillId} study time`).toBeGreaterThanOrEqual(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have base cost defined for all skills', () => {
|
||||||
|
for (const [skillId, def] of Object.entries(SKILLS_DEF)) {
|
||||||
|
expect(def.base, `${skillId} base cost`).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have correct categories for skills', () => {
|
||||||
|
const validCategories = ['mana', 'study', 'research', 'ascension', 'enchant',
|
||||||
|
'effectResearch', 'invocation', 'pact', 'fabrication', 'golemancy', 'craft'];
|
||||||
|
|
||||||
|
for (const [skillId, def] of Object.entries(SKILLS_DEF)) {
|
||||||
|
expect(validCategories, `${skillId} category`).toContain(def.cat);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Upgrade Tree Structure', () => {
|
||||||
|
it('should have valid effect types for all upgrades', () => {
|
||||||
|
const validTypes = ['multiplier', 'bonus', 'special'];
|
||||||
|
|
||||||
|
for (const [skillId, path] of Object.entries(SKILL_EVOLUTION_PATHS)) {
|
||||||
|
for (const tier of path.tiers) {
|
||||||
|
for (const upgrade of tier.upgrades) {
|
||||||
|
expect(validTypes, `${upgrade.id} effect type`).toContain(upgrade.effect.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have milestone 5 or 10 for all upgrades', () => {
|
||||||
|
for (const [skillId, path] of Object.entries(SKILL_EVOLUTION_PATHS)) {
|
||||||
|
for (const tier of path.tiers) {
|
||||||
|
for (const upgrade of tier.upgrades) {
|
||||||
|
expect([5, 10], `${upgrade.id} milestone`).toContain(upgrade.milestone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have unique upgrade IDs within each skill', () => {
|
||||||
|
for (const [skillId, path] of Object.entries(SKILL_EVOLUTION_PATHS)) {
|
||||||
|
const allIds: string[] = [];
|
||||||
|
for (const tier of path.tiers) {
|
||||||
|
for (const upgrade of tier.upgrades) {
|
||||||
|
expect(allIds, `${upgrade.id} should be unique in ${skillId}`).not.toContain(upgrade.id);
|
||||||
|
allIds.push(upgrade.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have descriptions for all upgrades', () => {
|
||||||
|
for (const [skillId, path] of Object.entries(SKILL_EVOLUTION_PATHS)) {
|
||||||
|
for (const tier of path.tiers) {
|
||||||
|
for (const upgrade of tier.upgrades) {
|
||||||
|
expect(upgrade.desc, `${upgrade.id} should have description`).toBeTruthy();
|
||||||
|
expect(upgrade.desc.length, `${upgrade.id} description length`).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have special descriptions for special effects', () => {
|
||||||
|
for (const [skillId, path] of Object.entries(SKILL_EVOLUTION_PATHS)) {
|
||||||
|
for (const tier of path.tiers) {
|
||||||
|
for (const upgrade of tier.upgrades) {
|
||||||
|
if (upgrade.effect.type === 'special') {
|
||||||
|
expect(upgrade.effect.specialDesc, `${upgrade.id} should have special description`).toBeTruthy();
|
||||||
|
expect(upgrade.effect.specialId, `${upgrade.id} should have special ID`).toBeTruthy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Skill Level and Tier Calculations', () => {
|
||||||
|
it('should calculate tier 2 level 1 as equivalent to tier 1 level 10', () => {
|
||||||
|
// Base skill gives +100 max mana per level
|
||||||
|
// At tier 1 level 10: 10 * 100 = 1000 max mana
|
||||||
|
// At tier 2 level 1 with 10x multiplier: 1 * 100 * 10 = 1000 max mana
|
||||||
|
const tier1Level10Effect = 10 * 100; // level * base
|
||||||
|
const tier2Level1Effect = 1 * 100 * 10; // level * base * tierMultiplier
|
||||||
|
|
||||||
|
expect(tier2Level1Effect).toBe(tier1Level10Effect);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have increasing power with tiers', () => {
|
||||||
|
// Tier 3 level 1 should be stronger than tier 2 level 10
|
||||||
|
const tier2Level10 = 10 * 100 * 10; // level * base * tierMultiplier
|
||||||
|
const tier3Level1 = 1 * 100 * 100; // level * base * tierMultiplier
|
||||||
|
|
||||||
|
expect(tier3Level1).toBe(tier2Level10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have correct max tier for skills', () => {
|
||||||
|
// Skills with max 5 should typically have 2-3 tiers
|
||||||
|
// Skills with max 10 should typically have 3-5 tiers
|
||||||
|
|
||||||
|
const manaWellPath = SKILL_EVOLUTION_PATHS.manaWell;
|
||||||
|
expect(manaWellPath.tiers.length).toBe(5); // manaWell has 5 tiers
|
||||||
|
|
||||||
|
const manaFlowPath = SKILL_EVOLUTION_PATHS.manaFlow;
|
||||||
|
expect(manaFlowPath.tiers.length).toBe(5); // manaFlow has 5 tiers
|
||||||
|
});
|
||||||
|
});
|
||||||
588
src/lib/game/__tests__/skills.test.ts
Normal file
588
src/lib/game/__tests__/skills.test.ts
Normal file
@@ -0,0 +1,588 @@
|
|||||||
|
/**
|
||||||
|
* Comprehensive Skill Tests
|
||||||
|
*
|
||||||
|
* Tests each skill to verify they work exactly as their descriptions say.
|
||||||
|
* Updated for the new skill system with tiers and upgrade trees.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import {
|
||||||
|
computeMaxMana,
|
||||||
|
computeElementMax,
|
||||||
|
computeRegen,
|
||||||
|
computeClickMana,
|
||||||
|
calcInsight,
|
||||||
|
getMeditationBonus,
|
||||||
|
} from '../computed-stats';
|
||||||
|
import {
|
||||||
|
SKILLS_DEF,
|
||||||
|
PRESTIGE_DEF,
|
||||||
|
GUARDIANS,
|
||||||
|
getStudySpeedMultiplier,
|
||||||
|
getStudyCostMultiplier,
|
||||||
|
ELEMENTS,
|
||||||
|
} from '../constants';
|
||||||
|
import {
|
||||||
|
SKILL_EVOLUTION_PATHS,
|
||||||
|
getUpgradesForSkillAtMilestone,
|
||||||
|
getNextTierSkill,
|
||||||
|
getTierMultiplier,
|
||||||
|
generateTierSkillDef,
|
||||||
|
canTierUp,
|
||||||
|
} from '../skill-evolution';
|
||||||
|
import type { GameState } from '../types';
|
||||||
|
|
||||||
|
// ─── Test Helpers ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||||
|
const elements: Record<string, { current: number; max: number; unlocked: boolean }> = {};
|
||||||
|
const baseElements = ['fire', 'water', 'air', 'earth', 'light', 'dark', 'death', 'transference', 'metal', 'sand', 'crystal', 'stellar', 'void', 'lightning'];
|
||||||
|
baseElements.forEach((k) => {
|
||||||
|
elements[k] = { current: 0, max: 10, unlocked: baseElements.slice(0, 4).includes(k) };
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
day: 1,
|
||||||
|
hour: 0,
|
||||||
|
loopCount: 0,
|
||||||
|
gameOver: false,
|
||||||
|
victory: false,
|
||||||
|
paused: false,
|
||||||
|
rawMana: 100,
|
||||||
|
meditateTicks: 0,
|
||||||
|
totalManaGathered: 0,
|
||||||
|
elements,
|
||||||
|
currentFloor: 1,
|
||||||
|
floorHP: 100,
|
||||||
|
floorMaxHP: 100,
|
||||||
|
castProgress: 0,
|
||||||
|
currentRoom: {
|
||||||
|
roomType: 'combat',
|
||||||
|
enemies: [{ id: 'enemy', hp: 100, maxHP: 100, armor: 0, dodgeChance: 0, element: 'fire' }],
|
||||||
|
},
|
||||||
|
maxFloorReached: 1,
|
||||||
|
signedPacts: [],
|
||||||
|
activeSpell: 'manaBolt',
|
||||||
|
currentAction: 'meditate',
|
||||||
|
spells: { manaBolt: { learned: true, level: 1, studyProgress: 0 } },
|
||||||
|
skills: {},
|
||||||
|
skillProgress: {},
|
||||||
|
skillUpgrades: {},
|
||||||
|
skillTiers: {},
|
||||||
|
parallelStudyTarget: null,
|
||||||
|
equippedInstances: { mainHand: null, offHand: null, head: null, body: null, hands: null, accessory1: null, accessory2: null },
|
||||||
|
equipmentInstances: {},
|
||||||
|
enchantmentDesigns: [],
|
||||||
|
designProgress: null,
|
||||||
|
preparationProgress: null,
|
||||||
|
applicationProgress: null,
|
||||||
|
equipmentCraftingProgress: null,
|
||||||
|
unlockedEffects: [],
|
||||||
|
equipmentSpellStates: [],
|
||||||
|
equipment: { mainHand: null, offHand: null, head: null, body: null, hands: null, accessory: null },
|
||||||
|
inventory: [],
|
||||||
|
blueprints: {},
|
||||||
|
lootInventory: { materials: {}, blueprints: [] },
|
||||||
|
schedule: [],
|
||||||
|
autoSchedule: false,
|
||||||
|
studyQueue: [],
|
||||||
|
craftQueue: [],
|
||||||
|
currentStudyTarget: null,
|
||||||
|
achievements: { unlocked: [], progress: {} },
|
||||||
|
totalSpellsCast: 0,
|
||||||
|
totalDamageDealt: 0,
|
||||||
|
totalCraftsCompleted: 0,
|
||||||
|
attunements: {
|
||||||
|
enchanter: { id: 'enchanter', active: true, level: 1, experience: 0 },
|
||||||
|
invoker: { id: 'invoker', active: false, level: 1, experience: 0 },
|
||||||
|
fabricator: { id: 'fabricator', active: false, level: 1, experience: 0 },
|
||||||
|
},
|
||||||
|
golemancy: {
|
||||||
|
enabledGolems: [],
|
||||||
|
summonedGolems: [],
|
||||||
|
lastSummonFloor: 0,
|
||||||
|
},
|
||||||
|
insight: 0,
|
||||||
|
totalInsight: 0,
|
||||||
|
prestigeUpgrades: {},
|
||||||
|
memorySlots: 3,
|
||||||
|
memories: [],
|
||||||
|
incursionStrength: 0,
|
||||||
|
containmentWards: 0,
|
||||||
|
log: [],
|
||||||
|
loopInsight: 0,
|
||||||
|
...overrides,
|
||||||
|
} as GameState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Mana Skills Tests ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Mana Skills', () => {
|
||||||
|
describe('Mana Well (+100 max mana)', () => {
|
||||||
|
it('should add 100 max mana per level', () => {
|
||||||
|
const state0 = createMockState({ skills: { manaWell: 0 } });
|
||||||
|
const state1 = createMockState({ skills: { manaWell: 1 } });
|
||||||
|
const state5 = createMockState({ skills: { manaWell: 5 } });
|
||||||
|
const state10 = createMockState({ skills: { manaWell: 10 } });
|
||||||
|
|
||||||
|
expect(computeMaxMana(state0, { maxManaBonus: 0, maxManaMultiplier: 1 })).toBe(100);
|
||||||
|
expect(computeMaxMana(state1, { maxManaBonus: 0, maxManaMultiplier: 1 })).toBe(100 + 100);
|
||||||
|
expect(computeMaxMana(state5, { maxManaBonus: 0, maxManaMultiplier: 1 })).toBe(100 + 500);
|
||||||
|
expect(computeMaxMana(state10, { maxManaBonus: 0, maxManaMultiplier: 1 })).toBe(100 + 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.manaWell.desc).toBe("+100 max mana");
|
||||||
|
expect(SKILLS_DEF.manaWell.max).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have upgrade tree', () => {
|
||||||
|
expect(SKILL_EVOLUTION_PATHS.manaWell).toBeDefined();
|
||||||
|
expect(SKILL_EVOLUTION_PATHS.manaWell.tiers.length).toBe(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Mana Flow (+1 regen/hr)', () => {
|
||||||
|
it('should add 1 regen per hour per level', () => {
|
||||||
|
const state0 = createMockState({ skills: { manaFlow: 0 } });
|
||||||
|
const state1 = createMockState({ skills: { manaFlow: 1 } });
|
||||||
|
const state5 = createMockState({ skills: { manaFlow: 5 } });
|
||||||
|
|
||||||
|
const effects = { regenBonus: 0, regenMultiplier: 1, permanentRegenBonus: 0 };
|
||||||
|
expect(computeRegen(state0, effects)).toBe(2);
|
||||||
|
expect(computeRegen(state1, effects)).toBe(2 + 1);
|
||||||
|
expect(computeRegen(state5, effects)).toBe(2 + 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.manaFlow.desc).toBe("+1 regen/hr");
|
||||||
|
expect(SKILLS_DEF.manaFlow.max).toBe(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Mana Spring (+2 mana regen)', () => {
|
||||||
|
it('should add 2 mana regen', () => {
|
||||||
|
const state0 = createMockState({ skills: { manaSpring: 0 } });
|
||||||
|
const state1 = createMockState({ skills: { manaSpring: 1 } });
|
||||||
|
|
||||||
|
const effects = { regenBonus: 0, regenMultiplier: 1, permanentRegenBonus: 0 };
|
||||||
|
expect(computeRegen(state0, effects)).toBe(2);
|
||||||
|
expect(computeRegen(state1, effects)).toBe(2 + 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.manaSpring.desc).toBe("+2 mana regen");
|
||||||
|
expect(SKILLS_DEF.manaSpring.max).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Elemental Attunement (+50 elem mana cap)', () => {
|
||||||
|
it('should add 50 element mana capacity per level', () => {
|
||||||
|
const state0 = createMockState({ skills: { elemAttune: 0 } });
|
||||||
|
const state1 = createMockState({ skills: { elemAttune: 1 } });
|
||||||
|
const state5 = createMockState({ skills: { elemAttune: 5 } });
|
||||||
|
|
||||||
|
expect(computeElementMax(state0, { elementCapBonus: 0, elementCapMultiplier: 1 })).toBe(10);
|
||||||
|
expect(computeElementMax(state1, { elementCapBonus: 0, elementCapMultiplier: 1 })).toBe(10 + 50);
|
||||||
|
expect(computeElementMax(state5, { elementCapBonus: 0, elementCapMultiplier: 1 })).toBe(10 + 250);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.elemAttune.desc).toBe("+50 elem mana cap");
|
||||||
|
expect(SKILLS_DEF.elemAttune.max).toBe(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Mana Overflow (+25% mana from clicks)', () => {
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.manaOverflow.desc).toBe("+25% mana from clicks");
|
||||||
|
expect(SKILLS_DEF.manaOverflow.max).toBe(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require Mana Well 3', () => {
|
||||||
|
expect(SKILLS_DEF.manaOverflow.req).toEqual({ manaWell: 3 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Mana Tap (+1 mana/click)', () => {
|
||||||
|
it('should add 1 mana per click', () => {
|
||||||
|
const state0 = createMockState({ skills: { manaTap: 0 } });
|
||||||
|
const state1 = createMockState({ skills: { manaTap: 1 } });
|
||||||
|
|
||||||
|
expect(computeClickMana(state0, { clickManaBonus: 0, clickManaMultiplier: 1 })).toBe(1);
|
||||||
|
expect(computeClickMana(state1, { clickManaBonus: 0, clickManaMultiplier: 1 })).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.manaTap.desc).toBe("+1 mana/click");
|
||||||
|
expect(SKILLS_DEF.manaTap.max).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Mana Surge (+3 mana/click)', () => {
|
||||||
|
it('should add 3 mana per click', () => {
|
||||||
|
const state1 = createMockState({ skills: { manaSurge: 1 } });
|
||||||
|
expect(computeClickMana(state1, { clickManaBonus: 0, clickManaMultiplier: 1 })).toBe(1 + 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should stack with Mana Tap', () => {
|
||||||
|
const state = createMockState({ skills: { manaTap: 1, manaSurge: 1 } });
|
||||||
|
expect(computeClickMana(state, { clickManaBonus: 0, clickManaMultiplier: 1 })).toBe(1 + 1 + 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require Mana Tap 1', () => {
|
||||||
|
expect(SKILLS_DEF.manaSurge.req).toEqual({ manaTap: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Study Skills Tests ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Study Skills', () => {
|
||||||
|
describe('Quick Learner (+10% study speed)', () => {
|
||||||
|
it('should multiply study speed by 10% per level', () => {
|
||||||
|
expect(getStudySpeedMultiplier({})).toBe(1);
|
||||||
|
expect(getStudySpeedMultiplier({ quickLearner: 1 })).toBe(1.1);
|
||||||
|
expect(getStudySpeedMultiplier({ quickLearner: 3 })).toBe(1.3);
|
||||||
|
expect(getStudySpeedMultiplier({ quickLearner: 5 })).toBe(1.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.quickLearner.desc).toBe("+10% study speed");
|
||||||
|
expect(SKILLS_DEF.quickLearner.max).toBe(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Focused Mind (-5% study mana cost)', () => {
|
||||||
|
it('should reduce study mana cost by 5% per level', () => {
|
||||||
|
expect(getStudyCostMultiplier({})).toBe(1);
|
||||||
|
expect(getStudyCostMultiplier({ focusedMind: 1 })).toBe(0.95);
|
||||||
|
expect(getStudyCostMultiplier({ focusedMind: 3 })).toBe(0.85);
|
||||||
|
expect(getStudyCostMultiplier({ focusedMind: 5 })).toBe(0.75);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.focusedMind.desc).toBe("-5% study mana cost");
|
||||||
|
expect(SKILLS_DEF.focusedMind.max).toBe(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Meditation Focus (Up to 2.5x regen after 4hrs)', () => {
|
||||||
|
it('should provide meditation bonus caps', () => {
|
||||||
|
expect(SKILLS_DEF.meditation.desc).toContain("2.5x");
|
||||||
|
expect(SKILLS_DEF.meditation.max).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Knowledge Retention (+20% study progress saved)', () => {
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.knowledgeRetention.desc).toBe("+20% study progress saved on cancel");
|
||||||
|
expect(SKILLS_DEF.knowledgeRetention.max).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Deep Trance (Extend to 6hrs for 3x)', () => {
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.deepTrance.desc).toContain("6hrs");
|
||||||
|
expect(SKILLS_DEF.deepTrance.max).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require Meditation 1', () => {
|
||||||
|
expect(SKILLS_DEF.deepTrance.req).toEqual({ meditation: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Void Meditation (Extend to 8hrs for 5x)', () => {
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.voidMeditation.desc).toContain("8hrs");
|
||||||
|
expect(SKILLS_DEF.voidMeditation.max).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should require Deep Trance 1', () => {
|
||||||
|
expect(SKILLS_DEF.voidMeditation.req).toEqual({ deepTrance: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Ascension Skills Tests ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Ascension Skills', () => {
|
||||||
|
describe('Insight Harvest (+10% insight gain)', () => {
|
||||||
|
it('should multiply insight gain by 10% per level', () => {
|
||||||
|
const state0 = createMockState({ maxFloorReached: 10, skills: { insightHarvest: 0 } });
|
||||||
|
const state1 = createMockState({ maxFloorReached: 10, skills: { insightHarvest: 1 } });
|
||||||
|
const state5 = createMockState({ maxFloorReached: 10, skills: { insightHarvest: 5 } });
|
||||||
|
|
||||||
|
const insight0 = calcInsight(state0);
|
||||||
|
const insight1 = calcInsight(state1);
|
||||||
|
const insight5 = calcInsight(state5);
|
||||||
|
|
||||||
|
expect(insight1).toBeGreaterThan(insight0);
|
||||||
|
expect(insight5).toBeGreaterThan(insight1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.insightHarvest.desc).toBe("+10% insight gain");
|
||||||
|
expect(SKILLS_DEF.insightHarvest.max).toBe(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Guardian Bane (+20% dmg vs guardians)', () => {
|
||||||
|
it('skill definition should match description', () => {
|
||||||
|
expect(SKILLS_DEF.guardianBane.desc).toBe("+20% dmg vs guardians");
|
||||||
|
expect(SKILLS_DEF.guardianBane.max).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Enchanter Skills Tests ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Enchanter Skills', () => {
|
||||||
|
describe('Enchanting (Unlock enchantment design)', () => {
|
||||||
|
it('skill definition should exist', () => {
|
||||||
|
expect(SKILLS_DEF.enchanting).toBeDefined();
|
||||||
|
expect(SKILLS_DEF.enchanting.attunement).toBe('enchanter');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Efficient Enchant (-5% enchantment capacity cost)', () => {
|
||||||
|
it('skill definition should exist', () => {
|
||||||
|
expect(SKILLS_DEF.efficientEnchant).toBeDefined();
|
||||||
|
expect(SKILLS_DEF.efficientEnchant.max).toBe(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Disenchanting (Recover mana from removed enchantments)', () => {
|
||||||
|
it('skill definition should exist', () => {
|
||||||
|
expect(SKILLS_DEF.disenchanting).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Golemancy Skills Tests ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Golemancy Skills', () => {
|
||||||
|
describe('Golem Mastery (+10% golem damage)', () => {
|
||||||
|
it('skill definition should exist', () => {
|
||||||
|
expect(SKILLS_DEF.golemMastery).toBeDefined();
|
||||||
|
expect(SKILLS_DEF.golemMastery.attunementReq).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Golem Efficiency (+5% attack speed)', () => {
|
||||||
|
it('skill definition should exist', () => {
|
||||||
|
expect(SKILLS_DEF.golemEfficiency).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Golem Longevity (+1 floor duration)', () => {
|
||||||
|
it('skill definition should exist', () => {
|
||||||
|
expect(SKILLS_DEF.golemLongevity).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Golem Siphon (-10% maintenance)', () => {
|
||||||
|
it('skill definition should exist', () => {
|
||||||
|
expect(SKILLS_DEF.golemSiphon).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Meditation Bonus Tests ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Meditation Bonus', () => {
|
||||||
|
it('should start at 1x with no meditation', () => {
|
||||||
|
expect(getMeditationBonus(0, {})).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should ramp up over time without skills', () => {
|
||||||
|
const bonus1hr = getMeditationBonus(25, {}); // 1 hour of ticks
|
||||||
|
expect(bonus1hr).toBeGreaterThan(1);
|
||||||
|
|
||||||
|
const bonus4hr = getMeditationBonus(100, {}); // 4 hours
|
||||||
|
expect(bonus4hr).toBeGreaterThan(bonus1hr);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cap at 1.5x without meditation skill', () => {
|
||||||
|
const bonus = getMeditationBonus(200, {}); // 8 hours
|
||||||
|
expect(bonus).toBe(1.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should give 2.5x with meditation skill after 4 hours', () => {
|
||||||
|
const bonus = getMeditationBonus(100, { meditation: 1 });
|
||||||
|
expect(bonus).toBe(2.5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should give 3.0x with deepTrance skill after 6 hours', () => {
|
||||||
|
const bonus = getMeditationBonus(150, { meditation: 1, deepTrance: 1 });
|
||||||
|
expect(bonus).toBe(3.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should give 5.0x with voidMeditation skill after 8 hours', () => {
|
||||||
|
const bonus = getMeditationBonus(200, { meditation: 1, deepTrance: 1, voidMeditation: 1 });
|
||||||
|
expect(bonus).toBe(5.0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Skill Prerequisites Tests ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Skill Prerequisites', () => {
|
||||||
|
it('Mana Overflow should require Mana Well 3', () => {
|
||||||
|
expect(SKILLS_DEF.manaOverflow.req).toEqual({ manaWell: 3 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Mana Surge should require Mana Tap 1', () => {
|
||||||
|
expect(SKILLS_DEF.manaSurge.req).toEqual({ manaTap: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Deep Trance should require Meditation 1', () => {
|
||||||
|
expect(SKILLS_DEF.deepTrance.req).toEqual({ meditation: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Void Meditation should require Deep Trance 1', () => {
|
||||||
|
expect(SKILLS_DEF.voidMeditation.req).toEqual({ deepTrance: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Efficient Enchant should require Enchanting 3', () => {
|
||||||
|
expect(SKILLS_DEF.efficientEnchant.req).toEqual({ enchanting: 3 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Study Time Tests ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Study Times', () => {
|
||||||
|
it('all skills should have reasonable study times', () => {
|
||||||
|
Object.entries(SKILLS_DEF).forEach(([id, skill]) => {
|
||||||
|
expect(skill.studyTime).toBeGreaterThan(0);
|
||||||
|
expect(skill.studyTime).toBeLessThanOrEqual(72);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ascension skills should have long study times', () => {
|
||||||
|
const ascensionSkills = Object.entries(SKILLS_DEF).filter(([, s]) => s.cat === 'ascension');
|
||||||
|
ascensionSkills.forEach(([, skill]) => {
|
||||||
|
expect(skill.studyTime).toBeGreaterThanOrEqual(20);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Prestige Upgrade Tests ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Prestige Upgrades', () => {
|
||||||
|
it('all prestige upgrades should have valid costs', () => {
|
||||||
|
Object.entries(PRESTIGE_DEF).forEach(([id, upgrade]) => {
|
||||||
|
expect(upgrade.cost).toBeGreaterThan(0);
|
||||||
|
expect(upgrade.max).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Mana Well prestige should add 500 starting max mana', () => {
|
||||||
|
const state0 = createMockState({ prestigeUpgrades: { manaWell: 0 } });
|
||||||
|
const state1 = createMockState({ prestigeUpgrades: { manaWell: 1 } });
|
||||||
|
const state5 = createMockState({ prestigeUpgrades: { manaWell: 5 } });
|
||||||
|
|
||||||
|
expect(computeMaxMana(state0, { maxManaBonus: 0, maxManaMultiplier: 1 })).toBe(100);
|
||||||
|
expect(computeMaxMana(state1, { maxManaBonus: 0, maxManaMultiplier: 1 })).toBe(100 + 500);
|
||||||
|
expect(computeMaxMana(state5, { maxManaBonus: 0, maxManaMultiplier: 1 })).toBe(100 + 2500);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Elemental Attunement prestige should add 25 element cap', () => {
|
||||||
|
const state0 = createMockState({ prestigeUpgrades: { elementalAttune: 0 } });
|
||||||
|
const state1 = createMockState({ prestigeUpgrades: { elementalAttune: 1 } });
|
||||||
|
const state10 = createMockState({ prestigeUpgrades: { elementalAttune: 10 } });
|
||||||
|
|
||||||
|
expect(computeElementMax(state0, { elementCapBonus: 0, elementCapMultiplier: 1 })).toBe(10);
|
||||||
|
expect(computeElementMax(state1, { elementCapBonus: 0, elementCapMultiplier: 1 })).toBe(10 + 25);
|
||||||
|
expect(computeElementMax(state10, { elementCapBonus: 0, elementCapMultiplier: 1 })).toBe(10 + 250);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Integration Tests ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Integration Tests', () => {
|
||||||
|
it('skill costs should scale with level', () => {
|
||||||
|
const skill = SKILLS_DEF.manaWell;
|
||||||
|
for (let level = 0; level < skill.max; level++) {
|
||||||
|
const cost = skill.base * (level + 1);
|
||||||
|
expect(cost).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('all skills should have valid categories', () => {
|
||||||
|
const validCategories = ['mana', 'study', 'ascension', 'enchant', 'effectResearch', 'invocation', 'pact', 'fabrication', 'golemancy', 'research', 'craft'];
|
||||||
|
Object.values(SKILLS_DEF).forEach(skill => {
|
||||||
|
expect(validCategories).toContain(skill.cat);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('all prerequisite skills should exist', () => {
|
||||||
|
Object.entries(SKILLS_DEF).forEach(([id, skill]) => {
|
||||||
|
if (skill.req) {
|
||||||
|
Object.keys(skill.req).forEach(reqId => {
|
||||||
|
expect(SKILLS_DEF[reqId]).toBeDefined();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('all prerequisite levels should be within skill max', () => {
|
||||||
|
Object.entries(SKILLS_DEF).forEach(([id, skill]) => {
|
||||||
|
if (skill.req) {
|
||||||
|
Object.entries(skill.req).forEach(([reqId, reqLevel]) => {
|
||||||
|
expect(reqLevel).toBeLessThanOrEqual(SKILLS_DEF[reqId].max);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('all attunement-requiring skills should have valid attunement', () => {
|
||||||
|
const validAttunements = ['enchanter', 'invoker', 'fabricator'];
|
||||||
|
Object.entries(SKILLS_DEF).forEach(([id, skill]) => {
|
||||||
|
if (skill.attunement) {
|
||||||
|
expect(validAttunements).toContain(skill.attunement);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Skill Evolution Tests ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Skill Evolution', () => {
|
||||||
|
it('skills with max > 1 should have evolution paths', () => {
|
||||||
|
const skillsWithMaxGt1 = Object.entries(SKILLS_DEF)
|
||||||
|
.filter(([_, def]) => def.max > 1)
|
||||||
|
.map(([id]) => id);
|
||||||
|
|
||||||
|
for (const skillId of skillsWithMaxGt1) {
|
||||||
|
expect(SKILL_EVOLUTION_PATHS[skillId], `Missing evolution path for ${skillId}`).toBeDefined();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('tier multiplier should be 10^(tier-1)', () => {
|
||||||
|
expect(getTierMultiplier('manaWell')).toBe(1);
|
||||||
|
expect(getTierMultiplier('manaWell_t2')).toBe(10);
|
||||||
|
expect(getTierMultiplier('manaWell_t3')).toBe(100);
|
||||||
|
expect(getTierMultiplier('manaWell_t4')).toBe(1000);
|
||||||
|
expect(getTierMultiplier('manaWell_t5')).toBe(10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getNextTierSkill should return correct next tier', () => {
|
||||||
|
expect(getNextTierSkill('manaWell')).toBe('manaWell_t2');
|
||||||
|
expect(getNextTierSkill('manaWell_t2')).toBe('manaWell_t3');
|
||||||
|
expect(getNextTierSkill('manaWell_t5')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('generateTierSkillDef should return valid definitions', () => {
|
||||||
|
const tier1 = generateTierSkillDef('manaWell', 1);
|
||||||
|
expect(tier1).not.toBeNull();
|
||||||
|
expect(tier1?.name).toBe('Mana Well');
|
||||||
|
expect(tier1?.multiplier).toBe(1);
|
||||||
|
|
||||||
|
const tier2 = generateTierSkillDef('manaWell', 2);
|
||||||
|
expect(tier2).not.toBeNull();
|
||||||
|
expect(tier2?.name).toBe('Deep Reservoir');
|
||||||
|
expect(tier2?.multiplier).toBe(10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('✅ All skill tests defined.');
|
||||||
@@ -78,7 +78,7 @@ export function getFloorMaxHP(floor: number): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getFloorElement(floor: number): string {
|
export function getFloorElement(floor: number): string {
|
||||||
return FLOOR_ELEM_CYCLE[(floor - 1) % 8];
|
return FLOOR_ELEM_CYCLE[(floor - 1) % FLOOR_ELEM_CYCLE.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Equipment Spell Helper ─────────────────────────────────────────────────────
|
// ─── Equipment Spell Helper ─────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -831,6 +831,322 @@ export const SKILL_EVOLUTION_PATHS: Record<string, SkillEvolutionPath> = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
// ADDITIONAL SKILL UPGRADE TREES
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
// ─── Mana Overflow (max 5) ──────────────────────────────────────────────────
|
||||||
|
manaOverflow: {
|
||||||
|
baseSkillId: 'manaOverflow',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'manaOverflow',
|
||||||
|
name: 'Mana Overflow',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'mo_l5_surge', name: 'Click Surge', desc: '+50% click mana when above 90% mana', milestone: 5, effect: { type: 'special', specialId: 'clickSurge', specialDesc: 'High mana click bonus' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tier: 2,
|
||||||
|
skillId: 'manaOverflow_t2',
|
||||||
|
name: 'Mana Torrent',
|
||||||
|
multiplier: 10,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'mo_t2_l5_flood', name: 'Mana Flood', desc: '+75% click mana when above 75% mana', milestone: 5, effect: { type: 'special', specialId: 'manaFlood', specialDesc: 'Enhanced click bonus' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Knowledge Retention (max 3) ────────────────────────────────────────────
|
||||||
|
knowledgeRetention: {
|
||||||
|
baseSkillId: 'knowledgeRetention',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'knowledgeRetention',
|
||||||
|
name: 'Knowledge Retention',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Efficient Enchant (max 5) ──────────────────────────────────────────────
|
||||||
|
efficientEnchant: {
|
||||||
|
baseSkillId: 'efficientEnchant',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'efficientEnchant',
|
||||||
|
name: 'Efficient Enchant',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'ee_l5_thrifty', name: 'Thrifty Enchanter', desc: '+10% chance for free enchantment', milestone: 5, effect: { type: 'special', specialId: 'thriftyEnchanter', specialDesc: 'Free enchant chance' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tier: 2,
|
||||||
|
skillId: 'efficientEnchant_t2',
|
||||||
|
name: 'Master Efficiency',
|
||||||
|
multiplier: 10,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'ee_t2_l5_optimize', name: 'Optimized Enchanting', desc: '+25% chance for free enchantment', milestone: 5, effect: { type: 'special', specialId: 'optimizedEnchanting', specialDesc: 'Enhanced free chance' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Disenchanting (max 3) ──────────────────────────────────────────────────
|
||||||
|
disenchanting: {
|
||||||
|
baseSkillId: 'disenchanting',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'disenchanting',
|
||||||
|
name: 'Disenchanting',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Enchant Speed (max 5) ──────────────────────────────────────────────────
|
||||||
|
enchantSpeed: {
|
||||||
|
baseSkillId: 'enchantSpeed',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'enchantSpeed',
|
||||||
|
name: 'Enchant Speed',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'es_l5_hasty', name: 'Hasty Enchanter', desc: '+25% design speed for repeat enchantments', milestone: 5, effect: { type: 'special', specialId: 'hastyEnchanter', specialDesc: 'Faster repeat designs' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tier: 2,
|
||||||
|
skillId: 'enchantSpeed_t2',
|
||||||
|
name: 'Swift Enchanter',
|
||||||
|
multiplier: 10,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'es_t2_l5_instant', name: 'Instant Designs', desc: '10% chance for instant design completion', milestone: 5, effect: { type: 'special', specialId: 'instantDesigns', specialDesc: 'Instant design chance' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Essence Refining (max 5) ───────────────────────────────────────────────
|
||||||
|
essenceRefining: {
|
||||||
|
baseSkillId: 'essenceRefining',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'essenceRefining',
|
||||||
|
name: 'Essence Refining',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'er_l5_pure', name: 'Pure Essence', desc: '+25% enchantment power for tier 1 enchantments', milestone: 5, effect: { type: 'special', specialId: 'pureEssence', specialDesc: 'Enhanced tier 1 power' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tier: 2,
|
||||||
|
skillId: 'essenceRefining_t2',
|
||||||
|
name: 'Essence Mastery',
|
||||||
|
multiplier: 10,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'er_t2_l5_perfect', name: 'Perfect Essence', desc: '+50% enchantment power for all tiers', milestone: 5, effect: { type: 'multiplier', stat: 'enchantPower', value: 0.5 } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Scroll Crafting (max 3) ────────────────────────────────────────────────
|
||||||
|
scrollCrafting: {
|
||||||
|
baseSkillId: 'scrollCrafting',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'scrollCrafting',
|
||||||
|
name: 'Scroll Crafting',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Efficient Crafting (max 5) ─────────────────────────────────────────────
|
||||||
|
effCrafting: {
|
||||||
|
baseSkillId: 'effCrafting',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'effCrafting',
|
||||||
|
name: 'Eff. Crafting',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'ec_l5_batch', name: 'Batch Crafting', desc: 'Craft 2 items at once with 75% speed each', milestone: 5, effect: { type: 'special', specialId: 'batchCrafting', specialDesc: 'Parallel crafting' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tier: 2,
|
||||||
|
skillId: 'effCrafting_t2',
|
||||||
|
name: 'Master Crafter',
|
||||||
|
multiplier: 10,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'ec_t2_l5_mass', name: 'Mass Production', desc: 'Craft 3 items at once at full speed', milestone: 5, effect: { type: 'special', specialId: 'massProduction', specialDesc: 'Triple crafting' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Field Repair (max 5) ───────────────────────────────────────────────────
|
||||||
|
fieldRepair: {
|
||||||
|
baseSkillId: 'fieldRepair',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'fieldRepair',
|
||||||
|
name: 'Field Repair',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'fr_l5_scavenge', name: 'Scavenge', desc: 'Recover 10% of materials from broken items', milestone: 5, effect: { type: 'special', specialId: 'scavenge', specialDesc: 'Material recovery' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tier: 2,
|
||||||
|
skillId: 'fieldRepair_t2',
|
||||||
|
name: 'Master Repair',
|
||||||
|
multiplier: 10,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'fr_t2_l5_reclaim', name: 'Reclaim', desc: 'Recover 25% of materials from broken items', milestone: 5, effect: { type: 'special', specialId: 'reclaim', specialDesc: 'Enhanced recovery' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Elemental Crafting (max 3) ─────────────────────────────────────────────
|
||||||
|
elemCrafting: {
|
||||||
|
baseSkillId: 'elemCrafting',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'elemCrafting',
|
||||||
|
name: 'Elem. Crafting',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Golem Efficiency (max 5) ───────────────────────────────────────────────
|
||||||
|
golemEfficiency: {
|
||||||
|
baseSkillId: 'golemEfficiency',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'golemEfficiency',
|
||||||
|
name: 'Golem Efficiency',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'ge_l5_rapid', name: 'Rapid Strikes', desc: '+25% attack speed for first 3 floors', milestone: 5, effect: { type: 'special', specialId: 'rapidStrikes', specialDesc: 'Opening speed boost' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tier: 2,
|
||||||
|
skillId: 'golemEfficiency_t2',
|
||||||
|
name: 'Swift Golems',
|
||||||
|
multiplier: 10,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'ge_t2_l5_blitz', name: 'Blitz Attack', desc: '+50% attack speed for first 5 floors', milestone: 5, effect: { type: 'special', specialId: 'blitzAttack', specialDesc: 'Extended speed boost' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Golem Longevity (max 3) ────────────────────────────────────────────────
|
||||||
|
golemLongevity: {
|
||||||
|
baseSkillId: 'golemLongevity',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'golemLongevity',
|
||||||
|
name: 'Golem Longevity',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Golem Siphon (max 3) ───────────────────────────────────────────────────
|
||||||
|
golemSiphon: {
|
||||||
|
baseSkillId: 'golemSiphon',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'golemSiphon',
|
||||||
|
name: 'Golem Siphon',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Insight Harvest (max 5) ────────────────────────────────────────────────
|
||||||
|
insightHarvest: {
|
||||||
|
baseSkillId: 'insightHarvest',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'insightHarvest',
|
||||||
|
name: 'Insight Harvest',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'ih_l5_bounty', name: 'Insight Bounty', desc: '+25% insight from guardian kills', milestone: 5, effect: { type: 'special', specialId: 'insightBounty', specialDesc: 'Guardian insight bonus' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tier: 2,
|
||||||
|
skillId: 'insightHarvest_t2',
|
||||||
|
name: 'Insight Mastery',
|
||||||
|
multiplier: 10,
|
||||||
|
upgrades: [
|
||||||
|
{ id: 'ih_t2_l5_harvest', name: 'Greater Harvest', desc: '+50% insight from all sources', milestone: 5, effect: { type: 'multiplier', stat: 'insightGain', value: 0.5 } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Temporal Memory (max 3) ────────────────────────────────────────────────
|
||||||
|
temporalMemory: {
|
||||||
|
baseSkillId: 'temporalMemory',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'temporalMemory',
|
||||||
|
name: 'Temporal Memory',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// ─── Guardian Bane (max 3) ──────────────────────────────────────────────────
|
||||||
|
guardianBane: {
|
||||||
|
baseSkillId: 'guardianBane',
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
tier: 1,
|
||||||
|
skillId: 'guardianBane',
|
||||||
|
name: 'Guardian Bane',
|
||||||
|
multiplier: 1,
|
||||||
|
upgrades: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -261,17 +261,17 @@ describe('SkillStore', () => {
|
|||||||
it('should not start studying without prerequisites', () => {
|
it('should not start studying without prerequisites', () => {
|
||||||
useManaStore.getState().addRawMana(990, 1000);
|
useManaStore.getState().addRawMana(990, 1000);
|
||||||
|
|
||||||
// deepReservoir requires manaWell 5
|
// manaOverflow requires manaWell 3
|
||||||
const result = useSkillStore.getState().startStudyingSkill('deepReservoir', 1000);
|
const result = useSkillStore.getState().startStudyingSkill('manaOverflow', 1000);
|
||||||
|
|
||||||
expect(result.started).toBe(false);
|
expect(result.started).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should start studying with prerequisites met', () => {
|
it('should start studying with prerequisites met', () => {
|
||||||
useManaStore.getState().addRawMana(990, 1000);
|
useManaStore.getState().addRawMana(990, 1000);
|
||||||
useSkillStore.getState().setSkillLevel('manaWell', 5);
|
useSkillStore.getState().setSkillLevel('manaWell', 3);
|
||||||
|
|
||||||
const result = useSkillStore.getState().startStudyingSkill('deepReservoir', 1000);
|
const result = useSkillStore.getState().startStudyingSkill('manaOverflow', 1000);
|
||||||
|
|
||||||
expect(result.started).toBe(true);
|
expect(result.started).toBe(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -61,17 +61,17 @@ describe('SkillStore', () => {
|
|||||||
|
|
||||||
it('should not start studying without prerequisites', () => {
|
it('should not start studying without prerequisites', () => {
|
||||||
const skillStore = useSkillStore.getState();
|
const skillStore = useSkillStore.getState();
|
||||||
// deepReservoir requires manaWell level 5
|
// manaOverflow requires manaWell level 3
|
||||||
const result = skillStore.startStudyingSkill('deepReservoir', 1000);
|
const result = skillStore.startStudyingSkill('manaOverflow', 1000);
|
||||||
|
|
||||||
expect(result.started).toBe(false);
|
expect(result.started).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should start studying with prerequisites met', () => {
|
it('should start studying with prerequisites met', () => {
|
||||||
useSkillStore.setState({ skills: { manaWell: 5 } });
|
useSkillStore.setState({ skills: { manaWell: 3 } });
|
||||||
|
|
||||||
const skillStore = useSkillStore.getState();
|
const skillStore = useSkillStore.getState();
|
||||||
const result = skillStore.startStudyingSkill('deepReservoir', 1000);
|
const result = skillStore.startStudyingSkill('manaOverflow', 1000);
|
||||||
|
|
||||||
expect(result.started).toBe(true);
|
expect(result.started).toBe(true);
|
||||||
});
|
});
|
||||||
@@ -271,36 +271,36 @@ describe('ManaStore', () => {
|
|||||||
|
|
||||||
describe('craftComposite', () => {
|
describe('craftComposite', () => {
|
||||||
it('should craft composite element with correct ingredients', () => {
|
it('should craft composite element with correct ingredients', () => {
|
||||||
// Set up ingredients for blood (life + water)
|
// Set up ingredients for metal (fire + earth)
|
||||||
useManaStore.setState({
|
useManaStore.setState({
|
||||||
elements: {
|
elements: {
|
||||||
...useManaStore.getState().elements,
|
...useManaStore.getState().elements,
|
||||||
life: { current: 5, max: 10, unlocked: true },
|
fire: { current: 5, max: 10, unlocked: true },
|
||||||
water: { current: 5, max: 10, unlocked: true },
|
earth: { current: 5, max: 10, unlocked: true },
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = useManaStore.getState().craftComposite('blood', ['life', 'water']);
|
const result = useManaStore.getState().craftComposite('metal', ['fire', 'earth']);
|
||||||
|
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
|
|
||||||
const state = useManaStore.getState();
|
const state = useManaStore.getState();
|
||||||
expect(state.elements.life.current).toBe(4);
|
expect(state.elements.fire.current).toBe(4);
|
||||||
expect(state.elements.water.current).toBe(4);
|
expect(state.elements.earth.current).toBe(4);
|
||||||
expect(state.elements.blood.current).toBe(1);
|
expect(state.elements.metal.current).toBe(1);
|
||||||
expect(state.elements.blood.unlocked).toBe(true);
|
expect(state.elements.metal.unlocked).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not craft without ingredients', () => {
|
it('should not craft without ingredients', () => {
|
||||||
useManaStore.setState({
|
useManaStore.setState({
|
||||||
elements: {
|
elements: {
|
||||||
...useManaStore.getState().elements,
|
...useManaStore.getState().elements,
|
||||||
life: { current: 0, max: 10, unlocked: true },
|
fire: { current: 0, max: 10, unlocked: true },
|
||||||
water: { current: 0, max: 10, unlocked: true },
|
earth: { current: 0, max: 10, unlocked: true },
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = useManaStore.getState().craftComposite('blood', ['life', 'water']);
|
const result = useManaStore.getState().craftComposite('metal', ['fire', 'earth']);
|
||||||
|
|
||||||
expect(result).toBe(false);
|
expect(result).toBe(false);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Comprehensive Store Tests
|
* Comprehensive Store Tests
|
||||||
*
|
*
|
||||||
* Tests the split store architecture to ensure all stores work correctly together.
|
* Tests the split store architecture to ensure all stores work correctly together.
|
||||||
|
* Updated for the new skill system with tiers and upgrade trees.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, expect, beforeEach } from 'bun:test';
|
import { describe, it, expect, beforeEach } from 'bun:test';
|
||||||
@@ -89,8 +90,20 @@ function createMockState(overrides: Partial<GameState> = {}): GameState {
|
|||||||
autoSchedule: false,
|
autoSchedule: false,
|
||||||
studyQueue: [],
|
studyQueue: [],
|
||||||
craftQueue: [],
|
craftQueue: [],
|
||||||
|
attunements: {
|
||||||
|
enchanter: { id: 'enchanter', active: true, level: 1, experience: 0 },
|
||||||
|
invoker: { id: 'invoker', active: false, level: 1, experience: 0 },
|
||||||
|
fabricator: { id: 'fabricator', active: false, level: 1, experience: 0 },
|
||||||
|
},
|
||||||
|
golemancy: {
|
||||||
|
enabledGolems: [],
|
||||||
|
summonedGolems: [],
|
||||||
|
lastSummonFloor: 0,
|
||||||
|
},
|
||||||
|
equippedInstances: { mainHand: null, offHand: null, head: null, body: null, hands: null, accessory1: null, accessory2: null },
|
||||||
|
equipmentInstances: {},
|
||||||
...overrides,
|
...overrides,
|
||||||
};
|
} as GameState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Utility Function Tests ─────────────────────────────────────────────────
|
// ─── Utility Function Tests ─────────────────────────────────────────────────
|
||||||
@@ -140,43 +153,42 @@ describe('Mana Calculations', () => {
|
|||||||
expect(computeMaxMana(state)).toBe(100 + 5 * 100);
|
expect(computeMaxMana(state)).toBe(100 + 5 * 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add mana from deepReservoir skill', () => {
|
|
||||||
const state = createMockState({ skills: { deepReservoir: 3 } });
|
|
||||||
expect(computeMaxMana(state)).toBe(100 + 3 * 500);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add mana from prestige upgrades', () => {
|
it('should add mana from prestige upgrades', () => {
|
||||||
const state = createMockState({ prestigeUpgrades: { manaWell: 3 } });
|
const state = createMockState({ prestigeUpgrades: { manaWell: 3 } });
|
||||||
expect(computeMaxMana(state)).toBe(100 + 3 * 500);
|
expect(computeMaxMana(state)).toBe(100 + 3 * 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should stack all mana bonuses', () => {
|
it('should stack manaWell skill and prestige', () => {
|
||||||
const state = createMockState({
|
const state = createMockState({
|
||||||
skills: { manaWell: 5, deepReservoir: 2 },
|
skills: { manaWell: 5 },
|
||||||
prestigeUpgrades: { manaWell: 2 },
|
prestigeUpgrades: { manaWell: 2 },
|
||||||
});
|
});
|
||||||
expect(computeMaxMana(state)).toBe(100 + 5 * 100 + 2 * 500 + 2 * 500);
|
expect(computeMaxMana(state)).toBe(100 + 5 * 100 + 2 * 500);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('computeRegen', () => {
|
describe('computeRegen', () => {
|
||||||
it('should return base regen with no upgrades', () => {
|
it('should return base regen with no upgrades', () => {
|
||||||
|
// Base regen is 2 (attunement regen is added separately in the store)
|
||||||
const state = createMockState();
|
const state = createMockState();
|
||||||
expect(computeRegen(state)).toBe(2);
|
expect(computeRegen(state)).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add regen from manaFlow skill', () => {
|
it('should add regen from manaFlow skill', () => {
|
||||||
|
// Base 2 + manaFlow 5
|
||||||
const state = createMockState({ skills: { manaFlow: 5 } });
|
const state = createMockState({ skills: { manaFlow: 5 } });
|
||||||
expect(computeRegen(state)).toBe(2 + 5 * 1);
|
expect(computeRegen(state)).toBe(2 + 5 * 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add regen from manaSpring skill', () => {
|
it('should add regen from manaSpring skill', () => {
|
||||||
|
// Base 2 + manaSpring 2
|
||||||
const state = createMockState({ skills: { manaSpring: 1 } });
|
const state = createMockState({ skills: { manaSpring: 1 } });
|
||||||
expect(computeRegen(state)).toBe(2 + 2);
|
expect(computeRegen(state)).toBe(2 + 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should multiply by temporal echo prestige', () => {
|
it('should multiply by temporal echo prestige', () => {
|
||||||
const state = createMockState({ prestigeUpgrades: { temporalEcho: 2 } });
|
const state = createMockState({ prestigeUpgrades: { temporalEcho: 2 } });
|
||||||
|
// Base 2 * 1.2 = 2.4
|
||||||
expect(computeRegen(state)).toBe(2 * 1.2);
|
expect(computeRegen(state)).toBe(2 * 1.2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -231,19 +243,6 @@ describe('Combat Calculations', () => {
|
|||||||
expect(dmg).toBeGreaterThanOrEqual(5); // Base damage (can be higher with crit)
|
expect(dmg).toBeGreaterThanOrEqual(5); // Base damage (can be higher with crit)
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add damage from combatTrain skill', () => {
|
|
||||||
const state = createMockState({ skills: { combatTrain: 5 } });
|
|
||||||
const dmg = calcDamage(state, 'manaBolt');
|
|
||||||
expect(dmg).toBeGreaterThanOrEqual(5 + 5 * 5); // 5 base + 25 from skill
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should multiply by arcaneFury skill', () => {
|
|
||||||
const state = createMockState({ skills: { arcaneFury: 3 } });
|
|
||||||
const dmg = calcDamage(state, 'manaBolt');
|
|
||||||
// 5 * 1.3 = 6.5 minimum (without crit)
|
|
||||||
expect(dmg).toBeGreaterThanOrEqual(5 * 1.3 * 0.8);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have elemental bonuses', () => {
|
it('should have elemental bonuses', () => {
|
||||||
const state = createMockState({
|
const state = createMockState({
|
||||||
spells: {
|
spells: {
|
||||||
@@ -280,15 +279,22 @@ describe('Combat Calculations', () => {
|
|||||||
|
|
||||||
describe('getFloorElement', () => {
|
describe('getFloorElement', () => {
|
||||||
it('should cycle through elements in order', () => {
|
it('should cycle through elements in order', () => {
|
||||||
|
// FLOOR_ELEM_CYCLE has 7 elements: fire, water, air, earth, light, dark, death
|
||||||
expect(getFloorElement(1)).toBe('fire');
|
expect(getFloorElement(1)).toBe('fire');
|
||||||
expect(getFloorElement(2)).toBe('water');
|
expect(getFloorElement(2)).toBe('water');
|
||||||
expect(getFloorElement(3)).toBe('air');
|
expect(getFloorElement(3)).toBe('air');
|
||||||
expect(getFloorElement(4)).toBe('earth');
|
expect(getFloorElement(4)).toBe('earth');
|
||||||
|
expect(getFloorElement(5)).toBe('light');
|
||||||
|
expect(getFloorElement(6)).toBe('dark');
|
||||||
|
expect(getFloorElement(7)).toBe('death');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should wrap around after 8 floors', () => {
|
it('should wrap around after 7 floors', () => {
|
||||||
expect(getFloorElement(9)).toBe('fire');
|
// Floor 8 should be fire (wraps around)
|
||||||
expect(getFloorElement(10)).toBe('water');
|
expect(getFloorElement(8)).toBe('fire');
|
||||||
|
expect(getFloorElement(9)).toBe('water');
|
||||||
|
expect(getFloorElement(15)).toBe('fire'); // (15-1) % 7 = 0
|
||||||
|
expect(getFloorElement(16)).toBe('water'); // (16-1) % 7 = 1
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -463,7 +469,8 @@ describe('Spell Cost System', () => {
|
|||||||
|
|
||||||
describe('Skill Definitions', () => {
|
describe('Skill Definitions', () => {
|
||||||
it('all skills should have valid categories', () => {
|
it('all skills should have valid categories', () => {
|
||||||
const validCategories = ['mana', 'combat', 'study', 'craft', 'research', 'ascension'];
|
const validCategories = ['mana', 'study', 'research', 'ascension', 'enchant',
|
||||||
|
'effectResearch', 'invocation', 'pact', 'fabrication', 'golemancy', 'craft'];
|
||||||
Object.values(SKILLS_DEF).forEach(skill => {
|
Object.values(SKILLS_DEF).forEach(skill => {
|
||||||
expect(validCategories).toContain(skill.cat);
|
expect(validCategories).toContain(skill.cat);
|
||||||
});
|
});
|
||||||
@@ -531,15 +538,16 @@ describe('Prestige Upgrades', () => {
|
|||||||
// ─── Guardian Tests ──────────────────────────────────────────────────────
|
// ─── Guardian Tests ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
describe('Guardian Definitions', () => {
|
describe('Guardian Definitions', () => {
|
||||||
it('should have guardians every 10 floors', () => {
|
it('should have guardians on expected floors (no floor 70)', () => {
|
||||||
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100].forEach(floor => {
|
// Floor 70 was removed from the game
|
||||||
|
[10, 20, 30, 40, 50, 60, 80, 90, 100].forEach(floor => {
|
||||||
expect(GUARDIANS[floor]).toBeDefined();
|
expect(GUARDIANS[floor]).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have increasing HP', () => {
|
it('should have increasing HP', () => {
|
||||||
let prevHP = 0;
|
let prevHP = 0;
|
||||||
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100].forEach(floor => {
|
[10, 20, 30, 40, 50, 60, 80, 90, 100].forEach(floor => {
|
||||||
expect(GUARDIANS[floor].hp).toBeGreaterThan(prevHP);
|
expect(GUARDIANS[floor].hp).toBeGreaterThan(prevHP);
|
||||||
prevHP = GUARDIANS[floor].hp;
|
prevHP = GUARDIANS[floor].hp;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -68,11 +68,27 @@ export const SPECIAL_EFFECTS = {
|
|||||||
MANA_TORRENT: 'manaTorrent', // +50% regen when above 75% mana
|
MANA_TORRENT: 'manaTorrent', // +50% regen when above 75% mana
|
||||||
FLOW_SURGE: 'flowSurge', // Clicks restore 2x regen for 1 hour
|
FLOW_SURGE: 'flowSurge', // Clicks restore 2x regen for 1 hour
|
||||||
MANA_OVERFLOW: 'manaOverflow', // Raw mana can exceed max by 20%
|
MANA_OVERFLOW: 'manaOverflow', // Raw mana can exceed max by 20%
|
||||||
|
MANA_WATERFALL: 'manaWaterfall', // +0.25 regen per 100 max mana (upgraded cascade)
|
||||||
|
ETERNAL_FLOW: 'eternalFlow', // Regen immune to all penalties
|
||||||
|
|
||||||
// Mana Well special effects
|
// Mana Well special effects
|
||||||
DESPERATE_WELLS: 'desperateWells', // +50% regen when below 25% mana
|
DESPERATE_WELLS: 'desperateWells', // +50% regen when below 25% mana
|
||||||
MANA_ECHO: 'manaEcho', // 10% chance double mana from clicks
|
MANA_ECHO: 'manaEcho', // 10% chance double mana from clicks
|
||||||
EMERGENCY_RESERVE: 'emergencyReserve', // Keep 10% mana on new loop
|
EMERGENCY_RESERVE: 'emergencyReserve', // Keep 10% mana on new loop
|
||||||
|
MANA_THRESHOLD: 'manaThreshold', // +30% max mana, -10% regen trade-off
|
||||||
|
MANA_CONVERSION: 'manaConversion', // Convert 5% max mana to click bonus
|
||||||
|
PANIC_RESERVE: 'panicReserve', // +100% regen when below 10% mana
|
||||||
|
MANA_CONDENSE: 'manaCondense', // +1% max mana per 1000 gathered
|
||||||
|
DEEP_RESERVE: 'deepReserve', // +0.5 regen per 100 max mana
|
||||||
|
MANA_TIDE: 'manaTide', // Regen pulses ±50%
|
||||||
|
VOID_STORAGE: 'voidStorage', // Store 150% max temporarily
|
||||||
|
MANA_CORE: 'manaCore', // 0.5% max mana as regen
|
||||||
|
MANA_HEART: 'manaHeart', // +10% max mana per loop
|
||||||
|
MANA_GENESIS: 'manaGenesis', // Generate 1% max mana per hour
|
||||||
|
|
||||||
|
// Mana Overflow special effects
|
||||||
|
CLICK_SURGE: 'clickSurge', // +50% click mana above 90% mana
|
||||||
|
MANA_FLOOD: 'manaFlood', // +75% click mana above 75% mana
|
||||||
|
|
||||||
// Combat special effects
|
// Combat special effects
|
||||||
FIRST_STRIKE: 'firstStrike', // +15% damage on first attack each floor
|
FIRST_STRIKE: 'firstStrike', // +15% damage on first attack each floor
|
||||||
@@ -100,6 +116,30 @@ export const SPECIAL_EFFECTS = {
|
|||||||
EXOTIC_MASTERY: 'exoticMastery', // +20% exotic element damage
|
EXOTIC_MASTERY: 'exoticMastery', // +20% exotic element damage
|
||||||
ELEMENTAL_RESONANCE: 'elementalResonance', // Using element spells restores 1 of that element
|
ELEMENTAL_RESONANCE: 'elementalResonance', // Using element spells restores 1 of that element
|
||||||
MANA_CONDUIT: 'manaConduit', // Meditation regenerates elemental mana
|
MANA_CONDUIT: 'manaConduit', // Meditation regenerates elemental mana
|
||||||
|
|
||||||
|
// Enchanting special effects
|
||||||
|
ENCHANT_MASTERY: 'enchantMastery', // 2 enchantment designs in progress
|
||||||
|
ENCHANT_PRESERVATION: 'enchantPreservation', // 25% chance free enchant
|
||||||
|
THRIFTY_ENCHANTER: 'thriftyEnchanter', // +10% chance free enchantment
|
||||||
|
OPTIMIZED_ENCHANTING: 'optimizedEnchanting', // +25% chance free enchantment
|
||||||
|
HASTY_ENCHANTER: 'hastyEnchanter', // +25% speed for repeat designs
|
||||||
|
INSTANT_DESIGNS: 'instantDesigns', // 10% instant design completion
|
||||||
|
PURE_ESSENCE: 'pureEssence', // +25% power for tier 1 enchants
|
||||||
|
|
||||||
|
// Crafting special effects
|
||||||
|
BATCH_CRAFTING: 'batchCrafting', // Craft 2 items at 75% speed each
|
||||||
|
MASS_PRODUCTION: 'massProduction', // Craft 3 items at full speed
|
||||||
|
SCAVENGE: 'scavenge', // Recover 10% materials from broken
|
||||||
|
RECLAIM: 'reclaim', // Recover 25% materials from broken
|
||||||
|
|
||||||
|
// Golemancy special effects
|
||||||
|
GOLEM_FURY: 'golemFury', // +50% attack speed for first 2 floors
|
||||||
|
GOLEM_RESONANCE: 'golemResonance', // Golems share 10% damage
|
||||||
|
RAPID_STRIKES: 'rapidStrikes', // +25% attack speed for first 3 floors
|
||||||
|
BLITZ_ATTACK: 'blitzAttack', // +50% attack speed for first 5 floors
|
||||||
|
|
||||||
|
// Ascension special effects
|
||||||
|
INSIGHT_BOUNTY: 'insightBounty', // +25% insight from guardians
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// ─── Upgrade Definition Cache ─────────────────────────────────────────────────
|
// ─── Upgrade Definition Cache ─────────────────────────────────────────────────
|
||||||
|
|||||||
672
worklog.md
672
worklog.md
@@ -1,672 +0,0 @@
|
|||||||
# Mana Loop Worklog
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 1
|
|
||||||
Agent: Main
|
|
||||||
Task: Fix skill upgrade effects not being applied
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- Identified the issue: special effects like Mana Cascade were defined but never processed
|
|
||||||
- Added `specials: Set<string>` to ComputedEffects interface in upgrade-effects.ts
|
|
||||||
- Created SPECIAL_EFFECTS constant with all special effect IDs
|
|
||||||
- Added `hasSpecial()` helper function to check if a special is active
|
|
||||||
- Updated `computeEffects()` to collect special effects into the set
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Created the foundation for special effect tracking
|
|
||||||
- All special effects are now recognized and can be checked with hasSpecial()
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 2
|
|
||||||
Agent: Main
|
|
||||||
Task: Implement special effect application in game logic
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- Created `computeEffectiveRegen()` function that applies:
|
|
||||||
- Mana Cascade: +0.1 regen per 100 max mana
|
|
||||||
- Mana Torrent: +50% regen when above 75% mana
|
|
||||||
- Desperate Wells: +50% regen when below 25% mana
|
|
||||||
- Steady Stream: Immune to incursion penalty
|
|
||||||
- Updated `gatherMana()` to apply Mana Echo (10% chance double mana)
|
|
||||||
- Updated combat in tick() to apply:
|
|
||||||
- Damage multipliers/bonuses from upgrades
|
|
||||||
- Executioner: +100% damage to enemies below 25% HP
|
|
||||||
- Berserker: +50% damage when mana < 50%
|
|
||||||
- Adrenaline Rush: Restore 5% mana on kill
|
|
||||||
- Updated study in tick() to apply:
|
|
||||||
- Study speed multipliers from upgrades
|
|
||||||
- Mental Clarity: +10% study speed when mana > 75%
|
|
||||||
- Study Rush: First hour is 2x speed
|
|
||||||
- Study Refund: 25% mana back on completion
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Key tier 1 special effects are now functional
|
|
||||||
- Regen, combat, and study systems all apply upgrades correctly
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 3
|
|
||||||
Agent: Main
|
|
||||||
Task: Display special effects in UI
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- Imported computeEffects, hasSpecial, SPECIAL_EFFECTS to page.tsx
|
|
||||||
- Added `upgradeEffects` computation in derived stats
|
|
||||||
- Added `manaCascadeBonus` calculation for display
|
|
||||||
- Updated `effectiveRegen` to use `effectiveRegenWithSpecials`
|
|
||||||
- Updated Mana Stats section to show:
|
|
||||||
- Steady Stream immunity indicator
|
|
||||||
- Mana Cascade bonus amount
|
|
||||||
- Mana Torrent/Desperate Wells conditional bonuses
|
|
||||||
- Updated Active Upgrades section to render special effects with cyan color
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Special effects are now visible in the Stats tab
|
|
||||||
- Players can see exactly how much Mana Cascade contributes
|
|
||||||
- Active Upgrades shows all effect types including specials
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 4
|
|
||||||
Agent: Main
|
|
||||||
Task: Test and verify implementation
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- Added 11 new tests for special effects:
|
|
||||||
- Mana Cascade recognition and calculation
|
|
||||||
- Steady Stream recognition
|
|
||||||
- Mana Echo recognition
|
|
||||||
- Combat special effects (Executioner, Berserker, Adrenaline Rush)
|
|
||||||
- Study special effects (Mental Clarity, Study Rush)
|
|
||||||
- Effect stacking tests
|
|
||||||
- All 203 tests pass
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Complete test coverage for implemented special effects
|
|
||||||
- All lint checks pass
|
|
||||||
- Dev server running without errors
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 5
|
|
||||||
Agent: Main
|
|
||||||
Task: Fix Deep Wellspring meditation efficiency upgrade
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- Identified issue: `meditationEfficiency` was tracked in computed effects but not used
|
|
||||||
- Updated `getMeditationBonus()` function signature to accept `meditationEfficiency` parameter
|
|
||||||
- Updated meditation bonus calculation to multiply by efficiency
|
|
||||||
- Updated `tick()` in store.ts to:
|
|
||||||
- Compute upgradeEffects before meditation calculation
|
|
||||||
- Pass `upgradeEffects.meditationEfficiency` to `getMeditationBonus()`
|
|
||||||
- Updated page.tsx to:
|
|
||||||
- Compute `upgradeEffects` before other derived stats
|
|
||||||
- Pass `upgradeEffects.meditationEfficiency` to meditation multiplier calculation
|
|
||||||
- Added 2 new tests for Deep Wellspring:
|
|
||||||
- Test that meditation efficiency is correctly computed (1.5x for +50%)
|
|
||||||
- Test that meditation bonus is boosted by efficiency (2.5 * 1.5 = 3.75)
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Deep Wellspring (+50% meditation efficiency) now works correctly
|
|
||||||
- All 205 tests pass
|
|
||||||
- Dev server running without errors
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 6
|
|
||||||
Agent: Main
|
|
||||||
Task: Fix multiple issues: Quick Learner tier-up reset, Parallel Mind upgrade, meditation display location, parallel study UI, combat spell usage verification
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Fixed Quick Learner tier-up reset issue**: Modified `tierUpSkill()` to NOT copy upgrades from old tier to new tier. Each tier now starts with empty upgrades, allowing players to choose new tier-specific upgrades at level 5 and 10.
|
|
||||||
- **Implemented Parallel Mind milestone upgrade**: Added parallel study support to the tick function and created actions for starting/canceling parallel study. Players can now study two things at once at 50% speed each.
|
|
||||||
- **Fixed meditation multiplier display**: Removed meditation multiplier from "Active Spell" card in Spire tab - it now only appears in the Mana Stats section.
|
|
||||||
- **Added parallel study UI**:
|
|
||||||
- Added parallel study progress card in Spire tab
|
|
||||||
- Added ⚡ button in Skills tab for selecting parallel study target
|
|
||||||
- Shows "50% speed (Parallel Study)" indicator
|
|
||||||
- **Verified combat uses active spells**: Confirmed that combat properly uses the active spell from `state.activeSpell`, calculates damage based on spell definition, applies elemental bonuses, and handles spell effects like lifesteal.
|
|
||||||
- **Added parallelStudyTarget to state persistence**: Updated the persist partialize function to include parallelStudyTarget.
|
|
||||||
- **Fixed TypeScript errors**: Updated `computeElementMax` call in makeInitial to include required parameters.
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Quick Learner and other skills no longer lose progress when tiering up - players choose new upgrades for each tier
|
|
||||||
- Parallel Mind upgrade is now functional - allows studying two skills/spells simultaneously
|
|
||||||
- Meditation multiplier moved to correct location (Mana Stats only)
|
|
||||||
- Combat verified to use active spells properly with all effects
|
|
||||||
- All lint checks pass, dev server running without errors
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 7
|
|
||||||
Agent: Main
|
|
||||||
Task: Implement cast speed system for combat
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- Added `castSpeed` property to `SpellDef` type in types.ts (default 1 cast/hour)
|
|
||||||
- Added cast speeds to all spells in SPELLS_DEF:
|
|
||||||
- Tier 0 (basic): 2.5-3 casts/hr (fast, cheap)
|
|
||||||
- Tier 1 (elemental): 1.5-3 casts/hr (varies by spell nature)
|
|
||||||
- Tier 2 (advanced): 0.8-1.8 casts/hr (slower, more powerful)
|
|
||||||
- Tier 3 (master): 0.5-0.75 casts/hr (slow but devastating)
|
|
||||||
- Tier 4 (legendary): 0.4-0.5 casts/hr (very slow, extremely powerful)
|
|
||||||
- Added `castProgress` to GameState (tracks progress to next cast, 0-1)
|
|
||||||
- Updated combat tick logic:
|
|
||||||
- Accumulates cast progress based on spell cast speed and attack speed bonuses
|
|
||||||
- When progress >= 1, triggers a spell cast (deducts mana, applies damage)
|
|
||||||
- Handles multiple casts per tick if cast speed is high
|
|
||||||
- Resets progress on floor change
|
|
||||||
- Attack speed affected by:
|
|
||||||
- Quick Cast skill: +5% per level
|
|
||||||
- Attack Speed upgrade multipliers from skill milestones
|
|
||||||
- Updated UI:
|
|
||||||
- Active Spell card now shows cast speed (casts/hr)
|
|
||||||
- Added cast progress bar when climbing
|
|
||||||
- DPS calculation now uses actual cast speed formula
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Combat now properly uses spell cast speed instead of arbitrary damage multiplier
|
|
||||||
- Each spell has unique cast speed (faster spells = more DPS but lower damage per cast)
|
|
||||||
- Attack speed upgrades (Quick Cast, Quick Strikes) properly speed up casting
|
|
||||||
- Players can see cast progress and DPS in real-time
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 8
|
|
||||||
Agent: Main
|
|
||||||
Task: Implement Crafting/Equipment System (Replace Combat Skills with Enchanting)
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Verified combat skills removed and enchanting skills added**: constants.ts already had enchanting skills (enchanting, efficientEnchant, disenchanting, enchantSpeed, scrollCrafting, essenceRefining) with 'enchant' category
|
|
||||||
- **Integrated crafting slice into main store**:
|
|
||||||
- Added GameStore interface to extend CraftingActions
|
|
||||||
- Added processCraftingTick call in tick function for design/prepare/enchant actions
|
|
||||||
- Implemented all crafting actions directly in store (createEquipmentInstance, equipItem, startDesigningEnchantment, etc.)
|
|
||||||
- Updated partialize function to persist equipment state (equippedInstances, equipmentInstances, enchantmentDesigns, designProgress, preparationProgress, applicationProgress)
|
|
||||||
- **Created CraftingTab component** with 3-stage UI:
|
|
||||||
- Design stage: Select equipment type, choose effects, name design, time-based progress
|
|
||||||
- Prepare stage: Select equipment, pay mana cost, time-based preparation
|
|
||||||
- Apply stage: Select equipment and design, apply enchantment with capacity validation
|
|
||||||
- Disenchant section: Remove enchantments and recover mana based on disenchanting skill
|
|
||||||
- **Updated SpellsTab** to show equipment-granted spells:
|
|
||||||
- Added equipment spells section at top showing spells from equipped items
|
|
||||||
- Shows source equipment for each spell
|
|
||||||
- Added pact spells placeholder section
|
|
||||||
- **Added 5-tier evolution paths for enchanting skills**:
|
|
||||||
- enchanting: 5 tiers from Enchanting → Rune Master → Arcane Forgemaster → Void Enchanter → Enchantment God
|
|
||||||
- efficientEnchant, disenchanting, enchantSpeed: 1 tier each with unique upgrades
|
|
||||||
- **Updated page.tsx** with Crafting tab:
|
|
||||||
- Added CraftingTab import and TabsTrigger for crafting
|
|
||||||
- Added TabsContent rendering CraftingTab with all required props
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Complete 3-stage enchantment system (Design → Prepare → Apply)
|
|
||||||
- Equipment capacity limits enchantments
|
|
||||||
- Starting equipment: Basic Staff (with Mana Bolt), Civilian Shirt, Civilian Shoes
|
|
||||||
- Spells only learnable from equipment and pacts
|
|
||||||
- All crafting skills have evolution paths
|
|
||||||
- All lint checks pass
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 9
|
|
||||||
Agent: Main
|
|
||||||
Task: Implement Attunement System - Core Framework
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Created attunement type definitions in types.ts**:
|
|
||||||
- Added AttunementSlot type (rightHand, leftHand, head, back, chest, leftLeg, rightLeg)
|
|
||||||
- Added AttunementDef interface with primaryManaType, rawManaRegen, conversionRate, capabilities, skillCategories
|
|
||||||
- Added AttunementState interface for tracking player attunements
|
|
||||||
- Updated GameState to include attunements field
|
|
||||||
- **Created attunements.ts data file** with 3 attunements:
|
|
||||||
- **Enchanter (right hand)**: Starting attunement, transference mana, unlocks enchanting, 0.5 raw regen/hr, 0.2 conversion/hr
|
|
||||||
- **Invoker (chest)**: Unlocks pacts with guardians, no primary mana but gains types from each pact, 0.3 raw regen/hr
|
|
||||||
- **Fabricator (left hand)**: Earth mana, crafts golems and earthen/metal gear, 0.4 raw regen/hr, 0.25 conversion/hr
|
|
||||||
- **Updated store.ts**:
|
|
||||||
- Added attunement imports
|
|
||||||
- Updated makeInitial() to start with Enchanter attunement active
|
|
||||||
- Unlocks transference element for Enchanter
|
|
||||||
- Added attunement mana conversion logic in tick()
|
|
||||||
- Updated computeRegen() to include attunement bonuses
|
|
||||||
- **Updated SKILL_CATEGORIES** to be attunement-based:
|
|
||||||
- Core categories (always available): mana, study, research, ascension
|
|
||||||
- Enchanter categories: enchant, effectResearch
|
|
||||||
- Invoker categories: invocation, pact
|
|
||||||
- Fabricator categories: fabrication, golemancy
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Player starts with Enchanter attunement on right hand
|
|
||||||
- Attunements provide raw mana regen and convert to primary mana types
|
|
||||||
- Skills are now organized by attunement (foundation for skill tab overhaul)
|
|
||||||
- Lint checks pass, ready for UI implementation
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 10
|
|
||||||
Agent: Main
|
|
||||||
Task: Implement Attunement System - UI Overhaul
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Created AttunementsTab component** (src/components/game/tabs/AttunementsTab.tsx):
|
|
||||||
- Displays all 3 attunements with their status (active/locked)
|
|
||||||
- Shows primary mana type and current mana for active attunements
|
|
||||||
- Displays raw mana regen and conversion rate stats
|
|
||||||
- Shows capabilities unlocked by each attunement
|
|
||||||
- Displays available skill categories based on active attunements
|
|
||||||
- Uses color coding and visual effects for active attunements
|
|
||||||
- **Updated page.tsx**:
|
|
||||||
- Added AttunementsTab import
|
|
||||||
- Added "✨ Attune" tab between Spire and Skills
|
|
||||||
- Added TabsContent for attunements
|
|
||||||
- **Updated SkillsTab.tsx**:
|
|
||||||
- Added import for getAvailableSkillCategories
|
|
||||||
- Modified skill rendering to filter categories by attunement access
|
|
||||||
- Skills now only show if the player has the appropriate attunement
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- New Attunements tab shows all attunement details
|
|
||||||
- Skills are filtered based on active attunements
|
|
||||||
- Player can see exactly which skill categories they have access to
|
|
||||||
- Visual feedback shows active vs locked attunements
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 11
|
|
||||||
Agent: Main
|
|
||||||
Task: Fix build errors - correct imports for getActiveEquipmentSpells and getTotalDPS
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- Identified build failure: getActiveEquipmentSpells and getTotalDPS were being imported from store.ts but are defined in computed-stats.ts
|
|
||||||
- Fixed imports in page.tsx:
|
|
||||||
- Changed to import getActiveEquipmentSpells and getTotalDPS from computed-stats.ts
|
|
||||||
- Fixed imports in SpireTab.tsx:
|
|
||||||
- Changed to import getActiveEquipmentSpells and getTotalDPS from computed-stats.ts
|
|
||||||
- Removed duplicate local function definition of getActiveEquipmentSpells
|
|
||||||
- Cleaned up unused imports (ENCHANTMENT_EFFECTS, EQUIPMENT_TYPES)
|
|
||||||
- Committed and pushed fix to repository
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Build errors resolved
|
|
||||||
- All imports correctly reference computed-stats.ts for DPS and equipment spell functions
|
|
||||||
- Code is cleaner with no duplicate function definitions
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 12
|
|
||||||
Agent: Main
|
|
||||||
Task: Fix prerender error - Cannot read properties of undefined (reading 'count')
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- Identified the issue: ComboState type was referenced in ComboMeter.tsx but never defined
|
|
||||||
- The `combo` property was also missing from GameState interface and initial state
|
|
||||||
- Added ComboState interface to types.ts with:
|
|
||||||
- count: number (current combo hits)
|
|
||||||
- maxCombo: number (highest combo this session)
|
|
||||||
- multiplier: number (current damage multiplier)
|
|
||||||
- elementChain: string[] (last 3 elements used)
|
|
||||||
- decayTimer: number (hours until decay starts)
|
|
||||||
- Added `combo: ComboState` field to GameState interface
|
|
||||||
- Added default combo state to makeInitial() in store.ts:
|
|
||||||
- count: 0, maxCombo: 0, multiplier: 1, elementChain: [], decayTimer: 0
|
|
||||||
- Added `combo` and `attunements` to persist partialize function for save/load
|
|
||||||
- Fixed ComboMeter.tsx to import ComboState from types.ts
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- ComboState type properly defined and used
|
|
||||||
- combo field now properly initialized in game state
|
|
||||||
- Persist function now saves both combo and attunements
|
|
||||||
- Build should now pass prerendering stage
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 13
|
|
||||||
Agent: Main
|
|
||||||
Task: Fix prerender error - Cannot read properties of undefined (reading 'materials')
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- Identified the issue: LootInventory type was referenced but never defined
|
|
||||||
- The `lootInventory` property was also missing from GameState interface and initial state
|
|
||||||
- Added LootInventory interface to types.ts with:
|
|
||||||
- materials: Record<string, number> (materialId -> count)
|
|
||||||
- blueprints: string[] (blueprint IDs discovered)
|
|
||||||
- Added `lootInventory: LootInventory` field to GameState interface
|
|
||||||
- Added default lootInventory to makeInitial() in store.ts
|
|
||||||
- Added lootInventory to persist partialize function
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- LootInventory type properly defined and used
|
|
||||||
- lootInventory field now properly initialized in game state
|
|
||||||
- Persist function now saves loot inventory
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 14
|
|
||||||
Agent: Main
|
|
||||||
Task: Fix prerender error - Add missing AchievementState and stats tracking
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- Identified multiple missing types and fields referenced in page.tsx
|
|
||||||
- Added AchievementDef interface to types.ts with:
|
|
||||||
- id, name, desc, category, requirement, reward, hidden
|
|
||||||
- Added AchievementState interface with:
|
|
||||||
- unlocked: string[] (IDs of unlocked achievements)
|
|
||||||
- progress: Record<string, number> (progress toward requirements)
|
|
||||||
- Added to GameState:
|
|
||||||
- achievements: AchievementState
|
|
||||||
- totalSpellsCast: number
|
|
||||||
- totalDamageDealt: number
|
|
||||||
- totalCraftsCompleted: number
|
|
||||||
- Added default values to makeInitial() in store.ts
|
|
||||||
- Added all new fields to persist partialize function
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- All missing types and fields now properly defined
|
|
||||||
- Achievement system can track unlocked achievements and progress
|
|
||||||
- Stats tracking for spells cast, damage dealt, and crafts completed
|
|
||||||
- Build should now pass without undefined property errors
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 15
|
|
||||||
Agent: Main
|
|
||||||
Task: Implement multiple UI improvements and attunement leveling
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Added mana pools display to ManaDisplay component**:
|
|
||||||
- Shows all unlocked elemental mana types
|
|
||||||
- Collapsible section with progress bars for each element
|
|
||||||
- Sorted by current amount
|
|
||||||
- Added elements prop to ManaDisplay
|
|
||||||
|
|
||||||
- **Added Debug tab**:
|
|
||||||
- Reset game with confirmation
|
|
||||||
- Mana debug (+10, +100, +1K, +10K, Fill to max)
|
|
||||||
- Time control (set day 1, 10, 20, 30)
|
|
||||||
- Attunement unlock buttons
|
|
||||||
- Attunement XP buttons
|
|
||||||
- Elemental mana unlock and add buttons
|
|
||||||
- Quick actions (unlock all base elements, utility elements, skip to floor 100)
|
|
||||||
|
|
||||||
- **Removed ComboMeter from UI**:
|
|
||||||
- Removed from header in page.tsx
|
|
||||||
- Removed from SpireTab.tsx
|
|
||||||
|
|
||||||
- **Updated AttunementsTab with level scaling**:
|
|
||||||
- Added XP progress bar
|
|
||||||
- Added level-scaled regen and conversion display
|
|
||||||
- Added max level indicator
|
|
||||||
- Removed 'scrollCrafting' from Enchanter capabilities
|
|
||||||
|
|
||||||
- **Implemented attunement leveling system**:
|
|
||||||
- Added getAttunementXPForLevel() and MAX_ATTUNEMENT_LEVEL = 10
|
|
||||||
- XP scales exponentially: 100 * 3^(level-2)
|
|
||||||
- Added addAttunementXP function with auto level-up
|
|
||||||
- Added debug functions: debugUnlockAttunement, debugAddElementalMana, debugSetTime, debugAddAttunementXP, debugSetFloor
|
|
||||||
|
|
||||||
- **Implemented attunement passive conversion with level scaling**:
|
|
||||||
- Updated tick() to use getAttunementConversionRate() for level-scaled rates
|
|
||||||
- Conversion rate scales exponentially with level (1.5^(level-1) multiplier)
|
|
||||||
|
|
||||||
- **Made skill categories collapsible**:
|
|
||||||
- Added collapsedCategories state
|
|
||||||
- Click on category header to toggle
|
|
||||||
- Shows ChevronDown/ChevronRight icons
|
|
||||||
- Shows skill count badge
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- All requested UI improvements completed
|
|
||||||
- Attunement leveling system fully functional
|
|
||||||
- Debug tab enables testing and development
|
|
||||||
- Skills tab is more manageable with collapsible categories
|
|
||||||
- Removed unused features (ComboMeter, scrollCrafting)
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 23
|
|
||||||
Agent: Main
|
|
||||||
Task: Remove familiar system completely and fix inconsistencies
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Removed familiar system files**:
|
|
||||||
- Deleted `src/lib/game/data/familiars.ts` (520 lines)
|
|
||||||
- Deleted `src/lib/game/familiar-slice.ts` (368 lines)
|
|
||||||
- Deleted `src/components/game/tabs/FamiliarTab.tsx` (583 lines)
|
|
||||||
- **Removed familiar types from types.ts**:
|
|
||||||
- Removed FamiliarRole type
|
|
||||||
- Removed FamiliarAbilityType type
|
|
||||||
- Removed FamiliarAbility interface
|
|
||||||
- Removed FamiliarDef interface
|
|
||||||
- Removed FamiliarInstance interface
|
|
||||||
- **Fixed SpireTab spell effects display**:
|
|
||||||
- Updated to show valid effect types (burn, stun, pierce, multicast, buff)
|
|
||||||
- Removed invalid lifesteal and freeze effect displays
|
|
||||||
- **Fixed test files**:
|
|
||||||
- Removed Executioner test from store.test.ts (effect was previously removed)
|
|
||||||
- Removed familiar fields from skills.test.ts mock state
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Familiar system completely removed - simplifies game and keeps focus on Invoker pact system
|
|
||||||
- All tests pass (32 tests in computed-stats.test.ts)
|
|
||||||
- Lint passes
|
|
||||||
- Build succeeds
|
|
||||||
- Spell effects now show correct types
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 24
|
|
||||||
Agent: Main
|
|
||||||
Task: Add enchantments/spells for each mana type including compound types
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Added compound mana type spells to constants.ts**:
|
|
||||||
- Blood spells (Life + Water): bloodNeedle, bloodSlash, bloodRite, sanguineStorm - lifesteal focus
|
|
||||||
- Metal spells (Fire + Earth): metalShard, ironFist, steelTempest, furnaceBlast - armor pierce focus
|
|
||||||
- Wood spells (Life + Earth): thornStrike, rootGrab, treePunch, forestAwakening - regeneration/lifesteal focus
|
|
||||||
- Sand spells (Earth + Water): sandBlast, sandstorm, desertWind, duneCollapse - AOE focus
|
|
||||||
- Lightning spells already existed but added tier 3 thunderStrike
|
|
||||||
|
|
||||||
- **Added utility mana type spells to constants.ts**:
|
|
||||||
- Mental spells: mindSpike, psychicBlast, mindCrush, cerebralAnnihilation - stun focus
|
|
||||||
- Transference spells: transferStrike, manaRip, essenceDrain, soulTransfer - lifesteal focus (Enchanter primary)
|
|
||||||
- Force spells: forcePush, kineticBlast, gravityWell, telekineticStorm - AOE/fast damage focus
|
|
||||||
|
|
||||||
- **Added research skills for compound and utility spells**:
|
|
||||||
- Tier 1 research skills for all compound types (blood, metal, wood, sand, lightning)
|
|
||||||
- Tier 2 advanced research for all compound types
|
|
||||||
- Tier 3 master research for all compound types
|
|
||||||
- Tier 1-3 research skills for utility types (mental, transference, force)
|
|
||||||
- All skills have appropriate prerequisites based on parent element research
|
|
||||||
|
|
||||||
- **Updated EFFECT_RESEARCH_MAPPING**:
|
|
||||||
- Added mappings for all 32 new spells to their respective research skills
|
|
||||||
- Compound spells require parent element research (e.g., blood needs life/death + water)
|
|
||||||
- Utility spells have their own independent research paths
|
|
||||||
|
|
||||||
- **Added enchantment effects for all new spells**:
|
|
||||||
- 40+ new enchantment effects added to enchantment-effects.ts
|
|
||||||
- All spells have appropriate capacity costs and descriptions
|
|
||||||
- All spell effects are for ALL_CASTER equipment category
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Complete spell coverage for all mana types (base, utility, compound, exotic)
|
|
||||||
- All mana types now have tier 1-3 spells with unique mechanics
|
|
||||||
- Research system properly gates access to advanced spell enchantments
|
|
||||||
- All 44 tests pass
|
|
||||||
- Lint passes clean
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 25
|
|
||||||
Agent: Main
|
|
||||||
Task: Remove banned mana types (life, blood, wood, mental, force) and lifesteal
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Removed banned mana types from ELEMENTS**:
|
|
||||||
- Removed life, blood, wood, mental, force from ELEMENTS definition
|
|
||||||
- Updated crystal recipe from sand + sand + mental to sand + sand + light
|
|
||||||
- Updated FLOOR_ELEM_CYCLE to remove life
|
|
||||||
- Updated ELEMENT_OPPOSITES to remove life/death pair
|
|
||||||
- Updated ELEMENT_ICON_NAMES to remove banned elements
|
|
||||||
|
|
||||||
- **Removed spells using banned elements**:
|
|
||||||
- Removed life spells: lifeTap, thornWhip, entangle, worldTree
|
|
||||||
- Removed blood spells: bloodNeedle, bloodSlash, bloodRite, sanguineStorm
|
|
||||||
- Removed wood spells: thornStrike, rootGrab, treePunch, forestAwakening
|
|
||||||
- Removed mental spells: mindSpike, psychicBlast, mindCrush, cerebralAnnihilation
|
|
||||||
- Removed force spells: forcePush, kineticBlast, gravityWell, telekineticStorm
|
|
||||||
- Removed transference spells with lifesteal: transferStrike, manaRip, essenceDrain, soulTransfer
|
|
||||||
|
|
||||||
- **Removed lifesteal from remaining spells**:
|
|
||||||
- drain - removed lifesteal effect
|
|
||||||
- soulRend - removed lifesteal effect
|
|
||||||
- deathMark - removed lifesteal effect
|
|
||||||
- voidBlade - changed from lifesteal to +20% damage buff
|
|
||||||
|
|
||||||
- **Updated guardians**:
|
|
||||||
- Floor 20 (Aqua Regia): Changed from "Water spells have 10% lifesteal" to "Water spells deal +15% damage"
|
|
||||||
- Floor 60 (Umbra Mortis): Changed from "Dark spells have 20% lifesteal" to "Dark spells deal +25% damage to armored enemies"
|
|
||||||
- Floor 70 (Vita Sempiterna): REMOVED (life element guardian)
|
|
||||||
|
|
||||||
- **Removed research skills for banned spell types**:
|
|
||||||
- Removed researchBloodSpells, researchWoodSpells, researchMentalSpells, researchForceSpells
|
|
||||||
- Removed advanced and master variants of above
|
|
||||||
- Updated researchLifeDeathSpells description
|
|
||||||
|
|
||||||
- **Updated EFFECT_RESEARCH_MAPPING**:
|
|
||||||
- Removed lifesteal_5 from researchSpecialEffects
|
|
||||||
- Removed all blood, wood, mental, force spell mappings
|
|
||||||
|
|
||||||
- **Updated enchantment-effects.ts**:
|
|
||||||
- Removed lifesteal_5 effect
|
|
||||||
- Removed all blood, wood, mental, force spell enchantments
|
|
||||||
- Updated remaining spell descriptions to remove lifesteal mentions
|
|
||||||
|
|
||||||
- **Removed lifesteal from types.ts**:
|
|
||||||
- Removed 'lifesteal' from SpellEffect type union
|
|
||||||
|
|
||||||
- **Removed lifesteal handling from store.ts**:
|
|
||||||
- Removed lifesteal effect processing code
|
|
||||||
|
|
||||||
- **Updated SpireTab.tsx**:
|
|
||||||
- Removed lifesteal badge rendering
|
|
||||||
- Added badge rendering for other effect types
|
|
||||||
|
|
||||||
- **Added documentation**:
|
|
||||||
- Added "BANNED CONTENT" section to AGENTS.md explaining lifesteal/healing ban
|
|
||||||
- Added mana types overview to AGENTS.md with all current mana types
|
|
||||||
- Added mana types overview to README.md
|
|
||||||
|
|
||||||
- **Fixed tests**:
|
|
||||||
- Updated bug-fixes.test.ts to test existing elements (transference, metal, sand, lightning)
|
|
||||||
- All 44 tests now pass
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Lifesteal and healing completely removed from player abilities
|
|
||||||
- Banned mana types (life, blood, wood, mental, force) removed from game
|
|
||||||
- Crystal recipe updated to use light instead of mental
|
|
||||||
- Documentation updated with banned content policy
|
|
||||||
- All tests pass, lint clean
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 26
|
|
||||||
Agent: Main
|
|
||||||
Task: Mobile UX improvements, debug options, and enchanting system fix
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Moved LootInventoryDisplay to separate LootTab**:
|
|
||||||
- Created new LootTab.tsx component
|
|
||||||
- Better mobile UX - materials no longer clutter main page
|
|
||||||
- Added to tabs in page.tsx
|
|
||||||
- **Moved AchievementsDisplay to separate AchievementsTab**:
|
|
||||||
- Created new AchievementsTab.tsx component
|
|
||||||
- Better mobile UX - achievements have their own space
|
|
||||||
- Added to tabs in page.tsx
|
|
||||||
- **Added skill research debug options to DebugTab**:
|
|
||||||
- "Level Up All Enchanting Skills" button
|
|
||||||
- "Level Up All Research Skills" button
|
|
||||||
- "Unlock All Effects" button
|
|
||||||
- "Max All Skills" button
|
|
||||||
- **Fixed enchanting system**:
|
|
||||||
- Disenchant is now ONLY possible in the Prepare stage
|
|
||||||
- Apply stage no longer shows disenchant/recover buttons
|
|
||||||
- Apply stage filters out already-enchanted gear
|
|
||||||
- Shows message "No unenchanted equipment available. Disenchant in Prepare stage first."
|
|
||||||
- Prepare stage shows disenchant option only for enchanted gear
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Mobile UX improved with dedicated tabs for Loot and Achievements
|
|
||||||
- Debug options added for testing skill research system
|
|
||||||
- Enchanting system now properly enforces disenchant-only-in-prepare rule
|
|
||||||
- Cannot apply new enchantments to already enchanted gear
|
|
||||||
- Committed and pushed to git (94cc9a1)
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 27
|
|
||||||
Agent: Main
|
|
||||||
Task: Implement Golemancy System
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Created golem data definitions** (`src/lib/game/data/golems.ts`):
|
|
||||||
- Base golem: Earth Golem (unlocks at Fabricator Level 2)
|
|
||||||
- Elemental variants: Steel (metal), Crystal, Sand golems
|
|
||||||
- Advanced hybrids (Enchanter 5 + Fabricator 5): Lava, Galvanic, Obsidian, Prism, Quicksilver, Voidstone
|
|
||||||
- Each golem has unique stats, mana costs, and special abilities
|
|
||||||
|
|
||||||
- **Added golemancy types to types.ts**:
|
|
||||||
- `SummonedGolem` interface for active golems
|
|
||||||
- `GolemancyState` interface for enabled/summoned golems tracking
|
|
||||||
- Added `golemancy` to GameState
|
|
||||||
|
|
||||||
- **Updated store.ts with golemancy**:
|
|
||||||
- Initialized golemancy state in `makeInitial()`
|
|
||||||
- Added `toggleGolem` and `setEnabledGolems` actions
|
|
||||||
- Added golemancy to persist partialize for save/load
|
|
||||||
|
|
||||||
- **Added golemancy skills to constants.ts**:
|
|
||||||
- Golem Mastery (+10% golem damage)
|
|
||||||
- Golem Efficiency (+5% attack speed)
|
|
||||||
- Golem Longevity (+1 floor duration)
|
|
||||||
- Golem Siphon (-10% maintenance cost)
|
|
||||||
- Advanced Golemancy (unlock hybrid recipes)
|
|
||||||
- Golem Resonance (+1 slot at Fabricator 10)
|
|
||||||
|
|
||||||
- **Created GolemancyTab component**:
|
|
||||||
- Displays available golem slots based on Fabricator level
|
|
||||||
- Shows all unlocked and locked golems
|
|
||||||
- Displays golem stats, costs, and status
|
|
||||||
- Toggle golems on/off for summoning
|
|
||||||
|
|
||||||
- **Updated README.md**:
|
|
||||||
- Added golemancy system documentation
|
|
||||||
- Updated enchanting documentation
|
|
||||||
- Removed familiar system references
|
|
||||||
- Added Banned Content section
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Golemancy system foundation implemented
|
|
||||||
- Golems unlock based on Fabricator level and mana types
|
|
||||||
- UI for selecting and managing golems
|
|
||||||
- Documentation updated
|
|
||||||
- Lint passes
|
|
||||||
|
|
||||||
---
|
|
||||||
Task ID: 28
|
|
||||||
Agent: Main
|
|
||||||
Task: Fix level upgrade reset loop bug and add golem display to SpireTab
|
|
||||||
|
|
||||||
Work Log:
|
|
||||||
- **Fixed upgrade reset bug in commitSkillUpgrades()**:
|
|
||||||
- Root cause: `commitSkillUpgrades()` was replacing ALL upgrades for a skill instead of merging by milestone
|
|
||||||
- When selecting level 10 upgrades, it would wipe out level 5 selections (and vice versa)
|
|
||||||
- Added optional `milestone` parameter (5 | 10) to the function
|
|
||||||
- When milestone is specified, the function now:
|
|
||||||
- Keeps existing upgrades from OTHER milestones
|
|
||||||
- Only replaces upgrades for the CURRENT milestone
|
|
||||||
- Updated type definition in GameStore interface
|
|
||||||
- Updated SkillsTab.tsx to pass `upgradeDialogMilestone` when committing
|
|
||||||
|
|
||||||
- **Fixed tier-up upgrade reset in tierUpSkill()**:
|
|
||||||
- Root cause: `tierUpSkill()` was setting new tier's upgrades to empty array `[]`
|
|
||||||
- When tiering up, all previous tier's upgrades were lost
|
|
||||||
- Now copies current tier's upgrades to the new tier
|
|
||||||
- Example: Tier 1's L5/L10 upgrades persist when becoming Tier 2
|
|
||||||
|
|
||||||
- **Added summoned golems display to SpireTab**:
|
|
||||||
- Imported GOLEMS_DEF and helper functions
|
|
||||||
- Added Mountain icon import
|
|
||||||
- Added "Active Golems" card that appears when golems are summoned
|
|
||||||
- Shows each golem's name, damage, attack speed, and armor pierce
|
|
||||||
- Displays attack progress bar when climbing
|
|
||||||
- AOE badge for golems with area attacks
|
|
||||||
|
|
||||||
Stage Summary:
|
|
||||||
- Level 5 and level 10 upgrades no longer reset each other
|
|
||||||
- Upgrades now carry over when tiering up to the next tier
|
|
||||||
- Players can safely select upgrades at both milestones across all tiers
|
|
||||||
- Summoned golems now visible in Spire tab during combat
|
|
||||||
- Lint passes, pushed to git (e2671d7)
|
|
||||||
Reference in New Issue
Block a user