Merge 69178d08bc into 9bc10bd391
This commit is contained in:
commit
d13eca28e0
|
|
@ -0,0 +1,138 @@
|
|||
runAfterLoad(function(){
|
||||
const patternCache = {};
|
||||
let currentPatternID = null;
|
||||
let nextPatternID = 1;
|
||||
const undoStack = [];
|
||||
const redoStack = [];
|
||||
const maxHistory = 50;
|
||||
|
||||
function saveAction(pixels){
|
||||
undoStack.push(pixels.map(px=>({x:px.x,y:px.y,element:px.element,color:px.color,patternID:px.patternID})));
|
||||
if(undoStack.length>maxHistory) undoStack.shift();
|
||||
redoStack.length=0;
|
||||
}
|
||||
|
||||
function undo(){
|
||||
if(!undoStack.length) return;
|
||||
const action = undoStack.pop();
|
||||
const redoPixels = [];
|
||||
action.forEach(a=>{
|
||||
const px = pixelMap[a.x][a.y];
|
||||
redoPixels.push({x:a.x,y:a.y,element:px.element,color:px.color,patternID:px.patternID});
|
||||
px.element=a.element; px.color=a.color; px.patternID=a.patternID;
|
||||
});
|
||||
redoStack.push(redoPixels);
|
||||
}
|
||||
|
||||
function redo(){
|
||||
if(!redoStack.length) return;
|
||||
const action = redoStack.pop();
|
||||
const undoPixels = [];
|
||||
action.forEach(a=>{
|
||||
const px = pixelMap[a.x][a.y];
|
||||
undoPixels.push({x:a.x,y:a.y,element:px.element,color:px.color,patternID:px.patternID});
|
||||
px.element=a.element; px.color=a.color; px.patternID=a.patternID;
|
||||
});
|
||||
undoStack.push(undoPixels);
|
||||
}
|
||||
|
||||
elements.undo_tool = {
|
||||
color:"#f00", tool:undo, category:"tools", description:"Undo last action"
|
||||
};
|
||||
elements.redo_tool = {
|
||||
color:"#0f0", tool:redo, category:"tools", description:"Redo last undone action"
|
||||
};
|
||||
|
||||
elements.pattern_painter = {
|
||||
color:"#888", tool:function(pixel,x,y){
|
||||
if(!currentPatternID){
|
||||
currentPatternID="pattern_"+(nextPatternID++);
|
||||
patternCache[currentPatternID]=Array(8).fill().map(()=>Array(8).fill(0));
|
||||
}
|
||||
const pat = patternCache[currentPatternID];
|
||||
const mx=Math.floor((x*8)%8), my=Math.floor((y*8)%8);
|
||||
pat[my][mx]=1-pat[my][mx];
|
||||
}, category:"tools", description:"Pattern painter (8x8)"
|
||||
};
|
||||
|
||||
function drawPattern(ctx, pat, dx, dy, s){
|
||||
for(let py=0;py<8;py++)for(let px=0;px<8;px++){
|
||||
if(pat[py][px]){
|
||||
ctx.fillStyle="#444";
|
||||
ctx.fillRect(dx+px*s/8, dy+py*s/8, s/8, s/8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elements.decor_block={
|
||||
color:"#fff", behavior:behaviors.WALL, category:"solids",
|
||||
onPlace:p=>{if(currentPatternID)p.patternID=currentPatternID; saveAction([p]);},
|
||||
renderer:function(p,ctx,dx,dy,s){
|
||||
if(p.patternID) drawPattern(ctx, patternCache[p.patternID], dx, dy, s);
|
||||
else { ctx.fillStyle=p.color; ctx.fillRect(dx,dy,s,s); }
|
||||
}
|
||||
};
|
||||
|
||||
elements.batch_filler={
|
||||
color:"#66f",
|
||||
tool:function(pixel){
|
||||
const area=[];
|
||||
for(let dx=-2;dx<=2;dx++)for(let dy=-2;dy<=2;dy++){
|
||||
const px=pixelMap[pixel.x+dx]?.[pixel.y+dy];
|
||||
if(px){px.element="decor_block"; if(currentPatternID) px.patternID=currentPatternID; area.push(px);}
|
||||
}
|
||||
saveAction(area);
|
||||
},
|
||||
category:"tools",
|
||||
description:"Fill 5x5 area with pattern"
|
||||
};
|
||||
|
||||
elements.stamp_tool={
|
||||
color:"#ff0",
|
||||
tool:function(pixel){
|
||||
if(!currentPatternID) return;
|
||||
const area=[];
|
||||
for(let dx=0;dx<8;dx++)for(let dy=0;dy<8;dy++){
|
||||
const px=pixelMap[pixel.x+dx]?.[pixel.y+dy];
|
||||
if(px){px.element="decor_block"; px.patternID=currentPatternID; area.push(px);}
|
||||
}
|
||||
saveAction(area);
|
||||
},
|
||||
category:"tools",
|
||||
description:"Stamp 8x8 pattern"
|
||||
};
|
||||
|
||||
elements.gradient_tool={
|
||||
color:"#0ff",
|
||||
tool:function(pixel){
|
||||
const area=[];
|
||||
for(let dx=0;dx<5;dx++)for(let dy=0;dy<5;dy++){
|
||||
const px=pixelMap[pixel.x+dx]?.[pixel.y+dy];
|
||||
if(px){
|
||||
const r=Math.floor(255*dx/4), b=Math.floor(255*dy/4);
|
||||
px.element="decor_block";
|
||||
px.color=`rgb(${r},0,${b})`;
|
||||
area.push(px);
|
||||
}
|
||||
}
|
||||
saveAction(area);
|
||||
},
|
||||
category:"tools",
|
||||
description:"Paint 5x5 gradient"
|
||||
};
|
||||
|
||||
elements.curve_tool={
|
||||
color:"#f0f",
|
||||
tool:function(pixel){
|
||||
const area=[];
|
||||
for(let i=0;i<5;i++){
|
||||
const px=pixelMap[pixel.x+i]?.[pixel.y+Math.floor(Math.sin(i/4*Math.PI)*4)];
|
||||
if(px){px.element="decor_block"; if(currentPatternID) px.patternID=currentPatternID; area.push(px);}
|
||||
}
|
||||
saveAction(area);
|
||||
},
|
||||
category:"tools",
|
||||
description:"Draw sine curve with pattern"
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
runAfterLoad(function(){
|
||||
const patternCache = {};
|
||||
let currentPatternID = null;
|
||||
let nextPatternID = 1;
|
||||
|
||||
elements.pattern_painter = {
|
||||
color:"#888", tool:function(pixel,x,y){
|
||||
if(!currentPatternID){
|
||||
currentPatternID = "pattern_"+(nextPatternID++);
|
||||
patternCache[currentPatternID] = Array(8).fill().map(()=>Array(8).fill(0));
|
||||
}
|
||||
const pat = patternCache[currentPatternID];
|
||||
const mx = Math.floor((x*8)%8), my = Math.floor((y*8)%8);
|
||||
pat[my][mx] = 1 - pat[my][mx];
|
||||
},
|
||||
category:"tools",
|
||||
description:"Pattern painter (8x8)"
|
||||
};
|
||||
|
||||
function drawPattern(ctx, pat, dx, dy, s){
|
||||
for(let py=0;py<8;py++){
|
||||
for(let px=0;px<8;px++){
|
||||
if(pat[py][px]){
|
||||
ctx.fillStyle = "#444";
|
||||
ctx.fillRect(dx + px*s/8, dy + py*s/8, s/8, s/8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function blendColors(c1,c2,t){
|
||||
const parse = c=>parseInt(c.slice(1),16);
|
||||
const r1=((parse(c1)>>16)&255), g1=((parse(c1)>>8)&255), b1=(parse(c1)&255);
|
||||
const r2=((parse(c2)>>16)&255), g2=((parse(c2)>>8)&255), b2=(parse(c2)&255);
|
||||
const r=Math.floor(r1*(1-t)+r2*t), g=Math.floor(g1*(1-t)+g2*t), b=Math.floor(b1*(1-t)+b2*t);
|
||||
return "#"+((1<<24)+(r<<16)+(g<<8)+b).toString(16).slice(1);
|
||||
}
|
||||
|
||||
elements.decor_block = {
|
||||
color:"#fff", behavior:behaviors.WALL, category:"solids",
|
||||
onPlace: p=>{ if(currentPatternID) p.patternID=currentPatternID; },
|
||||
renderer: function(p, ctx, dx, dy, s){
|
||||
if(p.patternID){
|
||||
drawPattern(ctx, patternCache[p.patternID], dx, dy, s);
|
||||
} else { ctx.fillStyle=p.color; ctx.fillRect(dx, dy, s, s); }
|
||||
}
|
||||
};
|
||||
|
||||
elements.fader_block = {
|
||||
color:"#fff", behavior:behaviors.WALL, category:"solids",
|
||||
onPlace: p=>{ if(currentPatternID) p.patternID=currentPatternID; },
|
||||
renderer: function(p, ctx, dx, dy, s){
|
||||
const pat = p.patternID ? patternCache[p.patternID] : null;
|
||||
if(!pat){ ctx.fillStyle=p.color; ctx.fillRect(dx, dy, s, s); return; }
|
||||
for(let py=0;py<8;py++){
|
||||
const t = py/7;
|
||||
for(let px=0;px<8;px++){
|
||||
const c = pat[py][px] ? blendColors("#000","#555",t) : blendColors("#ccc","#eee",t);
|
||||
ctx.fillStyle=c;
|
||||
ctx.fillRect(dx+px*s/8, dy+py*s/8, s/8, s/8);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
elements.cloth = {
|
||||
color:"#aaa", behavior:behaviors.POWDER, category:"solids",
|
||||
onPlace: p=>{ if(currentPatternID) p.patternID=currentPatternID; p.vy=0; },
|
||||
tick: function(p){
|
||||
if(p.y+1>=pixelGridHeight) return;
|
||||
const below = pixelMap[p.x][p.y+1];
|
||||
if(!below||elements[below.element].behavior!==behaviors.WALL) p.y++;
|
||||
},
|
||||
renderer: function(p, ctx, dx, dy, s){
|
||||
if(p.patternID) drawPattern(ctx, patternCache[p.patternID], dx, dy, s);
|
||||
else ctx.fillStyle=p.color; ctx.fillRect(dx, dy, s, s);
|
||||
}
|
||||
};
|
||||
|
||||
elements.fence_block = {
|
||||
color:"#666", behavior:behaviors.WALL, category:"solids",
|
||||
onPlace: p=>{ if(currentPatternID) p.patternID=currentPatternID; },
|
||||
renderer: function(p, ctx, dx, dy, s){
|
||||
if(p.patternID) drawPattern(ctx, patternCache[p.patternID], dx, dy, s);
|
||||
else ctx.fillStyle=p.color; ctx.fillRect(dx, dy, s, s);
|
||||
}
|
||||
};
|
||||
|
||||
runEveryTick(function(){
|
||||
renderPrePixel(function(p, ctx, dx, dy, s){
|
||||
if(p.element==="decor_block"||p.element==="fader_block"||p.element==="cloth"||p.element==="fence_block"){
|
||||
const shade = ctx.createLinearGradient(dx, dy, dx, dy+s);
|
||||
shade.addColorStop(0,"rgba(0,0,0,0)");
|
||||
shade.addColorStop(1,"rgba(0,0,0,0.3)");
|
||||
ctx.fillStyle=shade;
|
||||
ctx.fillRect(dx, dy, s, s);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue