1505 lines
50 KiB
JavaScript
1505 lines
50 KiB
JavaScript
// Texture Generator
|
|
// Proceduralno generiranje tekstur in sprite-ov
|
|
class TextureGenerator {
|
|
// Generiraj player sprite (32x32px pixel art)
|
|
static createPlayerSprite(scene, key = 'player') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Body
|
|
ctx.fillStyle = '#A0522D'; // Sienna skin
|
|
ctx.fillRect(10, 8, 12, 12); // Head
|
|
ctx.fillStyle = '#2F4F4F'; // DarkSlateGray shirt
|
|
ctx.fillRect(8, 20, 16, 12);
|
|
|
|
// Eyes
|
|
ctx.fillStyle = '#FFFFFF';
|
|
ctx.fillRect(12, 12, 2, 2);
|
|
ctx.fillRect(18, 12, 2, 2);
|
|
ctx.fillStyle = '#000000';
|
|
ctx.fillRect(13, 12, 1, 1);
|
|
ctx.fillRect(19, 12, 1, 1);
|
|
|
|
// Hair
|
|
ctx.fillStyle = '#000000';
|
|
ctx.fillRect(10, 6, 12, 4);
|
|
ctx.fillRect(8, 8, 2, 6);
|
|
ctx.fillRect(22, 8, 2, 6);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
// Generiraj walking animacijo
|
|
static createPlayerWalkSprite(scene, key = 'player_walk') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 128, 32);
|
|
const ctx = canvas.getContext();
|
|
// Simple placeholders for 4 frames
|
|
for (let i = 0; i < 4; i++) {
|
|
ctx.fillStyle = '#2F4F4F';
|
|
ctx.fillRect(i * 32 + 8, 6, 16, 26);
|
|
}
|
|
canvas.refresh();
|
|
}
|
|
|
|
// Generiraj NPC sprite
|
|
static createNPCSprite(scene, key = 'npc', type = 'zombie') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
|
|
// Elite Zombie: Red body, glowing pink eyes
|
|
if (type === 'elite_zombie') {
|
|
ctx.fillStyle = '#8B0000'; // DarkRed
|
|
ctx.fillRect(8, 6, 16, 26);
|
|
|
|
// Glowing Eyes
|
|
ctx.fillStyle = '#FF1493'; // Deep Pink (glow)
|
|
ctx.fillRect(10, 10, 4, 4);
|
|
ctx.fillRect(20, 10, 4, 4);
|
|
} else {
|
|
ctx.fillStyle = (type === 'zombie') ? '#556B2F' : '#DEB887';
|
|
ctx.fillRect(8, 6, 16, 26);
|
|
|
|
// Eyes (Red for zombie)
|
|
ctx.fillStyle = (type === 'zombie') ? '#FF0000' : '#000000';
|
|
ctx.fillRect(10, 10, 4, 4);
|
|
ctx.fillRect(20, 10, 4, 4);
|
|
}
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createMerchantSprite(scene, key = 'merchant_texture') {
|
|
if (scene.textures.exists(key)) return;
|
|
console.log('🧙♂️ Creating Merchant Sprite Texture...');
|
|
try {
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Body (BRIGHT GOLD robe - zelo viden!)
|
|
ctx.fillStyle = '#FFD700'; // Gold
|
|
ctx.fillRect(8, 6, 16, 26);
|
|
|
|
// Head
|
|
ctx.fillStyle = '#FFE4C4'; // Bisque skin
|
|
ctx.fillRect(10, 4, 12, 12);
|
|
|
|
// Hat (Red Merchants Hat)
|
|
ctx.fillStyle = '#8B0000'; // DarkRed
|
|
ctx.fillRect(8, 2, 16, 4);
|
|
ctx.fillRect(10, 0, 12, 2); // Hat Top
|
|
|
|
// Backpack (Brown - visible on sides)
|
|
ctx.fillStyle = '#8B4513';
|
|
ctx.fillRect(4, 10, 4, 14); // Left side pack
|
|
ctx.fillRect(24, 10, 4, 14); // Right side pack
|
|
|
|
// Straps
|
|
ctx.fillStyle = '#A0522D';
|
|
ctx.fillRect(8, 10, 16, 2);
|
|
|
|
// Eyes
|
|
ctx.fillStyle = '#000000';
|
|
ctx.fillRect(13, 8, 2, 2);
|
|
ctx.fillRect(17, 8, 2, 2);
|
|
|
|
// Beard (White)
|
|
ctx.fillStyle = '#F5F5F5';
|
|
ctx.fillRect(10, 12, 12, 4);
|
|
ctx.fillRect(12, 16, 8, 2);
|
|
|
|
canvas.refresh();
|
|
console.log('✅ Merchant Sprite Created!');
|
|
}
|
|
catch (error) {
|
|
console.error('❌ ERROR creating merchant sprite:', error);
|
|
}
|
|
}
|
|
|
|
static createCloudSprite(scene, key = 'cloud') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 64, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 64, 32);
|
|
ctx.fillStyle = 'rgba(255,255,255,0.6)';
|
|
ctx.beginPath(); ctx.arc(20, 20, 15, 0, Math.PI * 2); ctx.fill();
|
|
ctx.beginPath(); ctx.arc(35, 15, 18, 0, Math.PI * 2); ctx.fill();
|
|
ctx.beginPath(); ctx.arc(50, 20, 12, 0, Math.PI * 2); ctx.fill();
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createCropSprite(scene, key, stage = 4) {
|
|
if (scene.textures.exists(key)) scene.textures.remove(key); // FORCE REFRESH to see updates
|
|
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Improved Wheat Visuals
|
|
const startY = 32;
|
|
|
|
// 1. Stems (Green -> Yellowish)
|
|
ctx.fillStyle = (stage < 3) ? '#32CD32' : '#DAA520';
|
|
const h = stage * 6;
|
|
|
|
// Draw distinct stalks
|
|
ctx.fillRect(10, startY - h, 2, h); // Left stalk
|
|
ctx.fillRect(16, startY - h, 2, h); // Center stalk
|
|
ctx.fillRect(22, startY - h, 2, h); // Right stalk
|
|
|
|
// 2. Heads (Wheat ears) - Stage 3 & 4
|
|
if (stage >= 3) {
|
|
ctx.fillStyle = '#FFD700'; // Gold
|
|
// Left head
|
|
ctx.beginPath(); ctx.ellipse(11, startY - h, 3, 5, -0.2, 0, Math.PI * 2); ctx.fill();
|
|
// Center head
|
|
ctx.beginPath(); ctx.ellipse(17, startY - h - 2, 3, 6, 0, 0, Math.PI * 2); ctx.fill();
|
|
// Right head
|
|
ctx.beginPath(); ctx.ellipse(23, startY - h, 3, 5, 0.2, 0, Math.PI * 2); ctx.fill();
|
|
}
|
|
|
|
// 3. Withered
|
|
if (stage === 5) {
|
|
ctx.fillStyle = '#8B4513';
|
|
ctx.fillRect(10, 24, 14, 8); // Dead pile
|
|
}
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createStructureSprite(scene, key, type) {
|
|
if (scene.textures.exists(key)) return;
|
|
const width = (type === 'house' || type === 'ruin') ? 64 : 32;
|
|
const height = (type === 'house' || type === 'ruin') ? 64 : 32;
|
|
const canvas = scene.textures.createCanvas(key, width, height);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, width, height);
|
|
|
|
if (type === 'ruin') {
|
|
// Isometric Ruin / Rocks for user
|
|
ctx.fillStyle = '#696969'; // DimGray
|
|
// Big Rock 1
|
|
ctx.beginPath(); ctx.arc(20, 50, 14, 0, Math.PI * 2); ctx.fill();
|
|
// Big Rock 2
|
|
ctx.beginPath(); ctx.arc(45, 55, 12, 0, Math.PI * 2); ctx.fill();
|
|
// Details
|
|
ctx.fillStyle = '#555555';
|
|
ctx.beginPath(); ctx.arc(25, 45, 6, 0, Math.PI * 2); ctx.fill();
|
|
} else if (type === 'house' || type === 'house_lv2') {
|
|
// --- ISOMETRIC HOUSE ---
|
|
const isLv2 = (type === 'house_lv2');
|
|
|
|
// Walls
|
|
const wallColor = isLv2 ? '#A0522D' : '#8B4513'; // Lv2 is lighter/refined
|
|
ctx.fillStyle = wallColor;
|
|
ctx.fillRect(10, 24, 44, 32);
|
|
|
|
// Roof (Triangle/Pyramid-ish)
|
|
const roofColor = isLv2 ? '#8B0000' : '#4682B4'; // Lv2 Red Roof, Lv1 Blue
|
|
ctx.fillStyle = roofColor;
|
|
ctx.beginPath();
|
|
ctx.moveTo(32, 2); // Top
|
|
ctx.lineTo(8, 24); // Left
|
|
ctx.lineTo(56, 24); // Right
|
|
ctx.fill();
|
|
|
|
// Door
|
|
ctx.fillStyle = '#5C4033'; // Dark Wood
|
|
ctx.fillRect(26, 40, 12, 16);
|
|
|
|
// Windows
|
|
ctx.fillStyle = '#87CEEB'; // SkyBlue
|
|
ctx.fillRect(14, 34, 8, 8); // Window 1
|
|
ctx.fillRect(42, 34, 8, 8); // Window 2
|
|
|
|
// Lv2 Extras: Chimney or extra floor indicator
|
|
if (isLv2) {
|
|
ctx.fillStyle = '#555';
|
|
ctx.fillRect(40, 10, 6, 12); // Chimney
|
|
|
|
// Second floor window
|
|
ctx.fillStyle = '#87CEEB';
|
|
ctx.beginPath(); ctx.arc(32, 16, 4, 0, Math.PI * 2); ctx.fill();
|
|
}
|
|
|
|
} else if (name === 'stable') {
|
|
// Stable
|
|
ctx.fillStyle = '#8B4513'; // Wood
|
|
ctx.fillRect(0, 16, 32, 16);
|
|
ctx.fillStyle = '#A0522D'; // Roof
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, 16); ctx.lineTo(16, 0); ctx.lineTo(32, 16);
|
|
ctx.fill();
|
|
// Door
|
|
ctx.fillStyle = '#000';
|
|
ctx.fillRect(10, 20, 12, 12);
|
|
} else {
|
|
// Generic box for others
|
|
ctx.fillStyle = '#8B4513';
|
|
ctx.fillRect(0, 0, width, height);
|
|
}
|
|
canvas.refresh();
|
|
}
|
|
|
|
// ========== 3D VOLUMETRIC GENERATORS (RESTORED) ==========
|
|
|
|
// Generiraj 3D volumetric tree (Minecraft-style)
|
|
static createTreeSprite(scene, key = 'tree') {
|
|
if (scene.textures.exists(key)) return;
|
|
|
|
const size = 64;
|
|
const height = 96; // Taller for 3D effect
|
|
const canvas = scene.textures.createCanvas(key, size, height);
|
|
const ctx = canvas.getContext();
|
|
|
|
ctx.clearRect(0, 0, size, height);
|
|
|
|
// Helper to draw isometric-ish cube face
|
|
const drawCube = (x, y, w, h, d, colorTop, colorSide, colorFront) => {
|
|
// Front face
|
|
ctx.fillStyle = colorFront;
|
|
ctx.fillRect(x, y + d, w, h);
|
|
|
|
// Top face (fake perspective)
|
|
ctx.fillStyle = colorTop;
|
|
ctx.beginPath();
|
|
ctx.moveTo(x, y + d);
|
|
ctx.lineTo(x + d, y);
|
|
ctx.lineTo(x + w + d, y);
|
|
ctx.lineTo(x + w, y + d);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
// Side face
|
|
ctx.fillStyle = colorSide;
|
|
ctx.beginPath();
|
|
ctx.moveTo(x + w, y + d);
|
|
ctx.lineTo(x + w + d, y);
|
|
ctx.lineTo(x + w + d, y + h);
|
|
ctx.lineTo(x + w, y + h + d);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
};
|
|
|
|
// Trunk
|
|
const trunkColor = '#8B4513';
|
|
const trunkTop = '#A0522D';
|
|
const trunkDark = '#663300';
|
|
|
|
drawCube(24, 60, 16, 24, 8, trunkTop, trunkDark, trunkColor);
|
|
|
|
// Leaves chunks (Minecraft style)
|
|
const leafColor = '#228B22'; // ForestGreen
|
|
const leafTop = '#32CD32'; // LimeGreen
|
|
const leafDark = '#006400'; // DarkGreen
|
|
|
|
// Bottom layer
|
|
drawCube(12, 30, 40, 20, 10, leafTop, leafDark, leafColor);
|
|
// Top layer
|
|
drawCube(20, 10, 24, 20, 8, leafTop, leafDark, leafColor);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
// Generiraj 3D volumetric Rock (Gray Cubes)
|
|
static createRockSprite(scene, key = 'rock') {
|
|
if (scene.textures.exists(key)) return;
|
|
const size = 48;
|
|
const canvas = scene.textures.createCanvas(key, size, size);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, size, size);
|
|
|
|
// Simple Voxel Draw
|
|
const drawVoxel = (x, y, c) => {
|
|
ctx.fillStyle = c;
|
|
ctx.fillRect(x, y, 10, 10);
|
|
ctx.fillStyle = '#444444'; // Side
|
|
ctx.fillRect(x + 10, y, 4, 10);
|
|
ctx.fillStyle = '#333333'; // Bottom
|
|
ctx.fillRect(x, y + 10, 10, 4);
|
|
};
|
|
const gray = '#808080';
|
|
|
|
drawVoxel(10, 25, gray);
|
|
drawVoxel(20, 20, gray);
|
|
drawVoxel(15, 15, gray);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
// Generiraj 3D flower (simple volumetric)
|
|
static createFlowerSprite(scene, key = 'flower') {
|
|
if (scene.textures.exists(key)) return;
|
|
const size = 32;
|
|
const canvas = scene.textures.createCanvas(key, size, size);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, size, size);
|
|
|
|
ctx.fillStyle = '#228B22'; // Stem
|
|
ctx.fillRect(15, 10, 2, 10);
|
|
|
|
ctx.fillStyle = '#FF69B4'; // Flower
|
|
ctx.fillRect(12, 8, 8, 8);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createBushSprite(scene, key = 'bush') {
|
|
if (scene.textures.exists(key)) return;
|
|
const size = 32;
|
|
const canvas = scene.textures.createCanvas(key, size, size);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, size, size);
|
|
|
|
ctx.fillStyle = '#228B22';
|
|
ctx.beginPath(); ctx.arc(16, 16, 12, 0, Math.PI * 2); ctx.fill();
|
|
ctx.fillStyle = 'red'; // Berries
|
|
ctx.beginPath(); ctx.arc(12, 12, 2, 0, Math.PI * 2); ctx.fill();
|
|
ctx.beginPath(); ctx.arc(20, 18, 2, 0, Math.PI * 2); ctx.fill();
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createGravestoneSprite(scene, key = 'gravestone') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 48, 64);
|
|
const ctx = canvas.getContext();
|
|
|
|
// Clear with transparency
|
|
ctx.clearRect(0, 0, 48, 64);
|
|
|
|
// Stone base (darker gray)
|
|
ctx.fillStyle = '#4A4A4A';
|
|
ctx.fillRect(14, 52, 20, 8); // Base platform
|
|
|
|
// Main gravestone body (gray stone)
|
|
ctx.fillStyle = '#707070';
|
|
ctx.fillRect(16, 24, 16, 28); // Tall rectangle
|
|
|
|
// Rounded top
|
|
ctx.beginPath();
|
|
ctx.arc(24, 24, 8, Math.PI, 0, false);
|
|
ctx.fill();
|
|
|
|
// Moss/weathering effect (dark green patches)
|
|
ctx.fillStyle = '#2D5016';
|
|
ctx.fillRect(17, 30, 3, 4);
|
|
ctx.fillRect(28, 35, 2, 3);
|
|
ctx.fillRect(19, 45, 4, 3);
|
|
|
|
// Cracks (dark lines)
|
|
ctx.fillStyle = '#3A3A3A';
|
|
ctx.fillRect(20, 28, 1, 10); // Vertical crack
|
|
ctx.fillRect(27, 32, 1, 8);
|
|
|
|
// Cross/RIP symbol (lighter gray)
|
|
ctx.fillStyle = '#909090';
|
|
// Vertical bar
|
|
ctx.fillRect(23, 32, 2, 8);
|
|
// Horizontal bar
|
|
ctx.fillRect(21, 34, 6, 2);
|
|
|
|
// Shadow (semi-transparent black)
|
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
|
|
ctx.ellipse(24, 60, 10, 3, 0, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createFenceSprite(scene, key = 'fence_full') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 48, 48);
|
|
const ctx = canvas.getContext();
|
|
|
|
// Clear with transparency
|
|
ctx.clearRect(0, 0, 48, 48);
|
|
|
|
// Wood color (brown)
|
|
const woodDark = '#6B4423';
|
|
const woodLight = '#8B5A3C';
|
|
|
|
// Vertical posts (3 posts)
|
|
for (let i = 0; i < 3; i++) {
|
|
const x = 8 + i * 16;
|
|
|
|
// Main post
|
|
ctx.fillStyle = woodLight;
|
|
ctx.fillRect(x, 8, 6, 32);
|
|
|
|
// Dark side (shading)
|
|
ctx.fillStyle = woodDark;
|
|
ctx.fillRect(x + 5, 8, 1, 32);
|
|
|
|
// Top point (fence picket style)
|
|
ctx.fillStyle = woodLight;
|
|
ctx.beginPath();
|
|
ctx.moveTo(x, 8);
|
|
ctx.lineTo(x + 3, 4);
|
|
ctx.lineTo(x + 6, 8);
|
|
ctx.fill();
|
|
}
|
|
|
|
// Horizontal planks (2 planks)
|
|
ctx.fillStyle = woodLight;
|
|
ctx.fillRect(4, 16, 40, 4); // Top plank
|
|
ctx.fillRect(4, 28, 40, 4); // Bottom plank
|
|
|
|
// Plank shading
|
|
ctx.fillStyle = woodDark;
|
|
ctx.fillRect(4, 19, 40, 1); // Top plank shadow
|
|
ctx.fillRect(4, 31, 40, 1); // Bottom plank shadow
|
|
|
|
// Wood grain (subtle lines)
|
|
ctx.fillStyle = 'rgba(107, 68, 35, 0.3)';
|
|
for (let i = 0; i < 3; i++) {
|
|
const x = 8 + i * 16;
|
|
ctx.fillRect(x + 1, 10, 1, 25);
|
|
ctx.fillRect(x + 3, 12, 1, 20);
|
|
}
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createChestSprite(scene, key = 'chest') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Chest body (brown)
|
|
ctx.fillStyle = '#8B4513'; // SaddleBrown
|
|
ctx.fillRect(6, 12, 20, 16);
|
|
|
|
// Lock (gold)
|
|
ctx.fillStyle = '#FFD700';
|
|
ctx.fillRect(14, 18, 4, 6);
|
|
|
|
// Lid
|
|
ctx.fillStyle = '#A0522D';
|
|
ctx.fillRect(6, 8, 20, 4);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createSpawnerSprite(scene, key = 'spawner') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Dark portal/spawner
|
|
ctx.fillStyle = '#2F4F4F'; // DarkSlateGray
|
|
ctx.fillRect(8, 8, 16, 16);
|
|
|
|
// Red glow
|
|
ctx.fillStyle = '#8B0000';
|
|
ctx.fillRect(10, 10, 12, 12);
|
|
|
|
// Center black hole
|
|
ctx.fillStyle = '#000000';
|
|
ctx.beginPath();
|
|
ctx.arc(16, 16, 4, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createSignpostSprite(scene, key = 'signpost', text = '→') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Wooden post (brown)
|
|
ctx.fillStyle = '#8B4513';
|
|
ctx.fillRect(14, 8, 4, 24);
|
|
|
|
// Sign board
|
|
ctx.fillStyle = '#D2691E';
|
|
ctx.fillRect(6, 10, 20, 12);
|
|
|
|
// Border
|
|
ctx.strokeStyle = '#000';
|
|
ctx.lineWidth = 1;
|
|
ctx.strokeRect(6, 10, 20, 12);
|
|
|
|
// Text
|
|
ctx.fillStyle = '#000';
|
|
ctx.font = 'bold 14px Courier';
|
|
ctx.textAlign = 'center';
|
|
ctx.fillText(text, 16, 19);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createToolSprites(scene) {
|
|
// --- 3D VOXEL TOOLS ---
|
|
|
|
const refresh = (key) => {
|
|
if (scene.textures.exists(key)) scene.textures.remove(key);
|
|
const c = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = c.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
return { c, ctx };
|
|
};
|
|
|
|
// Helper: Draw 3D Pixel/Block
|
|
const drawBlock = (ctx, x, y, color) => {
|
|
ctx.fillStyle = color;
|
|
ctx.fillRect(x, y, 2, 2); // Front
|
|
ctx.fillStyle = 'rgba(0,0,0,0.3)'; // Side shadow
|
|
ctx.fillRect(x + 2, y, 1, 2);
|
|
ctx.fillRect(x, y + 2, 3, 1);
|
|
};
|
|
|
|
// 1. AXE (3D)
|
|
{
|
|
const { c, ctx } = refresh('item_axe');
|
|
// Handle
|
|
for (let i = 0; i < 8; i++) drawBlock(ctx, 12 + i, 20 - i * 2, '#8B4513');
|
|
// Head
|
|
for (let x = 0; x < 3; x++) {
|
|
for (let y = 0; y < 4; y++) {
|
|
drawBlock(ctx, 18 + x * 2, 6 + y * 2, '#708090'); // Gray Metal
|
|
}
|
|
}
|
|
drawBlock(ctx, 24, 8, '#C0C0C0'); // Edge
|
|
drawBlock(ctx, 24, 10, '#C0C0C0');
|
|
c.refresh();
|
|
}
|
|
|
|
// 2. PICKAXE (3D)
|
|
{
|
|
const { c, ctx } = refresh('item_pickaxe');
|
|
// Handle
|
|
for (let i = 0; i < 8; i++) drawBlock(ctx, 14, 8 + i * 2, '#8B4513');
|
|
// Head (Arc)
|
|
drawBlock(ctx, 6, 10, '#696969');
|
|
drawBlock(ctx, 8, 8, '#696969');
|
|
drawBlock(ctx, 10, 6, '#696969');
|
|
drawBlock(ctx, 12, 6, '#696969');
|
|
drawBlock(ctx, 14, 6, '#696969'); // Center
|
|
drawBlock(ctx, 16, 6, '#696969');
|
|
drawBlock(ctx, 18, 6, '#696969');
|
|
drawBlock(ctx, 20, 8, '#696969');
|
|
drawBlock(ctx, 22, 10, '#696969');
|
|
c.refresh();
|
|
}
|
|
|
|
// 3. HOE (3D)
|
|
{
|
|
const { c, ctx } = refresh('item_hoe');
|
|
// Handle
|
|
for (let i = 0; i < 9; i++) drawBlock(ctx, 14 + i, 4 + i * 2, '#8B4513');
|
|
// Head
|
|
drawBlock(ctx, 12, 4, '#778899');
|
|
drawBlock(ctx, 14, 4, '#778899');
|
|
drawBlock(ctx, 16, 6, '#778899');
|
|
drawBlock(ctx, 16, 8, '#778899'); // Blade
|
|
c.refresh();
|
|
}
|
|
|
|
// 4. SWORD (3D)
|
|
{
|
|
const { c, ctx } = refresh('item_sword');
|
|
// Handle
|
|
drawBlock(ctx, 14, 24, '#8B4513');
|
|
drawBlock(ctx, 14, 26, '#8B4513');
|
|
// Guard
|
|
drawBlock(ctx, 10, 22, '#FFD700');
|
|
drawBlock(ctx, 12, 22, '#FFD700');
|
|
drawBlock(ctx, 14, 22, '#FFD700');
|
|
drawBlock(ctx, 16, 22, '#FFD700');
|
|
drawBlock(ctx, 18, 22, '#FFD700');
|
|
// Blade
|
|
for (let i = 0; i < 8; i++) drawBlock(ctx, 14, 6 + i * 2, '#C0C0C0');
|
|
c.refresh();
|
|
}
|
|
|
|
// 5. WATERING CAN (3D)
|
|
{
|
|
const { c, ctx } = refresh('item_watering_can');
|
|
// Body 3x3 blocks
|
|
for (let x = 0; x < 4; x++)
|
|
for (let y = 0; y < 3; y++)
|
|
drawBlock(ctx, 10 + x * 2, 14 + y * 2, '#A9A9A9');
|
|
|
|
// Spout
|
|
drawBlock(ctx, 18, 12, '#A9A9A9');
|
|
drawBlock(ctx, 20, 10, '#A9A9A9');
|
|
// Handle
|
|
drawBlock(ctx, 8, 12, '#696969');
|
|
drawBlock(ctx, 8, 10, '#696969');
|
|
drawBlock(ctx, 10, 8, '#696969');
|
|
c.refresh();
|
|
}
|
|
|
|
// 6. BUCKET (Empty)
|
|
{
|
|
const { c, ctx } = refresh('item_bucket');
|
|
// Body
|
|
drawBlock(ctx, 12, 14, '#C0C0C0');
|
|
drawBlock(ctx, 14, 14, '#C0C0C0');
|
|
drawBlock(ctx, 16, 14, '#C0C0C0');
|
|
|
|
drawBlock(ctx, 11, 12, '#C0C0C0');
|
|
drawBlock(ctx, 17, 12, '#C0C0C0');
|
|
|
|
drawBlock(ctx, 10, 10, '#C0C0C0');
|
|
drawBlock(ctx, 18, 10, '#C0C0C0');
|
|
|
|
// Handle
|
|
ctx.fillStyle = '#696969';
|
|
ctx.fillRect(10, 6, 12, 2);
|
|
|
|
c.refresh();
|
|
}
|
|
|
|
// 7. BUCKET_MILK
|
|
{
|
|
const { c, ctx } = refresh('item_bucket_milk');
|
|
// Body (Same as bucket)
|
|
drawBlock(ctx, 12, 14, '#C0C0C0');
|
|
drawBlock(ctx, 14, 14, '#C0C0C0');
|
|
drawBlock(ctx, 16, 14, '#C0C0C0');
|
|
drawBlock(ctx, 11, 12, '#C0C0C0');
|
|
drawBlock(ctx, 17, 12, '#C0C0C0');
|
|
|
|
// Milk Content
|
|
ctx.fillStyle = '#FFFFFF';
|
|
ctx.fillRect(12, 10, 8, 4);
|
|
|
|
c.refresh();
|
|
}
|
|
|
|
// 8. BUCKET_MILK_GLOWING
|
|
{
|
|
const { c, ctx } = refresh('item_bucket_milk_glowing');
|
|
// Body
|
|
drawBlock(ctx, 12, 14, '#C0C0C0');
|
|
drawBlock(ctx, 14, 14, '#C0C0C0');
|
|
drawBlock(ctx, 16, 14, '#C0C0C0');
|
|
drawBlock(ctx, 11, 12, '#C0C0C0');
|
|
drawBlock(ctx, 17, 12, '#C0C0C0');
|
|
|
|
// Glowing Milk Content
|
|
ctx.fillStyle = '#00FF00';
|
|
ctx.fillRect(12, 10, 8, 4);
|
|
|
|
c.refresh();
|
|
}
|
|
|
|
}
|
|
|
|
static createItemSprites(scene) {
|
|
// Placeholder item generation
|
|
const items = [
|
|
{ name: 'wood', color: '#8B4513' }, // Rjava
|
|
{ name: 'stone', color: '#808080' }, // Siva
|
|
{ name: 'seeds', color: '#90EE90' }, // Svetlo zelena
|
|
{ name: 'wheat', color: '#FFD700' }, // Zlata
|
|
{ name: 'corn', color: '#FFD700' }, // Zlata (Corn)
|
|
{ name: 'seeds_corn', color: '#B22222' },// FireBrick seeds
|
|
{ name: 'item_bone', color: '#F5F5DC' }, // Beige
|
|
{ name: 'item_scrap', color: '#B87333' }, // Copper/Bronze (kovinski kos)
|
|
{ name: 'item_chip', color: '#00CED1' }, // DarkTurquoise (elektronski chip)
|
|
{ name: 'artefact_old', color: '#8B4513' }, // Ancient Pot (Brown)
|
|
{ name: 'milk', color: '#FFFFFF' }, // White
|
|
{ name: 'glowing_milk', color: '#00FF00' }, // Green
|
|
{ name: 'animal_feed', color: '#F4A460' }, // Sandy Brown (Feed)
|
|
{ name: 'cannabis', color: '#228B22' }, // Green
|
|
{ name: 'cannabis_seeds', color: '#55ff55' }, // Lighter Green
|
|
{ name: 'hemp_fiber', color: '#DEB887' }, // Tan/Beige
|
|
{ name: 'hemp_clothing', color: '#2E8B57' } // SeaGreen
|
|
];
|
|
items.forEach(item => {
|
|
const it = typeof item === 'string' ? item : item.name;
|
|
const color = typeof item === 'string' ? 'gold' : item.color;
|
|
const k = (it.startsWith('item_')) ? it : 'item_' + it;
|
|
|
|
if (scene.textures.exists(k)) scene.textures.remove(k); // FORCE REFRESH
|
|
|
|
const c = scene.textures.createCanvas(k, 32, 32);
|
|
const x = c.getContext();
|
|
x.clearRect(0, 0, 32, 32);
|
|
|
|
// SPECIAL ICONS
|
|
if (it === 'corn') {
|
|
// Corn Cob
|
|
x.fillStyle = '#228B22'; // Husk
|
|
x.beginPath(); x.ellipse(16, 16, 6, 12, 0, 0, Math.PI * 2); x.fill();
|
|
x.fillStyle = '#FFD700'; // Kernels
|
|
x.beginPath(); x.ellipse(16, 16, 3, 8, 0, 0, Math.PI * 2); x.fill();
|
|
}
|
|
else if (it === 'wheat') {
|
|
// Wheat Sheaf
|
|
x.strokeStyle = '#FFD700';
|
|
x.lineWidth = 2;
|
|
x.beginPath();
|
|
x.moveTo(12, 28); x.lineTo(20, 4); // Stalk 1
|
|
x.moveTo(16, 28); x.lineTo(16, 4); // Stalk 2
|
|
x.moveTo(20, 28); x.lineTo(12, 4); // Stalk 3
|
|
x.stroke();
|
|
// Tie
|
|
x.fillStyle = '#DAA520';
|
|
x.fillRect(13, 22, 6, 3);
|
|
}
|
|
else if (it === 'cannabis') {
|
|
// Cannabis Leaf (Stylized)
|
|
x.fillStyle = '#228B22';
|
|
// Center leaf
|
|
x.beginPath(); x.ellipse(16, 12, 3, 10, 0, 0, Math.PI * 2); x.fill();
|
|
// Side leaves
|
|
x.beginPath(); x.ellipse(10, 16, 3, 8, -Math.PI / 4, 0, Math.PI * 2); x.fill();
|
|
x.beginPath(); x.ellipse(22, 16, 3, 8, Math.PI / 4, 0, Math.PI * 2); x.fill();
|
|
x.beginPath(); x.ellipse(10, 24, 3, 6, -Math.PI / 2.5, 0, Math.PI * 2); x.fill();
|
|
x.beginPath(); x.ellipse(22, 24, 3, 6, Math.PI / 2.5, 0, Math.PI * 2); x.fill();
|
|
}
|
|
else if (it.includes('seeds')) {
|
|
// Seed Bag
|
|
x.fillStyle = '#DEB887'; // Burlywood bag
|
|
x.beginPath();
|
|
x.moveTo(10, 8); x.lineTo(22, 8); // Top
|
|
x.lineTo(26, 26); x.lineTo(6, 26); // Bottom
|
|
x.fill();
|
|
// Label/Dots
|
|
x.fillStyle = color;
|
|
x.beginPath(); x.arc(16, 18, 4, 0, Math.PI * 2); x.fill();
|
|
}
|
|
else if (it === 'cannabis_buds') {
|
|
// Stylized "bud" (forest green clusters)
|
|
x.fillStyle = '#228B22';
|
|
for (let i = 0; i < 5; i++) {
|
|
const ang = (i / 5) * Math.PI * 2;
|
|
x.beginPath();
|
|
x.arc(16 + Math.cos(ang) * 6, 16 + Math.sin(ang) * 6, 5, 0, Math.PI * 2);
|
|
x.fill();
|
|
}
|
|
x.fillStyle = '#8FBC8F'; // Frosted look
|
|
x.beginPath(); x.arc(16, 16, 4, 0, Math.PI * 2); x.fill();
|
|
}
|
|
else if (it === 'hemp_fiber') {
|
|
// Bundle of fibers (tan/straw color)
|
|
x.strokeStyle = '#DEB887';
|
|
x.lineWidth = 2;
|
|
for (let i = 0; i < 5; i++) {
|
|
x.beginPath();
|
|
x.moveTo(10 + i * 3, 24);
|
|
x.quadraticCurveTo(16, 16, 10 + (4 - i) * 3, 8);
|
|
x.stroke();
|
|
}
|
|
// Tie
|
|
x.fillStyle = '#8B4513';
|
|
x.fillRect(10, 15, 12, 2);
|
|
}
|
|
else if (it === 'hemp_clothing') {
|
|
// Simple T-shirt shape
|
|
x.fillStyle = '#2E8B57'; // SeaGreen
|
|
x.fillRect(8, 10, 16, 18); // Body
|
|
x.fillRect(4, 10, 6, 8); // Left sleeve
|
|
x.fillRect(22, 10, 6, 8); // Right sleeve
|
|
// Collar
|
|
x.fillStyle = '#000';
|
|
x.beginPath(); x.arc(16, 10, 4, 0, Math.PI); x.fill();
|
|
}
|
|
else {
|
|
// Default Circle
|
|
x.fillStyle = color;
|
|
x.beginPath(); x.arc(16, 16, 10, 0, Math.PI * 2); x.fill();
|
|
}
|
|
c.refresh();
|
|
});
|
|
}
|
|
|
|
static createSaplingSprite(scene, key = 'tree_sapling') {
|
|
if (scene.textures.exists(key)) return;
|
|
const size = 32;
|
|
const canvas = scene.textures.createCanvas(key, size, size);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, size, size);
|
|
|
|
// Majhno steblo
|
|
ctx.fillStyle = '#8B4513';
|
|
ctx.fillRect(14, 20, 4, 12);
|
|
|
|
// Parnosti listov
|
|
ctx.fillStyle = '#32CD32';
|
|
ctx.beginPath(); ctx.arc(12, 18, 4, 0, Math.PI * 2); ctx.fill();
|
|
ctx.beginPath(); ctx.arc(20, 18, 4, 0, Math.PI * 2); ctx.fill();
|
|
ctx.beginPath(); ctx.arc(16, 12, 5, 0, Math.PI * 2); ctx.fill();
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createCornSprites(scene) {
|
|
for (let i = 1; i <= 4; i++) {
|
|
const key = `corn_stage_${i}`;
|
|
if (scene.textures.exists(key)) continue;
|
|
|
|
// Corn is taller
|
|
const canvas = scene.textures.createCanvas(key, 32, 64);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 64);
|
|
|
|
const startY = 64;
|
|
|
|
// Stalk
|
|
ctx.fillStyle = '#556B2F'; // DarkOliveGreen
|
|
const height = 10 + (i * 10);
|
|
ctx.fillRect(14, startY - height, 4, height);
|
|
|
|
// Leaves
|
|
ctx.fillStyle = '#32CD32'; // LimeGreen
|
|
if (i >= 2) {
|
|
ctx.beginPath(); ctx.ellipse(10, startY - height + 10, 8, 3, 0.5, 0, Math.PI * 2); ctx.fill();
|
|
ctx.beginPath(); ctx.ellipse(22, startY - height + 15, 8, 3, -0.5, 0, Math.PI * 2); ctx.fill();
|
|
}
|
|
|
|
// Cobs (Only stage 4)
|
|
if (i === 4) {
|
|
ctx.fillStyle = '#FFD700'; // Gold
|
|
ctx.fillRect(12, startY - height + 20, 4, 8);
|
|
ctx.fillRect(18, startY - height + 25, 4, 8);
|
|
}
|
|
|
|
canvas.refresh();
|
|
}
|
|
}
|
|
|
|
// Helper to generate ALL textures at once
|
|
generateAll() {
|
|
TextureGenerator.createPlayerSprite(this.scene);
|
|
TextureGenerator.createPlayerWalkSprite(this.scene);
|
|
TextureGenerator.createNPCSprite(this.scene, 'npc', 'zombie');
|
|
TextureGenerator.createMerchantSprite(this.scene, 'merchant_texture');
|
|
|
|
TextureGenerator.createFlowerSprite(this.scene);
|
|
TextureGenerator.createBushSprite(this.scene);
|
|
TextureGenerator.createSaplingSprite(this.scene, 'tree_sapling');
|
|
TextureGenerator.createTreeSprite(this.scene);
|
|
TextureGenerator.createRockSprite(this.scene);
|
|
TextureGenerator.createCloudSprite(this.scene);
|
|
|
|
// Crops
|
|
TextureGenerator.createCropSprite(this.scene, 'crop_stage_1', 1);
|
|
TextureGenerator.createCropSprite(this.scene, 'crop_stage_2', 2);
|
|
TextureGenerator.createCropSprite(this.scene, 'crop_stage_3', 3);
|
|
TextureGenerator.createCropSprite(this.scene, 'crop_stage_4', 4);
|
|
TextureGenerator.createCornSprites(this.scene); // Added Corn
|
|
|
|
TextureGenerator.createGravestoneSprite(this.scene);
|
|
TextureGenerator.createFenceSprite(this.scene); // Procedural fence with transparency
|
|
TextureGenerator.createToolSprites(this.scene);
|
|
TextureGenerator.createItemSprites(this.scene);
|
|
TextureGenerator.createScooterSprite(this.scene, 'scooter', false);
|
|
TextureGenerator.createBoatSprite(this.scene, 'boat');
|
|
TextureGenerator.createScooterSprite(this.scene, 'scooter_broken', true);
|
|
TextureGenerator.createAnimatedWaterSprite(this.scene);
|
|
TextureGenerator.createPathStoneSprite(this.scene, 'path_stone');
|
|
TextureGenerator.createSmallRockSprite(this.scene, 'small_rock_1');
|
|
TextureGenerator.createSmallRockSprite(this.scene, 'small_rock_2');
|
|
TextureGenerator.createFlowerVariant(this.scene, 'flower_red', '#FF4444');
|
|
TextureGenerator.createFlowerVariant(this.scene, 'flower_yellow', '#FFFF44');
|
|
TextureGenerator.createFlowerVariant(this.scene, 'flower_blue', '#4444FF');
|
|
TextureGenerator.createMushroomSprite(this.scene, 'mushroom_red', '#FF4444', '#FFFFFF');
|
|
TextureGenerator.createMushroomSprite(this.scene, 'mushroom_brown', '#8B4513', '#FFE4C4');
|
|
TextureGenerator.createFallenLogSprite(this.scene, 'fallen_log');
|
|
TextureGenerator.createPuddleSprite(this.scene, 'puddle');
|
|
TextureGenerator.createFurnaceSprite(this.scene, 'furnace');
|
|
TextureGenerator.createMintSprite(this.scene, 'mint');
|
|
TextureGenerator.createOwlSprite(this.scene, 'owl');
|
|
TextureGenerator.createBatSprite(this.scene, 'bat');
|
|
|
|
// Structures (Fix checkered pattern bug)
|
|
TextureGenerator.createChestSprite(this.scene, 'chest');
|
|
TextureGenerator.createSpawnerSprite(this.scene, 'spawner');
|
|
TextureGenerator.createStructureSprite(this.scene, 'ruin', 'ruin');
|
|
TextureGenerator.createStructureSprite(this.scene, 'arena', 'ruin');
|
|
TextureGenerator.createSignpostSprite(this.scene, 'signpost_city', '→');
|
|
TextureGenerator.createSignpostSprite(this.scene, 'signpost_farm', '←');
|
|
TextureGenerator.createSignpostSprite(this.scene, 'signpost_both', '⇅');
|
|
|
|
// Mutants
|
|
TextureGenerator.createTrollSprite(this.scene, 'troll');
|
|
TextureGenerator.createElfSprite(this.scene, 'elf');
|
|
|
|
// Animals
|
|
TextureGenerator.createCowSprite(this.scene, 'cow', false);
|
|
TextureGenerator.createCowSprite(this.scene, 'cow_mutant', true);
|
|
TextureGenerator.createChickenSprite(this.scene, 'chicken', false);
|
|
TextureGenerator.createChickenSprite(this.scene, 'chicken_mutant', true);
|
|
|
|
// Villager
|
|
TextureGenerator.createVillagerSprite(this.scene, 'villager');
|
|
}
|
|
|
|
static createOwlSprite(scene, key = 'owl') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Body
|
|
ctx.fillStyle = '#8B4513'; // SaddleBrown
|
|
ctx.fillRect(8, 8, 16, 20);
|
|
|
|
// Eyes
|
|
ctx.fillStyle = '#FFD700'; // Gold eyes
|
|
ctx.beginPath(); ctx.arc(12, 12, 3, 0, Math.PI * 2); ctx.fill();
|
|
ctx.beginPath(); ctx.arc(20, 12, 3, 0, Math.PI * 2); ctx.fill();
|
|
// Pupils
|
|
ctx.fillStyle = '#000';
|
|
ctx.beginPath(); ctx.arc(12, 12, 1, 0, Math.PI * 2); ctx.fill();
|
|
ctx.beginPath(); ctx.arc(20, 12, 1, 0, Math.PI * 2); ctx.fill();
|
|
|
|
// Beak
|
|
ctx.fillStyle = '#FFA500'; // Orange
|
|
ctx.beginPath();
|
|
ctx.moveTo(16, 14); ctx.lineTo(14, 18); ctx.lineTo(18, 18);
|
|
ctx.fill();
|
|
|
|
// Wings (folded)
|
|
ctx.fillStyle = '#A0522D';
|
|
ctx.fillRect(6, 12, 4, 12);
|
|
ctx.fillRect(22, 12, 4, 12);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createBatSprite(scene, key = 'bat') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Body
|
|
ctx.fillStyle = '#222';
|
|
ctx.beginPath(); ctx.ellipse(16, 16, 4, 6, 0, 0, Math.PI * 2); ctx.fill();
|
|
|
|
// Wings
|
|
ctx.fillStyle = '#333';
|
|
// Left Wing
|
|
ctx.beginPath();
|
|
ctx.moveTo(12, 16); ctx.lineTo(2, 6); ctx.lineTo(4, 20);
|
|
ctx.closePath(); ctx.fill();
|
|
// Right Wing
|
|
ctx.beginPath();
|
|
ctx.moveTo(20, 16); ctx.lineTo(30, 6); ctx.lineTo(28, 20);
|
|
ctx.closePath(); ctx.fill();
|
|
|
|
// Eyes
|
|
ctx.fillStyle = '#f00';
|
|
ctx.fillRect(15, 14, 1, 1);
|
|
ctx.fillRect(17, 14, 1, 1);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createTrollSprite(scene, key) {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 48, 48); // Bigger
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 48, 48);
|
|
|
|
// Body (Big, Green)
|
|
ctx.fillStyle = '#228B22'; // ForestGreen
|
|
ctx.fillRect(10, 10, 28, 30);
|
|
|
|
// Head
|
|
ctx.fillStyle = '#105510';
|
|
ctx.fillRect(14, 4, 20, 16);
|
|
|
|
// Eyes (Red)
|
|
ctx.fillStyle = '#ff0000';
|
|
ctx.fillRect(18, 10, 4, 4);
|
|
ctx.fillRect(26, 10, 4, 4);
|
|
|
|
// Club
|
|
ctx.fillStyle = '#5c4033';
|
|
ctx.fillRect(36, 12, 8, 24);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createElfSprite(scene, key) {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 48);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 48);
|
|
|
|
// Body (Slim, Pale)
|
|
ctx.fillStyle = '#98FB98'; // PaleGreen (Mutated Elf)
|
|
ctx.fillRect(10, 16, 12, 24);
|
|
|
|
// Head
|
|
ctx.fillStyle = '#E0FFFF'; // LightCyan
|
|
ctx.fillRect(10, 4, 12, 12);
|
|
|
|
// Ears (Pointy)
|
|
ctx.fillStyle = '#E0FFFF';
|
|
ctx.beginPath(); ctx.moveTo(8, 8); ctx.lineTo(4, 4); ctx.lineTo(10, 12); ctx.fill();
|
|
ctx.beginPath(); ctx.moveTo(24, 8); ctx.lineTo(28, 4); ctx.lineTo(22, 12); ctx.fill();
|
|
|
|
// Eyes (Glowing)
|
|
ctx.fillStyle = '#00FFFF';
|
|
ctx.fillRect(12, 8, 2, 2);
|
|
ctx.fillRect(18, 8, 2, 2);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createCowSprite(scene, key, activeMutation) {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 48, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 48, 32);
|
|
|
|
// Body
|
|
ctx.fillStyle = activeMutation ? '#9ACD32' : '#FFFFFF'; // YellowGreen or White
|
|
ctx.fillRect(8, 10, 32, 16);
|
|
|
|
// Spots (if normal)
|
|
if (!activeMutation) {
|
|
ctx.fillStyle = '#000000';
|
|
ctx.fillRect(14, 12, 6, 6);
|
|
ctx.fillRect(28, 18, 8, 4);
|
|
} else {
|
|
// Glowing veins
|
|
ctx.fillStyle = '#00FF00';
|
|
ctx.fillRect(12, 14, 24, 2);
|
|
}
|
|
|
|
// Head
|
|
ctx.fillStyle = activeMutation ? '#9ACD32' : '#FFFFFF';
|
|
ctx.fillRect(0, 8, 12, 12);
|
|
|
|
// Legs
|
|
ctx.fillStyle = '#000000';
|
|
ctx.fillRect(10, 26, 4, 6);
|
|
ctx.fillRect(34, 26, 4, 6);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createBoatSprite(scene, key = 'boat') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 48, 24);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 48, 24);
|
|
|
|
// Hull (Wood)
|
|
ctx.fillStyle = '#8B4513';
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, 0);
|
|
ctx.lineTo(48, 0);
|
|
ctx.lineTo(40, 24);
|
|
ctx.lineTo(8, 24);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
// Rim
|
|
ctx.strokeStyle = '#603010';
|
|
ctx.lineWidth = 2;
|
|
ctx.strokeRect(2, 2, 44, 20);
|
|
|
|
// Seat
|
|
ctx.fillStyle = '#603010';
|
|
ctx.fillRect(16, 8, 16, 4);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createChickenSprite(scene, key, activeMutation) {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 24, 24);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 24, 24);
|
|
|
|
// Body
|
|
ctx.fillStyle = activeMutation ? '#ADFF2F' : '#FFFFFF'; // GreenYellow or White
|
|
ctx.beginPath(); ctx.arc(12, 14, 8, 0, Math.PI * 2); ctx.fill();
|
|
|
|
// Head
|
|
ctx.beginPath(); ctx.arc(16, 8, 5, 0, Math.PI * 2); ctx.fill();
|
|
|
|
// Beak
|
|
ctx.fillStyle = '#ffaa00';
|
|
ctx.beginPath(); ctx.moveTo(20, 8); ctx.lineTo(24, 10); ctx.lineTo(20, 12); ctx.fill();
|
|
|
|
// Comb
|
|
ctx.fillStyle = '#ff0000';
|
|
ctx.fillRect(15, 3, 2, 3);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createPathStoneSprite(scene, key = 'path_stone') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Flat stone for paths
|
|
ctx.fillStyle = '#888888';
|
|
ctx.beginPath();
|
|
ctx.ellipse(16, 20, 12, 8, 0, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
// Cracks/Details
|
|
ctx.strokeStyle = '#666666';
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath();
|
|
ctx.moveTo(10, 18);
|
|
ctx.lineTo(22, 18);
|
|
ctx.stroke();
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createSmallRockSprite(scene, key) {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 24, 24);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 24, 24);
|
|
|
|
// Small rock pile
|
|
ctx.fillStyle = '#707070';
|
|
ctx.beginPath();
|
|
ctx.arc(12, 16, 6, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
// Highlight
|
|
ctx.fillStyle = '#909090';
|
|
ctx.beginPath();
|
|
ctx.arc(10, 14, 3, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createFlowerVariant(scene, key, color) {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Stem
|
|
ctx.fillStyle = '#228B22';
|
|
ctx.fillRect(15, 16, 2, 10);
|
|
|
|
// Petals
|
|
ctx.fillStyle = color;
|
|
for (let i = 0; i < 5; i++) {
|
|
const angle = (i / 5) * Math.PI * 2;
|
|
const px = 16 + Math.cos(angle) * 4;
|
|
const py = 16 + Math.sin(angle) * 4;
|
|
ctx.beginPath();
|
|
ctx.arc(px, py, 3, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
|
|
// Center
|
|
ctx.fillStyle = '#FFD700';
|
|
ctx.beginPath();
|
|
ctx.arc(16, 16, 2, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createMushroomSprite(scene, key, capColor, stemColor) {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 32);
|
|
|
|
// Stem
|
|
ctx.fillStyle = stemColor;
|
|
ctx.fillRect(13, 16, 6, 10);
|
|
|
|
// Cap (mushroom head)
|
|
ctx.fillStyle = capColor;
|
|
ctx.beginPath();
|
|
ctx.ellipse(16, 16, 10, 6, 0, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
// Spots on cap (white dots)
|
|
ctx.fillStyle = '#FFFFFF';
|
|
for (let i = 0; i < 3; i++) {
|
|
const angle = (i / 3) * Math.PI * 2;
|
|
const px = 16 + Math.cos(angle) * 5;
|
|
const py = 16 + Math.sin(angle) * 3;
|
|
ctx.beginPath();
|
|
ctx.arc(px, py, 2, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
}
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createFallenLogSprite(scene, key = 'fallen_log') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 48, 32);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 48, 32);
|
|
|
|
// Log body (horizontal)
|
|
ctx.fillStyle = '#8B4513';
|
|
ctx.fillRect(4, 14, 40, 10);
|
|
|
|
// Bark texture
|
|
ctx.fillStyle = '#654321';
|
|
for (let i = 0; i < 5; i++) {
|
|
ctx.fillRect(6 + i * 8, 14, 2, 10);
|
|
}
|
|
|
|
// Log rings (ends)
|
|
ctx.fillStyle = '#D2691E';
|
|
ctx.beginPath();
|
|
ctx.arc(6, 19, 5, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
ctx.strokeStyle = '#654321';
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath();
|
|
ctx.arc(6, 19, 3, 0, Math.PI * 2);
|
|
ctx.stroke();
|
|
ctx.beginPath();
|
|
ctx.arc(6, 19, 1.5, 0, Math.PI * 2);
|
|
ctx.stroke();
|
|
|
|
// Small mushrooms growing on log
|
|
ctx.fillStyle = '#FF6347';
|
|
ctx.beginPath();
|
|
ctx.arc(20, 12, 3, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
ctx.fillStyle = '#FFE4C4';
|
|
ctx.fillRect(19, 13, 2, 3);
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createPuddleSprite(scene, key = 'puddle') {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 32, 24);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 32, 24);
|
|
|
|
// Puddle shape (irregular oval)
|
|
ctx.fillStyle = 'rgba(70, 130, 180, 0.6)';
|
|
ctx.beginPath();
|
|
ctx.ellipse(16, 16, 12, 8, 0, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
// Reflection highlights
|
|
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
|
|
ctx.beginPath();
|
|
ctx.ellipse(12, 14, 4, 2, 0, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
// Darker edge
|
|
ctx.strokeStyle = 'rgba(30, 60, 90, 0.5)';
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath();
|
|
ctx.ellipse(16, 16, 12, 8, 0, 0, Math.PI * 2);
|
|
ctx.stroke();
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createScooterSprite(scene, key = 'scooter', isBroken = false) {
|
|
if (scene.textures.exists(key)) return;
|
|
const canvas = scene.textures.createCanvas(key, 48, 48);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, 48, 48);
|
|
|
|
const color = isBroken ? '#696969' : '#FF4500'; // Rusty Gray vs OrangeRed
|
|
const wheelColor = '#1a1a1a';
|
|
|
|
// Wheels
|
|
ctx.fillStyle = wheelColor;
|
|
ctx.beginPath(); ctx.arc(12, 38, 5, 0, Math.PI * 2); ctx.fill(); // Back
|
|
ctx.beginPath(); ctx.arc(38, 38, 5, 0, Math.PI * 2); ctx.fill(); // Front
|
|
// Spokes
|
|
ctx.fillStyle = '#555';
|
|
ctx.beginPath(); ctx.arc(12, 38, 2, 0, Math.PI * 2); ctx.fill();
|
|
ctx.beginPath(); ctx.arc(38, 38, 2, 0, Math.PI * 2); ctx.fill();
|
|
|
|
// Base
|
|
ctx.fillStyle = color;
|
|
ctx.fillRect(12, 32, 26, 5); // Deck
|
|
|
|
// Angled Stem
|
|
ctx.beginPath();
|
|
ctx.moveTo(38, 34);
|
|
ctx.lineTo(34, 14);
|
|
ctx.lineTo(38, 14);
|
|
ctx.lineTo(42, 34);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
// Handlebars
|
|
ctx.strokeStyle = isBroken ? '#444' : '#C0C0C0';
|
|
ctx.lineWidth = 3;
|
|
ctx.beginPath();
|
|
ctx.moveTo(30, 14);
|
|
ctx.lineTo(42, 14);
|
|
ctx.stroke();
|
|
|
|
// Vertical post support
|
|
ctx.fillStyle = color;
|
|
ctx.fillRect(10, 30, 4, 4); // Rear wheel guard
|
|
|
|
if (isBroken) {
|
|
// Rust spots
|
|
ctx.fillStyle = '#8B4513';
|
|
ctx.fillRect(15, 33, 4, 2);
|
|
ctx.fillRect(35, 20, 2, 3);
|
|
}
|
|
|
|
canvas.refresh();
|
|
}
|
|
|
|
static createAnimatedWaterSprite(scene) {
|
|
// Create 4 separate textures for water animation frames
|
|
const frames = 4;
|
|
const frameWidth = 48;
|
|
const frameHeight = 60;
|
|
|
|
for (let f = 0; f < frames; f++) {
|
|
const key = `water_frame_${f}`;
|
|
if (scene.textures.exists(key)) continue;
|
|
|
|
const canvas = scene.textures.createCanvas(key, frameWidth, frameHeight);
|
|
const ctx = canvas.getContext();
|
|
ctx.clearRect(0, 0, frameWidth, frameHeight);
|
|
|
|
const P = 2;
|
|
const baseColor = 0x4444ff;
|
|
const shimmerOffset = Math.sin((f / frames) * Math.PI * 2) * 20;
|
|
const waterColor = Phaser.Display.Color.IntegerToColor(baseColor).lighten(shimmerOffset).color;
|
|
|
|
const xs = P;
|
|
const xe = 48 + P;
|
|
const midX = 24 + P;
|
|
const topY = P;
|
|
const midY = 12 + P;
|
|
const bottomY = 24 + P;
|
|
const depth = 20;
|
|
|
|
// Left Face
|
|
ctx.fillStyle = Phaser.Display.Color.IntegerToColor(waterColor).darken(30).rgba;
|
|
ctx.beginPath();
|
|
ctx.moveTo(midX, bottomY);
|
|
ctx.lineTo(midX, bottomY + depth);
|
|
ctx.lineTo(xs, midY + depth);
|
|
ctx.lineTo(xs, midY);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
// Right Face
|
|
ctx.fillStyle = Phaser.Display.Color.IntegerToColor(waterColor).darken(20).rgba;
|
|
ctx.beginPath();
|
|
ctx.moveTo(xe, midY);
|
|
ctx.lineTo(xe, midY + depth);
|
|
ctx.lineTo(midX, bottomY + depth);
|
|
ctx.lineTo(midX, bottomY);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
// Top Face
|
|
ctx.fillStyle = Phaser.Display.Color.IntegerToColor(waterColor).rgba;
|
|
ctx.beginPath();
|
|
ctx.moveTo(xs, midY);
|
|
ctx.lineTo(midX, topY);
|
|
ctx.lineTo(xe, midY);
|
|
ctx.lineTo(midX, bottomY);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
// Shimmer highlights
|
|
ctx.fillStyle = `rgba(255, 255, 255, ${0.1 + Math.abs(shimmerOffset) / 100})`;
|
|
for (let i = 0; i < 5; i++) {
|
|
const sx = xs + 8 + Math.random() * 28;
|
|
const sy = topY + 4 + Math.random() * 16;
|
|
ctx.fillRect(sx, sy, 3, 2);
|
|
}
|
|
|
|
// Wave lines
|
|
ctx.strokeStyle = `rgba(100, 100, 255, ${0.3 + (f / frames) * 0.2})`;
|
|
ctx.lineWidth = 1;
|
|
ctx.beginPath();
|
|
ctx.moveTo(xs + 10, midY + 8);
|
|
ctx.lineTo(xe - 10, midY - 2);
|
|
ctx.stroke();
|
|
|
|
canvas.refresh();
|
|
}
|
|
}
|
|
|
|
static createFurnaceSprite(scene, key = 'furnace') {
|
|
if (scene.textures.exists(key)) return;
|
|
const graphics = scene.make.graphics({ x: 0, y: 0, add: false });
|
|
// Furnace block (gray)
|
|
graphics.fillStyle(0x555555, 1);
|
|
graphics.fillRect(8, 14, 16, 18);
|
|
graphics.fillStyle(0x333333, 1);
|
|
graphics.fillRect(12, 22, 8, 6); // Hole
|
|
graphics.fillStyle(0xffaa00, 1);
|
|
graphics.fillRect(14, 24, 4, 3); // Embers
|
|
graphics.generateTexture(key, 32, 32);
|
|
}
|
|
|
|
static createMintSprite(scene, key = 'mint') {
|
|
if (scene.textures.exists(key)) return;
|
|
const graphics = scene.make.graphics({ x: 0, y: 0, add: false });
|
|
graphics.fillStyle(0x777799, 1); // Steel blue
|
|
graphics.fillRect(6, 12, 20, 20);
|
|
graphics.fillStyle(0xffd700, 1); // Gold coin icon
|
|
graphics.fillCircle(16, 18, 5);
|
|
graphics.generateTexture(key, 32, 32);
|
|
}
|
|
|
|
static createVillagerSprite(scene, key = 'villager') {
|
|
if (scene.textures.exists(key)) return;
|
|
const graphics = scene.make.graphics({ x: 0, y: 0, add: false });
|
|
|
|
// Farmer/Villager - simple human sprite
|
|
// Head (skin tone)
|
|
graphics.fillStyle(0xffdbac, 1);
|
|
graphics.fillCircle(16, 8, 4);
|
|
|
|
// Body (blue shirt)
|
|
graphics.fillStyle(0x4477bb, 1);
|
|
graphics.fillRect(12, 12, 8, 10);
|
|
|
|
// Arms
|
|
graphics.fillStyle(0xffdbac, 1);
|
|
graphics.fillRect(10, 14, 2, 6); // Left arm
|
|
graphics.fillRect(20, 14, 2, 6); // Right arm
|
|
|
|
// Pants (brown)
|
|
graphics.fillStyle(0x654321, 1);
|
|
graphics.fillRect(12, 22, 8, 8);
|
|
|
|
// Eyes
|
|
graphics.fillStyle(0x000000, 1);
|
|
graphics.fillRect(14, 7, 1, 1); // Left eye
|
|
graphics.fillRect(17, 7, 1, 1); // Right eye
|
|
|
|
graphics.generateTexture(key, 32, 32);
|
|
graphics.destroy();
|
|
}
|
|
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
}
|
|
}
|