sandboxels/mods/CsC.js

139 lines
4.1 KiB
JavaScript

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"
};
});