Add Cursor runs for Claude and Gemini models
This commit is contained in:
1 parent
9bc21c2ace
commit
3936cd26af
17 files changed
+5169
-4
No files matched your search
@@ -0,0 +1,877 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Minecraft Clone</title>
|
||||
<style>
|
||||
*{margin:0;padding:0;box-sizing:border-box}
|
||||
html,body{width:100%;height:100%;overflow:hidden;background:#000;font-family:'Segoe UI',system-ui,sans-serif}
|
||||
canvas{display:block;width:100%;height:100%}
|
||||
#overlay{position:fixed;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:rgba(0,0,0,0.75);color:#fff;z-index:200;cursor:pointer;user-select:none}
|
||||
#overlay h1{font-size:48px;font-weight:900;letter-spacing:4px;margin-bottom:24px;text-shadow:0 0 40px rgba(100,200,255,0.4)}
|
||||
#overlay .controls{font-size:15px;line-height:2;color:#aaa;text-align:center}
|
||||
#overlay .controls span{color:#fff;font-weight:600;background:rgba(255,255,255,0.1);padding:2px 8px;border-radius:4px;margin:0 2px}
|
||||
#overlay .start{margin-top:32px;font-size:20px;color:#7cf;animation:pulse 2s ease-in-out infinite}
|
||||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}}
|
||||
#overlay.hidden{display:none}
|
||||
#crosshair{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);pointer-events:none;z-index:100;width:1px;height:1px}
|
||||
#crosshair::before,#crosshair::after{content:'';position:absolute;background:rgba(255,255,255,0.8)}
|
||||
#crosshair::before{width:24px;height:2px;top:-1px;left:-12px}
|
||||
#crosshair::after{width:2px;height:24px;top:-12px;left:-1px}
|
||||
#hotbar{position:fixed;bottom:16px;left:50%;transform:translateX(-50%);display:flex;gap:2px;background:rgba(0,0,0,0.65);padding:4px;border-radius:6px;z-index:100;border:1px solid rgba(255,255,255,0.1)}
|
||||
.hslot{width:52px;height:52px;border:2px solid rgba(255,255,255,0.15);border-radius:4px;display:flex;align-items:center;justify-content:center;position:relative;cursor:pointer;transition:border-color 0.15s}
|
||||
.hslot.sel{border-color:rgba(120,210,255,0.9);box-shadow:0 0 12px rgba(120,210,255,0.3)}
|
||||
.hslot canvas{width:36px;height:36px;image-rendering:pixelated}
|
||||
.hslot .num{position:absolute;top:2px;left:5px;font-size:10px;color:rgba(255,255,255,0.5);font-weight:600}
|
||||
#debug{position:fixed;top:12px;left:12px;color:rgba(255,255,255,0.6);font-size:12px;font-family:'Cascadia Code','SF Mono',monospace;z-index:100;pointer-events:none;line-height:1.6;white-space:pre}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="overlay">
|
||||
<h1>MINECRAFT</h1>
|
||||
<div class="controls">
|
||||
<span>W A S D</span> Move / <span>Space</span> Jump / <span>Shift</span> Descend<br>
|
||||
<span>Mouse</span> Look / <span>Left Click</span> Break / <span>Right Click</span> Place<br>
|
||||
<span>1-9</span> Select Block / <span>Scroll</span> Cycle Blocks
|
||||
</div>
|
||||
<div class="start">Click to Play</div>
|
||||
</div>
|
||||
<div id="crosshair"></div>
|
||||
<div id="hotbar"></div>
|
||||
<div id="debug"></div>
|
||||
<script src="https://unpkg.com/three@0.160.0/build/three.min.js"></script>
|
||||
<script>
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
var CHUNK=16, CHUNK_H=128, RDIST=7, SEA=28;
|
||||
var GRAV=28, JVEL=9, PH=1.7, PW=0.3, REACH=6;
|
||||
|
||||
var B={AIR:0,GRASS:1,DIRT:2,STONE:3,SAND:4,WATER:5,WOOD:6,LEAVES:7,SNOW:8,OBSIDIAN:9,GOLD:10,CRYSTAL:11,BEDROCK:12,BONE:13,BRICK:14,GLASS:15,LAVA:16};
|
||||
var BNAME=['','Grass','Dirt','Stone','Sand','Water','Wood','Leaves','Snow','Obsidian','Gold','Crystal','Bedrock','Bone','Brick','Glass','Lava'];
|
||||
var TRANS=new Set([B.AIR,B.WATER,B.GLASS,B.LEAVES]);
|
||||
function isSolid(t){return t!==B.AIR&&t!==B.WATER}
|
||||
var HOTBAR=[B.GRASS,B.DIRT,B.STONE,B.SAND,B.WOOD,B.BRICK,B.OBSIDIAN,B.GOLD,B.CRYSTAL];
|
||||
|
||||
function PerlinNoise(seed){
|
||||
this.p=new Uint8Array(512);
|
||||
var perm=new Uint8Array(256);
|
||||
for(var i=0;i<256;i++)perm[i]=i;
|
||||
var s=(seed*2147483647)|0;if(s<=0)s=1;
|
||||
for(var i=255;i>0;i--){s=(s*16807)%2147483647;var j=s%(i+1);var t=perm[i];perm[i]=perm[j];perm[j]=t;}
|
||||
for(var i=0;i<512;i++)this.p[i]=perm[i&255];
|
||||
}
|
||||
PerlinNoise.prototype.fade=function(t){return t*t*t*(t*(t*6-15)+10)};
|
||||
PerlinNoise.prototype.lerp=function(a,b,t){return a+t*(b-a)};
|
||||
PerlinNoise.prototype.grad2=function(h,x,y){var v=h&3;return((v&1)?-x:x)+((v&2)?-y:y)};
|
||||
PerlinNoise.prototype.grad3=function(h,x,y,z){var b=h&15;var u=b<8?x:y;var v=b<4?y:(b===12||b===14?x:z);return((b&1)?-u:u)+((b&2)?-v:v)};
|
||||
PerlinNoise.prototype.noise2=function(x,y){
|
||||
var X=Math.floor(x)&255,Y=Math.floor(y)&255;
|
||||
x-=Math.floor(x);y-=Math.floor(y);
|
||||
var u=this.fade(x),v=this.fade(y),p=this.p;
|
||||
var A=p[X]+Y,C=p[X+1]+Y;
|
||||
return this.lerp(this.lerp(this.grad2(p[A],x,y),this.grad2(p[C],x-1,y),u),this.lerp(this.grad2(p[A+1],x,y-1),this.grad2(p[C+1],x-1,y-1),u),v);
|
||||
};
|
||||
PerlinNoise.prototype.noise3=function(x,y,z){
|
||||
var X=Math.floor(x)&255,Y=Math.floor(y)&255,Z=Math.floor(z)&255;
|
||||
x-=Math.floor(x);y-=Math.floor(y);z-=Math.floor(z);
|
||||
var u=this.fade(x),v=this.fade(y),w=this.fade(z),p=this.p;
|
||||
var A=p[X]+Y,AA=p[A]+Z,AB=p[A+1]+Z,C=p[X+1]+Y,BA=p[C]+Z,BB=p[C+1]+Z;
|
||||
return this.lerp(this.lerp(this.lerp(this.grad3(p[AA],x,y,z),this.grad3(p[BA],x-1,y,z),u),this.lerp(this.grad3(p[AB],x,y-1,z),this.grad3(p[BB],x-1,y-1,z),u),v),this.lerp(this.lerp(this.grad3(p[AA+1],x,y,z-1),this.grad3(p[BA+1],x-1,y,z-1),u),this.lerp(this.grad3(p[AB+1],x,y-1,z-1),this.grad3(p[BB+1],x-1,y-1,z-1),u),v),w);
|
||||
};
|
||||
PerlinNoise.prototype.fbm2=function(x,y,oct){
|
||||
var val=0,amp=1,freq=1,max=0;
|
||||
for(var i=0;i<oct;i++){val+=this.noise2(x*freq,y*freq)*amp;max+=amp;amp*=0.5;freq*=2;}
|
||||
return val/max;
|
||||
};
|
||||
PerlinNoise.prototype.fbm3=function(x,y,z,oct){
|
||||
var val=0,amp=1,freq=1,max=0;
|
||||
for(var i=0;i<oct;i++){val+=this.noise3(x*freq,y*freq,z*freq)*amp;max+=amp;amp*=0.5;freq*=2;}
|
||||
return val/max;
|
||||
};
|
||||
|
||||
var noise=new PerlinNoise(7777);
|
||||
|
||||
function srand(x,y){var n=Math.sin(x*127.1+y*311.7)*43758.5453;return n-Math.floor(n);}
|
||||
|
||||
function terrainH(wx,wz){
|
||||
var base=noise.fbm2(wx*0.008,wz*0.008,6)*35+32;
|
||||
var mt=Math.max(0,noise.fbm2(wx*0.003+100,wz*0.003+100,4))*30;
|
||||
var dt=noise.fbm2(wx*0.03,wz*0.03,3)*5;
|
||||
return Math.floor(Math.max(1,base+mt+dt));
|
||||
}
|
||||
|
||||
function getBiome(wx,wz){
|
||||
var t=noise.noise2(wx*0.004,wz*0.004);
|
||||
var h=noise.noise2(wx*0.004+500,wz*0.004+500);
|
||||
if(t>0.25)return'desert';
|
||||
if(t<-0.3)return'tundra';
|
||||
if(h>0.15)return'forest';
|
||||
return'plains';
|
||||
}
|
||||
|
||||
function isCave(wx,wy,wz){
|
||||
if(wy<2||wy>55)return false;
|
||||
return noise.fbm3(wx*0.04,wy*0.06,wz*0.04,3)>0.55;
|
||||
}
|
||||
|
||||
function shouldTree(wx,wz){
|
||||
var biome=getBiome(wx,wz);
|
||||
var th=biome==='forest'?0.55:biome==='plains'?0.78:999;
|
||||
if(th>1)return false;
|
||||
var gx=Math.floor(wx/4)*4,gz=Math.floor(wz/4)*4;
|
||||
var ox=Math.floor(srand(gx,gz)*3),oz=Math.floor(srand(gz,gx)*3);
|
||||
if(wx!==gx+ox||wz!==gz+oz)return false;
|
||||
return srand(wx*13+7,wz*17+3)>(1-th)&&terrainH(wx,wz)>SEA;
|
||||
}
|
||||
function treeH(wx,wz){return 4+Math.floor(srand(wx*31+5,wz*37+11)*4);}
|
||||
|
||||
var atlasCanvas=null;
|
||||
function buildAtlas(){
|
||||
var size=16;
|
||||
atlasCanvas=document.createElement('canvas');
|
||||
atlasCanvas.width=atlasCanvas.height=256;
|
||||
var actx=atlasCanvas.getContext('2d');
|
||||
function drawTex(idx,fn){
|
||||
var c=document.createElement('canvas');c.width=c.height=size;
|
||||
fn(c.getContext('2d'),size);
|
||||
actx.drawImage(c,(idx%16)*size,Math.floor(idx/16)*size);
|
||||
}
|
||||
function fill(ctx,s,cols){
|
||||
for(var y=0;y<s;y++)for(var x=0;x<s;x++){
|
||||
ctx.fillStyle=cols[Math.floor(srand(x+0.5,y+0.5)*cols.length)];
|
||||
ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
}
|
||||
drawTex(0,function(ctx,s){fill(ctx,s,['#4a8a2a','#52952e','#5da33a','#489025','#67b040','#3e7820']);});
|
||||
drawTex(1,function(ctx,s){
|
||||
for(var y=0;y<s;y++)for(var x=0;x<s;x++){
|
||||
if(y<3){var c=['#4a8a2a','#52952e','#5da33a','#3e7820'];ctx.fillStyle=c[Math.floor(srand(x+.1,y+.1)*c.length)];}
|
||||
else if(y<5){ctx.fillStyle=srand(x+.2,y+.2)>.5?'#6b5435':'#4a8a2a';}
|
||||
else{var c=['#866043','#7a5539','#92694d','#6b4c30'];ctx.fillStyle=c[Math.floor(srand(x+.3,y+.3)*c.length)];}
|
||||
ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
});
|
||||
drawTex(2,function(ctx,s){fill(ctx,s,['#866043','#7a5539','#92694d','#6b4c30','#7e5b3f']);});
|
||||
drawTex(3,function(ctx,s){
|
||||
for(var y=0;y<s;y++)for(var x=0;x<s;x++){
|
||||
var c=['#828282','#777','#8e8e8e','#6e6e6e','#999'];
|
||||
var ci=Math.floor(srand(x+.5,y+.5)*c.length);
|
||||
if(srand(x*3.1+.7,y*2.3+.9)>.85)ci=3;
|
||||
ctx.fillStyle=c[ci];ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
});
|
||||
drawTex(4,function(ctx,s){fill(ctx,s,['#dbc896','#d0bd8a','#e5d3a3','#c9b07e','#d6c490']);});
|
||||
drawTex(5,function(ctx,s){
|
||||
for(var y=0;y<s;y++)for(var x=0;x<s;x++){
|
||||
var c=['#6b4423','#7b5433','#5b3413','#7e5a38'];
|
||||
var ci=Math.floor(srand(x+.4,y+.4)*c.length);
|
||||
if((x+Math.floor(srand(x*.1,y*.7)*2))%4<2)ci=0;
|
||||
ctx.fillStyle=c[ci];ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
});
|
||||
drawTex(6,function(ctx,s){
|
||||
for(var y=0;y<s;y++)for(var x=0;x<s;x++){
|
||||
var dx=x-7.5,dy=y-7.5,r=Math.sqrt(dx*dx+dy*dy);
|
||||
if(r<2)ctx.fillStyle='#5a3a15';
|
||||
else if(r<5)ctx.fillStyle=srand(x+.6,y+.6)>.5?'#b8945f':'#a0804a';
|
||||
else ctx.fillStyle=srand(x+.7,y+.7)>.5?'#c8a46f':'#b08850';
|
||||
ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
});
|
||||
drawTex(7,function(ctx,s){fill(ctx,s,['#3a7a1a','#4a8c28','#2e6e14','#55a035','#3d8020','#488a25']);});
|
||||
drawTex(8,function(ctx,s){fill(ctx,s,['#efeff0','#e4e4e8','#f5f5f8','#dcdce0','#eaeaee']);});
|
||||
drawTex(9,function(ctx,s){
|
||||
for(var y=0;y<s;y++)for(var x=0;x<s;x++){
|
||||
if(y<3)ctx.fillStyle=srand(x+.8,y+.8)>.5?'#efeff0':'#ddd';
|
||||
else if(y<5)ctx.fillStyle=srand(x+.9,y+.9)>.5?'#a09888':'#e0e0e0';
|
||||
else{var c=['#866043','#7a5539','#92694d','#6b4c30'];ctx.fillStyle=c[Math.floor(srand(x+1,y+1)*c.length)];}
|
||||
ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
});
|
||||
drawTex(10,function(ctx,s){
|
||||
for(var y=0;y<s;y++)for(var x=0;x<s;x++){
|
||||
var c=['#120820','#1a0c30','#0d0518','#150928'];
|
||||
ctx.fillStyle=srand(x*2.7+.3,y*3.1+.7)>.9?'#2a1848':c[Math.floor(srand(x+1.1,y+1.1)*c.length)];
|
||||
ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
});
|
||||
drawTex(11,function(ctx,s){
|
||||
for(var y=0;y<s;y++)for(var x=0;x<s;x++){
|
||||
var c=['#f0c800','#e8be10','#f8d520','#ddb408','#fce040'];
|
||||
var ci=Math.floor(srand(x+1.2,y+1.2)*c.length);
|
||||
if(x===0||x===15||y===0||y===15)ci=3;
|
||||
if((x===1||x===14||y===1||y===14)&&srand(x*1.5,y*1.5)>.7)ci=3;
|
||||
ctx.fillStyle=c[ci];ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
});
|
||||
drawTex(12,function(ctx,s){
|
||||
for(var y=0;y<s;y++)for(var x=0;x<s;x++){
|
||||
var n=srand(x+1.3,y+1.3);
|
||||
ctx.fillStyle=n>.7?'#b0f0ff':n>.4?'#80d8f0':'#60c0e8';
|
||||
ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
});
|
||||
drawTex(13,function(ctx,s){fill(ctx,s,['#333','#222','#444','#1a1a1a','#3a3a3a','#2a2a2a']);});
|
||||
drawTex(14,function(ctx,s){fill(ctx,s,['#e8dcc8','#d8ccb0','#f0e4d4','#c8b898','#e0d4c0']);});
|
||||
drawTex(15,function(ctx,s){
|
||||
for(var y=0;y<s;y++)for(var x=0;x<s;x++){
|
||||
if(y%4===0||(y<8?x%8===0:(x+4)%8===0))ctx.fillStyle='#bcac9c';
|
||||
else{var c=['#8e4438','#994c40','#7a3830','#a05548'];ctx.fillStyle=c[Math.floor(srand(x+1.5,y+1.5)*c.length)];}
|
||||
ctx.fillRect(x,y,1,1);
|
||||
}
|
||||
});
|
||||
drawTex(16,function(ctx,s){fill(ctx,s,['#c8e8ff','#b0d8f8','#d0eeff','#a8d0f0']);});
|
||||
drawTex(17,function(ctx,s){fill(ctx,s,['#ff4400','#ff6600','#ee3300','#ff5500','#dd2200']);});
|
||||
var tex=new THREE.CanvasTexture(atlasCanvas);
|
||||
tex.magFilter=THREE.NearestFilter;
|
||||
tex.minFilter=THREE.NearestFilter;
|
||||
tex.colorSpace=THREE.SRGBColorSpace;
|
||||
return tex;
|
||||
}
|
||||
|
||||
var FT={};
|
||||
FT[B.GRASS]={top:0,side:1,bottom:2};FT[B.DIRT]={top:2,side:2,bottom:2};
|
||||
FT[B.STONE]={top:3,side:3,bottom:3};FT[B.SAND]={top:4,side:4,bottom:4};
|
||||
FT[B.WATER]={top:16,side:16,bottom:16};FT[B.WOOD]={top:6,side:5,bottom:6};
|
||||
FT[B.LEAVES]={top:7,side:7,bottom:7};FT[B.SNOW]={top:8,side:9,bottom:2};
|
||||
FT[B.OBSIDIAN]={top:10,side:10,bottom:10};FT[B.GOLD]={top:11,side:11,bottom:11};
|
||||
FT[B.CRYSTAL]={top:12,side:12,bottom:12};FT[B.BEDROCK]={top:13,side:13,bottom:13};
|
||||
FT[B.BONE]={top:14,side:14,bottom:14};FT[B.BRICK]={top:15,side:15,bottom:15};
|
||||
FT[B.GLASS]={top:16,side:16,bottom:16};FT[B.LAVA]={top:17,side:17,bottom:17};
|
||||
|
||||
function texUV(idx){
|
||||
var u0=(idx%16)/16,v0=1-(Math.floor(idx/16)+1)/16;
|
||||
return{u0:u0,v0:v0,u1:u0+1/16,v1:v0+1/16};
|
||||
}
|
||||
|
||||
var FACES=[
|
||||
{dir:[1,0,0],verts:[[1,0,0],[1,1,0],[1,1,1],[1,0,1]],norm:[1,0,0],face:'side'},
|
||||
{dir:[-1,0,0],verts:[[0,0,1],[0,1,1],[0,1,0],[0,0,0]],norm:[-1,0,0],face:'side'},
|
||||
{dir:[0,1,0],verts:[[0,1,1],[1,1,1],[1,1,0],[0,1,0]],norm:[0,1,0],face:'top'},
|
||||
{dir:[0,-1,0],verts:[[0,0,0],[1,0,0],[1,0,1],[0,0,1]],norm:[0,-1,0],face:'bottom'},
|
||||
{dir:[0,0,1],verts:[[1,0,1],[1,1,1],[0,1,1],[0,0,1]],norm:[0,0,1],face:'side'},
|
||||
{dir:[0,0,-1],verts:[[0,0,0],[0,1,0],[1,1,0],[1,0,0]],norm:[0,0,-1],face:'side'}
|
||||
];
|
||||
|
||||
function Chunk(cx,cz,world){
|
||||
this.cx=cx;this.cz=cz;this.world=world;
|
||||
this.blocks=new Uint8Array(CHUNK*CHUNK_H*CHUNK);
|
||||
this.mesh=null;this.waterMesh=null;this.dirty=true;this.generated=false;
|
||||
}
|
||||
Chunk.prototype.idx=function(x,y,z){return(y*CHUNK+z)*CHUNK+x;};
|
||||
Chunk.prototype.getBlock=function(x,y,z){
|
||||
if(x<0||x>=CHUNK||z<0||z>=CHUNK||y<0||y>=CHUNK_H)return B.AIR;
|
||||
return this.blocks[this.idx(x,y,z)];
|
||||
};
|
||||
Chunk.prototype.setBlock=function(x,y,z,t){
|
||||
if(x<0||x>=CHUNK||z<0||z>=CHUNK||y<0||y>=CHUNK_H)return;
|
||||
this.blocks[this.idx(x,y,z)]=t;this.dirty=true;
|
||||
};
|
||||
Chunk.prototype.generate=function(){
|
||||
if(this.generated)return;this.generated=true;
|
||||
var ox=this.cx*CHUNK,oz=this.cz*CHUNK;
|
||||
for(var lx=0;lx<CHUNK;lx++)for(var lz=0;lz<CHUNK;lz++){
|
||||
var wx=ox+lx,wz=oz+lz,h=terrainH(wx,wz),biome=getBiome(wx,wz);
|
||||
this.blocks[this.idx(lx,0,lz)]=B.BEDROCK;
|
||||
for(var y=1;y<CHUNK_H;y++){
|
||||
if(y<=h){
|
||||
if(isCave(wx,y,wz)&&y>1&&y<h-1)this.blocks[this.idx(lx,y,lz)]=B.AIR;
|
||||
else if(y===h){
|
||||
if(biome==='desert')this.blocks[this.idx(lx,y,lz)]=B.SAND;
|
||||
else if(biome==='tundra')this.blocks[this.idx(lx,y,lz)]=B.SNOW;
|
||||
else this.blocks[this.idx(lx,y,lz)]=B.GRASS;
|
||||
}else if(y>h-4){
|
||||
this.blocks[this.idx(lx,y,lz)]=biome==='desert'?B.SAND:B.DIRT;
|
||||
}else this.blocks[this.idx(lx,y,lz)]=B.STONE;
|
||||
}else if(y<=SEA)this.blocks[this.idx(lx,y,lz)]=B.WATER;
|
||||
}
|
||||
}
|
||||
for(var lx=0;lx<CHUNK;lx++)for(var lz=0;lz<CHUNK;lz++){
|
||||
var wx=ox+lx,wz=oz+lz;
|
||||
if(!shouldTree(wx,wz))continue;
|
||||
var h=terrainH(wx,wz);if(h<=SEA)continue;
|
||||
var th=treeH(wx,wz);
|
||||
for(var y=h+1;y<=h+th&&y<CHUNK_H;y++)this.blocks[this.idx(lx,y,lz)]=B.WOOD;
|
||||
var cb=h+th-1,ct=h+th+2;
|
||||
for(var dy=cb;dy<=ct;dy++){
|
||||
var r=dy<=h+th?2:1;
|
||||
for(var dx=-r;dx<=r;dx++)for(var dz=-r;dz<=r;dz++){
|
||||
if(dx===0&&dz===0&&dy<=h+th)continue;
|
||||
if(Math.abs(dx)===r&&Math.abs(dz)===r&&srand(wx+dx,wz+dz)>.6)continue;
|
||||
var nx=lx+dx,nz=lz+dz;
|
||||
if(nx>=0&&nx<CHUNK&&nz>=0&&nz<CHUNK&&dy<CHUNK_H&&this.blocks[this.idx(nx,dy,nz)]===B.AIR)
|
||||
this.blocks[this.idx(nx,dy,nz)]=B.LEAVES;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.applyStructures(ox,oz);
|
||||
};
|
||||
Chunk.prototype.applyStructures=function(ox,oz){
|
||||
for(var si=0;si<STRUCTURES.length;si++){
|
||||
var s=STRUCTURES[si];
|
||||
if(ox+CHUNK<=s.minX||ox>s.maxX||oz+CHUNK<=s.minZ||oz>s.maxZ)continue;
|
||||
for(var lx=0;lx<CHUNK;lx++){
|
||||
var wx=ox+lx;if(wx<s.minX||wx>s.maxX)continue;
|
||||
for(var lz=0;lz<CHUNK;lz++){
|
||||
var wz=oz+lz;if(wz<s.minZ||wz>s.maxZ)continue;
|
||||
for(var wy=s.minY;wy<=s.maxY;wy++){
|
||||
if(wy<0||wy>=CHUNK_H)continue;
|
||||
var bt=s.gen(wx,wy,wz);
|
||||
if(bt>0)this.blocks[this.idx(lx,wy,lz)]=bt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Chunk.prototype.getNeighborBlock=function(lx,y,lz){
|
||||
if(lx>=0&&lx<CHUNK&&lz>=0&&lz<CHUNK){
|
||||
if(y<0||y>=CHUNK_H)return B.AIR;
|
||||
return this.blocks[this.idx(lx,y,lz)];
|
||||
}
|
||||
return this.world.getBlock(this.cx*CHUNK+lx,y,this.cz*CHUNK+lz);
|
||||
};
|
||||
Chunk.prototype.computeAO=function(x,y,z,face){
|
||||
var ao=[0,0,0,0],d=face.dir,t1,t2;
|
||||
if(d[0]!==0){t1=[0,1,0];t2=[0,0,1];}
|
||||
else if(d[1]!==0){t1=[1,0,0];t2=[0,0,1];}
|
||||
else{t1=[1,0,0];t2=[0,1,0];}
|
||||
var self=this;
|
||||
function s(dx,dy,dz){return isSolid(self.getNeighborBlock(x+d[0]+dx,y+d[1]+dy,z+d[2]+dz))?1:0;}
|
||||
var e0=s(-t1[0],-t1[1],-t1[2]),e1=s(t1[0],t1[1],t1[2]);
|
||||
var e2=s(-t2[0],-t2[1],-t2[2]),e3=s(t2[0],t2[1],t2[2]);
|
||||
var s00=s(-t1[0]-t2[0],-t1[1]-t2[1],-t1[2]-t2[2]);
|
||||
var s10=s(t1[0]-t2[0],t1[1]-t2[1],t1[2]-t2[2]);
|
||||
var s01=s(-t1[0]+t2[0],-t1[1]+t2[1],-t1[2]+t2[2]);
|
||||
var s11=s(t1[0]+t2[0],t1[1]+t2[1],t1[2]+t2[2]);
|
||||
function vao(a,b,c){return(a&&b)?0:(3-(a+b+c))/3;}
|
||||
ao[0]=vao(e0,e2,s00);ao[1]=vao(e1,e2,s10);ao[2]=vao(e1,e3,s11);ao[3]=vao(e0,e3,s01);
|
||||
return ao;
|
||||
};
|
||||
Chunk.prototype.buildMesh=function(mat,wmat){
|
||||
if(!this.dirty)return;this.dirty=false;
|
||||
var pos=[],norms=[],uvs=[],cols=[],idx=[];
|
||||
var wp=[],wn=[],wu=[],wc=[],wi=[];
|
||||
for(var y=0;y<CHUNK_H;y++)for(var z=0;z<CHUNK;z++)for(var x=0;x<CHUNK;x++){
|
||||
var bt=this.blocks[this.idx(x,y,z)];if(bt===B.AIR)continue;
|
||||
var isW=bt===B.WATER,ft=FT[bt];if(!ft)continue;
|
||||
for(var fi=0;fi<6;fi++){
|
||||
var f=FACES[fi];
|
||||
var nx=x+f.dir[0],ny=y+f.dir[1],nz=z+f.dir[2];
|
||||
var nb=this.getNeighborBlock(nx,ny,nz);
|
||||
var show=isW?(nb===B.AIR||(nb!==B.WATER&&TRANS.has(nb))):TRANS.has(nb);
|
||||
if(!show)continue;
|
||||
var tp=isW?wp:pos,tn=isW?wn:norms,tu=isW?wu:uvs,tc=isW?wc:cols,ti=isW?wi:idx;
|
||||
var vc=tp.length/3;
|
||||
var uv=texUV(ft[f.face]);
|
||||
var ao=isW?[1,1,1,1]:this.computeAO(x,y,z,f);
|
||||
for(var vi=0;vi<4;vi++){
|
||||
var v=f.verts[vi];tp.push(x+v[0],y+v[1],z+v[2]);
|
||||
tn.push(f.norm[0],f.norm[1],f.norm[2]);
|
||||
var a=0.4+0.6*ao[vi];tc.push(a,a,a);
|
||||
}
|
||||
tu.push(uv.u0,uv.v0,uv.u1,uv.v0,uv.u1,uv.v1,uv.u0,uv.v1);
|
||||
if(ao[0]+ao[2]>ao[1]+ao[3])ti.push(vc,vc+1,vc+2,vc,vc+2,vc+3);
|
||||
else ti.push(vc+1,vc+2,vc+3,vc+1,vc+3,vc);
|
||||
}
|
||||
}
|
||||
if(this.mesh){this.world.scene.remove(this.mesh);this.mesh.geometry.dispose();}
|
||||
if(this.waterMesh){this.world.scene.remove(this.waterMesh);this.waterMesh.geometry.dispose();}
|
||||
this.mesh=null;this.waterMesh=null;
|
||||
if(pos.length>0){
|
||||
var g=new THREE.BufferGeometry();
|
||||
g.setAttribute('position',new THREE.Float32BufferAttribute(pos,3));
|
||||
g.setAttribute('normal',new THREE.Float32BufferAttribute(norms,3));
|
||||
g.setAttribute('uv',new THREE.Float32BufferAttribute(uvs,2));
|
||||
g.setAttribute('color',new THREE.Float32BufferAttribute(cols,3));
|
||||
g.setIndex(idx);
|
||||
this.mesh=new THREE.Mesh(g,mat);
|
||||
this.mesh.position.set(this.cx*CHUNK,0,this.cz*CHUNK);
|
||||
this.world.scene.add(this.mesh);
|
||||
}
|
||||
if(wp.length>0){
|
||||
var g=new THREE.BufferGeometry();
|
||||
g.setAttribute('position',new THREE.Float32BufferAttribute(wp,3));
|
||||
g.setAttribute('normal',new THREE.Float32BufferAttribute(wn,3));
|
||||
g.setAttribute('uv',new THREE.Float32BufferAttribute(wu,2));
|
||||
g.setAttribute('color',new THREE.Float32BufferAttribute(wc,3));
|
||||
g.setIndex(wi);
|
||||
this.waterMesh=new THREE.Mesh(g,wmat);
|
||||
this.waterMesh.position.set(this.cx*CHUNK,0,this.cz*CHUNK);
|
||||
this.waterMesh.renderOrder=1;
|
||||
this.world.scene.add(this.waterMesh);
|
||||
}
|
||||
};
|
||||
Chunk.prototype.dispose=function(){
|
||||
if(this.mesh){this.world.scene.remove(this.mesh);this.mesh.geometry.dispose();this.mesh=null;}
|
||||
if(this.waterMesh){this.world.scene.remove(this.waterMesh);this.waterMesh.geometry.dispose();this.waterMesh=null;}
|
||||
};
|
||||
|
||||
var STRUCTURES=[];
|
||||
function buildStructures(){
|
||||
var cY=80;
|
||||
STRUCTURES.push({
|
||||
minX:-25,maxX:25,minY:cY-5,maxY:cY+45,minZ:50,maxZ:100,
|
||||
gen:function(wx,wy,wz){
|
||||
var x=wx,y=wy,z=wz-75,ax=Math.abs(x),az=Math.abs(z);
|
||||
if(y>=cY-5&&y<=cY-1&&ax<=22&&az<=22)return B.OBSIDIAN;
|
||||
if(y>=cY&&y<=cY+1&&ax<=20&&az<=20)return B.STONE;
|
||||
if(y>=cY+2&&y<=cY+14&&ax<=16&&az<=16&&(ax>=15||az>=15)){
|
||||
if(y===cY+14&&(ax+az)%2===0)return B.STONE;
|
||||
if(y<cY+14){
|
||||
if(y>=cY+5&&y<=cY+7&&az>=15&&ax<3)return 0;
|
||||
return B.STONE;
|
||||
}
|
||||
}
|
||||
var towers=[[-14,-14],[14,-14],[-14,14],[14,14]];
|
||||
for(var ti=0;ti<4;ti++){
|
||||
var tx=towers[ti][0],tz=towers[ti][1];
|
||||
var dx=x-tx,dz2=z-tz,r2=dx*dx+dz2*dz2;
|
||||
if(r2<=20&&y>=cY+2&&y<=cY+22)return B.BRICK;
|
||||
if(r2<=30&&y>cY+22&&y<=cY+24)return B.STONE;
|
||||
if(r2<=5&&y>cY+24&&y<=cY+32)return B.CRYSTAL;
|
||||
if(r2<=1&&y>cY+32&&y<=cY+35)return B.GOLD;
|
||||
}
|
||||
if(ax<=7&&az<=7&&y>=cY+2&&y<=cY+18){
|
||||
if(ax>=6||az>=6)return B.BRICK;
|
||||
if(y===cY+2)return B.GOLD;
|
||||
if(y===cY+18&&(ax+az)%2===0)return B.GOLD;
|
||||
}
|
||||
if(y>=cY-8&&y<cY-5&&ax<=22&&az<=22){
|
||||
if((ax+az)%5===0||srand(wx*.1,wz*.1)>.92)return B.CRYSTAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
var tX=70,tZ=-10,tB=30;
|
||||
STRUCTURES.push({
|
||||
minX:tX-30,maxX:tX+30,minY:tB-5,maxY:tB+75,minZ:tZ-30,maxZ:tZ+30,
|
||||
gen:function(wx,wy,wz){
|
||||
var dx=wx-tX,dz=wz-tZ,d2=dx*dx+dz*dz,y=wy;
|
||||
if(y>=tB&&y<=tB+55){
|
||||
var tr=Math.max(1,5-Math.floor((y-tB)/15));
|
||||
if(d2<=tr*tr)return B.WOOD;
|
||||
if(y<=tB+5&&d2<=tr*tr+4&&srand(wx,wz)>.3)return B.WOOD;
|
||||
}
|
||||
for(var i=0;i<6;i++){
|
||||
var angle=(i/6)*Math.PI*2+0.5;
|
||||
var bY=tB+25+i*5,bL=12-i;
|
||||
for(var d=3;d<bL;d++){
|
||||
var bx=tX+Math.round(Math.cos(angle)*d);
|
||||
var bz=tZ+Math.round(Math.sin(angle)*d);
|
||||
var by=bY+Math.round(d*0.25);
|
||||
if(Math.abs(wx-bx)<=1&&Math.abs(wz-bz)<=1&&Math.abs(wy-by)<=0)
|
||||
if(Math.abs(wx-bx)+Math.abs(wz-bz)<=1)return B.WOOD;
|
||||
}
|
||||
}
|
||||
var cc=tB+50,cdy=(y-cc)*1.3,cr2=22*22;
|
||||
if(d2+cdy*cdy<=cr2&&y>tB+35){
|
||||
if(noise.noise3(wx*.15,wy*.15,wz*.15)<.3)return B.LEAVES;
|
||||
}
|
||||
if(y>=tB&&y<=tB+55&&d2<=36&&srand(wx*7.1+wy*3.3,wz*5.7)>.97)return B.CRYSTAL;
|
||||
if(y>=tB-5&&y<tB){
|
||||
for(var i=0;i<8;i++){
|
||||
var a=(i/8)*Math.PI*2,rl=12+Math.floor(srand(i,0)*6);
|
||||
for(var d=4;d<rl;d++){
|
||||
var rx=tX+Math.round(Math.cos(a)*d),rz=tZ+Math.round(Math.sin(a)*d);
|
||||
var ry=tB-Math.floor((d-4)*.4);
|
||||
if(Math.abs(wx-rx)<=1&&Math.abs(wz-rz)<=1&&wy===ry)return B.WOOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
var dX=-55,dZ=55,dY=38;
|
||||
STRUCTURES.push({
|
||||
minX:dX-10,maxX:dX+45,minY:dY-3,maxY:dY+18,minZ:dZ-18,maxZ:dZ+18,
|
||||
gen:function(wx,wy,wz){
|
||||
var lx=wx-dX,ly=wy-dY,lz=wz-dZ;
|
||||
if(lx>=0&&lx<=38){
|
||||
var sy=Math.round(Math.sin(lx*.08)*2),sz=Math.round(Math.sin(lx*.12)*1.5);
|
||||
if(ly===sy&&Math.abs(lz-sz)<=0)return B.BONE;
|
||||
if(lx<35&&ly===sy+1&&lz===sz&&lx%3===0)return B.BONE;
|
||||
}
|
||||
if(lx>=-5&&lx<=3&&ly>=0&&ly<=5&&Math.abs(lz)<=3){
|
||||
if(ly===3&&lx===-3&&(lz===1||lz===-1))return B.LAVA;
|
||||
if(ly>=0&&ly<=4&&lx>=-5&&lx<=1&&(Math.abs(lz)===2||ly===0||ly===4||lx===-5||lx===1))return B.BONE;
|
||||
if(lx<=-3&&ly>=1&&ly<=3&&Math.abs(lz)<=1)return B.BONE;
|
||||
}
|
||||
for(var i=0;i<7;i++){
|
||||
var rx=6+i*4;
|
||||
if(lx===rx||lx===rx+1){
|
||||
var sy2=Math.round(Math.sin(rx*.08)*2),sz2=Math.round(Math.sin(rx*.12)*1.5);
|
||||
for(var r=0;r<=5;r++){
|
||||
var ry=sy2+Math.round(Math.sin(r*Math.PI/5)*5);
|
||||
var rz=sz2+Math.round(Math.cos(r*Math.PI/5)*4);
|
||||
if(ly===ry&&(lz===rz||lz===sz2*2-rz))return B.BONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(lx>=10&&lx<=12){
|
||||
for(var d=0;d<14;d++){
|
||||
var wy2=dY+Math.round(Math.sin(10*.08)*2)+3+Math.round(d*.3);
|
||||
var wz2=dZ+(lz>0?d+4:-d-4);
|
||||
if(Math.abs(wy-wy2)<=0&&Math.abs(wz-wz2)<=0&&lx>=10&&lx<=11)return B.BONE;
|
||||
}
|
||||
}
|
||||
if(lx>=32&&lx<=42){
|
||||
var ti=lx-32;
|
||||
var ty=Math.round(Math.sin(ti*.3)*2),tz=Math.round(Math.sin(ti*.2)*3);
|
||||
if(ly===ty&&Math.abs(lz-tz)<=(ti<5?1:0))return B.BONE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
var sX=-10,sZ=-65;
|
||||
STRUCTURES.push({
|
||||
minX:sX-12,maxX:sX+12,minY:25,maxY:90,minZ:sZ-12,maxZ:sZ+12,
|
||||
gen:function(wx,wy,wz){
|
||||
var spires=[[0,0,35],[-6,-4,25],[5,-6,28],[-4,7,22],[7,5,18]];
|
||||
for(var i=0;i<spires.length;i++){
|
||||
var sp=spires[i],sdx=wx-(sX+sp[0]),sdz=wz-(sZ+sp[1]),r2=sdx*sdx+sdz*sdz;
|
||||
var baseY=terrainH(sX+sp[0],sZ+sp[1]),topY=baseY+sp[2];
|
||||
if(wy>=baseY&&wy<=topY){
|
||||
var t=(wy-baseY)/sp[2],maxR=(1-t)*3+.5;
|
||||
if(r2<=maxR*maxR)return B.CRYSTAL;
|
||||
}
|
||||
}
|
||||
var ddx=wx-sX,ddz=wz-sZ;
|
||||
if(ddx*ddx+ddz*ddz<=64){
|
||||
var bY=terrainH(wx,wz);
|
||||
if((wy===bY+1||wy===bY+2)&&srand(wx*3.3,wz*7.7)>.85)return B.CRYSTAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
var rX=-75,rZ=-35,rY=32;
|
||||
STRUCTURES.push({
|
||||
minX:rX-18,maxX:rX+18,minY:rY,maxY:rY+14,minZ:rZ-18,maxZ:rZ+18,
|
||||
gen:function(wx,wy,wz){
|
||||
var lx=wx-rX,lz=wz-rZ,ly=wy-rY;
|
||||
if(ly===0&&Math.abs(lx)<=15&&Math.abs(lz)<=15){
|
||||
if(Math.abs(lx)>=14||Math.abs(lz)>=14)return B.STONE;
|
||||
if(Math.abs(lx)<=2&&Math.abs(lz)<=2)return B.GOLD;
|
||||
}
|
||||
if(Math.abs(lx)<=15&&Math.abs(lz)<=15&&(Math.abs(lx)>=13||Math.abs(lz)>=13)){
|
||||
var wh=Math.floor(srand(wx*.5,wz*.5)*6)+2;
|
||||
if(ly>0&&ly<=wh&&srand(wx*1.1+ly*2.2,wz*3.3)>.15)return B.STONE;
|
||||
}
|
||||
var pills=[[-10,-10],[10,-10],[-10,10],[10,10],[-10,0],[10,0],[0,-10],[0,10]];
|
||||
for(var i=0;i<pills.length;i++){
|
||||
var px=pills[i][0],pz=pills[i][1];
|
||||
if(Math.abs(lx-px)<=1&&Math.abs(lz-pz)<=1){
|
||||
var ph=Math.floor(srand(px+100,pz+100)*5)+6;
|
||||
if(ly>0&&ly<=ph)return B.STONE;
|
||||
}
|
||||
}
|
||||
if(Math.abs(lx)<=2&&Math.abs(lz)<=2&&ly>=1&&ly<=3){
|
||||
if(ly<=2)return B.OBSIDIAN;return B.GOLD;
|
||||
}
|
||||
if(ly>=1&&ly<=3&&Math.abs(lx)<=15&&Math.abs(lz)<=15&&srand(wx*2.2,wz*4.4+ly)>.96)return B.LEAVES;
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
var pX=30,pZ=-45;
|
||||
STRUCTURES.push({
|
||||
minX:pX-6,maxX:pX+6,minY:25,maxY:60,minZ:pZ-2,maxZ:pZ+2,
|
||||
gen:function(wx,wy,wz){
|
||||
var lx=wx-pX,lz=wz-pZ,baseY=terrainH(pX,pZ),ly=wy-baseY;
|
||||
if(Math.abs(lz)<=1){
|
||||
if(Math.abs(lx)===5&&ly>=0&&ly<=18)return B.OBSIDIAN;
|
||||
if(ly===0&&Math.abs(lx)<=5)return B.OBSIDIAN;
|
||||
if(ly===18&&Math.abs(lx)<=5)return B.OBSIDIAN;
|
||||
if(ly>=1&&ly<=17&&Math.abs(lx)<5&&lz===0)return B.CRYSTAL;
|
||||
if(ly===19&&Math.abs(lx)<=3&&lz===0)return B.GOLD;
|
||||
}
|
||||
if(ly===-1&&Math.abs(lx)<=6&&Math.abs(lz)<=2)return B.STONE;
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
buildStructures();
|
||||
|
||||
function World(scene){
|
||||
this.scene=scene;this.chunks=new Map();
|
||||
}
|
||||
World.prototype.key=function(cx,cz){return cx+','+cz;};
|
||||
World.prototype.getChunk=function(cx,cz){return this.chunks.get(this.key(cx,cz));};
|
||||
World.prototype.getBlock=function(wx,wy,wz){
|
||||
if(wy<0||wy>=CHUNK_H)return B.AIR;
|
||||
var cx=Math.floor(wx/CHUNK),cz=Math.floor(wz/CHUNK);
|
||||
var c=this.getChunk(cx,cz);if(!c||!c.generated)return B.AIR;
|
||||
var lx=((wx%CHUNK)+CHUNK)%CHUNK,lz=((wz%CHUNK)+CHUNK)%CHUNK;
|
||||
return c.getBlock(lx,wy,lz);
|
||||
};
|
||||
World.prototype.setBlock=function(wx,wy,wz,type){
|
||||
if(wy<0||wy>=CHUNK_H)return;
|
||||
var cx=Math.floor(wx/CHUNK),cz=Math.floor(wz/CHUNK);
|
||||
var c=this.getChunk(cx,cz);if(!c)return;
|
||||
var lx=((wx%CHUNK)+CHUNK)%CHUNK,lz=((wz%CHUNK)+CHUNK)%CHUNK;
|
||||
c.setBlock(lx,wy,lz,type);
|
||||
if(lx===0){var n=this.getChunk(cx-1,cz);if(n)n.dirty=true;}
|
||||
if(lx===CHUNK-1){var n=this.getChunk(cx+1,cz);if(n)n.dirty=true;}
|
||||
if(lz===0){var n=this.getChunk(cx,cz-1);if(n)n.dirty=true;}
|
||||
if(lz===CHUNK-1){var n=this.getChunk(cx,cz+1);if(n)n.dirty=true;}
|
||||
};
|
||||
World.prototype.update=function(px,pz,mat,wmat){
|
||||
var pcx=Math.floor(px/CHUNK),pcz=Math.floor(pz/CHUNK);
|
||||
var self=this;
|
||||
this.chunks.forEach(function(chunk,key){
|
||||
var dx=chunk.cx-pcx,dz=chunk.cz-pcz;
|
||||
if(Math.abs(dx)>RDIST+1||Math.abs(dz)>RDIST+1){chunk.dispose();self.chunks.delete(key);}
|
||||
});
|
||||
var toGen=[];
|
||||
for(var dx=-RDIST;dx<=RDIST;dx++)for(var dz=-RDIST;dz<=RDIST;dz++){
|
||||
if(dx*dx+dz*dz>RDIST*RDIST)continue;
|
||||
var cx=pcx+dx,cz=pcz+dz,k=this.key(cx,cz);
|
||||
if(!this.chunks.has(k))toGen.push({cx:cx,cz:cz,d:dx*dx+dz*dz});
|
||||
}
|
||||
toGen.sort(function(a,b){return a.d-b.d;});
|
||||
var gc=0;
|
||||
for(var i=0;i<toGen.length&&gc<3;i++){
|
||||
var g=toGen[i],chunk=new Chunk(g.cx,g.cz,this);
|
||||
chunk.generate();this.chunks.set(this.key(g.cx,g.cz),chunk);
|
||||
var adj=[[-1,0],[1,0],[0,-1],[0,1]];
|
||||
for(var j=0;j<4;j++){var a=this.getChunk(g.cx+adj[j][0],g.cz+adj[j][1]);if(a)a.dirty=true;}
|
||||
gc++;
|
||||
}
|
||||
var mc=0;
|
||||
this.chunks.forEach(function(chunk){
|
||||
if(mc>=4)return;
|
||||
if(chunk.dirty&&chunk.generated){chunk.buildMesh(mat,wmat);mc++;}
|
||||
});
|
||||
};
|
||||
|
||||
function Player(camera){
|
||||
this.camera=camera;
|
||||
this.pos=new THREE.Vector3(0,60,0);
|
||||
this.vel=new THREE.Vector3();
|
||||
this.yaw=0;this.pitch=0;this.onGround=false;
|
||||
this.keys={};this.speed=6;this.sens=0.002;this.locked=false;
|
||||
var self=this;
|
||||
document.addEventListener('keydown',function(e){self.keys[e.code]=true;if(e.code==='Space')e.preventDefault();});
|
||||
document.addEventListener('keyup',function(e){self.keys[e.code]=false;});
|
||||
document.addEventListener('mousemove',function(e){
|
||||
if(!self.locked)return;
|
||||
self.yaw-=e.movementX*self.sens;
|
||||
self.pitch-=e.movementY*self.sens;
|
||||
self.pitch=Math.max(-Math.PI/2+.01,Math.min(Math.PI/2-.01,self.pitch));
|
||||
});
|
||||
}
|
||||
Player.prototype.update=function(dt,world){
|
||||
var fw=new THREE.Vector3(-Math.sin(this.yaw),0,-Math.cos(this.yaw));
|
||||
var rt=new THREE.Vector3(Math.cos(this.yaw),0,-Math.sin(this.yaw));
|
||||
var mv=new THREE.Vector3();
|
||||
if(this.keys['KeyW'])mv.add(fw);if(this.keys['KeyS'])mv.sub(fw);
|
||||
if(this.keys['KeyA'])mv.sub(rt);if(this.keys['KeyD'])mv.add(rt);
|
||||
if(mv.lengthSq()>0)mv.normalize().multiplyScalar(this.speed);
|
||||
this.vel.x=mv.x;this.vel.z=mv.z;
|
||||
this.vel.y-=GRAV*dt;
|
||||
if(this.keys['Space']&&this.onGround){this.vel.y=JVEL;this.onGround=false;}
|
||||
var steps=4,sdt=dt/steps;
|
||||
for(var s=0;s<steps;s++){
|
||||
this.pos.x+=this.vel.x*sdt;this.collide('x',world);
|
||||
this.pos.y+=this.vel.y*sdt;this.collide('y',world);
|
||||
this.pos.z+=this.vel.z*sdt;this.collide('z',world);
|
||||
}
|
||||
if(this.pos.y<-10){this.pos.set(0,60,0);this.vel.set(0,0,0);}
|
||||
this.camera.position.set(this.pos.x,this.pos.y+PH,this.pos.z);
|
||||
var euler=new THREE.Euler(this.pitch,this.yaw,0,'YXZ');
|
||||
this.camera.quaternion.setFromEuler(euler);
|
||||
};
|
||||
Player.prototype.collide=function(axis,world){
|
||||
var hw=PW,hh=PH+.1;
|
||||
var mnx=Math.floor(this.pos.x-hw),mxx=Math.floor(this.pos.x+hw);
|
||||
var mny=Math.floor(this.pos.y),mxy=Math.floor(this.pos.y+hh);
|
||||
var mnz=Math.floor(this.pos.z-hw),mxz=Math.floor(this.pos.z+hw);
|
||||
for(var bx=mnx;bx<=mxx;bx++)for(var by=mny;by<=mxy;by++)for(var bz=mnz;bz<=mxz;bz++){
|
||||
if(!isSolid(world.getBlock(bx,by,bz)))continue;
|
||||
var ov=this.overlap(bx,by,bz);if(!ov)continue;
|
||||
if(axis==='x'){this.pos.x+=ov.x;this.vel.x=0;}
|
||||
else if(axis==='y'){this.pos.y+=ov.y;if(ov.y>0){this.onGround=true;this.vel.y=0;}else if(ov.y<0)this.vel.y=0;}
|
||||
else{this.pos.z+=ov.z;this.vel.z=0;}
|
||||
}
|
||||
};
|
||||
Player.prototype.overlap=function(bx,by,bz){
|
||||
var hw=PW,hh=PH+.1;
|
||||
var ox=Math.min(this.pos.x+hw-bx,bx+1-(this.pos.x-hw));
|
||||
var oy=Math.min(this.pos.y+hh-by,by+1-this.pos.y);
|
||||
var oz=Math.min(this.pos.z+hw-bz,bz+1-(this.pos.z-hw));
|
||||
if(ox<=0||oy<=0||oz<=0)return null;
|
||||
var mn=Math.min(ox,oy,oz),r={x:0,y:0,z:0};
|
||||
if(mn===ox)r.x=(this.pos.x<bx+.5)?-ox:ox;
|
||||
else if(mn===oy)r.y=(this.pos.y+hh/2<by+.5)?-oy:oy;
|
||||
else r.z=(this.pos.z<bz+.5)?-oz:oz;
|
||||
return r;
|
||||
};
|
||||
Player.prototype.getForward=function(){
|
||||
return new THREE.Vector3(-Math.sin(this.yaw)*Math.cos(this.pitch),Math.sin(this.pitch),-Math.cos(this.yaw)*Math.cos(this.pitch)).normalize();
|
||||
};
|
||||
|
||||
function castRay(origin,dir,world,maxD){
|
||||
var x=Math.floor(origin.x),y=Math.floor(origin.y),z=Math.floor(origin.z);
|
||||
var sx=dir.x>=0?1:-1,sy=dir.y>=0?1:-1,sz=dir.z>=0?1:-1;
|
||||
var tmx=dir.x!==0?((dir.x>0?x+1:x)-origin.x)/dir.x:1e9;
|
||||
var tmy=dir.y!==0?((dir.y>0?y+1:y)-origin.y)/dir.y:1e9;
|
||||
var tmz=dir.z!==0?((dir.z>0?z+1:z)-origin.z)/dir.z:1e9;
|
||||
var tdx=dir.x!==0?Math.abs(1/dir.x):1e9;
|
||||
var tdy=dir.y!==0?Math.abs(1/dir.y):1e9;
|
||||
var tdz=dir.z!==0?Math.abs(1/dir.z):1e9;
|
||||
var px=x,py=y,pz=z,dist=0;
|
||||
while(dist<maxD){
|
||||
var bt=world.getBlock(x,y,z);
|
||||
if(isSolid(bt))return{hit:true,x:x,y:y,z:z,px:px,py:py,pz:pz,block:bt};
|
||||
px=x;py=y;pz=z;
|
||||
if(tmx<tmy){
|
||||
if(tmx<tmz){dist=tmx;x+=sx;tmx+=tdx;}
|
||||
else{dist=tmz;z+=sz;tmz+=tdz;}
|
||||
}else{
|
||||
if(tmy<tmz){dist=tmy;y+=sy;tmy+=tdy;}
|
||||
else{dist=tmz;z+=sz;tmz+=tdz;}
|
||||
}
|
||||
}
|
||||
return{hit:false};
|
||||
}
|
||||
|
||||
var scene=new THREE.Scene();
|
||||
scene.background=new THREE.Color(0x87CEEB);
|
||||
scene.fog=new THREE.Fog(0x87CEEB,CHUNK*RDIST*0.6,CHUNK*RDIST*0.95);
|
||||
|
||||
var camera=new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,CHUNK*RDIST*1.2);
|
||||
var renderer=new THREE.WebGLRenderer({antialias:false});
|
||||
renderer.setSize(window.innerWidth,window.innerHeight);
|
||||
renderer.setPixelRatio(Math.min(window.devicePixelRatio,2));
|
||||
renderer.outputColorSpace=THREE.SRGBColorSpace;
|
||||
document.body.prepend(renderer.domElement);
|
||||
|
||||
scene.add(new THREE.HemisphereLight(0x87CEEB,0x6B4C30,0.6));
|
||||
var sun=new THREE.DirectionalLight(0xfff5e0,1.0);
|
||||
sun.position.set(50,100,30);scene.add(sun);
|
||||
scene.add(new THREE.AmbientLight(0x404060,0.35));
|
||||
|
||||
var tex=buildAtlas();
|
||||
var mat=new THREE.MeshLambertMaterial({map:tex,vertexColors:true,side:THREE.FrontSide});
|
||||
var wmat=new THREE.MeshLambertMaterial({map:tex,vertexColors:true,transparent:true,opacity:0.6,side:THREE.DoubleSide,depthWrite:false});
|
||||
|
||||
var world=new World(scene);
|
||||
var player=new Player(camera);
|
||||
|
||||
var hlBox=new THREE.LineSegments(
|
||||
new THREE.EdgesGeometry(new THREE.BoxGeometry(1.005,1.005,1.005)),
|
||||
new THREE.LineBasicMaterial({color:0x000000,linewidth:2,transparent:true,opacity:0.5})
|
||||
);
|
||||
hlBox.visible=false;scene.add(hlBox);
|
||||
|
||||
var selectedSlot=0;
|
||||
|
||||
function setupHotbar(){
|
||||
var hb=document.getElementById('hotbar');hb.innerHTML='';
|
||||
for(var i=0;i<HOTBAR.length;i++){
|
||||
var slot=document.createElement('div');
|
||||
slot.className='hslot'+(i===0?' sel':'');
|
||||
var num=document.createElement('span');num.className='num';num.textContent=i+1;
|
||||
slot.appendChild(num);
|
||||
var c=document.createElement('canvas');c.width=c.height=16;
|
||||
var ctx=c.getContext('2d');
|
||||
var ft=FT[HOTBAR[i]],ti=ft.top;
|
||||
var sx=(ti%16)*16,sy=Math.floor(ti/16)*16;
|
||||
ctx.drawImage(atlasCanvas,sx,sy,16,16,0,0,16,16);
|
||||
slot.appendChild(c);
|
||||
(function(idx){slot.addEventListener('click',function(e){e.stopPropagation();selectedSlot=idx;updateHotbar();});})(i);
|
||||
hb.appendChild(slot);
|
||||
}
|
||||
}
|
||||
function updateHotbar(){
|
||||
var slots=document.querySelectorAll('.hslot');
|
||||
for(var i=0;i<slots.length;i++)slots[i].className=i===selectedSlot?'hslot sel':'hslot';
|
||||
}
|
||||
|
||||
function spawnPlayer(){
|
||||
var sx=0,sz=0,h=terrainH(sx,sz);
|
||||
for(var si=0;si<STRUCTURES.length;si++){
|
||||
var s=STRUCTURES[si];
|
||||
if(sx>=s.minX&&sx<=s.maxX&&sz>=s.minZ&&sz<=s.maxZ)
|
||||
for(var y=s.maxY;y>=s.minY;y--)if(s.gen(sx,y,sz)>0){h=Math.max(h,y+1);break;}
|
||||
}
|
||||
player.pos.set(sx,h+2,sz);
|
||||
}
|
||||
|
||||
function rebuildNearby(wx,wy,wz){
|
||||
var cx=Math.floor(wx/CHUNK),cz=Math.floor(wz/CHUNK);
|
||||
for(var dx=-1;dx<=1;dx++)for(var dz=-1;dz<=1;dz++){
|
||||
var c=world.getChunk(cx+dx,cz+dz);if(c)c.dirty=true;
|
||||
}
|
||||
}
|
||||
|
||||
var overlay=document.getElementById('overlay');
|
||||
var canvas=renderer.domElement;
|
||||
|
||||
overlay.addEventListener('click',function(){canvas.requestPointerLock();});
|
||||
document.addEventListener('pointerlockchange',function(){
|
||||
player.locked=document.pointerLockElement===canvas;
|
||||
overlay.className=player.locked?'hidden':'';
|
||||
});
|
||||
|
||||
canvas.addEventListener('mousedown',function(e){
|
||||
if(!player.locked)return;
|
||||
var origin=camera.position.clone(),dir=player.getForward();
|
||||
var hit=castRay(origin,dir,world,REACH);
|
||||
if(e.button===0&&hit.hit){
|
||||
world.setBlock(hit.x,hit.y,hit.z,B.AIR);
|
||||
rebuildNearby(hit.x,hit.y,hit.z);
|
||||
}else if(e.button===2&&hit.hit){
|
||||
var ppx=Math.floor(player.pos.x),ppy=Math.floor(player.pos.y),ppz=Math.floor(player.pos.z);
|
||||
var ppy2=Math.floor(player.pos.y+PH);
|
||||
if(hit.px===ppx&&hit.pz===ppz&&(hit.py===ppy||hit.py===ppy2))return;
|
||||
world.setBlock(hit.px,hit.py,hit.pz,HOTBAR[selectedSlot]);
|
||||
rebuildNearby(hit.px,hit.py,hit.pz);
|
||||
}
|
||||
});
|
||||
canvas.addEventListener('contextmenu',function(e){e.preventDefault();});
|
||||
document.addEventListener('keydown',function(e){
|
||||
var n=parseInt(e.key);if(n>=1&&n<=9){selectedSlot=n-1;updateHotbar();}
|
||||
});
|
||||
canvas.addEventListener('wheel',function(e){
|
||||
if(!player.locked)return;
|
||||
selectedSlot=(selectedSlot+(e.deltaY>0?1:-1)+HOTBAR.length)%HOTBAR.length;
|
||||
updateHotbar();
|
||||
});
|
||||
window.addEventListener('resize',function(){
|
||||
camera.aspect=window.innerWidth/window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth,window.innerHeight);
|
||||
});
|
||||
|
||||
setupHotbar();
|
||||
spawnPlayer();
|
||||
|
||||
var clock=new THREE.Clock();
|
||||
function animate(){
|
||||
requestAnimationFrame(animate);
|
||||
var dt=Math.min(clock.getDelta(),0.05);
|
||||
player.update(dt,world);
|
||||
world.update(player.pos.x,player.pos.z,mat,wmat);
|
||||
var origin=camera.position.clone(),dir=player.getForward();
|
||||
var hit=castRay(origin,dir,world,REACH);
|
||||
if(hit.hit){hlBox.position.set(hit.x+.5,hit.y+.5,hit.z+.5);hlBox.visible=true;}
|
||||
else hlBox.visible=false;
|
||||
var p=player.pos;
|
||||
var dbg=document.getElementById('debug');
|
||||
dbg.textContent='XYZ: '+p.x.toFixed(1)+' / '+p.y.toFixed(1)+' / '+p.z.toFixed(1)+
|
||||
'\nChunk: '+Math.floor(p.x/CHUNK)+', '+Math.floor(p.z/CHUNK)+' Loaded: '+world.chunks.size+
|
||||
'\nBlock: '+BNAME[HOTBAR[selectedSlot]];
|
||||
renderer.render(scene,camera);
|
||||
}
|
||||
animate();
|
||||
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Whitespace-only changes.
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<title>Mythic Realms — A Voxel Pilgrimage</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="probe">init</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,628 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600" width="100%" height="100%">
|
||||
<defs>
|
||||
<!-- ========== ???? ========== -->
|
||||
<linearGradient id="skyGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#16264d"/>
|
||||
<stop offset="22%" stop-color="#3f5d8e"/>
|
||||
<stop offset="48%" stop-color="#c66e58"/>
|
||||
<stop offset="72%" stop-color="#f4ad7a"/>
|
||||
<stop offset="100%" stop-color="#fde2b8"/>
|
||||
</linearGradient>
|
||||
|
||||
<radialGradient id="sunGlow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#fff7d8" stop-opacity="0.95"/>
|
||||
<stop offset="30%" stop-color="#ffd56b" stop-opacity="0.55"/>
|
||||
<stop offset="65%" stop-color="#ff8a3a" stop-opacity="0.18"/>
|
||||
<stop offset="100%" stop-color="#ff5e2a" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="sunCore" cx="40%" cy="40%" r="60%">
|
||||
<stop offset="0%" stop-color="#fffae6"/>
|
||||
<stop offset="55%" stop-color="#ffe9a3"/>
|
||||
<stop offset="100%" stop-color="#ffbe5e"/>
|
||||
</radialGradient>
|
||||
|
||||
<linearGradient id="seaGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#7da7c4"/>
|
||||
<stop offset="35%" stop-color="#33648c"/>
|
||||
<stop offset="100%" stop-color="#0d2c4d"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="wetSandGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#caa57e"/>
|
||||
<stop offset="100%" stop-color="#8a6741"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="sandGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#f6d8a1"/>
|
||||
<stop offset="55%" stop-color="#dab177"/>
|
||||
<stop offset="100%" stop-color="#a47c4e"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="bodyGrad" x1="0.2" y1="0" x2="0.7" y2="1">
|
||||
<stop offset="0%" stop-color="#ffffff"/>
|
||||
<stop offset="55%" stop-color="#f2ecdc"/>
|
||||
<stop offset="100%" stop-color="#b3a482"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="bellyGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#ffffff"/>
|
||||
<stop offset="100%" stop-color="#e8e0c8"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="wingGrad" x1="0" y1="0" x2="0.4" y2="1">
|
||||
<stop offset="0%" stop-color="#d9c8a0"/>
|
||||
<stop offset="55%" stop-color="#8d7549"/>
|
||||
<stop offset="100%" stop-color="#3f3018"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="beakGrad" x1="0" y1="0" x2="0.3" y2="1">
|
||||
<stop offset="0%" stop-color="#ffe287"/>
|
||||
<stop offset="50%" stop-color="#f29d28"/>
|
||||
<stop offset="100%" stop-color="#a05810"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="pouchGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#ffd9a0"/>
|
||||
<stop offset="55%" stop-color="#ed984c"/>
|
||||
<stop offset="100%" stop-color="#a8521e"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="footGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#ffb350"/>
|
||||
<stop offset="100%" stop-color="#b85e10"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="frameGrad" x1="0" y1="0" x2="0.3" y2="1">
|
||||
<stop offset="0%" stop-color="#5dd9e8"/>
|
||||
<stop offset="55%" stop-color="#1ba5ba"/>
|
||||
<stop offset="100%" stop-color="#0e6473"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="scarfGrad" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0%" stop-color="#ff5142"/>
|
||||
<stop offset="100%" stop-color="#a51914"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="cloudGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#ffd09a" stop-opacity="0.9"/>
|
||||
<stop offset="100%" stop-color="#c66e58" stop-opacity="0.55"/>
|
||||
</linearGradient>
|
||||
|
||||
<radialGradient id="tireGrad" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="85%" stop-color="#2a2a2a"/>
|
||||
<stop offset="100%" stop-color="#000000"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="hubGrad" cx="40%" cy="40%" r="60%">
|
||||
<stop offset="0%" stop-color="#e8e8e8"/>
|
||||
<stop offset="60%" stop-color="#888"/>
|
||||
<stop offset="100%" stop-color="#2f2f2f"/>
|
||||
</radialGradient>
|
||||
|
||||
<linearGradient id="basketGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#d9a868"/>
|
||||
<stop offset="100%" stop-color="#6f4419"/>
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient id="fishGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#88cdee"/>
|
||||
<stop offset="100%" stop-color="#2a6c92"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- ============ ?? ============ -->
|
||||
<rect width="800" height="385" fill="url(#skyGrad)"/>
|
||||
|
||||
<!-- ??????? -->
|
||||
<circle cx="560" cy="260" r="200" fill="url(#sunGlow)"/>
|
||||
<circle cx="560" cy="260" r="62" fill="url(#sunCore)"/>
|
||||
<circle cx="551" cy="250" r="22" fill="#fffdf0" opacity="0.7"/>
|
||||
|
||||
<!-- ?? -->
|
||||
<g>
|
||||
<ellipse cx="120" cy="90" rx="85" ry="11" fill="url(#cloudGrad)"/>
|
||||
<ellipse cx="175" cy="108" rx="55" ry="8" fill="url(#cloudGrad)" opacity="0.75"/>
|
||||
<ellipse cx="660" cy="80" rx="95" ry="13" fill="url(#cloudGrad)" opacity="0.7"/>
|
||||
<ellipse cx="730" cy="95" rx="55" ry="8" fill="url(#cloudGrad)" opacity="0.5"/>
|
||||
<ellipse cx="380" cy="160" rx="120" ry="12" fill="#ffd0a2" opacity="0.35"/>
|
||||
<ellipse cx="90" cy="200" rx="100" ry="9" fill="#ffb98a" opacity="0.4"/>
|
||||
<ellipse cx="720" cy="220" rx="80" ry="7" fill="#ffba80" opacity="0.35"/>
|
||||
<ellipse cx="200" cy="280" rx="90" ry="6" fill="#ffaa70" opacity="0.4"/>
|
||||
<ellipse cx="620" cy="320" rx="100" ry="6" fill="#ffaa70" opacity="0.4"/>
|
||||
</g>
|
||||
|
||||
<!-- ???? -->
|
||||
<g stroke="#3b2818" stroke-width="1.6" fill="none" stroke-linecap="round" opacity="0.6">
|
||||
<path d="M 90 130 q 7 -7 14 0 q 7 -7 14 0"/>
|
||||
<path d="M 135 148 q 5 -5 10 0 q 5 -5 10 0"/>
|
||||
<path d="M 220 115 q 9 -9 18 0 q 9 -9 18 0"/>
|
||||
<path d="M 260 135 q 5 -5 10 0 q 5 -5 10 0"/>
|
||||
<path d="M 705 175 q 5 -5 10 0 q 5 -5 10 0"/>
|
||||
<path d="M 735 195 q 4 -4 8 0 q 4 -4 8 0"/>
|
||||
</g>
|
||||
|
||||
<!-- ??????? -->
|
||||
<g transform="translate(40, 385)" opacity="0.92">
|
||||
<path d="M -10 0 L 25 -48 L 80 -48 L 100 0 Z" fill="#3d4e66"/>
|
||||
<path d="M 25 -48 L 80 -48 L 82 -42 L 23 -42 Z" fill="#2c394d"/>
|
||||
<rect x="45" y="-82" width="14" height="34" fill="#e8e0d0"/>
|
||||
<rect x="45" y="-82" width="14" height="6" fill="#b8362a"/>
|
||||
<rect x="45" y="-68" width="14" height="6" fill="#b8362a"/>
|
||||
<rect x="45" y="-54" width="14" height="6" fill="#b8362a"/>
|
||||
<polygon points="43,-82 61,-82 52,-92" fill="#5a2a25"/>
|
||||
<circle cx="52" cy="-60" r="2.5" fill="#fff8a0"/>
|
||||
<circle cx="52" cy="-60" r="6" fill="#fff8a0" opacity="0.3"/>
|
||||
</g>
|
||||
|
||||
<!-- ???? -->
|
||||
<g transform="translate(700, 388)" opacity="0.92">
|
||||
<path d="M 0 8 L 32 8 L 26 14 L 6 14 Z" fill="#2a1e15"/>
|
||||
<path d="M 16 8 L 16 -30 L 29 6 Z" fill="#fff8ee"/>
|
||||
<path d="M 14 8 L 14 -14 L 4 6 Z" fill="#ece2c8"/>
|
||||
<line x1="16" y1="-32" x2="16" y2="8" stroke="#2a1e15" stroke-width="1.2"/>
|
||||
<path d="M 6 15 L 26 15 L 22 19 L 10 19 Z" fill="#2a1e15" opacity="0.3"/>
|
||||
</g>
|
||||
|
||||
<!-- ????? -->
|
||||
<g opacity="0.55">
|
||||
<path d="M 280 385 q 30 -22 70 -18 q 35 5 60 18 Z" fill="#4a5a72"/>
|
||||
<path d="M 295 380 q 25 -12 55 -8 q 30 4 45 8" stroke="#2c394d" stroke-width="1" fill="none"/>
|
||||
</g>
|
||||
|
||||
<!-- ============ ?? ============ -->
|
||||
<rect y="385" width="800" height="80" fill="url(#seaGrad)"/>
|
||||
|
||||
<!-- ?????????? -->
|
||||
<g fill="#ffeaa0" opacity="0.85">
|
||||
<rect x="530" y="387" width="60" height="3" rx="1.5"/>
|
||||
<rect x="520" y="394" width="80" height="3" rx="1.5"/>
|
||||
<rect x="535" y="401" width="50" height="3" rx="1.5"/>
|
||||
<rect x="512" y="408" width="92" height="3" rx="1.5"/>
|
||||
<rect x="540" y="415" width="40" height="3" rx="1.5"/>
|
||||
<rect x="525" y="422" width="68" height="3" rx="1.5"/>
|
||||
<rect x="545" y="429" width="30" height="3" rx="1.5"/>
|
||||
<rect x="535" y="436" width="48" height="3" rx="1.5"/>
|
||||
<rect x="550" y="443" width="20" height="3" rx="1.5"/>
|
||||
<rect x="540" y="450" width="38" height="3" rx="1.5"/>
|
||||
<rect x="555" y="457" width="18" height="3" rx="1.5"/>
|
||||
</g>
|
||||
|
||||
<!-- ???? -->
|
||||
<g stroke="#ffffff" stroke-width="1.4" fill="none" stroke-linecap="round" opacity="0.5">
|
||||
<path d="M 30 400 q 25 -3 50 0 q 25 3 50 0"/>
|
||||
<path d="M 200 410 q 30 -4 60 0 q 30 4 60 0"/>
|
||||
<path d="M 370 405 q 20 -3 40 0 q 20 3 40 0"/>
|
||||
<path d="M 660 400 q 25 -3 50 0 q 25 3 50 0"/>
|
||||
<path d="M 100 428 q 25 -3 50 0 q 25 3 50 0"/>
|
||||
<path d="M 290 432 q 30 -4 60 0 q 30 4 60 0"/>
|
||||
<path d="M 660 432 q 25 -3 50 0 q 25 3 50 0"/>
|
||||
<path d="M 50 450 q 22 -3 44 0 q 22 3 44 0"/>
|
||||
<path d="M 250 455 q 22 -3 44 0 q 22 3 44 0"/>
|
||||
</g>
|
||||
|
||||
<!-- ????? -->
|
||||
<line x1="0" y1="385" x2="800" y2="385" stroke="#ffd6a0" stroke-width="1.2" opacity="0.85"/>
|
||||
|
||||
<!-- ============ ?? ============ -->
|
||||
<!-- ??????????? -->
|
||||
<path d="M 0 444 q 200 -12 400 -4 t 400 16 L 800 500 L 0 500 Z" fill="url(#wetSandGrad)"/>
|
||||
|
||||
<!-- ????? -->
|
||||
<path d="M 0 444 q 200 -12 400 -4 t 400 16" stroke="#ffffff" stroke-width="5" fill="none" stroke-linecap="round" opacity="0.9"/>
|
||||
<path d="M 0 452 q 200 -8 400 -1 t 400 14" stroke="#ffffff" stroke-width="2.8" fill="none" stroke-linecap="round" opacity="0.55"/>
|
||||
<g fill="#ffffff" opacity="0.8">
|
||||
<circle cx="120" cy="446" r="2"/>
|
||||
<circle cx="240" cy="444" r="1.5"/>
|
||||
<circle cx="360" cy="443" r="2"/>
|
||||
<circle cx="480" cy="448" r="1.5"/>
|
||||
<circle cx="620" cy="452" r="2"/>
|
||||
<circle cx="710" cy="458" r="1.5"/>
|
||||
<circle cx="70" cy="449" r="1.5"/>
|
||||
<circle cx="180" cy="448" r="1.2"/>
|
||||
<circle cx="300" cy="445" r="1.2"/>
|
||||
<circle cx="550" cy="450" r="1.2"/>
|
||||
</g>
|
||||
|
||||
<!-- ???????? -->
|
||||
<path d="M 0 500 q 400 -10 800 4 L 800 600 L 0 600 Z" fill="url(#sandGrad)"/>
|
||||
|
||||
<!-- ???? -->
|
||||
<g fill="#a8814e" opacity="0.55">
|
||||
<circle cx="50" cy="528" r="1.2"/><circle cx="120" cy="540" r="1"/>
|
||||
<circle cx="180" cy="552" r="1.3"/><circle cx="80" cy="548" r="0.8"/>
|
||||
<circle cx="200" cy="538" r="1"/><circle cx="540" cy="545" r="1.2"/>
|
||||
<circle cx="600" cy="555" r="1"/><circle cx="660" cy="540" r="1.3"/>
|
||||
<circle cx="700" cy="555" r="0.9"/><circle cx="750" cy="548" r="1.1"/>
|
||||
<circle cx="630" cy="528" r="0.9"/><circle cx="40" cy="560" r="1"/>
|
||||
<circle cx="160" cy="532" r="0.8"/><circle cx="230" cy="552" r="1"/>
|
||||
<circle cx="280" cy="560" r="0.9"/><circle cx="520" cy="535" r="1"/>
|
||||
<circle cx="780" cy="556" r="1.1"/>
|
||||
<circle cx="100" cy="572" r="1"/><circle cx="320" cy="580" r="1.2"/>
|
||||
<circle cx="480" cy="575" r="1"/><circle cx="690" cy="580" r="1.1"/>
|
||||
</g>
|
||||
<g fill="#7a5b2e" opacity="0.4">
|
||||
<circle cx="90" cy="540" r="0.7"/><circle cx="150" cy="550" r="0.8"/>
|
||||
<circle cx="210" cy="554" r="0.7"/><circle cx="570" cy="540" r="0.8"/>
|
||||
<circle cx="640" cy="552" r="0.7"/><circle cx="690" cy="568" r="0.8"/>
|
||||
<circle cx="60" cy="555" r="0.7"/><circle cx="730" cy="538" r="0.7"/>
|
||||
<circle cx="380" cy="580" r="0.8"/><circle cx="440" cy="570" r="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- ???? -->
|
||||
<g transform="translate(640, 545)">
|
||||
<path d="M 0 0 q 9 -11 18 0 q -2 5 -9 5 q -7 0 -9 -5 Z" fill="#fceedd" stroke="#cd9466" stroke-width="0.8"/>
|
||||
<path d="M 5 0 q 4 -6 8 0 M 7 0 q 2 -3 4 0" stroke="#cd9466" stroke-width="0.6" fill="none"/>
|
||||
</g>
|
||||
<g transform="translate(720, 535)">
|
||||
<ellipse rx="7" ry="4.5" fill="#f5d4b8" stroke="#b07a4a" stroke-width="0.7"/>
|
||||
<path d="M -3 0 q 3 -3 6 0 M -2 1 q 2 -2 4 0" stroke="#b07a4a" stroke-width="0.5" fill="none"/>
|
||||
</g>
|
||||
<g transform="translate(70, 555) rotate(18)">
|
||||
<path d="M 0 0 L 0 -9 L 5 -7 L 5 2 Z" fill="#e8a86c" stroke="#8e5a28" stroke-width="0.6"/>
|
||||
<path d="M 0 -4 L 5 -3" stroke="#8e5a28" stroke-width="0.4"/>
|
||||
</g>
|
||||
|
||||
<!-- ?? -->
|
||||
<g transform="translate(195, 562)" fill="#e87a3a" stroke="#a83a1a" stroke-width="0.7">
|
||||
<path d="M 0 -8 L 2 -2 L 8 -2 L 3 2 L 5 8 L 0 4 L -5 8 L -3 2 L -8 -2 L -2 -2 Z"/>
|
||||
<circle cx="0" cy="0" r="1.5" fill="#a83a1a"/>
|
||||
</g>
|
||||
|
||||
<!-- ??? -->
|
||||
<g>
|
||||
<ellipse cx="450" cy="560" rx="5" ry="2.5" fill="#9b8b6a" opacity="0.8"/>
|
||||
<ellipse cx="350" cy="555" rx="4" ry="2" fill="#7a5e3e" opacity="0.7"/>
|
||||
<ellipse cx="555" cy="565" rx="3" ry="1.5" fill="#9b8b6a" opacity="0.6"/>
|
||||
</g>
|
||||
|
||||
<!-- ============ ????? ============ -->
|
||||
<ellipse cx="380" cy="555" rx="210" ry="10" fill="#5a3a1a" opacity="0.32"/>
|
||||
<ellipse cx="380" cy="555" rx="180" ry="6" fill="#3a2510" opacity="0.4"/>
|
||||
|
||||
<!-- ??????? -->
|
||||
<g opacity="0.55">
|
||||
<ellipse cx="170" cy="555" rx="22" ry="6" fill="#d9b078"/>
|
||||
<ellipse cx="140" cy="548" rx="14" ry="4" fill="#d9b078" opacity="0.85"/>
|
||||
<ellipse cx="115" cy="538" rx="10" ry="3" fill="#d9b078" opacity="0.7"/>
|
||||
<circle cx="192" cy="538" r="3" fill="#d9b078"/>
|
||||
<circle cx="162" cy="528" r="2.2" fill="#d9b078" opacity="0.8"/>
|
||||
<circle cx="138" cy="525" r="1.8" fill="#d9b078" opacity="0.7"/>
|
||||
<circle cx="100" cy="522" r="1.5" fill="#d9b078" opacity="0.6"/>
|
||||
</g>
|
||||
|
||||
<!-- ????? -->
|
||||
<g opacity="0.5">
|
||||
<path d="M 50 558 q 60 -3 110 0" stroke="#7a5028" stroke-width="3" fill="none"/>
|
||||
<path d="M 80 565 q 50 -3 90 0" stroke="#7a5028" stroke-width="2" fill="none"/>
|
||||
</g>
|
||||
|
||||
<!-- ============ ?? ============ -->
|
||||
<g transform="translate(240, 500)">
|
||||
<circle r="55" fill="url(#tireGrad)"/>
|
||||
<circle r="48" fill="#101010"/>
|
||||
<circle r="46" stroke="#5a5a5a" stroke-width="1.2" fill="#1a1a1a"/>
|
||||
<circle r="42" fill="#c8c8c8"/>
|
||||
<!-- ?????????? -->
|
||||
<circle r="38" stroke="#9a9a9a" stroke-dasharray="2 7" stroke-width="36" fill="none" opacity="0.35"/>
|
||||
<circle r="35" stroke="#fff" stroke-dasharray="1 12" stroke-width="34" fill="none" opacity="0.18"/>
|
||||
<circle r="9" fill="url(#hubGrad)"/>
|
||||
<circle r="4" fill="#1a1a1a"/>
|
||||
<g stroke="#3a3a3a" stroke-width="1" opacity="0.7">
|
||||
<path d="M -53 -8 l 4 0 M -53 0 l 4 0 M -53 8 l 4 0"/>
|
||||
<path d="M 49 -8 l 4 0 M 49 0 l 4 0 M 49 8 l 4 0"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- ============ ?????????? ============ -->
|
||||
<g>
|
||||
<path d="M 355 415 q -8 26 -22 38 q -8 7 -18 6" stroke="#b3a482" stroke-width="14" fill="none" stroke-linecap="round" opacity="0.9"/>
|
||||
<g transform="translate(315, 461) rotate(-18)">
|
||||
<path d="M 0 0 q -22 -4 -28 4 q 4 8 28 4 Z" fill="url(#footGrad)" stroke="#7a4810" stroke-width="0.8" opacity="0.9"/>
|
||||
<path d="M -8 1 q -2 5 1 6 M -16 1 q -2 5 1 6 M -22 2 q -2 4 0 5" stroke="#7a4810" stroke-width="0.6" fill="none"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- ============ ???? ============ -->
|
||||
<g>
|
||||
<!-- ??? -->
|
||||
<line x1="350" y1="500" x2="244" y2="500" stroke="url(#frameGrad)" stroke-width="9" stroke-linecap="round"/>
|
||||
<!-- ??? -->
|
||||
<line x1="340" y1="395" x2="244" y2="500" stroke="url(#frameGrad)" stroke-width="9" stroke-linecap="round"/>
|
||||
<!-- ?? -->
|
||||
<line x1="340" y1="395" x2="350" y2="500" stroke="url(#frameGrad)" stroke-width="11" stroke-linecap="round"/>
|
||||
<!-- ?? -->
|
||||
<line x1="350" y1="500" x2="500" y2="418" stroke="url(#frameGrad)" stroke-width="11" stroke-linecap="round"/>
|
||||
<!-- ??????????? -->
|
||||
<path d="M 340 397 Q 410 425, 497 414" stroke="url(#frameGrad)" stroke-width="11" fill="none" stroke-linecap="round"/>
|
||||
<!-- ?? -->
|
||||
<line x1="500" y1="418" x2="520" y2="500" stroke="url(#frameGrad)" stroke-width="9" stroke-linecap="round"/>
|
||||
|
||||
<!-- ?????? -->
|
||||
<line x1="349" y1="497" x2="245" y2="497" stroke="#bff2f8" stroke-width="1.5" opacity="0.8"/>
|
||||
<line x1="350" y1="498" x2="498" y2="416" stroke="#bff2f8" stroke-width="1.5" opacity="0.8"/>
|
||||
<path d="M 342 399 Q 410 426, 495 412" stroke="#bff2f8" stroke-width="1.2" fill="none" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- ???? -->
|
||||
<path d="M 186 500 a 54 54 0 0 1 108 0" stroke="url(#frameGrad)" stroke-width="6" fill="none" stroke-linecap="round"/>
|
||||
|
||||
<!-- ????? -->
|
||||
<g>
|
||||
<circle cx="350" cy="500" r="15" fill="#3a3a3a"/>
|
||||
<circle cx="350" cy="500" r="13" fill="#5a5a5a"/>
|
||||
<circle cx="350" cy="500" r="4" fill="#1a1a1a"/>
|
||||
<g stroke="#888" stroke-width="1" fill="none">
|
||||
<line x1="350" y1="483" x2="350" y2="480"/>
|
||||
<line x1="363" y1="488" x2="366" y2="485"/>
|
||||
<line x1="365" y1="500" x2="368" y2="500"/>
|
||||
<line x1="363" y1="512" x2="366" y2="515"/>
|
||||
<line x1="350" y1="517" x2="350" y2="520"/>
|
||||
<line x1="337" y1="512" x2="334" y2="515"/>
|
||||
<line x1="335" y1="500" x2="332" y2="500"/>
|
||||
<line x1="337" y1="488" x2="334" y2="485"/>
|
||||
</g>
|
||||
<path d="M 350 485 q 60 -32 145 -65" stroke="#444" stroke-width="2" fill="none" opacity="0.85"/>
|
||||
<path d="M 350 515 q 60 -22 145 -55" stroke="#444" stroke-width="2" fill="none" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- ????? -->
|
||||
<g>
|
||||
<line x1="350" y1="500" x2="378" y2="520" stroke="#222" stroke-width="5" stroke-linecap="round"/>
|
||||
<line x1="350" y1="500" x2="322" y2="480" stroke="#222" stroke-width="5" stroke-linecap="round"/>
|
||||
<rect x="372" y="517" width="16" height="7" rx="1.5" fill="#1a1a1a"/>
|
||||
<rect x="370" y="517" width="2" height="7" fill="#5a5a5a"/>
|
||||
<rect x="312" y="475" width="16" height="7" rx="1.5" fill="#1a1a1a"/>
|
||||
</g>
|
||||
|
||||
<!-- ?? -->
|
||||
<line x1="340" y1="395" x2="335" y2="380" stroke="#3a3a3a" stroke-width="5" stroke-linecap="round"/>
|
||||
|
||||
<!-- ????? -->
|
||||
<g transform="translate(335, 378)">
|
||||
<path d="M -24 -3 q -3 -9 11 -11 q 16 -2 23 1 q 9 3 -5 13 q -12 6 -29 -3 Z" fill="#4d2914"/>
|
||||
<path d="M -24 -3 q -3 -9 11 -11 q 16 -2 23 1" stroke="#8a4f25" stroke-width="0.8" fill="none"/>
|
||||
<ellipse cx="-3" cy="-4" rx="15" ry="3" fill="#7a4220" opacity="0.7"/>
|
||||
<ellipse cx="-3" cy="-6" rx="8" ry="1.5" fill="#a06030" opacity="0.5"/>
|
||||
</g>
|
||||
|
||||
<!-- ============ ????????? ============ -->
|
||||
<g>
|
||||
<!-- ?? -->
|
||||
<g transform="translate(303, 425)">
|
||||
<path d="M 0 0 L -54 18 L -28 -22 Z" fill="#b3a482"/>
|
||||
<path d="M 0 -4 L -62 -6 L -32 -34 Z" fill="#e8dfc8"/>
|
||||
<path d="M 0 -8 L -58 -28 L -28 -40 Z" fill="#fafaf0"/>
|
||||
<g stroke="#9b8b6a" stroke-width="0.6" opacity="0.65" fill="none">
|
||||
<path d="M -8 -4 L -45 -14"/>
|
||||
<path d="M -8 -10 L -45 -28"/>
|
||||
<path d="M -5 0 L -40 12"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- ????????? -->
|
||||
<path d="M 305 425
|
||||
C 268 397, 270 337, 322 320
|
||||
C 388 304, 438 340, 438 396
|
||||
C 438 428, 388 442, 348 438 Z" fill="url(#bodyGrad)"/>
|
||||
|
||||
<!-- ???? -->
|
||||
<path d="M 308 425 C 300 410, 308 390, 332 383 C 372 376, 408 395, 418 415 C 400 432, 360 438, 320 435 Z" fill="url(#bellyGrad)" opacity="0.85"/>
|
||||
|
||||
<!-- ????? -->
|
||||
<path d="M 380 322 C 420 336, 438 365, 438 396 C 432 422, 410 435, 388 437 C 420 422, 432 388, 422 352 Z" fill="#9b8b6a" opacity="0.28"/>
|
||||
|
||||
<!-- ?????? -->
|
||||
<g stroke="#c5b896" stroke-width="0.5" fill="none" opacity="0.5">
|
||||
<path d="M 290 378 q 25 -8 50 -5"/>
|
||||
<path d="M 290 393 q 28 -6 56 -3"/>
|
||||
<path d="M 295 408 q 30 -4 60 0"/>
|
||||
</g>
|
||||
|
||||
<!-- S ???? -->
|
||||
<path d="M 362 328
|
||||
C 410 318, 425 290, 408 258
|
||||
C 390 233, 395 215, 420 208"
|
||||
stroke="url(#bodyGrad)" stroke-width="38" fill="none" stroke-linecap="round"/>
|
||||
|
||||
<!-- ???? -->
|
||||
<path d="M 368 330
|
||||
C 410 318, 422 290, 405 260
|
||||
C 388 235, 395 220, 415 213"
|
||||
stroke="#c5b896" stroke-width="7" fill="none" stroke-linecap="round" opacity="0.5"/>
|
||||
<!-- ???? -->
|
||||
<path d="M 358 326
|
||||
C 400 314, 414 288, 400 258"
|
||||
stroke="#ffffff" stroke-width="4" fill="none" stroke-linecap="round" opacity="0.6"/>
|
||||
|
||||
<!-- ?????????? -->
|
||||
<g>
|
||||
<path d="M 400 226 q -12 5 -28 -7 q 20 -10 35 -5 Z" fill="url(#scarfGrad)"/>
|
||||
<path d="M 392 235 q -50 -3 -82 18 q 32 -1 86 -6 Z" fill="url(#scarfGrad)"/>
|
||||
<path d="M 392 245 q -55 2 -100 32 q 45 -3 105 -12 Z" fill="#a01515" opacity="0.92"/>
|
||||
<path d="M 388 238 q -42 4 -70 22 q 30 -2 75 -8 Z" fill="#ff6e58" opacity="0.55"/>
|
||||
<path d="M 350 253 q 8 4 0 10 M 320 266 q 8 4 0 10" stroke="#7a0e0e" stroke-width="0.8" fill="none" opacity="0.7"/>
|
||||
</g>
|
||||
|
||||
<!-- ?? -->
|
||||
<ellipse cx="423" cy="198" rx="34" ry="29" fill="url(#bodyGrad)"/>
|
||||
<ellipse cx="431" cy="206" rx="22" ry="20" fill="#b3a482" opacity="0.3"/>
|
||||
<ellipse cx="411" cy="186" rx="14" ry="10" fill="#ffffff" opacity="0.75"/>
|
||||
|
||||
<!-- ???? -->
|
||||
<path d="M 408 174 q 3 -10 9 -9 q -1 7 -1 13 Z" fill="#e8dfc8"/>
|
||||
<path d="M 419 170 q 5 -9 11 -5 q -3 7 -4 11 Z" fill="#e8dfc8"/>
|
||||
<path d="M 429 172 q 6 -7 10 -3 q -3 5 -4 8 Z" fill="#d9c8a0"/>
|
||||
|
||||
<!-- ?? -->
|
||||
<ellipse cx="435" cy="196" rx="6" ry="5.5" fill="#3a2818"/>
|
||||
|
||||
<!-- ????? -->
|
||||
<g>
|
||||
<path d="M 417 190 q 17 -7 32 -2 L 449 204 q -14 7 -30 2 Z" fill="#0d0d0d"/>
|
||||
<path d="M 417 190 q 17 -7 32 -2 L 449 204 q -14 7 -30 2 Z" fill="none" stroke="#444" stroke-width="1.2"/>
|
||||
<path d="M 423 194 q 8 -3 16 -1" stroke="#7ed0f0" stroke-width="1.8" fill="none" opacity="0.9"/>
|
||||
<path d="M 435 200 q 3 -1 6 -0.5" stroke="#fff" stroke-width="1" fill="none" opacity="0.75"/>
|
||||
<line x1="449" y1="198" x2="456" y2="196" stroke="#1a1a1a" stroke-width="1.6"/>
|
||||
<line x1="417" y1="192" x2="409" y2="188" stroke="#1a1a1a" stroke-width="1.5"/>
|
||||
</g>
|
||||
|
||||
<!-- ???? -->
|
||||
<path d="M 453 194 Q 545 204, 585 235 Q 565 240, 475 233 Q 451 230, 449 213 Z" fill="url(#beakGrad)"/>
|
||||
<path d="M 455 200 Q 540 208, 575 230 Q 545 225, 475 220 Q 455 215, 455 204 Z" fill="#ffd86b" opacity="0.6"/>
|
||||
<path d="M 465 211 Q 535 215, 577 235" stroke="#a85a10" stroke-width="0.9" fill="none" opacity="0.75"/>
|
||||
<ellipse cx="475" cy="211" rx="2.2" ry="1.2" fill="#5a2a08"/>
|
||||
<path d="M 580 237 q 8 4 0 9 q -10 -1 -12 -7 Z" fill="#8e4810"/>
|
||||
|
||||
<!-- ?? -->
|
||||
<path d="M 449 225
|
||||
Q 473 256, 528 260
|
||||
Q 568 256, 580 237
|
||||
Q 533 248, 481 238
|
||||
Q 461 233, 449 225 Z" fill="url(#pouchGrad)"/>
|
||||
<path d="M 458 233 q 40 22 80 21" stroke="#a8521e" stroke-width="0.9" fill="none" opacity="0.7"/>
|
||||
<path d="M 465 239 q 38 18 72 18" stroke="#a8521e" stroke-width="0.7" fill="none" opacity="0.55"/>
|
||||
<path d="M 473 245 q 35 14 65 14" stroke="#a8521e" stroke-width="0.6" fill="none" opacity="0.45"/>
|
||||
<path d="M 473 230 q 30 14 60 14" stroke="#fff" stroke-width="1.3" fill="none" opacity="0.45"/>
|
||||
|
||||
<path d="M 449 225 Q 533 248, 580 237" stroke="#7e3c08" stroke-width="1.2" fill="none" opacity="0.6"/>
|
||||
|
||||
<!-- ????????????? -->
|
||||
<g transform="translate(510, 385)">
|
||||
<path d="M 0 0 q -12 -7 -20 -4 q 0 6 0 12 q 8 0 20 -2 Z" fill="url(#fishGrad)"/>
|
||||
<path d="M -20 -4 L -28 -10 L -28 14 L -20 8 Z" fill="#2a6c92"/>
|
||||
<path d="M -28 -10 L -26 0 L -28 14" stroke="#88cdee" stroke-width="0.7" fill="none"/>
|
||||
<circle cx="-4" cy="2" r="1.2" fill="#1a1a1a"/>
|
||||
<circle cx="-3.7" cy="1.8" r="0.5" fill="#fff"/>
|
||||
<path d="M -12 5 q 6 1 10 0" stroke="#1a4a6a" stroke-width="0.5" fill="none"/>
|
||||
<path d="M -14 -3 q 3 0 3 3 M -8 -3 q 3 0 3 3 M -14 3 q 3 0 3 3 M -8 3 q 3 0 3 3" stroke="#1a4a6a" stroke-width="0.3" fill="none" opacity="0.6"/>
|
||||
</g>
|
||||
|
||||
<!-- ???? -->
|
||||
<path d="M 352 348
|
||||
C 392 338, 425 358, 445 396
|
||||
C 432 425, 388 425, 360 405
|
||||
C 342 390, 338 368, 352 348 Z" fill="url(#wingGrad)"/>
|
||||
|
||||
<!-- ?????? -->
|
||||
<g fill="#6e5a32" opacity="0.6">
|
||||
<path d="M 360 365 q 22 6 50 24 q -10 3 -25 0 q -18 -8 -30 -18 Z"/>
|
||||
<path d="M 364 385 q 22 6 56 22 q -12 4 -26 1 q -22 -8 -32 -19 Z"/>
|
||||
<path d="M 370 403 q 25 6 60 16 q -12 4 -25 2 q -25 -6 -36 -14 Z"/>
|
||||
</g>
|
||||
|
||||
<!-- ?????? -->
|
||||
<g fill="none" stroke="#3f3018" stroke-width="0.8" opacity="0.7">
|
||||
<path d="M 358 358 q 25 4 50 22"/>
|
||||
<path d="M 362 376 q 25 4 56 22"/>
|
||||
<path d="M 368 394 q 28 4 60 16"/>
|
||||
<path d="M 378 410 q 30 3 55 8"/>
|
||||
</g>
|
||||
|
||||
<!-- ??????????? -->
|
||||
<g fill="#2a1f10">
|
||||
<path d="M 432 410 q 12 2 14 12 q -6 6 -16 0 Z"/>
|
||||
<path d="M 422 420 q 12 0 14 12 q -6 6 -16 -2 Z"/>
|
||||
<path d="M 410 426 q 10 2 12 13 q -8 5 -14 -3 Z"/>
|
||||
</g>
|
||||
|
||||
<!-- ???? -->
|
||||
<path d="M 358 354 q 16 -8 36 -5 q -5 8 -22 12 q -12 1 -16 -8 Z" fill="#f5ecc8" opacity="0.7"/>
|
||||
<path d="M 354 351 q 20 -8 42 -3" stroke="#fff" stroke-width="1.2" fill="none" opacity="0.5"/>
|
||||
|
||||
<!-- ??"??"????????? -->
|
||||
<path d="M 440 398 Q 472 402, 494 414" stroke="url(#wingGrad)" stroke-width="14" fill="none" stroke-linecap="round"/>
|
||||
<path d="M 440 396 Q 472 400, 492 412" stroke="#6e5a32" stroke-width="3" fill="none" stroke-linecap="round" opacity="0.7"/>
|
||||
|
||||
<!-- ?????? -->
|
||||
<ellipse cx="498" cy="416" rx="10" ry="8" fill="#6e5a32"/>
|
||||
<ellipse cx="498" cy="414" rx="6" ry="3" fill="#9b8b6a" opacity="0.7"/>
|
||||
|
||||
<!-- ??? -->
|
||||
<path d="M 370 428 Q 372 470, 382 516" stroke="url(#bodyGrad)" stroke-width="15" fill="none" stroke-linecap="round"/>
|
||||
<path d="M 374 430 Q 376 470, 384 512" stroke="#c5b896" stroke-width="3" fill="none" stroke-linecap="round" opacity="0.5"/>
|
||||
|
||||
<!-- ???? -->
|
||||
<g transform="translate(382, 519) rotate(-5)">
|
||||
<path d="M -6 -3 q 6 -3 12 -2 L 32 0 q 6 4 -2 9 L 0 11 q -6 -3 -2 -11 Z" fill="url(#footGrad)" stroke="#7a4810" stroke-width="0.9"/>
|
||||
<path d="M 8 0 q 1 6 -1 9 M 18 0 q 1 6 -2 9 M 28 0 q 0 5 -3 7" stroke="#7a4810" stroke-width="0.6" fill="none"/>
|
||||
<path d="M 0 -2 q 16 -1 28 1" stroke="#ffd070" stroke-width="0.9" fill="none" opacity="0.75"/>
|
||||
<path d="M 32 -1 l 2 -2 M 22 -2 l 2 -2 M 12 -2 l 1 -2" stroke="#3a1808" stroke-width="0.7"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- ============ ???? ============ -->
|
||||
<g>
|
||||
<line x1="500" y1="418" x2="500" y2="388" stroke="#2a2a2a" stroke-width="7" stroke-linecap="round"/>
|
||||
<path d="M 500 388 Q 500 374, 512 374 Q 524 374, 524 386 L 522 396"
|
||||
stroke="#222" stroke-width="6.5" fill="none" stroke-linecap="round"/>
|
||||
<path d="M 500 388 Q 500 374, 488 374 Q 476 374, 476 386 L 478 396"
|
||||
stroke="#222" stroke-width="6.5" fill="none" stroke-linecap="round"/>
|
||||
<rect x="516" y="383" width="12" height="8" rx="2.5" fill="#b8362a"/>
|
||||
<rect x="472" y="383" width="12" height="8" rx="2.5" fill="#b8362a"/>
|
||||
<rect x="516" y="384" width="12" height="2" fill="#ff6e58" opacity="0.7"/>
|
||||
<rect x="472" y="384" width="12" height="2" fill="#ff6e58" opacity="0.7"/>
|
||||
|
||||
<!-- ?? -->
|
||||
<g transform="translate(490, 384)">
|
||||
<circle r="5" fill="#c5862a"/>
|
||||
<circle r="3.5" fill="#e8a850"/>
|
||||
<circle r="1" fill="#5a3a18"/>
|
||||
<path d="M -3 -2 q 3 -1 4 1" stroke="#fff8a0" stroke-width="0.6" fill="none" opacity="0.8"/>
|
||||
</g>
|
||||
|
||||
<!-- ??? -->
|
||||
<g transform="translate(480, 358)">
|
||||
<path d="M 0 0 L 48 0 L 54 26 L -6 26 Z" fill="url(#basketGrad)"/>
|
||||
<path d="M 0 0 L 48 0 L 49 4 L -1 4 Z" fill="#a87a3a"/>
|
||||
<g stroke="#4a2f15" stroke-width="0.6" fill="none" opacity="0.75">
|
||||
<line x1="-3" y1="9" x2="51" y2="9"/>
|
||||
<line x1="-4" y1="15" x2="52" y2="15"/>
|
||||
<line x1="-5" y1="21" x2="53" y2="21"/>
|
||||
<line x1="4" y1="4" x2="4" y2="26"/>
|
||||
<line x1="14" y1="4" x2="14" y2="26"/>
|
||||
<line x1="24" y1="4" x2="24" y2="26"/>
|
||||
<line x1="34" y1="4" x2="34" y2="26"/>
|
||||
<line x1="44" y1="4" x2="44" y2="26"/>
|
||||
</g>
|
||||
<path d="M 0 1 L 48 1" stroke="#ffd9a0" stroke-width="0.8" opacity="0.8"/>
|
||||
<!-- ???? -->
|
||||
<path d="M 5 0 q 19 -12 38 0" stroke="#6f4419" stroke-width="1.5" fill="none"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- ============ ?? ============ -->
|
||||
<g transform="translate(520, 500)">
|
||||
<circle r="55" fill="url(#tireGrad)"/>
|
||||
<circle r="48" fill="#101010"/>
|
||||
<circle r="46" stroke="#5a5a5a" stroke-width="1.2" fill="#1a1a1a"/>
|
||||
<circle r="42" fill="#c8c8c8"/>
|
||||
<circle r="38" stroke="#9a9a9a" stroke-dasharray="2 7" stroke-width="36" fill="none" opacity="0.35"/>
|
||||
<circle r="35" stroke="#fff" stroke-dasharray="1 12" stroke-width="34" fill="none" opacity="0.18"/>
|
||||
<circle r="9" fill="url(#hubGrad)"/>
|
||||
<circle r="4" fill="#1a1a1a"/>
|
||||
<g stroke="#3a3a3a" stroke-width="1" opacity="0.7">
|
||||
<path d="M -53 -8 l 4 0 M -53 0 l 4 0 M -53 8 l 4 0"/>
|
||||
<path d="M 49 -8 l 4 0 M 49 0 l 4 0 M 49 8 l 4 0"/>
|
||||
</g>
|
||||
<ellipse cx="20" cy="-22" rx="11" ry="5" fill="#ffd86b" opacity="0.4"/>
|
||||
</g>
|
||||
|
||||
<!-- ???? -->
|
||||
<path d="M 466 500 a 54 54 0 0 1 108 0" stroke="url(#frameGrad)" stroke-width="6" fill="none" stroke-linecap="round"/>
|
||||
<ellipse cx="572" cy="494" rx="3" ry="2" fill="#ffd86b"/>
|
||||
|
||||
<!-- ============ ????? ============ -->
|
||||
<g stroke-linecap="round" opacity="0.7">
|
||||
<line x1="30" y1="470" x2="135" y2="468" stroke="#ffffff" stroke-width="3"/>
|
||||
<line x1="10" y1="495" x2="85" y2="493" stroke="#ffea99" stroke-width="2"/>
|
||||
<line x1="50" y1="520" x2="160" y2="518" stroke="#ffffff" stroke-width="3.5"/>
|
||||
<line x1="15" y1="540" x2="85" y2="540" stroke="#ffffff" stroke-width="2.5"/>
|
||||
<line x1="70" y1="556" x2="130" y2="556" stroke="#ffd86b" stroke-width="2" opacity="0.6"/>
|
||||
</g>
|
||||
|
||||
<!-- ????? -->
|
||||
<g fill="#f8f5e8" opacity="0.85">
|
||||
<ellipse cx="385" cy="265" rx="4" ry="1.5" transform="rotate(-15 385 265)"/>
|
||||
<ellipse cx="365" cy="285" rx="3" ry="1.2" transform="rotate(-20 365 285)"/>
|
||||
<ellipse cx="345" cy="305" rx="2.5" ry="1" transform="rotate(-25 345 305)"/>
|
||||
</g>
|
||||
|
||||
<!-- ??????? -->
|
||||
<circle cx="441" cy="198" r="1.2" fill="#ffffff" opacity="0.9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 28 KiB |
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,603 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Mythiccraft</title>
|
||||
<style>
|
||||
body { margin: 0; overflow: hidden; background: #000; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; user-select: none; }
|
||||
#crosshair {
|
||||
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
||||
color: rgba(255,255,255,0.8); font-size: 24px; font-weight: normal; pointer-events: none; text-shadow: 1px 1px 0 #000;
|
||||
}
|
||||
#toolbar {
|
||||
position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%);
|
||||
display: flex; gap: 8px; background: rgba(0,0,0,0.6); padding: 10px; border-radius: 12px;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.5);
|
||||
}
|
||||
.slot {
|
||||
width: 50px; height: 50px; border: 3px solid #444; border-radius: 6px;
|
||||
display: flex; align-items: flex-end; justify-content: center; color: white;
|
||||
font-size: 14px; font-weight: bold; text-shadow: 1px 1px 2px black; padding-bottom: 4px;
|
||||
box-sizing: border-box; cursor: pointer; background-size: cover; background-position: center;
|
||||
transition: transform 0.1s; image-rendering: pixelated;
|
||||
}
|
||||
.slot.selected { border-color: #fff; transform: scale(1.15); box-shadow: 0 0 15px rgba(255,255,255,0.5); z-index: 10;}
|
||||
#instructions {
|
||||
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
|
||||
background: rgba(26, 11, 46, 0.85); display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center; color: #fff; z-index: 200;
|
||||
}
|
||||
#instructions h1 { font-size: 64px; margin: 0; color: #f5d436; text-shadow: 0 0 20px rgba(245, 212, 54, 0.5); }
|
||||
#instructions p { font-size: 18px; color: #ccc; }
|
||||
#start-btn {
|
||||
padding: 15px 40px; font-size: 24px; cursor: pointer; margin-top: 30px;
|
||||
background: #f5d436; color: #1a0b2e; border: none; border-radius: 30px; font-weight: bold;
|
||||
transition: background 0.2s, transform 0.2s;
|
||||
}
|
||||
#start-btn:hover { background: #fff; transform: scale(1.05); }
|
||||
.controls-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 40px; margin-top: 40px; text-align: left; background: rgba(0,0,0,0.4); padding: 20px; border-radius: 10px;}
|
||||
.controls-grid span { color: #f5d436; font-weight: bold; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="crosshair">+</div>
|
||||
<div id="toolbar">
|
||||
<div class="slot selected" id="slot1">1</div>
|
||||
<div class="slot" id="slot2">2</div>
|
||||
<div class="slot" id="slot3">3</div>
|
||||
<div class="slot" id="slot4">4</div>
|
||||
<div class="slot" id="slot5">5</div>
|
||||
<div class="slot" id="slot6">6</div>
|
||||
</div>
|
||||
<div id="instructions">
|
||||
<h1>Mythiccraft</h1>
|
||||
<p>Explore the roots of the World Tree.</p>
|
||||
<button id="start-btn">Enter World</button>
|
||||
<div class="controls-grid">
|
||||
<div><span>WASD</span> / Arrows to Move</div>
|
||||
<div><span>Left Click</span> to Break Block</div>
|
||||
<div><span>SPACE</span> to Jump</div>
|
||||
<div><span>Right Click</span> to Place Block</div>
|
||||
<div><span>Mouse</span> to Look Around</div>
|
||||
<div><span>1 - 6</span> to Select Block Type</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Load Three.js and PointerLockControls -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script>
|
||||
|
||||
<script>
|
||||
// --- Perlin Noise Implementation ---
|
||||
class Perlin {
|
||||
constructor() {
|
||||
this.p = new Uint8Array(512);
|
||||
for(let i=0; i<256; i++) this.p[i] = Math.floor(Math.random()*256);
|
||||
for(let i=0; i<256; i++) this.p[256+i] = this.p[i];
|
||||
}
|
||||
fade(t) { return t * t * t * (t * (t * 6 - 15) + 10); }
|
||||
lerp(t, a, b) { return a + t * (b - a); }
|
||||
grad(hash, x, y, z) {
|
||||
let h = hash & 15;
|
||||
let u = h < 8 ? x : y;
|
||||
let v = h < 4 ? y : h === 12 || h === 14 ? x : z;
|
||||
return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
|
||||
}
|
||||
noise(x, y, z) {
|
||||
let X = Math.floor(x) & 255, Y = Math.floor(y) & 255, Z = Math.floor(z) & 255;
|
||||
x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z);
|
||||
let u = this.fade(x), v = this.fade(y), w = this.fade(z);
|
||||
let A = this.p[X] + Y, AA = this.p[A] + Z, AB = this.p[A + 1] + Z;
|
||||
let B = this.p[X + 1] + Y, BA = this.p[B] + Z, BB = this.p[B + 1] + Z;
|
||||
return this.lerp(w, this.lerp(v, this.lerp(u, this.grad(this.p[AA], x, y, z), this.grad(this.p[BA], x - 1, y, z)),
|
||||
this.lerp(u, this.grad(this.p[AB], x, y - 1, z), this.grad(this.p[BB], x - 1, y - 1, z))),
|
||||
this.lerp(v, this.lerp(u, this.grad(this.p[AA + 1], x, y, z - 1), this.grad(this.p[BA + 1], x - 1, y, z - 1)),
|
||||
this.lerp(u, this.grad(this.p[AB + 1], x, y - 1, z - 1), this.grad(this.p[BB + 1], x - 1, y - 1, z - 1))));
|
||||
}
|
||||
}
|
||||
const perlin = new Perlin();
|
||||
|
||||
// --- Texture Atlas Generator ---
|
||||
let atlasCanvas;
|
||||
function generateAtlas() {
|
||||
atlasCanvas = document.createElement('canvas');
|
||||
atlasCanvas.width = 256; atlasCanvas.height = 256;
|
||||
const ctx = atlasCanvas.getContext('2d');
|
||||
|
||||
function drawNoise(x, y, w, h, baseColor, varColor, noiseLevel) {
|
||||
ctx.fillStyle = baseColor;
|
||||
ctx.fillRect(x, y, w, h);
|
||||
for(let i=x; i<x+w; i+=2) {
|
||||
for(let j=y; j<y+h; j+=2) {
|
||||
if(Math.random() < noiseLevel) {
|
||||
ctx.fillStyle = Math.random() > 0.5 ? varColor : baseColor;
|
||||
ctx.fillRect(i, j, 2, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Row 0
|
||||
drawNoise(0, 0, 64, 64, '#3e6b2c', '#4a8035', 0.5); // 0: Grass Top
|
||||
drawNoise(64, 0, 64, 64, '#4a3018', '#382412', 0.5); // 1: Grass Side
|
||||
ctx.fillStyle = '#3e6b2c'; ctx.fillRect(64, 0, 64, 12);
|
||||
drawNoise(128, 0, 64, 64, '#4a3018', '#382412', 0.5); // 2: Dirt
|
||||
drawNoise(192, 0, 64, 64, '#555555', '#666666', 0.5); // 3: Stone
|
||||
// Row 1
|
||||
drawNoise(0, 64, 64, 64, '#2c1e10', '#1f150b', 0.5); // 4: Wood Side
|
||||
drawNoise(64, 64, 64, 64, '#54402c', '#423222', 0.5); // 5: Wood Top
|
||||
drawNoise(128, 64, 64, 64, '#1c3614', '#12240d', 0.8); // 6: Leaves
|
||||
drawNoise(192, 64, 64, 64, '#f5d436', '#fff17a', 0.6); // 7: Magic (Gold)
|
||||
|
||||
let tex = new THREE.CanvasTexture(atlasCanvas);
|
||||
tex.magFilter = THREE.NearestFilter;
|
||||
tex.minFilter = THREE.NearestFilter;
|
||||
return tex;
|
||||
}
|
||||
|
||||
// --- Toolbar Icons ---
|
||||
function setToolbarIcons() {
|
||||
let canvas = document.createElement('canvas');
|
||||
canvas.width = 64; canvas.height = 64;
|
||||
let ctx = canvas.getContext('2d');
|
||||
let drawIcon = (id, tx, ty) => {
|
||||
ctx.clearRect(0,0,64,64);
|
||||
ctx.drawImage(atlasCanvas, tx*64, ty*64, 64, 64, 0, 0, 64, 64);
|
||||
document.getElementById('slot' + id).style.backgroundImage = `url(${canvas.toDataURL()})`;
|
||||
};
|
||||
drawIcon(1, 1, 0); // Grass Side
|
||||
drawIcon(2, 2, 0); // Dirt
|
||||
drawIcon(3, 3, 0); // Stone
|
||||
drawIcon(4, 0, 1); // Wood Side
|
||||
drawIcon(5, 2, 1); // Leaves
|
||||
drawIcon(6, 3, 1); // Magic
|
||||
}
|
||||
|
||||
// --- Core Variables ---
|
||||
const chunkWidth = 16, chunkHeight = 160, renderDistance = 3;
|
||||
const chunks = new Map(), chunkMeshes = new Map();
|
||||
let lastChunkX = -999, lastChunkZ = -999;
|
||||
let currentBlockType = 1;
|
||||
|
||||
// --- Three.js Setup ---
|
||||
const scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x1a0b2e); // Mythical purple night
|
||||
scene.fog = new THREE.FogExp2(0x1a0b2e, 0.015);
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(80, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
const renderer = new THREE.WebGLRenderer({ antialias: false });
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
document.body.appendChild(renderer.domElement);
|
||||
|
||||
const controls = new THREE.PointerLockControls(camera, renderer.domElement);
|
||||
|
||||
// Lighting
|
||||
const dirLight = new THREE.DirectionalLight(0xaa88ff, 0.7);
|
||||
dirLight.position.set(-100, 150, -50);
|
||||
scene.add(dirLight);
|
||||
const ambientLight = new THREE.AmbientLight(0x403050, 1.0);
|
||||
scene.add(ambientLight);
|
||||
|
||||
// Stars
|
||||
const starsGeo = new THREE.BufferGeometry();
|
||||
const starsPositions = [];
|
||||
for(let i=0; i<2000; i++) {
|
||||
starsPositions.push((Math.random() - 0.5) * 1000, Math.random() * 400 + 50, (Math.random() - 0.5) * 1000);
|
||||
}
|
||||
starsGeo.setAttribute('position', new THREE.Float32BufferAttribute(starsPositions, 3));
|
||||
const starsMat = new THREE.PointsMaterial({color: 0xffffff, size: 1.5});
|
||||
scene.add(new THREE.Points(starsGeo, starsMat));
|
||||
|
||||
// Block Material
|
||||
const material = new THREE.MeshLambertMaterial({ map: generateAtlas(), transparent: true, alphaTest: 0.1 });
|
||||
setToolbarIcons();
|
||||
|
||||
// Active Block Outline
|
||||
const outlineGeo = new THREE.EdgesGeometry(new THREE.BoxGeometry(1.01, 1.01, 1.01));
|
||||
const outlineMat = new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.5 });
|
||||
const outlineMesh = new THREE.LineSegments(outlineGeo, outlineMat);
|
||||
outlineMesh.visible = false;
|
||||
scene.add(outlineMesh);
|
||||
|
||||
// --- Chunk Generation ---
|
||||
function generateChunk(cx, cz) {
|
||||
let data = new Uint8Array(16 * chunkHeight * 16);
|
||||
let treeX = 0, treeZ = 0;
|
||||
|
||||
for(let lx = 0; lx < 16; lx++) {
|
||||
for(let lz = 0; lz < 16; lz++) {
|
||||
let wx = cx * 16 + lx;
|
||||
let wz = cz * 16 + lz;
|
||||
|
||||
// Base Terrain
|
||||
let n = perlin.noise(wx * 0.02, 0, wz * 0.02);
|
||||
let n2 = perlin.noise(wx * 0.005, 0, wz * 0.005);
|
||||
let h = Math.floor((n * 0.8 + n2 * 1.2) * 15 + 30);
|
||||
if(h < 2) h = 2;
|
||||
|
||||
for(let y = 0; y < chunkHeight; y++) {
|
||||
let idx = (lx * 16 + lz) * chunkHeight + y;
|
||||
if(y === h) data[idx] = 1; // Grass
|
||||
else if(y < h && y > h - 4) data[idx] = 2; // Dirt
|
||||
else if(y <= h - 4) data[idx] = 3; // Stone
|
||||
else data[idx] = 0; // Air
|
||||
}
|
||||
|
||||
let distToTree = Math.sqrt((wx - treeX)**2 + (wz - treeZ)**2);
|
||||
|
||||
// Yggdrasil Trunk
|
||||
if(distToTree < 14 - (h * 0.03)) {
|
||||
for(let y = h; y < 115; y++) {
|
||||
data[(lx * 16 + lz) * chunkHeight + y] = 4; // Wood
|
||||
}
|
||||
}
|
||||
|
||||
// Roots
|
||||
if(distToTree >= 12 && distToTree < 40) {
|
||||
let angle = Math.atan2(wz - treeZ, wx - treeX);
|
||||
let rootFactor = Math.sin(angle * 7 + distToTree * 0.15);
|
||||
if(rootFactor > 0.6) {
|
||||
let rootHeight = h + (40 - distToTree) * 0.4;
|
||||
for(let y = h; y < rootHeight; y++) {
|
||||
data[(lx * 16 + lz) * chunkHeight + y] = 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Canopy
|
||||
if(distToTree < 55) {
|
||||
for(let y = 80; y < 150; y++) {
|
||||
let d3 = Math.sqrt((wx - treeX)**2 + (y - 115)**2 * 2.0 + (wz - treeZ)**2);
|
||||
if(d3 < 45) {
|
||||
let idx = (lx * 16 + lz) * chunkHeight + y;
|
||||
if(data[idx] === 0) {
|
||||
let leafNoise = perlin.noise(wx * 0.15, y * 0.15, wz * 0.15);
|
||||
if(leafNoise > -0.2) {
|
||||
if(Math.random() < 0.02) data[idx] = 6; // Magic fruit
|
||||
else data[idx] = 5; // Leaves
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Floating Islands
|
||||
if(distToTree > 60 && distToTree < 200) {
|
||||
let islandNoise = perlin.noise(wx * 0.04, 0, wz * 0.04);
|
||||
if(islandNoise > 0.72) {
|
||||
let islandY = 60 + Math.sin(wx * 0.03 + wz * 0.03) * 25;
|
||||
let islandThickness = (islandNoise - 0.72) * 25;
|
||||
for(let y = Math.floor(islandY - islandThickness); y <= Math.floor(islandY); y++) {
|
||||
let idx = (lx * 16 + lz) * chunkHeight + y;
|
||||
if(y === Math.floor(islandY)) data[idx] = 1;
|
||||
else data[idx] = 3;
|
||||
}
|
||||
// Shrine ruins
|
||||
if(islandNoise > 0.85 && Math.random() < 0.08) {
|
||||
for(let sy = 1; sy <= 3; sy++) data[(lx * 16 + lz) * chunkHeight + Math.floor(islandY + sy)] = 3;
|
||||
data[(lx * 16 + lz) * chunkHeight + Math.floor(islandY + 4)] = 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
// --- Data Access ---
|
||||
function getBlock(x, y, z) {
|
||||
if(y < 0 || y >= chunkHeight) return 0;
|
||||
let cx = Math.floor(x / 16), cz = Math.floor(z / 16);
|
||||
let key = cx + ',' + cz;
|
||||
if(!chunks.has(key)) return 0;
|
||||
let lx = x - cx * 16, lz = z - cz * 16;
|
||||
return chunks.get(key)[(lx * 16 + lz) * chunkHeight + y];
|
||||
}
|
||||
|
||||
function setBlock(x, y, z, v) {
|
||||
if(y < 0 || y >= chunkHeight) return;
|
||||
let cx = Math.floor(x / 16), cz = Math.floor(z / 16);
|
||||
let key = cx + ',' + cz;
|
||||
if(!chunks.has(key)) return;
|
||||
let lx = x - cx * 16, lz = z - cz * 16;
|
||||
chunks.get(key)[(lx * 16 + lz) * chunkHeight + y] = v;
|
||||
|
||||
buildChunkMesh(cx, cz);
|
||||
if(lx === 0) buildChunkMesh(cx - 1, cz);
|
||||
if(lx === 15) buildChunkMesh(cx + 1, cz);
|
||||
if(lz === 0) buildChunkMesh(cx, cz - 1);
|
||||
if(lz === 15) buildChunkMesh(cx, cz + 1);
|
||||
}
|
||||
|
||||
// --- Chunk Meshing ---
|
||||
function getBlockTexture(blockType, faceIndex) {
|
||||
switch(blockType) {
|
||||
case 1: return faceIndex === 2 ? 0 : (faceIndex === 3 ? 2 : 1);
|
||||
case 2: return 2;
|
||||
case 3: return 3;
|
||||
case 4: return (faceIndex === 2 || faceIndex === 3) ? 5 : 4;
|
||||
case 5: return 6;
|
||||
case 6: return 7;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const faces = [
|
||||
[ [1,1,1], [1,0,1], [1,0,0], [1,1,0] ], // Right X+
|
||||
[ [0,1,0], [0,0,0], [0,0,1], [0,1,1] ], // Left X-
|
||||
[ [0,1,0], [0,1,1], [1,1,1], [1,1,0] ], // Top Y+
|
||||
[ [0,0,1], [0,0,0], [1,0,0], [1,0,1] ], // Bottom Y-
|
||||
[ [0,1,1], [0,0,1], [1,0,1], [1,1,1] ], // Front Z+
|
||||
[ [1,1,0], [1,0,0], [0,0,0], [0,1,0] ] // Back Z-
|
||||
];
|
||||
const faceNormals = [ [1,0,0], [-1,0,0], [0,1,0], [0,-1,0], [0,0,1], [0,0,-1] ];
|
||||
|
||||
function buildChunkMesh(cx, cz) {
|
||||
let key = cx + ',' + cz;
|
||||
let data = chunks.get(key);
|
||||
if(!data) return;
|
||||
|
||||
let positions = [], normals = [], uvs = [], indices = [];
|
||||
let idxCounter = 0;
|
||||
|
||||
for(let lx = 0; lx < 16; lx++) {
|
||||
for(let lz = 0; lz < 16; lz++) {
|
||||
for(let y = 0; y < chunkHeight; y++) {
|
||||
let block = data[(lx * 16 + lz) * chunkHeight + y];
|
||||
if(block === 0) continue;
|
||||
|
||||
let wx = cx * 16 + lx, wz = cz * 16 + lz;
|
||||
|
||||
for(let f = 0; f < 6; f++) {
|
||||
let nx = wx + faceNormals[f][0];
|
||||
let ny = y + faceNormals[f][1];
|
||||
let nz = wz + faceNormals[f][2];
|
||||
|
||||
let neighborBlock = getBlock(nx, ny, nz);
|
||||
let drawFace = false;
|
||||
if (neighborBlock === 0) drawFace = true;
|
||||
else if (neighborBlock === 5 && block !== 5) drawFace = true;
|
||||
else if (neighborBlock === 5 && block === 6) drawFace = true;
|
||||
|
||||
if(drawFace) {
|
||||
let texIndex = getBlockTexture(block, f);
|
||||
let tu = (texIndex % 4) * 0.25;
|
||||
let ty = Math.floor(texIndex / 4);
|
||||
let tv = 1.0 - (ty + 1) * 0.25;
|
||||
|
||||
let fVerts = faces[f];
|
||||
for(let i=0; i<4; i++) {
|
||||
positions.push(wx + fVerts[i][0], y + fVerts[i][1], wz + fVerts[i][2]);
|
||||
normals.push(...faceNormals[f]);
|
||||
let uu = tu + (i===2 || i===3 ? 0.25 : 0);
|
||||
let vv = tv + (i===0 || i===3 ? 0.25 : 0);
|
||||
uvs.push(uu, vv);
|
||||
}
|
||||
indices.push(idxCounter, idxCounter+1, idxCounter+2, idxCounter, idxCounter+2, idxCounter+3);
|
||||
idxCounter += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let geometry = new THREE.BufferGeometry();
|
||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
||||
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
|
||||
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
|
||||
geometry.setIndex(indices);
|
||||
|
||||
if(chunkMeshes.has(key)) {
|
||||
scene.remove(chunkMeshes.get(key));
|
||||
chunkMeshes.get(key).geometry.dispose();
|
||||
}
|
||||
if(positions.length > 0) {
|
||||
let mesh = new THREE.Mesh(geometry, material);
|
||||
scene.add(mesh);
|
||||
chunkMeshes.set(key, mesh);
|
||||
} else {
|
||||
chunkMeshes.set(key, new THREE.Mesh()); // Empty placeholder to prevent rebuilds
|
||||
}
|
||||
}
|
||||
|
||||
function updateChunks(playerPos) {
|
||||
let px = Math.floor(playerPos.x / 16), pz = Math.floor(playerPos.z / 16);
|
||||
if(px !== lastChunkX || pz !== lastChunkZ) {
|
||||
lastChunkX = px; lastChunkZ = pz;
|
||||
for(let x = px - renderDistance; x <= px + renderDistance; x++) {
|
||||
for(let z = pz - renderDistance; z <= pz + renderDistance; z++) {
|
||||
let key = x + ',' + z;
|
||||
if(!chunks.has(key)) chunks.set(key, generateChunk(x, z));
|
||||
}
|
||||
}
|
||||
for(let x = px - renderDistance; x <= px + renderDistance; x++) {
|
||||
for(let z = pz - renderDistance; z <= pz + renderDistance; z++) {
|
||||
let key = x + ',' + z;
|
||||
if(!chunkMeshes.has(key)) buildChunkMesh(x, z);
|
||||
}
|
||||
}
|
||||
let toDelete = [];
|
||||
for(let [key, mesh] of chunkMeshes) {
|
||||
let [cx, cz] = key.split(',').map(Number);
|
||||
if(Math.abs(cx - px) > renderDistance + 1 || Math.abs(cz - pz) > renderDistance + 1) {
|
||||
scene.remove(mesh);
|
||||
if(mesh.geometry) mesh.geometry.dispose();
|
||||
toDelete.push(key);
|
||||
}
|
||||
}
|
||||
for(let key of toDelete) chunkMeshes.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Interaction & UI ---
|
||||
document.getElementById('start-btn').addEventListener('click', () => controls.lock());
|
||||
controls.addEventListener('lock', () => document.getElementById('instructions').style.display = 'none');
|
||||
controls.addEventListener('unlock', () => document.getElementById('instructions').style.display = 'flex');
|
||||
|
||||
let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false, canJump = false;
|
||||
document.addEventListener('keydown', (e) => {
|
||||
switch (e.code) {
|
||||
case 'ArrowUp': case 'KeyW': moveForward = true; break;
|
||||
case 'ArrowLeft': case 'KeyA': moveLeft = true; break;
|
||||
case 'ArrowDown': case 'KeyS': moveBackward = true; break;
|
||||
case 'ArrowRight': case 'KeyD': moveRight = true; break;
|
||||
case 'Space': if(canJump) { velocity.y = 12; canJump = false; } break;
|
||||
case 'Digit1': setBlockType(1); break;
|
||||
case 'Digit2': setBlockType(2); break;
|
||||
case 'Digit3': setBlockType(3); break;
|
||||
case 'Digit4': setBlockType(4); break;
|
||||
case 'Digit5': setBlockType(5); break;
|
||||
case 'Digit6': setBlockType(6); break;
|
||||
}
|
||||
});
|
||||
document.addEventListener('keyup', (e) => {
|
||||
switch (e.code) {
|
||||
case 'ArrowUp': case 'KeyW': moveForward = false; break;
|
||||
case 'ArrowLeft': case 'KeyA': moveLeft = false; break;
|
||||
case 'ArrowDown': case 'KeyS': moveBackward = false; break;
|
||||
case 'ArrowRight': case 'KeyD': moveRight = false; break;
|
||||
}
|
||||
});
|
||||
|
||||
function setBlockType(type) {
|
||||
currentBlockType = type;
|
||||
document.querySelectorAll('.slot').forEach(el => el.classList.remove('selected'));
|
||||
document.getElementById('slot' + type).classList.add('selected');
|
||||
}
|
||||
|
||||
const raycaster = new THREE.Raycaster();
|
||||
const center = new THREE.Vector2(0, 0);
|
||||
document.addEventListener('mousedown', (e) => {
|
||||
if(!controls.isLocked) return;
|
||||
raycaster.setFromCamera(center, camera);
|
||||
let validMeshes = Array.from(chunkMeshes.values()).filter(m => m.geometry);
|
||||
let intersects = raycaster.intersectObjects(validMeshes);
|
||||
|
||||
if(intersects.length > 0 && intersects[0].distance < 8) {
|
||||
let intersect = intersects[0];
|
||||
let normal = intersect.face.normal;
|
||||
|
||||
if(e.button === 0) { // Break
|
||||
let bx = Math.floor(intersect.point.x - normal.x * 0.5);
|
||||
let by = Math.floor(intersect.point.y - normal.y * 0.5);
|
||||
let bz = Math.floor(intersect.point.z - normal.z * 0.5);
|
||||
if(by > 0) setBlock(bx, by, bz, 0);
|
||||
} else if(e.button === 2) { // Place
|
||||
let bx = Math.floor(intersect.point.x + normal.x * 0.5);
|
||||
let by = Math.floor(intersect.point.y + normal.y * 0.5);
|
||||
let bz = Math.floor(intersect.point.z + normal.z * 0.5);
|
||||
|
||||
let cx = Math.floor(camera.position.x), cz = Math.floor(camera.position.z);
|
||||
let cy = Math.floor(camera.position.y), cyF = Math.floor(camera.position.y - 1.6);
|
||||
if(bx === cx && bz === cz && (by === cy || by === cyF)) return; // Prevent placing in player
|
||||
|
||||
if(by > 0 && by < chunkHeight - 1) setBlock(bx, by, bz, currentBlockType);
|
||||
}
|
||||
}
|
||||
});
|
||||
document.addEventListener('contextmenu', e => e.preventDefault());
|
||||
|
||||
// --- Physics & Loop ---
|
||||
let velocity = new THREE.Vector3();
|
||||
let direction = new THREE.Vector3();
|
||||
const clock = new THREE.Clock();
|
||||
const playerHeight = 1.6;
|
||||
|
||||
function checkCollision(pos) {
|
||||
let x0 = Math.floor(pos.x - 0.3), x1 = Math.floor(pos.x + 0.3);
|
||||
let z0 = Math.floor(pos.z - 0.3), z1 = Math.floor(pos.z + 0.3);
|
||||
let y0 = Math.floor(pos.y - playerHeight), y1 = Math.floor(pos.y + 0.2);
|
||||
for(let x = x0; x <= x1; x++) {
|
||||
for(let y = y0; y <= y1; y++) {
|
||||
for(let z = z0; z <= z1; z++) {
|
||||
if(getBlock(x, y, z) !== 0) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initial Spawn
|
||||
camera.position.set(0, 100, 70); // Spawn in front of the tree
|
||||
updateChunks(camera.position);
|
||||
let startY = 150;
|
||||
while(getBlock(0, startY, 70) === 0 && startY > 0) startY--;
|
||||
camera.position.set(0, startY + playerHeight + 1, 70);
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
if (controls.isLocked) {
|
||||
let delta = Math.min(clock.getDelta(), 0.1);
|
||||
|
||||
velocity.x -= velocity.x * 10.0 * delta;
|
||||
velocity.z -= velocity.z * 10.0 * delta;
|
||||
velocity.y -= 35.0 * delta; // Gravity
|
||||
|
||||
direction.z = Number(moveForward) - Number(moveBackward);
|
||||
direction.x = Number(moveRight) - Number(moveLeft);
|
||||
direction.normalize();
|
||||
|
||||
let speed = 50.0;
|
||||
if (moveForward || moveBackward) velocity.z -= direction.z * speed * delta;
|
||||
if (moveLeft || moveRight) velocity.x -= direction.x * speed * delta;
|
||||
|
||||
let cObj = controls.getObject();
|
||||
let prevPos = cObj.position.clone();
|
||||
|
||||
controls.moveRight(-velocity.x * delta);
|
||||
controls.moveForward(-velocity.z * delta);
|
||||
|
||||
if(checkCollision(cObj.position)) {
|
||||
let tx = cObj.position.x, tz = cObj.position.z;
|
||||
cObj.position.set(tx, prevPos.y, prevPos.z);
|
||||
if(checkCollision(cObj.position)) { cObj.position.x = prevPos.x; velocity.x = 0; }
|
||||
cObj.position.set(cObj.position.x, prevPos.y, tz);
|
||||
if(checkCollision(cObj.position)) { cObj.position.z = prevPos.z; velocity.z = 0; }
|
||||
}
|
||||
|
||||
cObj.position.y += velocity.y * delta;
|
||||
if(checkCollision(cObj.position)) {
|
||||
if(velocity.y < 0) {
|
||||
canJump = true;
|
||||
cObj.position.y = Math.floor(cObj.position.y - playerHeight) + 1 + playerHeight + 0.001;
|
||||
} else {
|
||||
cObj.position.y = Math.floor(cObj.position.y + 0.2) - 0.201;
|
||||
}
|
||||
velocity.y = 0;
|
||||
} else {
|
||||
canJump = false;
|
||||
}
|
||||
|
||||
updateChunks(cObj.position);
|
||||
|
||||
// Update Outline
|
||||
raycaster.setFromCamera(center, camera);
|
||||
let validMeshes = Array.from(chunkMeshes.values()).filter(m => m.geometry);
|
||||
let intersects = raycaster.intersectObjects(validMeshes);
|
||||
if(intersects.length > 0 && intersects[0].distance < 8) {
|
||||
let pt = intersects[0].point, nm = intersects[0].face.normal;
|
||||
outlineMesh.position.set(
|
||||
Math.floor(pt.x - nm.x * 0.5) + 0.5,
|
||||
Math.floor(pt.y - nm.y * 0.5) + 0.5,
|
||||
Math.floor(pt.z - nm.z * 0.5) + 0.5
|
||||
);
|
||||
outlineMesh.visible = true;
|
||||
} else {
|
||||
outlineMesh.visible = false;
|
||||
}
|
||||
} else {
|
||||
clock.getDelta();
|
||||
}
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
|
||||
animate();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Whitespace-only changes.
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,670 @@
|
||||
<svg viewBox="0 0 800 600" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
|
||||
<defs>
|
||||
<!-- Sky background -->
|
||||
<linearGradient id="skyGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#0b132b" />
|
||||
<stop offset="25%" stop-color="#1c2541" />
|
||||
<stop offset="50%" stop-color="#3a225d" />
|
||||
<stop offset="70%" stop-color="#8c2f39" />
|
||||
<stop offset="85%" stop-color="#eb6424" />
|
||||
<stop offset="100%" stop-color="#fbc02d" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Sun glow -->
|
||||
<radialGradient id="sunGrad" cx="0.5" cy="0.5" r="0.5">
|
||||
<stop offset="0%" stop-color="#ffffff" />
|
||||
<stop offset="20%" stop-color="#fff59d" />
|
||||
<stop offset="55%" stop-color="#ff9100" stop-opacity="0.8" />
|
||||
<stop offset="100%" stop-color="#ff3d00" stop-opacity="0" />
|
||||
</radialGradient>
|
||||
|
||||
<!-- Cloud gradient -->
|
||||
<linearGradient id="cloudGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#ffb74d" stop-opacity="0.45" />
|
||||
<stop offset="50%" stop-color="#e040fb" stop-opacity="0.3" />
|
||||
<stop offset="100%" stop-color="#4a148c" stop-opacity="0.05" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Ocean gradient -->
|
||||
<linearGradient id="seaGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#170c2a" />
|
||||
<stop offset="20%" stop-color="#161f4c" />
|
||||
<stop offset="55%" stop-color="#02476b" />
|
||||
<stop offset="85%" stop-color="#006c84" />
|
||||
<stop offset="100%" stop-color="#058085" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Sea wave highlight -->
|
||||
<linearGradient id="seaHighlight" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#ffe082" stop-opacity="0.8" />
|
||||
<stop offset="50%" stop-color="#ff8f00" stop-opacity="0.4" />
|
||||
<stop offset="100%" stop-color="#008080" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Sand gradient -->
|
||||
<linearGradient id="sandGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#fed766" />
|
||||
<stop offset="35%" stop-color="#f4b251" />
|
||||
<stop offset="75%" stop-color="#d07c34" />
|
||||
<stop offset="100%" stop-color="#803e12" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Wet sand reflection -->
|
||||
<linearGradient id="wetSand" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#45112e" stop-opacity="0.75" />
|
||||
<stop offset="40%" stop-color="#5a2c22" stop-opacity="0.6" />
|
||||
<stop offset="100%" stop-color="#240c02" stop-opacity="0.9" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Chrome metallic reflection -->
|
||||
<linearGradient id="chromeGrad" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#cbd5e1" />
|
||||
<stop offset="20%" stop-color="#f8fafc" />
|
||||
<stop offset="40%" stop-color="#94a3b8" />
|
||||
<stop offset="45%" stop-color="#475569" />
|
||||
<stop offset="55%" stop-color="#e2e8f0" />
|
||||
<stop offset="80%" stop-color="#f8fafc" />
|
||||
<stop offset="100%" stop-color="#334155" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Cruiser bike frame teal -->
|
||||
<linearGradient id="frameGrad" x1="0" y1="0" x2="1" y2="0">
|
||||
<stop offset="0%" stop-color="#26c6da" />
|
||||
<stop offset="50%" stop-color="#00acc1" />
|
||||
<stop offset="100%" stop-color="#006064" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Tire gradient -->
|
||||
<radialGradient id="tireGrad" cx="0.5" cy="0.5" r="0.5" fx="0.4" fy="0.4">
|
||||
<stop offset="0%" stop-color="#ffffff" />
|
||||
<stop offset="75%" stop-color="#f5f5f4" />
|
||||
<stop offset="90%" stop-color="#e7e5e4" />
|
||||
<stop offset="100%" stop-color="#b4b2af" />
|
||||
</radialGradient>
|
||||
|
||||
<!-- Leather brown -->
|
||||
<linearGradient id="leatherGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#a1887f" />
|
||||
<stop offset="40%" stop-color="#6d4c41" />
|
||||
<stop offset="100%" stop-color="#3e2723" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Pelican feathers -->
|
||||
<radialGradient id="pelicanFeather" cx="0.3" cy="0.3" r="0.7">
|
||||
<stop offset="0%" stop-color="#ffffff" />
|
||||
<stop offset="70%" stop-color="#f1f5f9" />
|
||||
<stop offset="90%" stop-color="#cbd5e1" />
|
||||
<stop offset="100%" stop-color="#94a3b8" />
|
||||
</radialGradient>
|
||||
|
||||
<!-- Pelican beak -->
|
||||
<linearGradient id="beakGrad" x1="0" y1="0" x2="1" y2="0.3">
|
||||
<stop offset="0%" stop-color="#ffe082" />
|
||||
<stop offset="40%" stop-color="#ffb300" />
|
||||
<stop offset="80%" stop-color="#ff7043" />
|
||||
<stop offset="100%" stop-color="#d84315" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Pelican pouch (semi-translucent) -->
|
||||
<linearGradient id="pouchGrad" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0%" stop-color="#ffe082" stop-opacity="0.9" />
|
||||
<stop offset="45%" stop-color="#ffb300" stop-opacity="0.85" />
|
||||
<stop offset="80%" stop-color="#ff7043" stop-opacity="0.75" />
|
||||
<stop offset="100%" stop-color="#d84315" stop-opacity="0.65" />
|
||||
</linearGradient>
|
||||
|
||||
<!-- Drop shadows -->
|
||||
<filter id="shadow" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="3" dy="5" stdDeviation="4" flood-color="#090514" flood-opacity="0.35" />
|
||||
</filter>
|
||||
|
||||
<filter id="bikeShadow" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feGaussianBlur stdDeviation="6" result="blur" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/>
|
||||
</filter>
|
||||
|
||||
<filter id="sunGlow" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="15" result="blur" />
|
||||
<feMerge>
|
||||
<feMergeNode in="blur" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
|
||||
<!-- Soft ambient shadow gradient -->
|
||||
<radialGradient id="groundShadow" cx="50%" cy="50%" r="50%">
|
||||
<stop offset="0%" stop-color="#140602" stop-opacity="0.7" />
|
||||
<stop offset="40%" stop-color="#140602" stop-opacity="0.4" />
|
||||
<stop offset="100%" stop-color="#140602" stop-opacity="0" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<!-- ==================== BACKGROUND ==================== -->
|
||||
<!-- Sky -->
|
||||
<rect width="800" height="600" fill="url(#skyGrad)" />
|
||||
|
||||
<!-- Sun -->
|
||||
<circle cx="560" cy="270" r="140" fill="url(#sunGrad)" filter="url(#sunGlow)" opacity="0.85" />
|
||||
<circle cx="560" cy="270" r="50" fill="#ffffff" filter="url(#sunGlow)" opacity="0.9" />
|
||||
|
||||
<!-- Sky Clouds -->
|
||||
<!-- Distant thin cloud -->
|
||||
<path d="M 100,140 C 180,120 280,130 350,150 C 370,120 440,110 490,130 C 530,115 590,125 610,145 C 670,140 720,150 750,165 L 750,180 L 100,180 Z" fill="url(#cloudGrad)" opacity="0.3" />
|
||||
<!-- Vibrant midground cloud -->
|
||||
<path d="M -20,230 C 30,205 90,200 130,220 C 170,195 240,190 290,215 C 330,200 390,210 420,235 C 470,225 520,235 550,255 L 550,280 L -20,280 Z" fill="url(#cloudGrad)" opacity="0.4" />
|
||||
|
||||
<!-- Seagulls in the distance -->
|
||||
<g stroke="#ffb74d" stroke-width="1.5" fill="none" opacity="0.7">
|
||||
<path d="M 120,95 Q 128,88 135,95 Q 142,88 150,95" />
|
||||
<path d="M 165,110 Q 171,105 177,110 Q 183,105 189,110" />
|
||||
<path d="M 220,100 Q 225,96 230,100 Q 235,96 240,100" opacity="0.5" transform="scale(0.8) translate(50, 20)" />
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== THE OCEAN ==================== -->
|
||||
<!-- Distant ocean back -->
|
||||
<rect x="0" y="270" width="800" height="130" fill="url(#seaGrad)" />
|
||||
|
||||
<!-- Sun reflection on water -->
|
||||
<ellipse cx="560" cy="335" rx="160" ry="60" fill="url(#seaHighlight)" style="mix-blend-mode: color-dodge;" opacity="0.7" />
|
||||
|
||||
<!-- Horizon glare strip -->
|
||||
<line x1="0" y1="270" x2="800" y2="270" stroke="#ffeb3b" stroke-width="2" opacity="0.6" />
|
||||
|
||||
<!-- Wave horizontal streaks (shimmer) -->
|
||||
<g stroke="#ffd54f" stroke-width="1" opacity="0.8">
|
||||
<line x1="420" y1="285" x2="680" y2="285" stroke-width="1.5" />
|
||||
<line x1="380" y1="295" x2="720" y2="295" stroke-width="2" />
|
||||
<line x1="480" y1="305" x2="660" y2="305" stroke-width="2.5" />
|
||||
<line x1="350" y1="315" x2="750" y2="315" stroke-width="3" />
|
||||
<line x1="450" y1="330" x2="700" y2="330" stroke-width="4" />
|
||||
<line x1="280" y1="345" x2="780" y2="345" stroke-width="3.5" />
|
||||
<line x1="400" y1="365" x2="740" y2="365" stroke-width="4" />
|
||||
</g>
|
||||
|
||||
<!-- Wave lines (scrolling look) -->
|
||||
<path d="M 0,330 C 150,320 250,345 400,335 C 550,325 650,345 800,335 L 800,400 L 0,400 Z" fill="#034f70" opacity="0.3" />
|
||||
<path d="M 0,360 C 120,355 220,380 380,365 C 540,350 680,385 800,365 L 800,420 L 0,420 Z" fill="#005d7a" opacity="0.4" />
|
||||
|
||||
<!-- Foam details near shore -->
|
||||
<path d="M 0,390 C 180,375 280,410 480,395 C 680,380 720,415 800,405" stroke="#e0f7fa" stroke-width="3" fill="none" opacity="0.6" />
|
||||
<path d="M -10,405 C 150,395 310,425 490,410 C 670,395 730,430 810,415" stroke="#ffffff" stroke-width="4" stroke-dasharray="10 5 15 8" fill="none" opacity="0.8" />
|
||||
|
||||
|
||||
<!-- ==================== THE BEACH SHORE ==================== -->
|
||||
<!-- Wet Sand Area -->
|
||||
<path d="M -10,400 Q 250,380 500,430 T 810,410 L 810,600 L -10,600 Z" fill="url(#wetSand)" />
|
||||
|
||||
<!-- Glowing Edge of Tide (Water foam on sand) -->
|
||||
<path d="M -10,402 Q 250,382 500,432 T 810,412" stroke="#ffffff" stroke-width="6" fill="none" filter="url(#sunGlow)" opacity="0.8" />
|
||||
<path d="M -10,402 Q 250,382 500,432 T 810,412" stroke="#ffe082" stroke-width="2" fill="none" opacity="0.9" />
|
||||
|
||||
<!-- Dry Sand Dune (Foreground) -->
|
||||
<path d="M -10,455 Q 180,430 380,480 T 810,465 L 810,600 L -10,600 Z" fill="url(#sandGrad)" />
|
||||
|
||||
<!-- Ambient Shadows under the bicycle wheels -->
|
||||
<!-- Rear wheel shadow -->
|
||||
<ellipse cx="230" cy="525" rx="85" ry="18" fill="url(#groundShadow)" />
|
||||
<!-- Front wheel shadow -->
|
||||
<ellipse cx="550" cy="525" rx="85" ry="18" fill="url(#groundShadow)" />
|
||||
<!-- Center stand/pedal shadow -->
|
||||
<ellipse cx="380" cy="528" rx="140" ry="22" fill="url(#groundShadow)" opacity="0.8" />
|
||||
|
||||
|
||||
<!-- ==================== BICYCLE REFLECTION (ON WET SAND) ==================== -->
|
||||
<!-- Highly stylized, low-opacity, slightly blurred vertical reflection -->
|
||||
<g transform="translate(0, 1050) scale(1, -1)" opacity="0.12" filter="url(#bikeShadow)">
|
||||
<!-- Back wheel reflection -->
|
||||
<circle cx="230" cy="525" r="70" fill="none" stroke="#ffe082" stroke-width="12" />
|
||||
<!-- Front wheel reflection -->
|
||||
<circle cx="550" cy="525" r="70" fill="none" stroke="#ffe082" stroke-width="12" />
|
||||
<!-- Frame reflection -->
|
||||
<path d="M 230,525 L 360,525 L 330,390 L 230,525 L 310,320 M 330,390 L 485,320 L 550,525 M 310,320 L 485,320" stroke="#00acc1" stroke-width="10" fill="none" />
|
||||
<!-- Pelican body reflection outline -->
|
||||
<ellipse cx="330" cy="270" rx="65" ry="50" fill="#ffffff" />
|
||||
<path d="M 330,220 C 350,160 410,140 430,120" stroke="#ffb300" stroke-width="18" stroke-linecap="round" fill="none" />
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== THE BICYCLE ==================== -->
|
||||
<g id="bicycle">
|
||||
<!-- REAR WHEEL -->
|
||||
<g id="rear-wheel">
|
||||
<!-- Outer Tire (Cream White) -->
|
||||
<circle cx="230" cy="450" r="72" fill="none" stroke="#eae6df" stroke-width="15" filter="url(#shadow)" />
|
||||
<!-- Tire Tread Grooves -->
|
||||
<circle cx="230" cy="450" r="78" fill="none" stroke="#d6d1ca" stroke-width="1" stroke-dasharray="4 8" />
|
||||
<circle cx="230" cy="450" r="65" fill="none" stroke="#d2cbc4" stroke-width="1" />
|
||||
<!-- Rim (Chrome) -->
|
||||
<circle cx="230" cy="450" r="64" fill="none" stroke="url(#chromeGrad)" stroke-width="5" />
|
||||
<circle cx="230" cy="450" r="61" fill="none" stroke="#334155" stroke-width="1" />
|
||||
<!-- Spokes (Thin shiny wires) -->
|
||||
<g stroke="#eaeaea" stroke-width="0.75" opacity="0.85">
|
||||
<line x1="230" y1="450" x2="166" y2="450" /> <line x1="230" y1="450" x2="294" y2="450" />
|
||||
<line x1="230" y1="450" x2="230" y2="386" /> <line x1="230" y1="450" x2="230" y2="514" />
|
||||
<line x1="230" y1="450" x2="185" y2="405" /> <line x1="230" y1="450" x2="275" y2="495" />
|
||||
<line x1="230" y1="450" x2="185" y2="495" /> <line x1="230" y1="450" x2="275" y2="405" />
|
||||
<line x1="230" y1="450" x2="201" y2="392" /> <line x1="230" y1="450" x2="259" y2="508" />
|
||||
<line x1="230" y1="450" x2="259" y2="392" /> <line x1="230" y1="450" x2="201" y2="508" />
|
||||
<line x1="230" y1="450" x2="169" y2="423" /> <line x1="230" y1="450" x2="291" y2="477" />
|
||||
<line x1="230" y1="450" x2="169" y2="477" /> <line x1="230" y1="450" x2="291" y2="423" />
|
||||
</g>
|
||||
<!-- Wheel Hub -->
|
||||
<circle cx="230" cy="450" r="10" fill="url(#chromeGrad)" stroke="#475569" stroke-width="1" />
|
||||
</g>
|
||||
|
||||
<!-- FRONT WHEEL -->
|
||||
<g id="front-wheel">
|
||||
<!-- Outer Tire (Cream White) -->
|
||||
<circle cx="550" cy="450" r="72" fill="none" stroke="#eae6df" stroke-width="15" filter="url(#shadow)" />
|
||||
<!-- Tire Tread Grooves -->
|
||||
<circle cx="550" cy="450" r="78" fill="none" stroke="#d6d1ca" stroke-width="1" stroke-dasharray="4 8" />
|
||||
<circle cx="550" cy="450" r="65" fill="none" stroke="#d2cbc4" stroke-width="1" />
|
||||
<!-- Rim (Chrome) -->
|
||||
<circle cx="550" cy="450" r="64" fill="none" stroke="url(#chromeGrad)" stroke-width="5" />
|
||||
<circle cx="550" cy="450" r="61" fill="none" stroke="#334155" stroke-width="1" />
|
||||
<!-- Spokes -->
|
||||
<g stroke="#eaeaea" stroke-width="0.75" opacity="0.85">
|
||||
<line x1="550" y1="450" x2="486" y2="450" /> <line x1="550" y1="450" x2="614" y2="450" />
|
||||
<line x1="550" y1="450" x2="550" y2="386" /> <line x1="550" y1="450" x2="550" y2="514" />
|
||||
<line x1="550" y1="450" x2="505" y2="405" /> <line x1="550" y1="450" x2="595" y2="495" />
|
||||
<line x1="550" y1="450" x2="505" y2="495" /> <line x1="550" y1="450" x2="595" y2="405" />
|
||||
<line x1="550" y1="450" x2="521" y2="392" /> <line x1="550" y1="450" x2="579" y2="508" />
|
||||
<line x1="550" y1="450" x2="579" y2="392" /> <line x1="550" y1="450" x2="521" y2="508" />
|
||||
<line x1="550" y1="450" x2="489" y2="423" /> <line x1="550" y1="450" x2="611" y2="477" />
|
||||
<line x1="550" y1="450" x2="489" y2="477" /> <line x1="550" y1="450" x2="611" y2="423" />
|
||||
</g>
|
||||
<!-- Wheel Hub -->
|
||||
<circle cx="550" cy="450" r="10" fill="url(#chromeGrad)" stroke="#475569" stroke-width="1" />
|
||||
</g>
|
||||
|
||||
<!-- CRANKSET, CHAIN & CHAINSTAYS -->
|
||||
<!-- Rear Fork / Chain Stay (behind chain) -->
|
||||
<line x1="230" y1="450" x2="360" y2="450" stroke="url(#chromeGrad)" stroke-width="6" stroke-linecap="round" />
|
||||
<!-- Rear Fork / Seat Stay (behind wheel) -->
|
||||
<line x1="230" y1="450" x2="315" y2="320" stroke="url(#chromeGrad)" stroke-width="5" stroke-linecap="round" />
|
||||
|
||||
<!-- Bicycle Chain Loop -->
|
||||
<path d="M 230,450 C 270,432 320,432 360,432 C 378,432 382,450 380,458 C 380,466 374,470 360,468 C 320,468 270,468 230,450 Z" fill="none" stroke="#475569" stroke-width="3" stroke-linejoin="round" />
|
||||
<!-- Chain Details (dots/links) -->
|
||||
<path d="M 230,450 C 270,432 320,432 360,432 M 380,458 C 380,466 374,470 360,468 C 320,468 270,468 230,450" fill="none" stroke="#94a3b8" stroke-width="2.5" stroke-dasharray="3 3" />
|
||||
|
||||
<!-- Large Front Chainring / Sprocket -->
|
||||
<circle cx="360" cy="450" r="24" fill="url(#chromeGrad)" stroke="#334155" stroke-width="1.5" />
|
||||
<!-- Chainring Cutouts -->
|
||||
<g fill="#16151d" opacity="0.6">
|
||||
<circle cx="360" cy="450" r="17" fill="none" stroke="#16151d" stroke-width="2" stroke-dasharray="6 4" />
|
||||
</g>
|
||||
|
||||
<!-- BIKE FRAME (Cruiser Style Curved Tubes) -->
|
||||
<!-- Seat Tube (straight) -->
|
||||
<line x1="315" y1="320" x2="360" y2="450" stroke="url(#frameGrad)" stroke-width="10" stroke-linecap="round" />
|
||||
|
||||
<!-- Curved Top Tube (Cruiser signature curve) -->
|
||||
<path d="M 315,335 C 370,335 440,305 485,305" fill="none" stroke="url(#frameGrad)" stroke-width="10" stroke-linecap="round" filter="url(#shadow)" />
|
||||
<!-- Second Parallel Thin Curved Tube -->
|
||||
<path d="M 315,360 C 370,360 430,335 482,330" fill="none" stroke="url(#frameGrad)" stroke-width="6" stroke-linecap="round" />
|
||||
|
||||
<!-- Down Tube (Curved down) -->
|
||||
<path d="M 315,350 C 345,410 350,440 360,450" fill="none" stroke="url(#frameGrad)" stroke-width="11" stroke-linecap="round" />
|
||||
<path d="M 360,450 C 400,450 450,420 485,310" fill="none" stroke="url(#frameGrad)" stroke-width="11" stroke-linecap="round" />
|
||||
|
||||
<!-- Front Fork (Chrome) -->
|
||||
<g id="front-fork">
|
||||
<line x1="485" y1="300" x2="550" y2="450" stroke="url(#chromeGrad)" stroke-width="9" stroke-linecap="round" filter="url(#shadow)" />
|
||||
<line x1="485" y1="275" x2="485" y2="300" stroke="url(#chromeGrad)" stroke-width="12" />
|
||||
<circle cx="485" cy="300" r="8" fill="#334155" />
|
||||
</g>
|
||||
|
||||
<!-- Fenders (Retro mudguards - Chrome) -->
|
||||
<!-- Rear Fender -->
|
||||
<path d="M 152,450 C 152,380 200,345 270,335" fill="none" stroke="url(#chromeGrad)" stroke-width="8" stroke-linecap="round" filter="url(#shadow)" />
|
||||
<line x1="158" y1="446" x2="230" y2="450" stroke="#475569" stroke-width="2.5" />
|
||||
<line x1="210" y1="375" x2="230" y2="450" stroke="#475569" stroke-width="2.5" />
|
||||
<!-- Front Fender -->
|
||||
<path d="M 488,405 C 505,385 540,373 585,390 C 605,405 615,425 618,450" fill="none" stroke="url(#chromeGrad)" stroke-width="8" stroke-linecap="round" filter="url(#shadow)" />
|
||||
<line x1="550" y1="450" x2="605" y2="408" stroke="#475569" stroke-width="2.5" />
|
||||
|
||||
<!-- HANDLEBARS & STEM -->
|
||||
<g id="handlebars">
|
||||
<!-- Handlebar Stem -->
|
||||
<line x1="485" y1="275" x2="480" y2="225" stroke="url(#chromeGrad)" stroke-width="8" />
|
||||
<!-- Handlebar curve facing backward -->
|
||||
<path d="M 480,225 C 475,210 455,210 435,225 C 425,232 420,242 415,255" fill="none" stroke="url(#chromeGrad)" stroke-width="7" stroke-linecap="round" />
|
||||
<!-- Handlebar Grip (Leather) -->
|
||||
<path d="M 420,245 L 413,258" stroke="url(#leatherGrad)" stroke-width="10" stroke-linecap="round" />
|
||||
<circle cx="413" cy="258" r="5" fill="#3e2723" />
|
||||
|
||||
<!-- Far handlebar side (slightly visible) -->
|
||||
<path d="M 480,225 C 488,215 503,215 515,223" fill="none" stroke="#94a3b8" stroke-width="5" stroke-linecap="round" opacity="0.7" />
|
||||
</g>
|
||||
|
||||
<!-- RETRO FRONT HEADLIGHT -->
|
||||
<g id="headlight">
|
||||
<path d="M 480,245 L 500,245 C 505,245 512,250 512,258 C 512,266 505,271 500,271 L 480,271 Z" fill="url(#chromeGrad)" stroke="#334155" stroke-width="1" />
|
||||
<!-- Golden Lens -->
|
||||
<path d="M 512,250 C 515,252 515,264 512,266 Z" fill="#fff59d" stroke="#fbc02d" stroke-width="1" />
|
||||
<!-- Light beam glow -->
|
||||
<polygon points="513,252 640,220 640,320 513,264" fill="url(#sunGrad)" opacity="0.25" style="mix-blend-mode: screen;" />
|
||||
<!-- Mounting bracket -->
|
||||
<line x1="485" y1="265" x2="482" y2="255" stroke="#475569" stroke-width="3" />
|
||||
</g>
|
||||
|
||||
<!-- SADDLE / SEAT -->
|
||||
<g id="saddle" filter="url(#shadow)">
|
||||
<!-- Seat Post -->
|
||||
<line x1="315" y1="320" x2="315" y2="295" stroke="url(#chromeGrad)" stroke-width="7" />
|
||||
<!-- Seat Springs -->
|
||||
<circle cx="310" cy="295" r="5" fill="none" stroke="#475569" stroke-width="2.5" />
|
||||
<circle cx="320" cy="295" r="5" fill="none" stroke="#475569" stroke-width="2.5" />
|
||||
<!-- Cushion (Leather) -->
|
||||
<path d="M 292,288 C 292,275 310,270 330,270 C 345,270 355,278 350,285 C 345,292 315,294 292,288 Z" fill="url(#leatherGrad)" stroke="#3e2723" stroke-width="1" />
|
||||
</g>
|
||||
|
||||
<!-- FRONT WICKER BASKET & FLOWERS -->
|
||||
<g id="basket" filter="url(#shadow)">
|
||||
<!-- Leather mounting straps -->
|
||||
<rect x="474" y="240" width="4" height="12" rx="1" fill="#5c4033" />
|
||||
<rect x="492" y="242" width="4" height="12" rx="1" fill="#5c4033" opacity="0.8" />
|
||||
<!-- Basket body -->
|
||||
<path d="M 495,250 L 545,255 C 550,255 553,260 550,270 L 538,312 C 536,318 531,320 525,320 L 485,315 C 480,315 478,310 480,302 L 488,255 C 489,250 492,250 495,250 Z" fill="#d7ccc8" stroke="#a1887f" stroke-width="1.5" />
|
||||
<!-- Woven texture lines -->
|
||||
<!-- Diagonals -->
|
||||
<path d="M 488,255 L 525,320 M 498,251 L 532,310 M 510,252 L 538,300 M 525,253 L 542,285 M 484,275 L 510,318 M 481,295 L 495,317" stroke="#b09e99" stroke-width="1" />
|
||||
<path d="M 545,255 L 485,315 M 535,254 L 481,302 M 520,253 L 480,288 M 505,251 L 480,272 M 548,272 L 505,318 M 550,290 L 522,320" stroke="#b09e99" stroke-width="1" />
|
||||
<!-- Rim -->
|
||||
<path d="M 488,252 Q 515,252 546,255" fill="none" stroke="#a1887f" stroke-width="4.5" stroke-linecap="round" />
|
||||
|
||||
<!-- Flowers peeking out of the basket -->
|
||||
<!-- Red Hibiscus -->
|
||||
<g transform="translate(492, 245) scale(0.6)">
|
||||
<circle cx="0" cy="0" r="12" fill="#e91e63" />
|
||||
<path d="M 0,0 C -15,-15 -25,-5 -15,10 C -5,25 15,25 20,5 C 25,-15 15,-25 0,-20 C -10,-15 -15,-5 0,0 Z" fill="#ff1744" opacity="0.9" />
|
||||
<circle cx="0" cy="0" r="4" fill="#ffeb3b" />
|
||||
<line x1="0" y1="0" x2="15" y2="-15" stroke="#ffeb3b" stroke-width="2" />
|
||||
<circle cx="15" cy="-15" r="2" fill="#ffeb3b" />
|
||||
</g>
|
||||
<!-- Green Leaves -->
|
||||
<path d="M 522,250 Q 535,235 530,225 Q 515,235 522,250 Z" fill="#2e7d32" />
|
||||
<path d="M 484,255 Q 470,245 475,235 Q 490,245 484,255 Z" fill="#1b5e20" />
|
||||
<!-- White Plumeria -->
|
||||
<g transform="translate(515, 246) scale(0.5)">
|
||||
<ellipse cx="0" cy="-10" rx="7" ry="12" fill="#ffffff" transform="rotate(0)" />
|
||||
<ellipse cx="10" cy="-5" rx="7" ry="12" fill="#ffffff" transform="rotate(72 10 -5)" />
|
||||
<ellipse cx="8" cy="8" rx="7" ry="12" fill="#ffffff" transform="rotate(144 8 8)" />
|
||||
<ellipse cx="-5" cy="8" rx="7" ry="12" fill="#ffffff" transform="rotate(216 -5 8)" />
|
||||
<ellipse cx="-8" cy="-5" rx="7" ry="12" fill="#ffffff" transform="rotate(288 -8 -5)" />
|
||||
<circle cx="0" cy="0" r="5" fill="#ffeb3b" />
|
||||
</g>
|
||||
<!-- A cute little silver fish peeking out -->
|
||||
<g transform="translate(532, 248) rotate(-20) scale(0.6)">
|
||||
<!-- Fish Body -->
|
||||
<path d="M 0,0 C 15,-10 30,-5 40,5 C 30,10 15,10 0,0" fill="#cfd8dc" stroke="#78909c" stroke-width="1" />
|
||||
<!-- Tail -->
|
||||
<path d="M 38,3 L 48,-3 L 45,5 L 48,12 L 38,6 Z" fill="#b0bec5" stroke="#78909c" stroke-width="1" />
|
||||
<!-- Eye -->
|
||||
<circle cx="8" cy="1" r="2.5" fill="#ffffff" />
|
||||
<circle cx="9" cy="1" r="1" fill="#000000" />
|
||||
<!-- Smiling Mouth -->
|
||||
<path d="M 3,4 Q 6,6 8,4" fill="none" stroke="#78909c" stroke-width="1" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== THE PELICAN RIDER ==================== -->
|
||||
<g id="pelican">
|
||||
<!-- BACK/FAR LEG & PEDAL (Drawn behind the body) -->
|
||||
<g id="far-leg">
|
||||
<!-- Far Hip/Thigh (highly shadowed) -->
|
||||
<path d="M 320,315 C 310,345 320,380 345,395" fill="none" stroke="#e65100" stroke-width="12" stroke-linecap="round" opacity="0.6" />
|
||||
<!-- Far Shin -->
|
||||
<line x1="345" y1="395" x2="395" y2="470" stroke="#f57c00" stroke-width="8" stroke-linecap="round" opacity="0.7" />
|
||||
<!-- Far Foot (on lower pedal) -->
|
||||
<path d="M 390,468 C 390,468 410,480 422,480 C 428,480 425,472 415,468 Z" fill="#ef6c00" opacity="0.7" />
|
||||
</g>
|
||||
<!-- Far Pedal (Lower, extending forward-down) -->
|
||||
<g id="far-pedal" opacity="0.8">
|
||||
<!-- Pedal Crank Arm -->
|
||||
<line x1="360" y1="450" x2="395" y2="475" stroke="url(#chromeGrad)" stroke-width="5.5" stroke-linecap="round" />
|
||||
<!-- Pedal Block -->
|
||||
<polygon points="385,471 410,471 405,481 380,481" fill="#1e293b" stroke="#475569" stroke-width="1" />
|
||||
</g>
|
||||
|
||||
<!-- PELICAN MAIN BODY -->
|
||||
<!-- Tail Feathers -->
|
||||
<g id="tail-feathers">
|
||||
<path d="M 230,270 C 220,285 225,305 242,315 C 255,310 262,295 265,280 Z" fill="#cbd5e1" stroke="#94a3b8" stroke-width="1" filter="url(#shadow)" />
|
||||
<path d="M 235,285 C 228,295 232,310 248,312 C 252,305 258,292 258,285 Z" fill="#94a3b8" />
|
||||
<!-- Layered fluff -->
|
||||
<path d="M 245,260 C 235,270 238,288 252,295 C 260,288 268,272 265,262 Z" fill="#f1f5f9" />
|
||||
</g>
|
||||
|
||||
<!-- Torso / Main Body -->
|
||||
<path id="torso" d="M 260,275 C 250,230 310,210 355,230 C 385,245 400,285 385,325 C 365,365 285,360 260,275 Z" fill="url(#pelicanFeather)" filter="url(#shadow)" />
|
||||
|
||||
<!-- Soft Feather Texture Overlays on Torso -->
|
||||
<g stroke="#e2e8f0" stroke-width="1.5" fill="none" opacity="0.8">
|
||||
<path d="M 285,255 C 295,260 305,255 315,260" />
|
||||
<path d="M 275,280 C 290,288 305,280 320,288" />
|
||||
<path d="M 290,305 C 305,315 320,305 335,312" />
|
||||
<path d="M 315,330 C 328,338 340,330 352,335" />
|
||||
<!-- Dark feather tips near rear -->
|
||||
<path d="M 258,278 C 265,285 272,280 280,285" stroke="#94a3b8" />
|
||||
<path d="M 264,298 C 272,305 280,298 288,305" stroke="#94a3b8" />
|
||||
</g>
|
||||
|
||||
<!-- WIND-BLOWN SCARF (Under the neck, blowing backwards) -->
|
||||
<g id="scarf-back" filter="url(#shadow)">
|
||||
<!-- Wave 1 (Longer tail) -->
|
||||
<path d="M 295,232 C 220,225 180,185 130,195 C 120,197 122,212 135,212 C 185,212 215,245 290,252 Z" fill="#d32f2f" />
|
||||
<!-- Red and White Stripes -->
|
||||
<path d="M 255,241 L 248,227 M 235,234 L 228,220 M 215,225 L 208,211 M 195,216 L 188,202 M 175,208 L 168,195 M 155,201 L 148,197 M 138,199 L 134,196" stroke="#ffffff" stroke-width="7" />
|
||||
<!-- Wave 2 (Shorter tail) -->
|
||||
<path d="M 295,242 C 240,242 200,210 155,225 C 148,227 150,240 160,240 C 205,240 235,262 290,257 Z" fill="#c62828" />
|
||||
<path d="M 260,251 L 252,242 M 240,249 L 232,240 M 220,246 L 212,237 M 200,243 L 192,234 M 180,239 L 172,231 M 164,236 L 158,230" stroke="#ffebee" stroke-width="5" />
|
||||
<!-- Fringes at the end -->
|
||||
<line x1="130" y1="195" x2="120" y2="192" stroke="#ffca28" stroke-width="2" />
|
||||
<line x1="131" y1="200" x2="121" y2="198" stroke="#ffffff" stroke-width="2" />
|
||||
<line x1="132" y1="205" x2="122" y2="204" stroke="#ffca28" stroke-width="2" />
|
||||
<line x1="133" y1="210" x2="123" y2="210" stroke="#ffffff" stroke-width="2" />
|
||||
</g>
|
||||
|
||||
<!-- S-CURVED FEATHERY NECK -->
|
||||
<g id="neck">
|
||||
<!-- Thick neck path -->
|
||||
<path d="M 345,230 C 370,180 340,115 390,105 C 400,102 415,110 422,125" fill="none" stroke="url(#pelicanFeather)" stroke-width="36" stroke-linecap="round" filter="url(#shadow)" />
|
||||
<!-- Shadow on neck curve -->
|
||||
<path d="M 345,230 C 370,180 340,115 390,105" fill="none" stroke="#94a3b8" stroke-width="4" stroke-linecap="round" opacity="0.25" />
|
||||
<!-- Feather fluff along the back of neck -->
|
||||
<path d="M 353,190 Q 343,180 348,172" stroke="#cbd5e1" stroke-width="2" fill="none" />
|
||||
<path d="M 358,165 Q 348,155 353,147" stroke="#cbd5e1" stroke-width="2" fill="none" />
|
||||
<path d="M 368,140 Q 358,130 363,122" stroke="#cbd5e1" stroke-width="2" fill="none" />
|
||||
</g>
|
||||
|
||||
<!-- FOREGROUND/NEAR LEG & PEDAL -->
|
||||
<!-- Near Pedal (Upper, extending up-back) -->
|
||||
<g id="near-pedal" filter="url(#shadow)">
|
||||
<!-- Pedal Crank Arm -->
|
||||
<line x1="360" y1="450" x2="330" y2="415" stroke="url(#chromeGrad)" stroke-width="7" stroke-linecap="round" />
|
||||
<!-- Pedal Hub -->
|
||||
<circle cx="330" cy="415" r="5" fill="#334155" />
|
||||
<!-- Pedal Block -->
|
||||
<polygon points="312,410 342,410 348,420 318,420" fill="#334155" stroke="#94a3b8" stroke-width="1" />
|
||||
</g>
|
||||
|
||||
<g id="near-leg" filter="url(#shadow)">
|
||||
<!-- Hip / Joint -->
|
||||
<path d="M 325,325 C 300,350 295,370 325,395" fill="none" stroke="url(#beakGrad)" stroke-width="14" stroke-linecap="round" />
|
||||
<!-- Thigh fluff shading -->
|
||||
<path d="M 305,335 C 295,350 295,362 312,375" fill="none" stroke="#cbd5e1" stroke-width="10" stroke-linecap="round" />
|
||||
<!-- Knee to Foot (Ankle) -->
|
||||
<line x1="325" y1="395" x2="330" y2="415" stroke="#f57c00" stroke-width="9" stroke-linecap="round" />
|
||||
<!-- Leg scales/wrinkles -->
|
||||
<line x1="325" y1="400" x2="329" y2="401" stroke="#e65100" stroke-width="1.5" />
|
||||
<line x1="326" y1="406" x2="330" y2="407" stroke="#e65100" stroke-width="1.5" />
|
||||
<!-- Big Webbed Foot gripping pedal -->
|
||||
<!-- Heel -->
|
||||
<circle cx="330" cy="415" r="6" fill="#ef6c00" />
|
||||
<!-- Three webbed toes overlapping pedal -->
|
||||
<path d="M 330,415 C 322,415 310,405 298,408 C 308,416 322,418 330,417" fill="#ff9800" stroke="#e65100" stroke-width="1" />
|
||||
<path d="M 330,415 C 320,422 305,422 295,422 C 308,425 324,422 330,418" fill="#ff9800" stroke="#e65100" stroke-width="1" />
|
||||
<path d="M 330,415 C 325,428 312,435 302,435 C 314,432 325,425 330,418" fill="#ff9800" stroke="#e65100" stroke-width="1" />
|
||||
<!-- Toe Claws -->
|
||||
<circle cx="297" cy="408" r="1.5" fill="#eceff1" />
|
||||
<circle cx="294" cy="422" r="1.5" fill="#eceff1" />
|
||||
<circle cx="301" cy="435" r="1.5" fill="#eceff1" />
|
||||
</g>
|
||||
|
||||
<!-- HEAD, EYE, BEAK, POUCH & SUNGLASSES -->
|
||||
<g id="head-group" filter="url(#shadow)">
|
||||
<!-- Head Base -->
|
||||
<circle cx="415" cy="115" r="22" fill="url(#pelicanFeather)" />
|
||||
<!-- Feather tuft / crest blowing back -->
|
||||
<path d="M 398,102 C 385,92 372,98 360,95 C 372,105 385,108 395,108 Z" fill="#cbd5e1" />
|
||||
<path d="M 394,112 C 380,105 365,112 352,110 C 366,118 380,118 393,116 Z" fill="#94a3b8" />
|
||||
<path d="M 397,122 C 385,118 372,128 358,128 C 372,132 385,130 395,125 Z" fill="#cbd5e1" />
|
||||
|
||||
<!-- Cute Expressive Eye -->
|
||||
<circle cx="418" cy="112" r="5.5" fill="#ffffff" />
|
||||
<circle cx="419" cy="112" r="4" fill="#00acc1" /> <!-- Bright blue iris -->
|
||||
<circle cx="420" cy="112" r="2.5" fill="#090d16" /> <!-- Pupil -->
|
||||
<circle cx="421" cy="110" r="1" fill="#ffffff" /> <!-- Catchlight -->
|
||||
<!-- Happy eyebrow -->
|
||||
<path d="M 411,105 Q 418,101 425,106" fill="none" stroke="#475569" stroke-width="2" stroke-linecap="round" />
|
||||
|
||||
<!-- FOREGROUND WING (Holding the Handlebars!) -->
|
||||
<!-- Wing Shoulder -->
|
||||
<path d="M 310,240 C 330,240 355,255 360,275 C 362,295 330,325 310,320 Z" fill="url(#pelicanFeather)" />
|
||||
<!-- Wing Feathers folding forward and down -->
|
||||
<path d="M 345,265 C 375,275 410,285 432,266 C 438,260 442,252 445,248" fill="none" stroke="url(#pelicanFeather)" stroke-width="16" stroke-linecap="round" />
|
||||
<!-- Wingtip feathers wrapping around the leather grip -->
|
||||
<!-- Feather finger 1 -->
|
||||
<path d="M 432,266 C 430,255 418,252 410,256" fill="none" stroke="#cbd5e1" stroke-width="7" stroke-linecap="round" />
|
||||
<!-- Feather finger 2 (wrapping handlebar) -->
|
||||
<path d="M 434,268 C 430,258 414,258 413,264" fill="none" stroke="#f1f5f9" stroke-width="6.5" stroke-linecap="round" />
|
||||
<!-- Feather finger 3 -->
|
||||
<path d="M 432,272 C 428,266 416,268 416,274" fill="none" stroke="#cbd5e1" stroke-width="6" stroke-linecap="round" />
|
||||
<!-- Wing Layer lines for 3D effect -->
|
||||
<path d="M 330,255 Q 355,270 380,275" fill="none" stroke="#cbd5e1" stroke-width="1.5" />
|
||||
<path d="M 325,275 Q 350,290 375,292" fill="none" stroke="#cbd5e1" stroke-width="1.5" />
|
||||
<path d="M 315,295 Q 340,305 365,302" fill="none" stroke="#94a3b8" stroke-width="1.5" opacity="0.6" />
|
||||
|
||||
<!-- THE BEAK (Upper bill) -->
|
||||
<!-- Long, beautiful, curved upper beak -->
|
||||
<path d="M 432,108 C 455,110 510,120 545,132 C 550,134 554,138 552,142 C 549,145 540,143 535,141 C 495,131 450,123 430,121 Z" fill="url(#beakGrad)" />
|
||||
<!-- Beak tip hook -->
|
||||
<path d="M 548,133 Q 555,138 550,143 Q 544,141 546,136 Z" fill="#bf360c" />
|
||||
<!-- Nostril slit -->
|
||||
<path d="M 445,115 Q 455,117 460,118" fill="none" stroke="#b71c1c" stroke-width="1" stroke-linecap="round" />
|
||||
|
||||
<!-- THE GIGANTIC THROAT POUCH -->
|
||||
<!-- Drop down elastic pouch -->
|
||||
<path d="M 430,121 C 430,121 432,185 450,195 C 470,205 515,180 535,141 C 490,131 450,123 430,121 Z" fill="url(#pouchGrad)" />
|
||||
<!-- Pouch texture wrinkles -->
|
||||
<path d="M 442,132 Q 448,168 456,182" fill="none" stroke="#ffb300" stroke-width="1" opacity="0.6" />
|
||||
<path d="M 455,132 Q 465,165 478,178" fill="none" stroke="#ffb300" stroke-width="1" opacity="0.6" />
|
||||
<path d="M 470,133 Q 482,160 500,165" fill="none" stroke="#ff8f00" stroke-width="1" opacity="0.5" />
|
||||
<path d="M 490,134 Q 502,152 518,154" fill="none" stroke="#ff8f00" stroke-width="1" opacity="0.5" />
|
||||
<!-- Lower jaw line supporting the pouch -->
|
||||
<path d="M 430,121 C 430,121 432,185 450,195 C 470,205 515,180 535,141" fill="none" stroke="#e65100" stroke-width="2.5" />
|
||||
|
||||
<!-- SILHOUETTE OF A LITTLE FISH INSIDE THE POUCH (Easter Egg!) -->
|
||||
<g opacity="0.22" transform="translate(452, 142) rotate(25) scale(0.85)">
|
||||
<path d="M 0,0 C 10,-8 25,-4 32,4 C 24,8 12,8 0,0" fill="#02476b" />
|
||||
<path d="M 30,2 L 38,-3 L 36,4 L 38,10 L 30,5 Z" fill="#02476b" />
|
||||
<circle cx="6" cy="1" r="1.5" fill="#ffe082" />
|
||||
</g>
|
||||
<!-- Small bubbles inside pouch around the fish -->
|
||||
<circle cx="475" cy="155" r="2" fill="#ffffff" opacity="0.3" />
|
||||
<circle cx="482" cy="150" r="1" fill="#ffffff" opacity="0.3" />
|
||||
<circle cx="468" cy="162" r="1.5" fill="#ffffff" opacity="0.2" />
|
||||
|
||||
<!-- COOL AVIATOR SUNGLASSES -->
|
||||
<g id="sunglasses">
|
||||
<!-- Lens facing us -->
|
||||
<path d="M 412,112 C 412,106 426,106 428,114 C 428,122 414,124 412,112 Z" fill="#263238" stroke="#ffb300" stroke-width="1.5" />
|
||||
<path d="M 412,112 C 412,106 426,106 428,114 C 428,122 414,124 412,112 Z" fill="url(#beakGrad)" opacity="0.4" style="mix-blend-mode: color-dodge;" />
|
||||
<!-- Diagonal glass reflection -->
|
||||
<polygon points="415,109 425,112 422,121 413,115" fill="#ffffff" opacity="0.25" />
|
||||
|
||||
<!-- Bridge -->
|
||||
<rect x="427" y="111" width="5" height="1.5" fill="#ffb300" />
|
||||
|
||||
<!-- Second Lens (partially hidden/angled) -->
|
||||
<path d="M 431,113 C 431,109 441,110 442,116 C 442,122 432,123 431,113 Z" fill="#263238" stroke="#ffb300" stroke-width="1.2" />
|
||||
<path d="M 431,113 C 431,109 441,110 442,116 C 442,122 432,123 431,113 Z" fill="url(#beakGrad)" opacity="0.4" style="mix-blend-mode: color-dodge;" />
|
||||
<polygon points="433,111 439,113 437,120 432,116" fill="#ffffff" opacity="0.2" />
|
||||
|
||||
<!-- Frame temples / arms going back -->
|
||||
<path d="M 412,111 Q 403,110 400,114" fill="none" stroke="#ffb300" stroke-width="1.2" />
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- FOREGROUND STRIPED SCARF KNOT & WIND FLOW (Drawn in front of neck/shoulder) -->
|
||||
<g id="scarf-front" filter="url(#shadow)">
|
||||
<!-- Knot around neck -->
|
||||
<path d="M 378,212 Q 388,210 398,220 Q 388,235 375,225 Z" fill="#d32f2f" />
|
||||
<!-- Scarf wrap -->
|
||||
<path d="M 364,195 C 378,195 396,204 398,218 C 392,230 374,224 358,210 Z" fill="#c62828" />
|
||||
<!-- Stripes on wrap -->
|
||||
<path d="M 368,195 Q 375,206 376,219 M 382,197 Q 388,208 390,222" stroke="#ffffff" stroke-width="4.5" fill="none" />
|
||||
<!-- Little hanging knot tie -->
|
||||
<path d="M 388,218 C 394,224 396,234 392,242 C 386,242 382,232 384,222 Z" fill="#b71c1c" />
|
||||
<path d="M 391,223 C 396,228 398,235 395,241" stroke="#ffffff" stroke-width="3" fill="none" />
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== DYNAMIC FOREGROUND EFFECTS ==================== -->
|
||||
<!-- Splashing wet sand droplets and dust kicked up by rear tire -->
|
||||
<g fill="#ffe082" opacity="0.5">
|
||||
<circle cx="152" cy="515" r="2.5" />
|
||||
<circle cx="145" cy="505" r="1.5" />
|
||||
<circle cx="138" cy="510" r="2" />
|
||||
<circle cx="142" cy="522" r="1" />
|
||||
<circle cx="160" cy="495" r="2" opacity="0.3" />
|
||||
<circle cx="172" cy="490" r="1.5" opacity="0.3" />
|
||||
</g>
|
||||
|
||||
<!-- Splashing water droplets and foam near the tires (riding through shallow water) -->
|
||||
<g fill="#ffffff" opacity="0.75">
|
||||
<path d="M 230,520 Q 215,505 195,502 Q 218,515 230,520 Z" />
|
||||
<path d="M 235,520 Q 250,502 270,498 Q 252,514 235,520 Z" />
|
||||
<path d="M 550,520 Q 532,508 515,504 Q 535,515 550,520 Z" />
|
||||
<path d="M 555,520 Q 572,504 595,500 Q 575,515 555,520 Z" />
|
||||
<!-- Droplets -->
|
||||
<circle cx="198" cy="495" r="2" />
|
||||
<circle cx="185" cy="501" r="1" />
|
||||
<circle cx="210" cy="488" r="1.5" />
|
||||
<circle cx="274" cy="492" r="2" />
|
||||
<circle cx="285" cy="498" r="1" />
|
||||
<circle cx="510" cy="498" r="1.5" />
|
||||
<circle cx="498" cy="502" r="1" />
|
||||
<circle cx="600" cy="494" r="2" />
|
||||
<circle cx="612" cy="500" r="1" />
|
||||
</g>
|
||||
|
||||
|
||||
<!-- ==================== FOREGROUND FRAMING ==================== -->
|
||||
<!-- Silhouetted beach grass blowing in the wind (Bottom right corner) -->
|
||||
<g fill="none" stroke="#240c02" stroke-linecap="round" opacity="0.95" filter="url(#shadow)">
|
||||
<!-- Sand hill bump -->
|
||||
<path d="M 680,600 Q 740,540 810,530 L 810,600 Z" fill="url(#sandGrad)" stroke="none" />
|
||||
<!-- Grass blades -->
|
||||
<path d="M 720,560 Q 690,470 640,440" stroke-width="4.5" />
|
||||
<path d="M 735,555 Q 710,450 660,420" stroke-width="5" />
|
||||
<path d="M 750,550 Q 735,430 670,395" stroke-width="5.5" />
|
||||
<path d="M 755,555 Q 755,460 710,425" stroke-width="4" />
|
||||
<path d="M 770,540 Q 775,440 720,405" stroke-width="5" />
|
||||
<path d="M 785,535 Q 805,420 755,385" stroke-width="6" />
|
||||
<path d="M 790,540 Q 820,445 780,410" stroke-width="4.5" />
|
||||
<path d="M 800,530 Q 840,450 810,420" stroke-width="3.5" />
|
||||
<!-- Warm highlights on grass from the sunset -->
|
||||
<path d="M 720,560 Q 690,470 640,440" stroke="#fbc02d" stroke-width="1.5" opacity="0.7" />
|
||||
<path d="M 735,555 Q 710,450 660,420" stroke="#ff9100" stroke-width="1.5" opacity="0.7" />
|
||||
<path d="M 750,550 Q 735,430 670,395" stroke="#fbc02d" stroke-width="2" opacity="0.8" />
|
||||
<path d="M 770,540 Q 775,440 720,405" stroke="#ff9100" stroke-width="1.5" opacity="0.7" />
|
||||
<path d="M 785,535 Q 805,420 755,385" stroke="#fbc02d" stroke-width="2.5" opacity="0.8" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 37 KiB |
Reference in new issue
Block a user