/** * CostumeContexts - Themed costume overlays for the avatar system * Depends on: AvatarRenderer, AvatarAnimations */ (function() { 'use strict'; // ============================================================================ // UTILITY FUNCTIONS // ============================================================================ function roundRect(ctx, x, y, w, h, r) { if (w < 2 * r) r = w / 2; if (h < 2 * r) r = h / 2; ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r); ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath(); } function darken(color, amount) { var hex = color.replace('#', ''); var r = Math.max(0, Math.floor(parseInt(hex.substr(0, 2), 16) * (1 - amount))); var g = Math.max(0, Math.floor(parseInt(hex.substr(2, 2), 16) * (1 - amount))); var b = Math.max(0, Math.floor(parseInt(hex.substr(4, 2), 16) * (1 - amount))); return '#' + r.toString(16).padStart(2, '0') + g.toString(16).padStart(2, '0') + b.toString(16).padStart(2, '0'); } function lighten(color, amount) { var hex = color.replace('#', ''); var r = Math.min(255, Math.floor(parseInt(hex.substr(0, 2), 16) + (255 - parseInt(hex.substr(0, 2), 16)) * amount)); var g = Math.min(255, Math.floor(parseInt(hex.substr(2, 2), 16) + (255 - parseInt(hex.substr(2, 2), 16)) * amount)); var b = Math.min(255, Math.floor(parseInt(hex.substr(4, 2), 16) + (255 - parseInt(hex.substr(4, 2), 16)) * amount)); return '#' + r.toString(16).padStart(2, '0') + g.toString(16).padStart(2, '0') + b.toString(16).padStart(2, '0'); } function getArmTransform(transforms, arm) { if (!transforms || !transforms[arm]) return { rotation: 0, x: 0, y: 0 }; return transforms[arm]; } // ============================================================================ // COSTUME: KNIGHT ARMOR // ============================================================================ var knightArmor = { id: 'knight-armor', name: 'Knight Armor', category: 'costume', avatarMode: 'FULL_BODY', animations: ['idle', 'walk', 'attack', 'hurt', 'celebrate'], defaultAnimation: 'idle', accessoryVisibility: { eyewear: false, headwear: true, effects: true }, layerOrder: ['avatar-body', 'costume-base', 'avatar-head', 'costume-overlay', 'earned-accessories'], recommendedFor: ['rpg', 'adventure', 'action'], description: 'Medieval knight in shining armor', avatarPosition: { x: 0.5, y: 0.5 }, avatarScale: 1.0, props: [ { id: 'sword', anchor: 'rightArm', offset: { x: 5, y: 20 } }, { id: 'shield', anchor: 'leftArm', offset: { x: -10, y: 0 } } ], contextEffects: { attack: { type: 'slash', color: '#c0c0c0' }, hurt: { type: 'sparks', color: '#ffa500' } }, renderCostumeUnder: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var metalColor = '#a8a9ad'; var metalDark = '#7d7f83'; var metalLight = '#d4d5d9'; // Cape (behind everything) ctx.fillStyle = '#8b0000'; ctx.beginPath(); ctx.moveTo(cx - 18 * scale, y + 68 * scale); ctx.quadraticCurveTo(cx - 25 * scale, y + 100 * scale, cx - 20 * scale + Math.sin(time * 2) * 3 * scale, y + 145 * scale); ctx.lineTo(cx + 20 * scale + Math.sin(time * 2 + 1) * 3 * scale, y + 145 * scale); ctx.quadraticCurveTo(cx + 25 * scale, y + 100 * scale, cx + 18 * scale, y + 68 * scale); ctx.closePath(); ctx.fill(); // Cape trim ctx.fillStyle = '#ffd700'; ctx.fillRect(cx - 20 * scale, y + 140 * scale, 40 * scale, 5 * scale); // Body armor (chestplate) ctx.fillStyle = metalColor; roundRect(ctx, cx - 22 * scale, y + 68 * scale, 44 * scale, 50 * scale, 4 * scale); ctx.fill(); // Chestplate details ctx.strokeStyle = metalDark; ctx.lineWidth = 2 * scale; ctx.beginPath(); ctx.moveTo(cx, y + 70 * scale); ctx.lineTo(cx, y + 115 * scale); ctx.stroke(); // Armor ridges ctx.strokeStyle = metalLight; ctx.lineWidth = 1 * scale; for (var i = 0; i < 3; i++) { ctx.beginPath(); ctx.moveTo(cx - 18 * scale, y + 78 * scale + i * 12 * scale); ctx.lineTo(cx + 18 * scale, y + 78 * scale + i * 12 * scale); ctx.stroke(); } // Leg armor (greaves) ctx.fillStyle = metalColor; roundRect(ctx, cx - 16 * scale, y + 120 * scale, 12 * scale, 35 * scale, 2 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 120 * scale, 12 * scale, 35 * scale, 2 * scale); ctx.fill(); // Knee guards ctx.fillStyle = metalLight; ctx.beginPath(); ctx.arc(cx - 10 * scale, y + 125 * scale, 5 * scale, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(cx + 10 * scale, y + 125 * scale, 5 * scale, 0, Math.PI * 2); ctx.fill(); // Armored boots ctx.fillStyle = metalDark; roundRect(ctx, cx - 18 * scale, y + 152 * scale, 14 * scale, 8 * scale, 2 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 152 * scale, 14 * scale, 8 * scale, 2 * scale); ctx.fill(); }, renderCostumeOver: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var metalColor = '#a8a9ad'; var metalDark = '#7d7f83'; var metalLight = '#d4d5d9'; // Shoulder pauldrons ctx.fillStyle = metalColor; ctx.beginPath(); ctx.ellipse(cx - 26 * scale, y + 70 * scale, 12 * scale, 8 * scale, -0.3, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(cx + 26 * scale, y + 70 * scale, 12 * scale, 8 * scale, 0.3, 0, Math.PI * 2); ctx.fill(); // Pauldron spikes ctx.fillStyle = metalLight; ctx.beginPath(); ctx.moveTo(cx - 32 * scale, y + 65 * scale); ctx.lineTo(cx - 35 * scale, y + 58 * scale); ctx.lineTo(cx - 28 * scale, y + 65 * scale); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx + 32 * scale, y + 65 * scale); ctx.lineTo(cx + 35 * scale, y + 58 * scale); ctx.lineTo(cx + 28 * scale, y + 65 * scale); ctx.fill(); // Helmet ctx.fillStyle = metalColor; ctx.beginPath(); ctx.arc(cx, y + 32 * scale, 24 * scale, Math.PI, 0, false); ctx.fill(); // Helmet face plate ctx.fillStyle = metalDark; roundRect(ctx, cx - 18 * scale, y + 20 * scale, 36 * scale, 28 * scale, 4 * scale); ctx.fill(); // Visor slits (eyes peek through) ctx.fillStyle = '#1a1a1a'; ctx.fillRect(cx - 14 * scale, y + 28 * scale, 10 * scale, 3 * scale); ctx.fillRect(cx + 4 * scale, y + 28 * scale, 10 * scale, 3 * scale); // Helmet crest ctx.fillStyle = '#8b0000'; ctx.beginPath(); ctx.moveTo(cx - 3 * scale, y + 8 * scale); ctx.lineTo(cx, y - 5 * scale); ctx.lineTo(cx + 3 * scale, y + 8 * scale); ctx.closePath(); ctx.fill(); // Sword (right hand) var rightArm = getArmTransform(transforms, 'rightArm'); ctx.save(); ctx.translate(cx + 26 * scale, y + 115 * scale); ctx.rotate((rightArm.rotation || 0) * Math.PI / 180); // Sword blade ctx.fillStyle = metalLight; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(-2 * scale, -35 * scale); ctx.lineTo(0, -45 * scale); ctx.lineTo(2 * scale, -35 * scale); ctx.closePath(); ctx.fill(); // Sword guard ctx.fillStyle = '#ffd700'; ctx.fillRect(-6 * scale, -2 * scale, 12 * scale, 4 * scale); // Sword handle ctx.fillStyle = '#4a3728'; ctx.fillRect(-2 * scale, 2 * scale, 4 * scale, 10 * scale); ctx.restore(); // Shield (left hand) var leftArm = getArmTransform(transforms, 'leftArm'); ctx.save(); ctx.translate(cx - 30 * scale, y + 95 * scale); ctx.rotate((leftArm.rotation || 0) * Math.PI / 180); // Shield body ctx.fillStyle = metalColor; ctx.beginPath(); ctx.moveTo(0, -15 * scale); ctx.lineTo(12 * scale, -10 * scale); ctx.lineTo(12 * scale, 10 * scale); ctx.lineTo(0, 20 * scale); ctx.lineTo(-12 * scale, 10 * scale); ctx.lineTo(-12 * scale, -10 * scale); ctx.closePath(); ctx.fill(); // Shield emblem ctx.fillStyle = '#8b0000'; ctx.beginPath(); ctx.arc(0, 0, 6 * scale, 0, Math.PI * 2); ctx.fill(); // Shield cross ctx.fillStyle = '#ffd700'; ctx.fillRect(-1 * scale, -8 * scale, 2 * scale, 16 * scale); ctx.fillRect(-6 * scale, -1 * scale, 12 * scale, 2 * scale); ctx.restore(); } }; // ============================================================================ // COSTUME: SPACE SUIT // ============================================================================ var spaceSuit = { id: 'space-suit', name: 'Space Suit', category: 'costume', avatarMode: 'FULL_BODY', animations: ['idle', 'walk', 'attack', 'hurt', 'celebrate'], defaultAnimation: 'idle', accessoryVisibility: { eyewear: true, headwear: true, effects: true }, layerOrder: ['avatar-body', 'costume-base', 'avatar-head', 'costume-overlay', 'earned-accessories'], recommendedFor: ['action', 'adventure', 'physics'], description: 'Astronaut ready for space exploration', avatarPosition: { x: 0.5, y: 0.5 }, avatarScale: 1.0, props: [], contextEffects: { hurt: { type: 'sparks', color: '#00ffff' } }, renderCostumeUnder: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var suitColor = '#f0f0f0'; var accentColor = '#4a90d9'; // Oxygen pack (on back) ctx.fillStyle = '#e0e0e0'; roundRect(ctx, cx - 12 * scale, y + 65 * scale, 24 * scale, 35 * scale, 4 * scale); ctx.fill(); // Pack details ctx.fillStyle = accentColor; ctx.fillRect(cx - 8 * scale, y + 70 * scale, 16 * scale, 4 * scale); ctx.fillRect(cx - 8 * scale, y + 80 * scale, 16 * scale, 4 * scale); // Pack lights var blink = Math.sin(time * 3) > 0 ? 1 : 0.3; ctx.fillStyle = 'rgba(0, 255, 100, ' + blink + ')'; ctx.beginPath(); ctx.arc(cx - 4 * scale, y + 92 * scale, 2 * scale, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'rgba(255, 100, 0, ' + (1 - blink) + ')'; ctx.beginPath(); ctx.arc(cx + 4 * scale, y + 92 * scale, 2 * scale, 0, Math.PI * 2); ctx.fill(); // Suit body ctx.fillStyle = suitColor; roundRect(ctx, cx - 24 * scale, y + 64 * scale, 48 * scale, 55 * scale, 6 * scale); ctx.fill(); // Blue accents on torso ctx.fillStyle = accentColor; ctx.fillRect(cx - 24 * scale, y + 64 * scale, 8 * scale, 55 * scale); ctx.fillRect(cx + 16 * scale, y + 64 * scale, 8 * scale, 55 * scale); // Suit legs ctx.fillStyle = suitColor; roundRect(ctx, cx - 18 * scale, y + 118 * scale, 14 * scale, 38 * scale, 4 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 118 * scale, 14 * scale, 38 * scale, 4 * scale); ctx.fill(); // Blue leg stripes ctx.fillStyle = accentColor; ctx.fillRect(cx - 18 * scale, y + 130 * scale, 14 * scale, 4 * scale); ctx.fillRect(cx + 4 * scale, y + 130 * scale, 14 * scale, 4 * scale); // Boots with magnetic plates ctx.fillStyle = '#555'; roundRect(ctx, cx - 20 * scale, y + 152 * scale, 16 * scale, 8 * scale, 2 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 152 * scale, 16 * scale, 8 * scale, 2 * scale); ctx.fill(); // Magnetic plate glow ctx.fillStyle = accentColor; ctx.fillRect(cx - 18 * scale, y + 158 * scale, 12 * scale, 2 * scale); ctx.fillRect(cx + 6 * scale, y + 158 * scale, 12 * scale, 2 * scale); }, renderCostumeOver: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var suitColor = '#f0f0f0'; var accentColor = '#4a90d9'; // Gloves ctx.fillStyle = suitColor; ctx.beginPath(); ctx.arc(cx - 26 * scale, y + 115 * scale, 6 * scale, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(cx + 26 * scale, y + 115 * scale, 6 * scale, 0, Math.PI * 2); ctx.fill(); // Glove accents ctx.fillStyle = accentColor; ctx.beginPath(); ctx.arc(cx - 26 * scale, y + 115 * scale, 3 * scale, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(cx + 26 * scale, y + 115 * scale, 3 * scale, 0, Math.PI * 2); ctx.fill(); // Helmet bubble ctx.fillStyle = 'rgba(200, 230, 255, 0.3)'; ctx.strokeStyle = '#ccc'; ctx.lineWidth = 3 * scale; ctx.beginPath(); ctx.arc(cx, y + 32 * scale, 28 * scale, 0, Math.PI * 2); ctx.fill(); ctx.stroke(); // Helmet reflection ctx.fillStyle = 'rgba(255, 255, 255, 0.2)'; ctx.beginPath(); ctx.ellipse(cx - 8 * scale, y + 22 * scale, 8 * scale, 12 * scale, -0.3, 0, Math.PI * 2); ctx.fill(); // Helmet ring ctx.fillStyle = '#ddd'; ctx.beginPath(); ctx.ellipse(cx, y + 55 * scale, 22 * scale, 6 * scale, 0, 0, Math.PI * 2); ctx.fill(); // Helmet accent ctx.fillStyle = accentColor; ctx.beginPath(); ctx.arc(cx, y + 5 * scale, 4 * scale, 0, Math.PI * 2); ctx.fill(); // Antenna ctx.strokeStyle = '#888'; ctx.lineWidth = 2 * scale; ctx.beginPath(); ctx.moveTo(cx + 20 * scale, y + 15 * scale); ctx.lineTo(cx + 28 * scale, y - 5 * scale); ctx.stroke(); ctx.fillStyle = '#ff3333'; ctx.beginPath(); ctx.arc(cx + 28 * scale, y - 5 * scale, 3 * scale, 0, Math.PI * 2); ctx.fill(); } }; // ============================================================================ // COSTUME: NINJA OUTFIT // ============================================================================ var ninjaOutfit = { id: 'ninja-outfit', name: 'Ninja Outfit', category: 'costume', avatarMode: 'FULL_BODY', animations: ['idle', 'walk', 'attack', 'hurt', 'celebrate'], defaultAnimation: 'idle', accessoryVisibility: { eyewear: true, headwear: false, effects: true }, layerOrder: ['avatar-body', 'costume-base', 'avatar-head', 'costume-overlay', 'earned-accessories'], recommendedFor: ['action', 'adventure', 'rpg'], description: 'Stealthy ninja warrior', avatarPosition: { x: 0.5, y: 0.5 }, avatarScale: 1.0, props: [ { id: 'katana', anchor: 'back', offset: { x: 0, y: -10 } } ], contextEffects: { attack: { type: 'slash', color: '#333' }, hurt: { type: 'smoke', color: '#555' } }, renderCostumeUnder: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var ninjaBlack = '#1a1a1a'; var ninjaGray = '#333'; // Katana on back ctx.save(); ctx.translate(cx + 15 * scale, y + 50 * scale); ctx.rotate(-0.4); // Scabbard ctx.fillStyle = '#2c1810'; roundRect(ctx, -3 * scale, 0, 6 * scale, 50 * scale, 2 * scale); ctx.fill(); // Handle wrap ctx.fillStyle = '#8b0000'; for (var i = 0; i < 5; i++) { ctx.fillRect(-3 * scale, -10 * scale + i * 4 * scale, 6 * scale, 2 * scale); } // Guard ctx.fillStyle = '#ffd700'; ctx.beginPath(); ctx.ellipse(0, 0, 5 * scale, 2 * scale, 0, 0, Math.PI * 2); ctx.fill(); ctx.restore(); // Body suit ctx.fillStyle = ninjaBlack; roundRect(ctx, cx - 22 * scale, y + 64 * scale, 44 * scale, 52 * scale, 4 * scale); ctx.fill(); // Belt with throwing stars ctx.fillStyle = ninjaGray; ctx.fillRect(cx - 22 * scale, y + 100 * scale, 44 * scale, 8 * scale); // Throwing stars on belt ctx.fillStyle = '#888'; for (var s = 0; s < 3; s++) { var sx = cx - 12 * scale + s * 12 * scale; var sy = y + 104 * scale; ctx.beginPath(); for (var p = 0; p < 4; p++) { var angle = p * Math.PI / 2; var px = sx + Math.cos(angle) * 3 * scale; var py = sy + Math.sin(angle) * 3 * scale; if (p === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py); } ctx.closePath(); ctx.fill(); } // Legs ctx.fillStyle = ninjaBlack; roundRect(ctx, cx - 16 * scale, y + 116 * scale, 12 * scale, 36 * scale, 3 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 116 * scale, 12 * scale, 36 * scale, 3 * scale); ctx.fill(); // Leg wraps ctx.strokeStyle = ninjaGray; ctx.lineWidth = 2 * scale; for (var w = 0; w < 4; w++) { ctx.beginPath(); ctx.moveTo(cx - 16 * scale, y + 125 * scale + w * 8 * scale); ctx.lineTo(cx - 4 * scale, y + 128 * scale + w * 8 * scale); ctx.stroke(); ctx.beginPath(); ctx.moveTo(cx + 4 * scale, y + 125 * scale + w * 8 * scale); ctx.lineTo(cx + 16 * scale, y + 128 * scale + w * 8 * scale); ctx.stroke(); } // Tabi boots ctx.fillStyle = ninjaBlack; ctx.beginPath(); ctx.moveTo(cx - 18 * scale, y + 152 * scale); ctx.lineTo(cx - 18 * scale, y + 160 * scale); ctx.lineTo(cx - 10 * scale, y + 160 * scale); ctx.lineTo(cx - 8 * scale, y + 155 * scale); ctx.lineTo(cx - 4 * scale, y + 160 * scale); ctx.lineTo(cx - 4 * scale, y + 152 * scale); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx + 4 * scale, y + 152 * scale); ctx.lineTo(cx + 4 * scale, y + 160 * scale); ctx.lineTo(cx + 10 * scale, y + 160 * scale); ctx.lineTo(cx + 12 * scale, y + 155 * scale); ctx.lineTo(cx + 18 * scale, y + 160 * scale); ctx.lineTo(cx + 18 * scale, y + 152 * scale); ctx.closePath(); ctx.fill(); }, renderCostumeOver: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var ninjaBlack = '#1a1a1a'; // Arm wraps ctx.fillStyle = ninjaBlack; roundRect(ctx, cx - 32 * scale, y + 70 * scale, 10 * scale, 45 * scale, 3 * scale); ctx.fill(); roundRect(ctx, cx + 22 * scale, y + 70 * scale, 10 * scale, 45 * scale, 3 * scale); ctx.fill(); // Face mask ctx.fillStyle = ninjaBlack; ctx.beginPath(); ctx.moveTo(cx - 22 * scale, y + 35 * scale); ctx.lineTo(cx - 22 * scale, y + 55 * scale); ctx.quadraticCurveTo(cx, y + 60 * scale, cx + 22 * scale, y + 55 * scale); ctx.lineTo(cx + 22 * scale, y + 35 * scale); ctx.quadraticCurveTo(cx, y + 38 * scale, cx - 22 * scale, y + 35 * scale); ctx.fill(); // Eye slit ctx.fillStyle = '#000'; ctx.fillRect(cx - 18 * scale, y + 28 * scale, 36 * scale, 6 * scale); // Headband ctx.fillStyle = '#8b0000'; ctx.fillRect(cx - 24 * scale, y + 15 * scale, 48 * scale, 6 * scale); // Headband tail ctx.beginPath(); ctx.moveTo(cx + 24 * scale, y + 15 * scale); ctx.quadraticCurveTo(cx + 35 * scale + Math.sin(time * 3) * 3 * scale, y + 25 * scale, cx + 40 * scale + Math.sin(time * 3) * 5 * scale, y + 35 * scale); ctx.lineTo(cx + 36 * scale + Math.sin(time * 3) * 5 * scale, y + 38 * scale); ctx.quadraticCurveTo(cx + 32 * scale + Math.sin(time * 3) * 3 * scale, y + 28 * scale, cx + 24 * scale, y + 21 * scale); ctx.closePath(); ctx.fill(); } }; // ============================================================================ // COSTUME: WIZARD ROBES // ============================================================================ var wizardRobes = { id: 'wizard-robes', name: 'Wizard Robes', category: 'costume', avatarMode: 'FULL_BODY', animations: ['idle', 'walk', 'attack', 'hurt', 'celebrate'], defaultAnimation: 'idle', accessoryVisibility: { eyewear: true, headwear: true, effects: true }, layerOrder: ['avatar-body', 'costume-base', 'avatar-head', 'costume-overlay', 'earned-accessories'], recommendedFor: ['rpg', 'puzzle', 'adventure'], description: 'Mystical wizard with flowing robes', avatarPosition: { x: 0.5, y: 0.5 }, avatarScale: 1.0, props: [ { id: 'staff', anchor: 'rightArm', offset: { x: 5, y: 0 } } ], contextEffects: { attack: { type: 'magic', color: '#9b59b6' }, celebrate: { type: 'sparkles', color: '#ffd700' } }, renderCostumeUnder: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var robeColor = '#2c3e50'; var robeLight = '#3d566e'; var accent = '#9b59b6'; // Main robe body ctx.fillStyle = robeColor; ctx.beginPath(); ctx.moveTo(cx - 22 * scale, y + 64 * scale); ctx.lineTo(cx - 30 * scale + Math.sin(time * 1.5) * 2 * scale, y + 158 * scale); ctx.quadraticCurveTo(cx, y + 162 * scale, cx + 30 * scale + Math.sin(time * 1.5 + 1) * 2 * scale, y + 158 * scale); ctx.lineTo(cx + 22 * scale, y + 64 * scale); ctx.closePath(); ctx.fill(); // Robe inner fold ctx.fillStyle = robeLight; ctx.beginPath(); ctx.moveTo(cx - 5 * scale, y + 70 * scale); ctx.lineTo(cx - 8 * scale, y + 155 * scale); ctx.lineTo(cx + 8 * scale, y + 155 * scale); ctx.lineTo(cx + 5 * scale, y + 70 * scale); ctx.closePath(); ctx.fill(); // Belt ctx.fillStyle = '#8b4513'; ctx.fillRect(cx - 22 * scale, y + 100 * scale, 44 * scale, 8 * scale); // Belt buckle ctx.fillStyle = '#ffd700'; roundRect(ctx, cx - 6 * scale, y + 99 * scale, 12 * scale, 10 * scale, 2 * scale); ctx.fill(); // Belt pouches ctx.fillStyle = '#6b3e26'; roundRect(ctx, cx - 20 * scale, y + 105 * scale, 8 * scale, 10 * scale, 2 * scale); ctx.fill(); roundRect(ctx, cx + 12 * scale, y + 105 * scale, 8 * scale, 10 * scale, 2 * scale); ctx.fill(); // Mystical runes on robe ctx.strokeStyle = accent; ctx.lineWidth = 1 * scale; ctx.globalAlpha = 0.5 + Math.sin(time * 2) * 0.3; // Left rune ctx.beginPath(); ctx.arc(cx - 15 * scale, y + 130 * scale, 5 * scale, 0, Math.PI * 2); ctx.stroke(); ctx.beginPath(); ctx.moveTo(cx - 15 * scale, y + 125 * scale); ctx.lineTo(cx - 15 * scale, y + 135 * scale); ctx.moveTo(cx - 20 * scale, y + 130 * scale); ctx.lineTo(cx - 10 * scale, y + 130 * scale); ctx.stroke(); // Right rune ctx.beginPath(); ctx.arc(cx + 15 * scale, y + 130 * scale, 5 * scale, 0, Math.PI * 2); ctx.stroke(); ctx.beginPath(); ctx.moveTo(cx + 12 * scale, y + 127 * scale); ctx.lineTo(cx + 18 * scale, y + 133 * scale); ctx.moveTo(cx + 18 * scale, y + 127 * scale); ctx.lineTo(cx + 12 * scale, y + 133 * scale); ctx.stroke(); ctx.globalAlpha = 1; }, renderCostumeOver: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var robeColor = '#2c3e50'; var accent = '#9b59b6'; // Sleeves ctx.fillStyle = robeColor; ctx.beginPath(); ctx.moveTo(cx - 22 * scale, y + 68 * scale); ctx.quadraticCurveTo(cx - 35 * scale, y + 90 * scale, cx - 32 * scale, y + 118 * scale); ctx.lineTo(cx - 25 * scale, y + 115 * scale); ctx.quadraticCurveTo(cx - 28 * scale, y + 90 * scale, cx - 18 * scale, y + 72 * scale); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx + 22 * scale, y + 68 * scale); ctx.quadraticCurveTo(cx + 35 * scale, y + 90 * scale, cx + 32 * scale, y + 118 * scale); ctx.lineTo(cx + 25 * scale, y + 115 * scale); ctx.quadraticCurveTo(cx + 28 * scale, y + 90 * scale, cx + 18 * scale, y + 72 * scale); ctx.closePath(); ctx.fill(); // Staff (right hand) var rightArm = getArmTransform(transforms, 'rightArm'); ctx.save(); ctx.translate(cx + 28 * scale, y + 90 * scale); ctx.rotate((rightArm.rotation || 0) * Math.PI / 180); // Staff shaft ctx.fillStyle = '#5d4037'; roundRect(ctx, -3 * scale, -50 * scale, 6 * scale, 80 * scale, 2 * scale); ctx.fill(); // Staff orb holder ctx.fillStyle = '#3d2817'; ctx.beginPath(); ctx.moveTo(-6 * scale, -50 * scale); ctx.quadraticCurveTo(-8 * scale, -58 * scale, 0, -62 * scale); ctx.quadraticCurveTo(8 * scale, -58 * scale, 6 * scale, -50 * scale); ctx.closePath(); ctx.fill(); // Staff orb var orbGlow = ctx.createRadialGradient(0, -68 * scale, 0, 0, -68 * scale, 10 * scale); orbGlow.addColorStop(0, '#e0b0ff'); orbGlow.addColorStop(0.5, accent); orbGlow.addColorStop(1, '#4a235a'); ctx.fillStyle = orbGlow; ctx.beginPath(); ctx.arc(0, -68 * scale, 8 * scale, 0, Math.PI * 2); ctx.fill(); // Orb glow effect ctx.globalAlpha = 0.3 + Math.sin(time * 3) * 0.2; ctx.fillStyle = accent; ctx.beginPath(); ctx.arc(0, -68 * scale, 12 * scale, 0, Math.PI * 2); ctx.fill(); ctx.globalAlpha = 1; ctx.restore(); // Hood/collar ctx.fillStyle = robeColor; ctx.beginPath(); ctx.moveTo(cx - 24 * scale, y + 55 * scale); ctx.quadraticCurveTo(cx - 28 * scale, y + 45 * scale, cx - 20 * scale, y + 40 * scale); ctx.lineTo(cx + 20 * scale, y + 40 * scale); ctx.quadraticCurveTo(cx + 28 * scale, y + 45 * scale, cx + 24 * scale, y + 55 * scale); ctx.quadraticCurveTo(cx, y + 50 * scale, cx - 24 * scale, y + 55 * scale); ctx.fill(); } }; // ============================================================================ // COSTUME: SUPERHERO CAPE // ============================================================================ var superheroCape = { id: 'superhero-cape', name: 'Superhero Cape', category: 'costume', avatarMode: 'FULL_BODY', animations: ['idle', 'walk', 'attack', 'hurt', 'celebrate'], defaultAnimation: 'idle', accessoryVisibility: { eyewear: true, headwear: true, effects: true }, layerOrder: ['avatar-body', 'costume-base', 'avatar-head', 'costume-overlay', 'earned-accessories'], recommendedFor: ['action', 'adventure'], description: 'Classic superhero with billowing cape', avatarPosition: { x: 0.5, y: 0.5 }, avatarScale: 1.0, props: [], contextEffects: { attack: { type: 'impact', color: '#ffff00' }, celebrate: { type: 'sparkles', color: '#ffd700' } }, renderCostumeUnder: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var suitColor = avatar && avatar.base && avatar.base.clothing && avatar.base.clothing.color ? avatar.base.clothing.color : '#c0392b'; var capeColor = darken(suitColor, 0.3); // Cape (billowing behind) ctx.fillStyle = capeColor; ctx.beginPath(); ctx.moveTo(cx - 20 * scale, y + 62 * scale); var wave1 = Math.sin(time * 2) * 5 * scale; var wave2 = Math.sin(time * 2 + 1) * 5 * scale; var wave3 = Math.sin(time * 2 + 2) * 5 * scale; ctx.quadraticCurveTo(cx - 30 * scale + wave1, y + 100 * scale, cx - 35 * scale + wave2, y + 155 * scale); ctx.quadraticCurveTo(cx, y + 160 * scale + wave3, cx + 35 * scale + wave2, y + 155 * scale); ctx.quadraticCurveTo(cx + 30 * scale + wave1, y + 100 * scale, cx + 20 * scale, y + 62 * scale); ctx.closePath(); ctx.fill(); // Cape inner lining ctx.fillStyle = lighten(capeColor, 0.2); ctx.beginPath(); ctx.moveTo(cx - 15 * scale, y + 65 * scale); ctx.quadraticCurveTo(cx - 22 * scale + wave1 * 0.5, y + 100 * scale, cx - 25 * scale + wave2 * 0.5, y + 145 * scale); ctx.lineTo(cx + 25 * scale + wave2 * 0.5, y + 145 * scale); ctx.quadraticCurveTo(cx + 22 * scale + wave1 * 0.5, y + 100 * scale, cx + 15 * scale, y + 65 * scale); ctx.closePath(); ctx.fill(); // Bodysuit ctx.fillStyle = suitColor; roundRect(ctx, cx - 22 * scale, y + 64 * scale, 44 * scale, 52 * scale, 4 * scale); ctx.fill(); // Belt ctx.fillStyle = '#ffd700'; ctx.fillRect(cx - 22 * scale, y + 105 * scale, 44 * scale, 6 * scale); // Belt buckle ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(cx, y + 108 * scale, 5 * scale, 0, Math.PI * 2); ctx.fill(); // Trunks ctx.fillStyle = darken(suitColor, 0.2); roundRect(ctx, cx - 18 * scale, y + 108 * scale, 36 * scale, 12 * scale, 2 * scale); ctx.fill(); // Legs (suit continues) ctx.fillStyle = suitColor; roundRect(ctx, cx - 16 * scale, y + 118 * scale, 12 * scale, 36 * scale, 3 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 118 * scale, 12 * scale, 36 * scale, 3 * scale); ctx.fill(); // Boots ctx.fillStyle = darken(suitColor, 0.4); roundRect(ctx, cx - 18 * scale, y + 145 * scale, 14 * scale, 15 * scale, 3 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 145 * scale, 14 * scale, 15 * scale, 3 * scale); ctx.fill(); // Boot tops ctx.fillStyle = '#ffd700'; ctx.fillRect(cx - 18 * scale, y + 145 * scale, 14 * scale, 3 * scale); ctx.fillRect(cx + 4 * scale, y + 145 * scale, 14 * scale, 3 * scale); }, renderCostumeOver: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var suitColor = avatar && avatar.base && avatar.base.clothing && avatar.base.clothing.color ? avatar.base.clothing.color : '#c0392b'; // Arms ctx.fillStyle = suitColor; roundRect(ctx, cx - 32 * scale, y + 68 * scale, 12 * scale, 45 * scale, 4 * scale); ctx.fill(); roundRect(ctx, cx + 20 * scale, y + 68 * scale, 12 * scale, 45 * scale, 4 * scale); ctx.fill(); // Gloves ctx.fillStyle = darken(suitColor, 0.4); ctx.beginPath(); ctx.arc(cx - 26 * scale, y + 115 * scale, 6 * scale, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(cx + 26 * scale, y + 115 * scale, 6 * scale, 0, Math.PI * 2); ctx.fill(); // Glove cuffs ctx.fillStyle = '#ffd700'; ctx.fillRect(cx - 32 * scale, y + 105 * scale, 12 * scale, 4 * scale); ctx.fillRect(cx + 20 * scale, y + 105 * scale, 12 * scale, 4 * scale); // Chest emblem ctx.fillStyle = '#ffd700'; ctx.beginPath(); ctx.moveTo(cx, y + 70 * scale); ctx.lineTo(cx - 12 * scale, y + 78 * scale); ctx.lineTo(cx - 8 * scale, y + 95 * scale); ctx.lineTo(cx, y + 88 * scale); ctx.lineTo(cx + 8 * scale, y + 95 * scale); ctx.lineTo(cx + 12 * scale, y + 78 * scale); ctx.closePath(); ctx.fill(); // Mask ctx.fillStyle = darken(suitColor, 0.3); ctx.beginPath(); ctx.moveTo(cx - 22 * scale, y + 25 * scale); ctx.quadraticCurveTo(cx - 25 * scale, y + 32 * scale, cx - 18 * scale, y + 38 * scale); ctx.lineTo(cx - 8 * scale, y + 32 * scale); ctx.lineTo(cx, y + 35 * scale); ctx.lineTo(cx + 8 * scale, y + 32 * scale); ctx.lineTo(cx + 18 * scale, y + 38 * scale); ctx.quadraticCurveTo(cx + 25 * scale, y + 32 * scale, cx + 22 * scale, y + 25 * scale); ctx.quadraticCurveTo(cx, y + 22 * scale, cx - 22 * scale, y + 25 * scale); ctx.fill(); // Eye holes ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.ellipse(cx - 12 * scale, y + 30 * scale, 6 * scale, 4 * scale, -0.2, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(cx + 12 * scale, y + 30 * scale, 6 * scale, 4 * scale, 0.2, 0, Math.PI * 2); ctx.fill(); } }; // ============================================================================ // COSTUME: ATHLETE UNIFORM // ============================================================================ var athleteUniform = { id: 'athlete-uniform', name: 'Athlete Uniform', category: 'costume', avatarMode: 'FULL_BODY', animations: ['idle', 'walk', 'attack', 'hurt', 'celebrate'], defaultAnimation: 'idle', accessoryVisibility: { eyewear: true, headwear: true, effects: true }, layerOrder: ['avatar-body', 'costume-base', 'avatar-head', 'costume-overlay', 'earned-accessories'], recommendedFor: ['sports', 'action', 'racing'], description: 'Athletic sports uniform', avatarPosition: { x: 0.5, y: 0.5 }, avatarScale: 1.0, props: [ { id: 'ball', anchor: 'rightArm', offset: { x: 10, y: 5 } } ], contextEffects: { celebrate: { type: 'confetti', color: '#ffd700' } }, renderCostumeUnder: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var jerseyColor = '#e74c3c'; var accentColor = '#fff'; // Jersey ctx.fillStyle = jerseyColor; roundRect(ctx, cx - 22 * scale, y + 64 * scale, 44 * scale, 45 * scale, 4 * scale); ctx.fill(); // Jersey stripes ctx.fillStyle = accentColor; ctx.fillRect(cx - 22 * scale, y + 75 * scale, 44 * scale, 4 * scale); ctx.fillRect(cx - 22 * scale, y + 95 * scale, 44 * scale, 4 * scale); // Number on back ctx.fillStyle = accentColor; ctx.font = 'bold ' + (16 * scale) + 'px Arial'; ctx.textAlign = 'center'; ctx.fillText('23', cx, y + 92 * scale); // Shorts ctx.fillStyle = '#2c3e50'; roundRect(ctx, cx - 18 * scale, y + 108 * scale, 36 * scale, 22 * scale, 3 * scale); ctx.fill(); // Shorts stripe ctx.fillStyle = jerseyColor; ctx.fillRect(cx - 18 * scale, y + 108 * scale, 4 * scale, 22 * scale); ctx.fillRect(cx + 14 * scale, y + 108 * scale, 4 * scale, 22 * scale); // Legs ctx.fillStyle = avatar && avatar.base ? avatar.base.skinTone || '#FFD5B8' : '#FFD5B8'; roundRect(ctx, cx - 14 * scale, y + 128 * scale, 10 * scale, 25 * scale, 3 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 128 * scale, 10 * scale, 25 * scale, 3 * scale); ctx.fill(); // Socks ctx.fillStyle = accentColor; roundRect(ctx, cx - 16 * scale, y + 140 * scale, 12 * scale, 15 * scale, 2 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 140 * scale, 12 * scale, 15 * scale, 2 * scale); ctx.fill(); // Sock stripes ctx.fillStyle = jerseyColor; ctx.fillRect(cx - 16 * scale, y + 145 * scale, 12 * scale, 2 * scale); ctx.fillRect(cx + 4 * scale, y + 145 * scale, 12 * scale, 2 * scale); // Athletic shoes ctx.fillStyle = '#1a1a1a'; roundRect(ctx, cx - 18 * scale, y + 152 * scale, 14 * scale, 8 * scale, 2 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 152 * scale, 14 * scale, 8 * scale, 2 * scale); ctx.fill(); // Shoe accents ctx.fillStyle = jerseyColor; ctx.fillRect(cx - 16 * scale, y + 154 * scale, 10 * scale, 2 * scale); ctx.fillRect(cx + 6 * scale, y + 154 * scale, 10 * scale, 2 * scale); }, renderCostumeOver: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var jerseyColor = '#e74c3c'; var accentColor = '#fff'; // Sleeves ctx.fillStyle = jerseyColor; roundRect(ctx, cx - 30 * scale, y + 66 * scale, 12 * scale, 20 * scale, 3 * scale); ctx.fill(); roundRect(ctx, cx + 18 * scale, y + 66 * scale, 12 * scale, 20 * scale, 3 * scale); ctx.fill(); // Sleeve stripes ctx.fillStyle = accentColor; ctx.fillRect(cx - 30 * scale, y + 78 * scale, 12 * scale, 3 * scale); ctx.fillRect(cx + 18 * scale, y + 78 * scale, 12 * scale, 3 * scale); // Headband ctx.fillStyle = jerseyColor; ctx.fillRect(cx - 22 * scale, y + 12 * scale, 44 * scale, 6 * scale); // Sweatbands on wrists ctx.fillStyle = accentColor; ctx.fillRect(cx - 30 * scale, y + 100 * scale, 10 * scale, 6 * scale); ctx.fillRect(cx + 20 * scale, y + 100 * scale, 10 * scale, 6 * scale); // Number on front (smaller) ctx.fillStyle = accentColor; ctx.font = 'bold ' + (12 * scale) + 'px Arial'; ctx.textAlign = 'center'; ctx.fillText('23', cx, y + 82 * scale); } }; // ============================================================================ // COSTUME: PIRATE OUTFIT // ============================================================================ var pirateOutfit = { id: 'pirate-outfit', name: 'Pirate Outfit', category: 'costume', avatarMode: 'FULL_BODY', animations: ['idle', 'walk', 'attack', 'hurt', 'celebrate'], defaultAnimation: 'idle', accessoryVisibility: { eyewear: true, headwear: true, effects: true }, layerOrder: ['avatar-body', 'costume-base', 'avatar-head', 'costume-overlay', 'earned-accessories'], recommendedFor: ['adventure', 'action', 'rpg'], description: 'Swashbuckling pirate captain', avatarPosition: { x: 0.5, y: 0.5 }, avatarScale: 1.0, props: [ { id: 'cutlass', anchor: 'rightArm', offset: { x: 5, y: 15 } } ], contextEffects: { attack: { type: 'slash', color: '#c0c0c0' } }, renderCostumeUnder: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var coatColor = '#8b0000'; var coatDark = '#5c0000'; // Long coat (back) ctx.fillStyle = coatColor; ctx.beginPath(); ctx.moveTo(cx - 24 * scale, y + 64 * scale); ctx.lineTo(cx - 28 * scale + Math.sin(time * 1.5) * 2 * scale, y + 155 * scale); ctx.quadraticCurveTo(cx, y + 158 * scale, cx + 28 * scale + Math.sin(time * 1.5 + 1) * 2 * scale, y + 155 * scale); ctx.lineTo(cx + 24 * scale, y + 64 * scale); ctx.closePath(); ctx.fill(); // Coat back detail ctx.fillStyle = coatDark; ctx.beginPath(); ctx.moveTo(cx - 5 * scale, y + 90 * scale); ctx.lineTo(cx - 8 * scale, y + 150 * scale); ctx.lineTo(cx + 8 * scale, y + 150 * scale); ctx.lineTo(cx + 5 * scale, y + 90 * scale); ctx.closePath(); ctx.fill(); // White shirt ctx.fillStyle = '#f5f5dc'; roundRect(ctx, cx - 18 * scale, y + 64 * scale, 36 * scale, 40 * scale, 3 * scale); ctx.fill(); // Shirt ruffles ctx.strokeStyle = '#ddd'; ctx.lineWidth = 1 * scale; for (var r = 0; r < 4; r++) { ctx.beginPath(); ctx.moveTo(cx - 8 * scale, y + 68 * scale + r * 8 * scale); ctx.quadraticCurveTo(cx - 4 * scale, y + 72 * scale + r * 8 * scale, cx, y + 68 * scale + r * 8 * scale); ctx.quadraticCurveTo(cx + 4 * scale, y + 72 * scale + r * 8 * scale, cx + 8 * scale, y + 68 * scale + r * 8 * scale); ctx.stroke(); } // Belt with buckle ctx.fillStyle = '#2c1810'; ctx.fillRect(cx - 22 * scale, y + 102 * scale, 44 * scale, 8 * scale); ctx.fillStyle = '#ffd700'; roundRect(ctx, cx - 8 * scale, y + 100 * scale, 16 * scale, 12 * scale, 2 * scale); ctx.fill(); ctx.fillStyle = '#2c1810'; roundRect(ctx, cx - 4 * scale, y + 103 * scale, 8 * scale, 6 * scale, 1 * scale); ctx.fill(); // Pants ctx.fillStyle = '#2c3e50'; roundRect(ctx, cx - 16 * scale, y + 108 * scale, 32 * scale, 45 * scale, 3 * scale); ctx.fill(); // Boots ctx.fillStyle = '#3d2314'; roundRect(ctx, cx - 18 * scale, y + 140 * scale, 14 * scale, 20 * scale, 3 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 140 * scale, 14 * scale, 20 * scale, 3 * scale); ctx.fill(); // Boot tops (folded) ctx.fillStyle = '#5d4037'; ctx.beginPath(); ctx.moveTo(cx - 18 * scale, y + 145 * scale); ctx.lineTo(cx - 20 * scale, y + 140 * scale); ctx.lineTo(cx - 4 * scale, y + 140 * scale); ctx.lineTo(cx - 4 * scale, y + 145 * scale); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx + 4 * scale, y + 145 * scale); ctx.lineTo(cx + 4 * scale, y + 140 * scale); ctx.lineTo(cx + 20 * scale, y + 140 * scale); ctx.lineTo(cx + 18 * scale, y + 145 * scale); ctx.closePath(); ctx.fill(); }, renderCostumeOver: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var coatColor = '#8b0000'; // Coat front panels ctx.fillStyle = coatColor; ctx.beginPath(); ctx.moveTo(cx - 22 * scale, y + 64 * scale); ctx.lineTo(cx - 26 * scale, y + 110 * scale); ctx.lineTo(cx - 10 * scale, y + 110 * scale); ctx.lineTo(cx - 10 * scale, y + 64 * scale); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx + 22 * scale, y + 64 * scale); ctx.lineTo(cx + 26 * scale, y + 110 * scale); ctx.lineTo(cx + 10 * scale, y + 110 * scale); ctx.lineTo(cx + 10 * scale, y + 64 * scale); ctx.closePath(); ctx.fill(); // Gold trim on coat ctx.strokeStyle = '#ffd700'; ctx.lineWidth = 2 * scale; ctx.beginPath(); ctx.moveTo(cx - 10 * scale, y + 64 * scale); ctx.lineTo(cx - 10 * scale, y + 110 * scale); ctx.stroke(); ctx.beginPath(); ctx.moveTo(cx + 10 * scale, y + 64 * scale); ctx.lineTo(cx + 10 * scale, y + 110 * scale); ctx.stroke(); // Gold buttons ctx.fillStyle = '#ffd700'; for (var b = 0; b < 3; b++) { ctx.beginPath(); ctx.arc(cx - 14 * scale, y + 72 * scale + b * 12 * scale, 2 * scale, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(cx + 14 * scale, y + 72 * scale + b * 12 * scale, 2 * scale, 0, Math.PI * 2); ctx.fill(); } // Cutlass (right hand) var rightArm = getArmTransform(transforms, 'rightArm'); ctx.save(); ctx.translate(cx + 28 * scale, y + 110 * scale); ctx.rotate((rightArm.rotation || 0) * Math.PI / 180); // Blade ctx.fillStyle = '#c0c0c0'; ctx.beginPath(); ctx.moveTo(0, 0); ctx.quadraticCurveTo(8 * scale, -20 * scale, 5 * scale, -40 * scale); ctx.lineTo(2 * scale, -40 * scale); ctx.quadraticCurveTo(5 * scale, -20 * scale, -2 * scale, 0); ctx.closePath(); ctx.fill(); // Guard ctx.fillStyle = '#ffd700'; ctx.beginPath(); ctx.ellipse(0, 2 * scale, 8 * scale, 4 * scale, 0.3, 0, Math.PI * 2); ctx.fill(); // Handle ctx.fillStyle = '#3d2314'; roundRect(ctx, -2 * scale, 4 * scale, 4 * scale, 12 * scale, 1 * scale); ctx.fill(); ctx.restore(); // Pirate hat ctx.fillStyle = '#1a1a1a'; ctx.beginPath(); ctx.moveTo(cx - 28 * scale, y + 8 * scale); ctx.quadraticCurveTo(cx - 32 * scale, y - 8 * scale, cx - 18 * scale, y - 18 * scale); ctx.lineTo(cx, y - 8 * scale); ctx.lineTo(cx + 18 * scale, y - 18 * scale); ctx.quadraticCurveTo(cx + 32 * scale, y - 8 * scale, cx + 28 * scale, y + 8 * scale); ctx.quadraticCurveTo(cx, y + 12 * scale, cx - 28 * scale, y + 8 * scale); ctx.fill(); // Gold hat trim ctx.strokeStyle = '#ffd700'; ctx.lineWidth = 2 * scale; ctx.stroke(); // Skull emblem ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(cx, y - 5 * scale, 5 * scale, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#000'; ctx.beginPath(); ctx.arc(cx - 2 * scale, y - 6 * scale, 1 * scale, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(cx + 2 * scale, y - 6 * scale, 1 * scale, 0, Math.PI * 2); ctx.fill(); } }; // ============================================================================ // COSTUME: SCIENTIST LAB COAT // ============================================================================ var scientistLabcoat = { id: 'scientist-labcoat', name: 'Scientist Lab Coat', category: 'costume', avatarMode: 'FULL_BODY', animations: ['idle', 'walk', 'attack', 'hurt', 'celebrate'], defaultAnimation: 'idle', accessoryVisibility: { eyewear: true, headwear: true, effects: true }, layerOrder: ['avatar-body', 'costume-base', 'avatar-head', 'costume-overlay', 'earned-accessories'], recommendedFor: ['puzzle', 'physics', 'creative'], description: 'Mad scientist ready to experiment', avatarPosition: { x: 0.5, y: 0.5 }, avatarScale: 1.0, props: [ { id: 'beaker', anchor: 'rightArm', offset: { x: 5, y: 10 } } ], contextEffects: { attack: { type: 'bubbles', color: '#00ff00' } }, renderCostumeUnder: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; // Shirt underneath ctx.fillStyle = '#3498db'; roundRect(ctx, cx - 18 * scale, y + 64 * scale, 36 * scale, 35 * scale, 3 * scale); ctx.fill(); // Tie ctx.fillStyle = '#2c3e50'; ctx.beginPath(); ctx.moveTo(cx - 4 * scale, y + 64 * scale); ctx.lineTo(cx + 4 * scale, y + 64 * scale); ctx.lineTo(cx + 3 * scale, y + 72 * scale); ctx.lineTo(cx, y + 100 * scale); ctx.lineTo(cx - 3 * scale, y + 72 * scale); ctx.closePath(); ctx.fill(); // Pants ctx.fillStyle = '#34495e'; roundRect(ctx, cx - 16 * scale, y + 98 * scale, 32 * scale, 55 * scale, 3 * scale); ctx.fill(); // Sensible shoes ctx.fillStyle = '#2c1810'; roundRect(ctx, cx - 18 * scale, y + 150 * scale, 14 * scale, 10 * scale, 2 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 150 * scale, 14 * scale, 10 * scale, 2 * scale); ctx.fill(); }, renderCostumeOver: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; // Lab coat ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.moveTo(cx - 24 * scale, y + 62 * scale); ctx.lineTo(cx - 26 * scale, y + 140 * scale); ctx.lineTo(cx - 10 * scale, y + 140 * scale); ctx.lineTo(cx - 10 * scale, y + 62 * scale); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx + 24 * scale, y + 62 * scale); ctx.lineTo(cx + 26 * scale, y + 140 * scale); ctx.lineTo(cx + 10 * scale, y + 140 * scale); ctx.lineTo(cx + 10 * scale, y + 62 * scale); ctx.closePath(); ctx.fill(); // Coat collar ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.moveTo(cx - 20 * scale, y + 55 * scale); ctx.lineTo(cx - 10 * scale, y + 70 * scale); ctx.lineTo(cx - 5 * scale, y + 60 * scale); ctx.quadraticCurveTo(cx, y + 58 * scale, cx + 5 * scale, y + 60 * scale); ctx.lineTo(cx + 10 * scale, y + 70 * scale); ctx.lineTo(cx + 20 * scale, y + 55 * scale); ctx.quadraticCurveTo(cx, y + 50 * scale, cx - 20 * scale, y + 55 * scale); ctx.fill(); // Coat sleeves ctx.fillStyle = '#fff'; roundRect(ctx, cx - 32 * scale, y + 64 * scale, 12 * scale, 45 * scale, 3 * scale); ctx.fill(); roundRect(ctx, cx + 20 * scale, y + 64 * scale, 12 * scale, 45 * scale, 3 * scale); ctx.fill(); // Pocket protector ctx.fillStyle = '#ecf0f1'; roundRect(ctx, cx - 22 * scale, y + 72 * scale, 10 * scale, 15 * scale, 1 * scale); ctx.fill(); // Pens in pocket ctx.fillStyle = '#3498db'; ctx.fillRect(cx - 21 * scale, y + 70 * scale, 2 * scale, 12 * scale); ctx.fillStyle = '#e74c3c'; ctx.fillRect(cx - 18 * scale, y + 71 * scale, 2 * scale, 11 * scale); ctx.fillStyle = '#2c3e50'; ctx.fillRect(cx - 15 * scale, y + 70 * scale, 2 * scale, 10 * scale); // ID badge ctx.fillStyle = '#fff'; roundRect(ctx, cx + 12 * scale, y + 75 * scale, 12 * scale, 18 * scale, 1 * scale); ctx.fill(); ctx.fillStyle = '#3498db'; ctx.fillRect(cx + 13 * scale, y + 76 * scale, 10 * scale, 6 * scale); ctx.fillStyle = '#ddd'; ctx.fillRect(cx + 14 * scale, y + 84 * scale, 8 * scale, 2 * scale); ctx.fillRect(cx + 14 * scale, y + 88 * scale, 6 * scale, 2 * scale); // Safety goggles (on forehead) ctx.fillStyle = '#2c3e50'; ctx.fillRect(cx - 24 * scale, y + 8 * scale, 48 * scale, 4 * scale); ctx.fillStyle = 'rgba(100, 200, 255, 0.6)'; ctx.beginPath(); ctx.ellipse(cx - 12 * scale, y + 10 * scale, 10 * scale, 6 * scale, 0, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(cx + 12 * scale, y + 10 * scale, 10 * scale, 6 * scale, 0, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#1a1a2e'; ctx.lineWidth = 2 * scale; ctx.stroke(); // Beaker in hand var rightArm = getArmTransform(transforms, 'rightArm'); ctx.save(); ctx.translate(cx + 28 * scale, y + 110 * scale); ctx.rotate((rightArm.rotation || 0) * Math.PI / 180); // Beaker glass ctx.fillStyle = 'rgba(200, 230, 255, 0.4)'; ctx.strokeStyle = '#aaa'; ctx.lineWidth = 1 * scale; ctx.beginPath(); ctx.moveTo(-5 * scale, -20 * scale); ctx.lineTo(-6 * scale, 0); ctx.lineTo(6 * scale, 0); ctx.lineTo(5 * scale, -20 * scale); ctx.closePath(); ctx.fill(); ctx.stroke(); // Bubbling liquid var liquidColor = 'rgba(0, 255, 100, 0.7)'; ctx.fillStyle = liquidColor; ctx.beginPath(); ctx.moveTo(-5 * scale, -5 * scale); ctx.lineTo(-6 * scale, 0); ctx.lineTo(6 * scale, 0); ctx.lineTo(5 * scale, -5 * scale); ctx.closePath(); ctx.fill(); // Bubbles ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'; for (var bub = 0; bub < 3; bub++) { var bubY = -8 * scale - ((time * 20 + bub * 30) % 15) * scale; var bubX = (bub - 1) * 3 * scale; ctx.beginPath(); ctx.arc(bubX, bubY, 1.5 * scale, 0, Math.PI * 2); ctx.fill(); } ctx.restore(); } }; // ============================================================================ // COSTUME: CHEF OUTFIT // ============================================================================ var chefOutfit = { id: 'chef-outfit', name: 'Chef Outfit', category: 'costume', avatarMode: 'FULL_BODY', animations: ['idle', 'walk', 'attack', 'hurt', 'celebrate'], defaultAnimation: 'idle', accessoryVisibility: { eyewear: true, headwear: true, effects: true }, layerOrder: ['avatar-body', 'costume-base', 'avatar-head', 'costume-overlay', 'earned-accessories'], recommendedFor: ['creative', 'puzzle'], description: 'Master chef ready to cook', avatarPosition: { x: 0.5, y: 0.5 }, avatarScale: 1.0, props: [ { id: 'spatula', anchor: 'rightArm', offset: { x: 5, y: 15 } } ], contextEffects: {}, renderCostumeUnder: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; // Chef jacket ctx.fillStyle = '#fff'; roundRect(ctx, cx - 22 * scale, y + 64 * scale, 44 * scale, 50 * scale, 4 * scale); ctx.fill(); // Double-breasted buttons ctx.fillStyle = '#ddd'; for (var row = 0; row < 2; row++) { for (var col = 0; col < 3; col++) { ctx.beginPath(); ctx.arc(cx + (row * 12 - 6) * scale, y + (75 + col * 12) * scale, 2 * scale, 0, Math.PI * 2); ctx.fill(); } } // Apron ctx.fillStyle = '#f5f5f5'; ctx.beginPath(); ctx.moveTo(cx - 18 * scale, y + 90 * scale); ctx.lineTo(cx - 20 * scale, y + 155 * scale); ctx.lineTo(cx + 20 * scale, y + 155 * scale); ctx.lineTo(cx + 18 * scale, y + 90 * scale); ctx.closePath(); ctx.fill(); // Apron strings ctx.strokeStyle = '#ddd'; ctx.lineWidth = 2 * scale; ctx.beginPath(); ctx.moveTo(cx - 18 * scale, y + 90 * scale); ctx.lineTo(cx - 25 * scale, y + 95 * scale); ctx.stroke(); ctx.beginPath(); ctx.moveTo(cx + 18 * scale, y + 90 * scale); ctx.lineTo(cx + 25 * scale, y + 95 * scale); ctx.stroke(); // Apron pocket ctx.fillStyle = '#eee'; roundRect(ctx, cx - 10 * scale, y + 115 * scale, 20 * scale, 15 * scale, 2 * scale); ctx.fill(); // Pants ctx.fillStyle = '#2c3e50'; roundRect(ctx, cx - 16 * scale, y + 112 * scale, 32 * scale, 42 * scale, 3 * scale); ctx.fill(); // Shoes ctx.fillStyle = '#1a1a1a'; roundRect(ctx, cx - 18 * scale, y + 150 * scale, 14 * scale, 10 * scale, 2 * scale); ctx.fill(); roundRect(ctx, cx + 4 * scale, y + 150 * scale, 14 * scale, 10 * scale, 2 * scale); ctx.fill(); }, renderCostumeOver: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; // Chef jacket sleeves ctx.fillStyle = '#fff'; roundRect(ctx, cx - 30 * scale, y + 66 * scale, 12 * scale, 40 * scale, 3 * scale); ctx.fill(); roundRect(ctx, cx + 18 * scale, y + 66 * scale, 12 * scale, 40 * scale, 3 * scale); ctx.fill(); // Kitchen towel over shoulder ctx.fillStyle = '#e74c3c'; ctx.beginPath(); ctx.moveTo(cx + 20 * scale, y + 64 * scale); ctx.quadraticCurveTo(cx + 30 * scale, y + 70 * scale, cx + 25 * scale, y + 90 * scale); ctx.lineTo(cx + 20 * scale, y + 88 * scale); ctx.quadraticCurveTo(cx + 24 * scale, y + 72 * scale, cx + 18 * scale, y + 66 * scale); ctx.closePath(); ctx.fill(); // Towel stripes ctx.fillStyle = '#fff'; ctx.fillRect(cx + 21 * scale, y + 72 * scale, 2 * scale, 14 * scale); ctx.fillRect(cx + 24 * scale, y + 74 * scale, 2 * scale, 12 * scale); // Chef hat ctx.fillStyle = '#fff'; // Puffy top ctx.beginPath(); ctx.arc(cx - 10 * scale, y - 25 * scale, 12 * scale, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(cx + 10 * scale, y - 25 * scale, 12 * scale, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(cx, y - 30 * scale, 14 * scale, 0, Math.PI * 2); ctx.fill(); // Hat band roundRect(ctx, cx - 20 * scale, y - 12 * scale, 40 * scale, 20 * scale, 2 * scale); ctx.fill(); // Hat band line ctx.fillStyle = '#ecf0f1'; ctx.fillRect(cx - 20 * scale, y + 3 * scale, 40 * scale, 5 * scale); // Utensils in apron pocket ctx.fillStyle = '#888'; ctx.fillRect(cx - 6 * scale, y + 110 * scale, 2 * scale, 12 * scale); ctx.fillRect(cx + 2 * scale, y + 112 * scale, 2 * scale, 10 * scale); // Spatula in hand var rightArm = getArmTransform(transforms, 'rightArm'); ctx.save(); ctx.translate(cx + 26 * scale, y + 108 * scale); ctx.rotate((rightArm.rotation || 0) * Math.PI / 180); // Handle ctx.fillStyle = '#5d4037'; roundRect(ctx, -2 * scale, 0, 4 * scale, 18 * scale, 1 * scale); ctx.fill(); // Spatula head ctx.fillStyle = '#888'; roundRect(ctx, -6 * scale, -15 * scale, 12 * scale, 18 * scale, 2 * scale); ctx.fill(); // Slots ctx.fillStyle = '#666'; ctx.fillRect(cx - 4 * scale - cx - 26 * scale, -12 * scale, 8 * scale, 2 * scale); ctx.fillRect(cx - 4 * scale - cx - 26 * scale, -8 * scale, 8 * scale, 2 * scale); ctx.fillRect(cx - 4 * scale - cx - 26 * scale, -4 * scale, 8 * scale, 2 * scale); ctx.restore(); } }; // ============================================================================ // COSTUME: COWBOY GEAR // ============================================================================ var cowboyGear = { id: 'cowboy-gear', name: 'Cowboy Gear', category: 'costume', avatarMode: 'FULL_BODY', animations: ['idle', 'walk', 'attack', 'hurt', 'celebrate'], defaultAnimation: 'idle', accessoryVisibility: { eyewear: true, headwear: true, effects: true }, layerOrder: ['avatar-body', 'costume-base', 'avatar-head', 'costume-overlay', 'earned-accessories'], recommendedFor: ['action', 'adventure'], description: 'Wild west cowboy', avatarPosition: { x: 0.5, y: 0.5 }, avatarScale: 1.0, props: [ { id: 'lasso', anchor: 'rightArm', offset: { x: 10, y: 0 } } ], contextEffects: {}, renderCostumeUnder: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; var vestColor = '#8b4513'; // Shirt ctx.fillStyle = '#c19a6b'; roundRect(ctx, cx - 20 * scale, y + 64 * scale, 40 * scale, 45 * scale, 3 * scale); ctx.fill(); // Shirt details (collar points) ctx.fillStyle = '#a08060'; ctx.beginPath(); ctx.moveTo(cx - 8 * scale, y + 62 * scale); ctx.lineTo(cx - 15 * scale, y + 75 * scale); ctx.lineTo(cx - 5 * scale, y + 70 * scale); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx + 8 * scale, y + 62 * scale); ctx.lineTo(cx + 15 * scale, y + 75 * scale); ctx.lineTo(cx + 5 * scale, y + 70 * scale); ctx.closePath(); ctx.fill(); // Vest ctx.fillStyle = vestColor; ctx.beginPath(); ctx.moveTo(cx - 20 * scale, y + 65 * scale); ctx.lineTo(cx - 22 * scale, y + 108 * scale); ctx.lineTo(cx - 8 * scale, y + 108 * scale); ctx.lineTo(cx - 8 * scale, y + 75 * scale); ctx.lineTo(cx - 12 * scale, y + 65 * scale); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx + 20 * scale, y + 65 * scale); ctx.lineTo(cx + 22 * scale, y + 108 * scale); ctx.lineTo(cx + 8 * scale, y + 108 * scale); ctx.lineTo(cx + 8 * scale, y + 75 * scale); ctx.lineTo(cx + 12 * scale, y + 65 * scale); ctx.closePath(); ctx.fill(); // Belt ctx.fillStyle = '#3d2314'; ctx.fillRect(cx - 20 * scale, y + 105 * scale, 40 * scale, 8 * scale); // Belt buckle (large oval) ctx.fillStyle = '#ffd700'; ctx.beginPath(); ctx.ellipse(cx, y + 109 * scale, 8 * scale, 5 * scale, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#b8860b'; ctx.beginPath(); ctx.ellipse(cx, y + 109 * scale, 5 * scale, 3 * scale, 0, 0, Math.PI * 2); ctx.fill(); // Jeans ctx.fillStyle = '#4a6fa5'; roundRect(ctx, cx - 16 * scale, y + 112 * scale, 32 * scale, 42 * scale, 3 * scale); ctx.fill(); // Jean seams ctx.strokeStyle = '#3d5a80'; ctx.lineWidth = 1 * scale; ctx.beginPath(); ctx.moveTo(cx - 10 * scale, y + 115 * scale); ctx.lineTo(cx - 10 * scale, y + 152 * scale); ctx.stroke(); ctx.beginPath(); ctx.moveTo(cx + 10 * scale, y + 115 * scale); ctx.lineTo(cx + 10 * scale, y + 152 * scale); ctx.stroke(); // Cowboy boots ctx.fillStyle = '#5d4037'; ctx.beginPath(); ctx.moveTo(cx - 18 * scale, y + 150 * scale); ctx.lineTo(cx - 20 * scale, y + 160 * scale); ctx.lineTo(cx - 4 * scale, y + 160 * scale); ctx.lineTo(cx - 4 * scale, y + 150 * scale); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx + 4 * scale, y + 150 * scale); ctx.lineTo(cx + 4 * scale, y + 160 * scale); ctx.lineTo(cx + 20 * scale, y + 160 * scale); ctx.lineTo(cx + 18 * scale, y + 150 * scale); ctx.closePath(); ctx.fill(); // Boot heel ctx.fillStyle = '#3d2314'; ctx.fillRect(cx - 18 * scale, y + 158 * scale, 4 * scale, 4 * scale); ctx.fillRect(cx + 14 * scale, y + 158 * scale, 4 * scale, 4 * scale); // Boot top decoration ctx.fillStyle = '#8b7355'; ctx.fillRect(cx - 17 * scale, y + 150 * scale, 12 * scale, 3 * scale); ctx.fillRect(cx + 5 * scale, y + 150 * scale, 12 * scale, 3 * scale); // Spurs ctx.fillStyle = '#c0c0c0'; ctx.beginPath(); ctx.arc(cx - 19 * scale, y + 158 * scale, 2 * scale, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(cx + 19 * scale, y + 158 * scale, 2 * scale, 0, Math.PI * 2); ctx.fill(); }, renderCostumeOver: function(ctx, avatar, x, y, scale, transforms, time) { var cx = x + 32 * scale; // Shirt sleeves ctx.fillStyle = '#c19a6b'; roundRect(ctx, cx - 28 * scale, y + 66 * scale, 10 * scale, 40 * scale, 3 * scale); ctx.fill(); roundRect(ctx, cx + 18 * scale, y + 66 * scale, 10 * scale, 40 * scale, 3 * scale); ctx.fill(); // Bandana around neck ctx.fillStyle = '#e74c3c'; ctx.beginPath(); ctx.moveTo(cx - 15 * scale, y + 58 * scale); ctx.quadraticCurveTo(cx, y + 65 * scale, cx + 15 * scale, y + 58 * scale); ctx.lineTo(cx + 12 * scale, y + 62 * scale); ctx.quadraticCurveTo(cx, y + 68 * scale, cx - 12 * scale, y + 62 * scale); ctx.closePath(); ctx.fill(); // Bandana knot ctx.beginPath(); ctx.arc(cx, y + 68 * scale, 4 * scale, 0, Math.PI * 2); ctx.fill(); // Bandana tails ctx.beginPath(); ctx.moveTo(cx - 2 * scale, y + 72 * scale); ctx.quadraticCurveTo(cx - 5 * scale, y + 85 * scale, cx - 8 * scale + Math.sin(time * 2) * 2 * scale, y + 92 * scale); ctx.lineTo(cx - 5 * scale + Math.sin(time * 2) * 2 * scale, y + 90 * scale); ctx.quadraticCurveTo(cx - 3 * scale, y + 82 * scale, cx, y + 72 * scale); ctx.fill(); ctx.beginPath(); ctx.moveTo(cx + 2 * scale, y + 72 * scale); ctx.quadraticCurveTo(cx + 5 * scale, y + 85 * scale, cx + 8 * scale + Math.sin(time * 2 + 0.5) * 2 * scale, y + 92 * scale); ctx.lineTo(cx + 5 * scale + Math.sin(time * 2 + 0.5) * 2 * scale, y + 90 * scale); ctx.quadraticCurveTo(cx + 3 * scale, y + 82 * scale, cx, y + 72 * scale); ctx.fill(); // Cowboy hat var hatColor = '#8b4513'; // Brim ctx.fillStyle = hatColor; ctx.beginPath(); ctx.ellipse(cx, y + 8 * scale, 32 * scale, 8 * scale, 0, 0, Math.PI * 2); ctx.fill(); // Crown ctx.beginPath(); ctx.moveTo(cx - 18 * scale, y + 6 * scale); ctx.quadraticCurveTo(cx - 20 * scale, y - 18 * scale, cx - 10 * scale, y - 22 * scale); ctx.quadraticCurveTo(cx, y - 16 * scale, cx + 10 * scale, y - 22 * scale); ctx.quadraticCurveTo(cx + 20 * scale, y - 18 * scale, cx + 18 * scale, y + 6 * scale); ctx.closePath(); ctx.fill(); // Hat band ctx.fillStyle = darken(hatColor, 0.3); ctx.fillRect(cx - 18 * scale, y + 1 * scale, 36 * scale, 5 * scale); // Hat band buckle ctx.fillStyle = '#ffd700'; ctx.fillRect(cx - 4 * scale, y, 8 * scale, 7 * scale); // Lasso in hand var rightArm = getArmTransform(transforms, 'rightArm'); ctx.save(); ctx.translate(cx + 26 * scale, y + 108 * scale); ctx.rotate((rightArm.rotation || 0) * Math.PI / 180); // Coiled rope ctx.strokeStyle = '#c19a6b'; ctx.lineWidth = 3 * scale; ctx.beginPath(); for (var coil = 0; coil < 3; coil++) { ctx.arc(0, -5 * scale + coil * 6 * scale, 8 * scale, 0, Math.PI * 1.8); } ctx.stroke(); // Rope end ctx.beginPath(); ctx.moveTo(8 * scale, 10 * scale); ctx.lineTo(15 * scale, 20 * scale); ctx.stroke(); ctx.restore(); } }; // ============================================================================ // CONTEXTS ARRAY // ============================================================================ var CONTEXTS = [ knightArmor, spaceSuit, ninjaOutfit, wizardRobes, superheroCape, athleteUniform, pirateOutfit, scientistLabcoat, chefOutfit, cowboyGear ]; // ============================================================================ // PUBLIC API // ============================================================================ window.CostumeContexts = { CONTEXTS: CONTEXTS, getById: function(id) { for (var i = 0; i < CONTEXTS.length; i++) { if (CONTEXTS[i].id === id) { return CONTEXTS[i]; } } return null; }, getAll: function() { return CONTEXTS.slice(); }, getRecommendedFor: function(gameType) { var recommended = []; for (var i = 0; i < CONTEXTS.length; i++) { if (CONTEXTS[i].recommendedFor && CONTEXTS[i].recommendedFor.indexOf(gameType) !== -1) { recommended.push(CONTEXTS[i]); } } return recommended; }, isAccessoryVisible: function(costumeId, accessoryType) { var costume = this.getById(costumeId); if (!costume || !costume.accessoryVisibility) { return true; } return costume.accessoryVisibility[accessoryType] !== false; } }; })();