Replace valley obstacles with lush green forest groves and pine tree graphics
This commit is contained in:
parent
d12b5615e5
commit
85ee13a513
|
|
@ -104,25 +104,23 @@ class GameEngine:
|
|||
curr_x += dx
|
||||
curr_y += dy
|
||||
|
||||
# 2. Procedural Valleys / chasms (5-7 winding gorges)
|
||||
for _ in range(random.randint(5, 7)):
|
||||
curr_x = random.randint(min_x + 5, max_x - 5)
|
||||
curr_y = random.randint(min_y + 5, max_y - 5)
|
||||
length = random.randint(16, 28)
|
||||
angle_dx = random.choice([-1, 1])
|
||||
angle_dy = random.choice([-1, 1])
|
||||
# 2. Procedural Dense Forests / Woodlands (6-8 organic forest groves)
|
||||
for _ in range(random.randint(6, 8)):
|
||||
center_x = random.randint(min_x + 6, max_x - 6)
|
||||
center_y = random.randint(min_y + 6, max_y - 6)
|
||||
grove_cells = [(center_x, center_y)]
|
||||
target_trees = random.randint(22, 36)
|
||||
|
||||
for _ in range(length):
|
||||
if (curr_x, curr_y) not in obstacles:
|
||||
add_obstacle(curr_x, curr_y, "valley")
|
||||
if random.random() < 0.35:
|
||||
curr_x += random.choice([-1, 0, 1])
|
||||
else:
|
||||
curr_x += angle_dx
|
||||
if random.random() < 0.35:
|
||||
curr_y += random.choice([-1, 0, 1])
|
||||
else:
|
||||
curr_y += angle_dy
|
||||
for _ in range(target_trees):
|
||||
if not grove_cells:
|
||||
break
|
||||
cx, cy = random.choice(grove_cells)
|
||||
if (cx, cy) not in obstacles:
|
||||
add_obstacle(cx, cy, "forest")
|
||||
for nx, ny in [(cx + 1, cy), (cx - 1, cy), (cx, cy + 1), (cx, cy - 1)]:
|
||||
if min_x <= nx <= max_x and min_y <= ny <= max_y and (nx, ny) not in obstacles:
|
||||
if random.random() < 0.65:
|
||||
grove_cells.append((nx, ny))
|
||||
|
||||
# Enforce max allowed obstacles (cap strictly below 50%)
|
||||
if len(obstacles) > max_allowed_obstacles:
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ class AvailableMovesResponse(BaseModel):
|
|||
|
||||
class ObstacleType(str, Enum):
|
||||
MOUNTAIN = "mountain"
|
||||
FOREST = "forest"
|
||||
VALLEY = "valley"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ def test_map_obstacles_mountains_valleys_and_connectivity():
|
|||
|
||||
obstacle_types = {obs["type"] for obs in obstacles}
|
||||
assert "mountain" in obstacle_types
|
||||
assert "valley" in obstacle_types
|
||||
assert "forest" in obstacle_types
|
||||
|
||||
obstacle_coords = {(obs["x"], obs["y"]) for obs in obstacles}
|
||||
|
||||
|
|
|
|||
|
|
@ -223,50 +223,91 @@ export const BoardCanvas: React.FC<BoardCanvasProps> = ({
|
|||
ctx.fillRect(x0, y0, cellSize, cellSize);
|
||||
}
|
||||
} else {
|
||||
// Valley / Chasm trench
|
||||
ctx.fillStyle = '#060913';
|
||||
// Forest / Woodland Grove (Green & Trees)
|
||||
ctx.fillStyle = '#022c22';
|
||||
ctx.fillRect(x0, y0, cellSize, cellSize);
|
||||
ctx.strokeStyle = '#312e81';
|
||||
ctx.strokeStyle = '#064e3b';
|
||||
ctx.lineWidth = 0.5;
|
||||
ctx.strokeRect(x0, y0, cellSize, cellSize);
|
||||
|
||||
if (cellSize >= 6) {
|
||||
// Canyon cliffs
|
||||
ctx.fillStyle = '#1e1b4b';
|
||||
// Sturdy timber tree trunk
|
||||
const trunkWidth = Math.max(1.5, cellSize * 0.14);
|
||||
const trunkHeight = cellSize * 0.22;
|
||||
ctx.fillStyle = '#78350f';
|
||||
ctx.fillRect(cx - trunkWidth / 2, y0 + cellSize * 0.70, trunkWidth, trunkHeight);
|
||||
|
||||
// Tier 3 (Bottom evergreen canopy - deep pine green)
|
||||
ctx.fillStyle = '#065f46';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x0, y0);
|
||||
ctx.lineTo(x0 + cellSize * 0.3, y0);
|
||||
ctx.lineTo(x0 + cellSize * 0.45, y0 + cellSize);
|
||||
ctx.lineTo(x0, y0 + cellSize);
|
||||
ctx.moveTo(cx, y0 + cellSize * 0.44);
|
||||
ctx.lineTo(cx - cellSize * 0.38, y0 + cellSize * 0.74);
|
||||
ctx.lineTo(cx + cellSize * 0.38, y0 + cellSize * 0.74);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// Tier 2 (Middle canopy - rich emerald)
|
||||
ctx.fillStyle = '#059669';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x0 + cellSize, y0);
|
||||
ctx.lineTo(x0 + cellSize * 0.7, y0);
|
||||
ctx.lineTo(x0 + cellSize * 0.55, y0 + cellSize);
|
||||
ctx.lineTo(x0 + cellSize, y0 + cellSize);
|
||||
ctx.moveTo(cx, y0 + cellSize * 0.26);
|
||||
ctx.lineTo(cx - cellSize * 0.30, y0 + cellSize * 0.52);
|
||||
ctx.lineTo(cx + cellSize * 0.30, y0 + cellSize * 0.52);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// Deep chasm rift fissure
|
||||
ctx.strokeStyle = '#4338ca';
|
||||
ctx.lineWidth = Math.max(0.8, cellSize * 0.08);
|
||||
// Tier 1 (Top canopy - vibrant fresh pine green)
|
||||
ctx.fillStyle = '#10b981';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx - cellSize * 0.05, y0);
|
||||
ctx.lineTo(cx + cellSize * 0.05, cy);
|
||||
ctx.lineTo(cx - cellSize * 0.05, y0 + cellSize);
|
||||
ctx.moveTo(cx, y0 + cellSize * 0.12);
|
||||
ctx.lineTo(cx - cellSize * 0.20, y0 + cellSize * 0.34);
|
||||
ctx.lineTo(cx + cellSize * 0.20, y0 + cellSize * 0.34);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// Sunlight foliage highlight along left edges
|
||||
ctx.strokeStyle = '#34d399';
|
||||
ctx.lineWidth = Math.max(0.6, cellSize * 0.04);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx, y0 + cellSize * 0.12);
|
||||
ctx.lineTo(cx - cellSize * 0.20, y0 + cellSize * 0.34);
|
||||
ctx.moveTo(cx - cellSize * 0.08, y0 + cellSize * 0.34);
|
||||
ctx.lineTo(cx - cellSize * 0.30, y0 + cellSize * 0.52);
|
||||
ctx.moveTo(cx - cellSize * 0.12, y0 + cellSize * 0.52);
|
||||
ctx.lineTo(cx - cellSize * 0.38, y0 + cellSize * 0.74);
|
||||
ctx.stroke();
|
||||
|
||||
// Glowing crevasse depth highlight
|
||||
ctx.strokeStyle = '#818cf8';
|
||||
ctx.lineWidth = Math.max(0.5, cellSize * 0.03);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cx, y0 + cellSize * 0.2);
|
||||
ctx.lineTo(cx, y0 + cellSize * 0.8);
|
||||
ctx.stroke();
|
||||
// If zoomed in large, render two flanking mini trees to form a lush dense grove
|
||||
if (cellSize >= 18) {
|
||||
// Left mini tree
|
||||
const lcx = cx - cellSize * 0.26;
|
||||
const ltrunkW = Math.max(1, cellSize * 0.08);
|
||||
ctx.fillStyle = '#78350f';
|
||||
ctx.fillRect(lcx - ltrunkW / 2, y0 + cellSize * 0.76, ltrunkW, cellSize * 0.14);
|
||||
|
||||
ctx.fillStyle = '#047857';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(lcx, y0 + cellSize * 0.48);
|
||||
ctx.lineTo(lcx - cellSize * 0.16, y0 + cellSize * 0.78);
|
||||
ctx.lineTo(lcx + cellSize * 0.16, y0 + cellSize * 0.78);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// Right mini tree
|
||||
const rcx = cx + cellSize * 0.26;
|
||||
const rtrunkW = Math.max(1, cellSize * 0.08);
|
||||
ctx.fillStyle = '#78350f';
|
||||
ctx.fillRect(rcx - rtrunkW / 2, y0 + cellSize * 0.76, rtrunkW, cellSize * 0.14);
|
||||
|
||||
ctx.fillStyle = '#059669';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(rcx, y0 + cellSize * 0.44);
|
||||
ctx.lineTo(rcx - cellSize * 0.16, y0 + cellSize * 0.78);
|
||||
ctx.lineTo(rcx + cellSize * 0.16, y0 + cellSize * 0.78);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
} else {
|
||||
ctx.fillStyle = '#1e1b4b';
|
||||
ctx.fillStyle = '#059669';
|
||||
ctx.fillRect(x0, y0, cellSize, cellSize);
|
||||
}
|
||||
}
|
||||
|
|
@ -575,8 +616,8 @@ export const BoardCanvas: React.FC<BoardCanvasProps> = ({
|
|||
{hoveredObstacle && (
|
||||
<>
|
||||
<span className="text-slate-500">|</span>
|
||||
<span className={hoveredObstacle.type === 'mountain' ? 'text-slate-300 font-bold' : 'text-indigo-400 font-bold'}>
|
||||
{hoveredObstacle.type === 'mountain' ? '▲ Mountain (Impassable)' : '▼ Valley (Impassable)'}
|
||||
<span className={hoveredObstacle.type === 'mountain' ? 'text-slate-300 font-bold' : 'text-emerald-400 font-bold'}>
|
||||
{hoveredObstacle.type === 'mountain' ? '▲ Mountain (Impassable)' : '🌲 Forest (Impassable)'}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
|
|
@ -590,9 +631,9 @@ export const BoardCanvas: React.FC<BoardCanvasProps> = ({
|
|||
<span className="inline-block w-2.5 h-2.5 rounded-sm bg-slate-600 border border-slate-400" />
|
||||
Mountain
|
||||
</span>
|
||||
<span className="flex items-center gap-1 text-indigo-300">
|
||||
<span className="inline-block w-2.5 h-2.5 rounded-sm bg-indigo-950 border border-indigo-500" />
|
||||
Valley
|
||||
<span className="flex items-center gap-1 text-emerald-400">
|
||||
<span className="inline-block w-2.5 h-2.5 rounded-sm bg-emerald-700 border border-emerald-500" />
|
||||
Forest
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export interface GridConfig {
|
|||
export interface Obstacle {
|
||||
x: number;
|
||||
y: number;
|
||||
type: 'mountain' | 'valley';
|
||||
type: 'mountain' | 'forest' | 'valley';
|
||||
}
|
||||
|
||||
export interface Player {
|
||||
|
|
|
|||
Loading…
Reference in New Issue