mtsedit/src/edit.c

474 lines
18 KiB
C

/*
* mtsedit/edit.c
*
* Copyright (C) 2019 bzt (bztsrc@gitlab)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* @brief The main edit area
*
*/
#include "main.h"
int dx = 0, dz = 0, cx = 0, cz = 0, ox = 0, oy = 0, up = 0, zoom = 128, zz = 0, zx = 0, zs = 0, grid = 1, curronly = 0;
char dstr[32];
/**
* Place a node
*/
void edit_placenode(int y, int z, int x, int i)
{
int j, k;
if(x >= 0 && x < 256 && y >= 0 && y < 256 && z >=0 && z < 256 && i != nodes[y][z][x].param0) {
mts_getbounds(0, NULL, NULL);
hist_add(y, z, x, nodes[y][z][x].param0, nodes[y][z][x].param2, i, nodes[y][z][x].param2);
if(x < mix) mix = x;
if(x > max) max = x;
if(y < miy) miy = y;
if(y > may) may = y;
if(z < miz) miz = z;
if(z > maz) maz = z;
if(nodes[y][z][x].param0) {
for(k = 0, j = 1; j < 15; j++)
if(palette[j] == nodes[y][z][x].param0) { k = j; break; }
if(!k) {
for(j = 15; j > activeblock + 1; j--) palette[j] = palette[j - 1];
palette[activeblock + 1] = nodes[y][z][x].param0;
}
}
blocks[nodes[y][z][x].param0].numref--;
blocks[i].numref++;
nodes[y][z][x].param0 = i;
nodes[y][z][x].param1 = i ? 127 : 0;
}
status = i ? blocks[i].name : NULL;
}
/**
* Add node at cursor to palette
*/
void edit_pipette(int y, int z, int x)
{
int j, k;
if(x >= 0 && x < 256 && y >= 0 && y < 256 && z >=0 && z < 256 && nodes[y][z][x].param0) {
if(nodes[y][z][x].param0) {
for(k = 0, j = 1; j < 15; j++)
if(palette[j] == nodes[y][z][x].param0) { activeblock = k = j; break; }
if(!k) {
for(j = 15; j > 1; j--) palette[j] = palette[j - 1];
activeblock = 1;
palette[activeblock] = nodes[y][z][x].param0;
}
}
status = blocks[nodes[y][z][x].param0].name;
}
}
/**
* Zoom in
*/
int edit_zoomin()
{
if(zoom < 256) { zoom += 32; return 1; }
return 0;
}
/**
* Zoom out
*/
int edit_zoomout()
{
if(zoom > 32) { zoom -= 32; return 1; }
return 0;
}
/**
* Redraw the Edit Area
*/
void edit_redraw(int full)
{
int i, j, k = 160, l, X, Z, x, y, z, sx, sy, mx, zl, s;
unsigned char *b = (uint8_t*)&theme[THEME_TABBG];
SDL_Rect dst, src;
zz = dz * zoom / 128; zx = dx * zoom / 128; zs = 32 * zoom / 128;
if(zz < 1) zz = 1;
if(zx < 1) zx = 1;
if(zs < 1) zs = 1;
zl = zs - 2*(2 + zz);
mx = (((bg->w / 2 / zx) + (bg->h / 2 / zz)) / 2) + 1;
sx = bg->w / 2;
sy = bg->h / 2;
if(full) {
dst.x = dst.y = 0; dst.w = bg->w; dst.h = bg->h;
SDL_FillRect(bg, &dst, theme[THEME_TABBG]);
src.x = 0; src.y = 4 * 32; dst.x = bg->w - 32; dst.y = bg->h - 32;
src.w = src.h = dst.w = dst.h = 32;
SDL_BlitSurface(icons, &src, bg, &dst);
if(!curronly) {
src.x = src.y = 0;
/* background, layers below */
for(y = mx > currlayer ? 0 : currlayer - mx; y < currlayer; y++) {
for(z = -mx; z < mx; z++) {
for(x = -mx; x < mx; x++) {
dst.w = dst.h = zs;
dst.x = sx + zx * (x - z);
dst.y = sy + zz * (z + x) + zl * (currlayer - y);
X = 127 + x + oy + ox;
Z = 127 + z + oy - ox;
if(X < 0 || X > 255 || Z < 0 || Z > 255 || dst.x < 0 || dst.x > bg->w || dst.y < 0 || dst.y > bg->h)
continue;
i = nodes[y][Z][X].param0;
if(i) {
j = nodes[y][Z][X].param2;
if(blocks[i].numpar2) j %= (int)blocks[i].numpar2; else j = 0;
if(!blocks[i].dr) {
s = 32 * (blocks[i].numpar2 ? blocks[i].numpar2 : 1)*32 * 4;
blocks[i].dr = (unsigned char *)malloc(s);
if(!blocks[i].dr) error(lang[ERR_MEM]);
memset(blocks[i].dr, 0, s);
if(blocks[i].img)
memcpy(blocks[i].dr, blocks[i].img, s);
else {
j = 0;
s = 32 * 32 * 4;
blocks[i].numpar2 = 1;
if(!blocks[i].tr) {
blocks[i].tr = (unsigned char *)malloc(s);
if(!blocks[i].tr) error(lang[ERR_MEM]);
memset(blocks[i].tr, 0, s);
}
memcpy(blocks[i].tr, (uint8_t*)icons->pixels + 32 * icons->pitch, s);
}
for(l = 0; l < s; l += 4) {
blocks[i].dr[l+0] = (b[0]*k + (256 - k)*blocks[i].dr[l+0])>>8;
blocks[i].dr[l+1] = (b[1]*k + (256 - k)*blocks[i].dr[l+1])>>8;
blocks[i].dr[l+2] = (b[2]*k + (256 - k)*blocks[i].dr[l+2])>>8;
}
}
blk->pixels = blocks[i].dr + j * 32 * 32 * 4;
if(dst.w == src.w)
SDL_BlitSurface(blk, &src, bg, &dst);
else
SDL_BlitScaled(blk, &src, bg, &dst);
}
}
}
}
}
/* draw grid */
if(grid && zz > 4) {
k = (x0 * zoom / 128);
y = (y3 * zoom / 128);
for(z = -mx; z < mx; z++) {
for(x = -mx; x < mx; x++) {
dst.x = sx + zx * (x - z) + k;
dst.y = sy + zz * (z + x) + y;
for(i = 0; i < zx; i++) {
l = dst.y + ((i+1) * zz) / zx;
if(dst.x + i > 0 && dst.x + zl+zx < bg->w && l > 0 && l < bg->h) {
*((uint32_t*)((uint8_t*)bg->pixels + (dst.x + i)*4 + l * bg->pitch)) = theme[THEME_INPBG];
*((uint32_t*)((uint8_t*)bg->pixels + (dst.x + zl+zx-i+1)*4 + l * bg->pitch)) = theme[THEME_INPBG];
}
}
}
}
}
if(!curronly) {
/* foreground, layers above */
memset(fg->pixels, 0, fg->pitch * fg->h);
mts_getbounds(0, NULL, NULL);
k = ((max - mix) + (maz - miz) + 1) / 2;
if(up > k) up = k;
for(y = currlayer + 1; y < (currlayer + mx < 255 ? currlayer + mx : 255); y++) {
for(z = -mx; z < mx; z++) {
for(x = -mx; x < mx; x++) {
dst.w = dst.h = zs;
dst.x = sx + zx * (x - z);
dst.y = sy + zz * (z + x - k + up) + zl * ((currlayer - y));
X = 127 + x + oy + ox;
Z = 127 + z + oy - ox;
if(X < 0 || X > 255 || Z < 0 || Z > 255 || dst.x < 0 || dst.x > fg->w || dst.y < 0 || dst.y > fg->h)
continue;
i = nodes[y][Z][X].param0;
if(i) {
j = nodes[y][Z][X].param2;
if(blocks[i].numpar2) j %= (int)blocks[i].numpar2; else j = 0;
if(!blocks[i].tr) {
s = 32 * (blocks[i].numpar2 ? blocks[i].numpar2 : 1)*32 * 4;
blocks[i].tr = (unsigned char *)malloc(s);
if(!blocks[i].tr) error(lang[ERR_MEM]);
if(blocks[i].img)
memcpy(blocks[i].tr, blocks[i].img, s);
else {
j = 0;
s = 32 * 32 * 4;
blocks[i].numpar2 = 1;
memcpy(blocks[i].tr, (uint8_t*)icons->pixels + 32 * icons->pitch, s);
}
for(l = 0; l < s; l += 4) blocks[i].tr[l+3] >>= 3;
}
blk->pixels = blocks[i].tr + j * 32 * 32 * 4;
if(dst.w == src.w)
SDL_BlitSurface(blk, &src, fg, &dst);
else
SDL_BlitScaled(blk, &src, fg, &dst);
}
}
}
}
}
}
/* current layer */
memset(cl->pixels, 0, cl->pitch * cl->h);
for(z = -mx; z < mx; z++) {
for(x = -mx; x < mx; x++) {
src.w = src.h = 32; dst.w = dst.h = zs; src.x = src.y = 0;
dst.x = sx + zx * (x - z);
dst.y = sy + zz * (z + x);
X = 127 + x + oy + ox;
Z = 127 + z + oy - ox;
if(X < 0 || X > 255 || Z < 0 || Z > 255 || dst.x < 0 || dst.x > cl->w || dst.y < 0 || dst.y > cl->h) continue;
i = nodes[currlayer][Z][X].param0;
if((X == cx && Z == cz) || brush_selected(X, Z)) {
blk->pixels = (uint8_t*)icons->pixels + 64 * icons->pitch;
if(dst.w == src.w)
SDL_BlitSurface(blk, &src, cl, &dst);
else
SDL_BlitScaled(blk, &src, cl, &dst);
}
if(i) {
j = nodes[currlayer][Z][X].param2;
if(blocks[i].numpar2) j %= (int)blocks[i].numpar2; else j = 0;
s = 32 * 32 * 4;
if(nodes[currlayer][Z][X].param1 & 0x80) {
memcpy(tmpblk, blocks[i].img ? blocks[i].img + j * s : (uint8_t*)icons->pixels + 32 * icons->pitch, s);
for(k = 0; k < s; k += 4)
if(tmpblk[k + 3])
tmpblk[k] = (255*64 + (256 - 64)*tmpblk[k])>>8;
blk->pixels = tmpblk;
} else {
blk->pixels = blocks[i].img ? blocks[i].img + j * s : (uint8_t*)icons->pixels + 32 * icons->pitch;
}
if(dst.w == src.w)
SDL_BlitSurface(blk, &src, cl, &dst);
else
SDL_BlitScaled(blk, &src, cl, &dst);
}
if((X == cx && Z == cz) || brush_selected(X, Z)) {
blk->pixels = (uint8_t*)icons->pixels + 96 * icons->pitch;
if(dst.w == src.w)
SDL_BlitSurface(blk, &src, cl, &dst);
else
SDL_BlitScaled(blk, &src, cl, &dst);
}
}
}
src.x = src.y = 32; dst.y = 0; dst.x = 36; src.w = dst.w = bg->w; src.h = dst.h = bg->h;
SDL_BlitSurface(bg, &src, screen, &dst);
SDL_BlitSurface(cl, &src, screen, &dst);
if(!curronly) SDL_BlitSurface(fg, &src, screen, &dst);
}
/**
* Edit Area scrolling event handler
*/
int edit_scroll(SDL_Event *event)
{
int lu = up, lx = ox, ly = oy;
if(shift) {
if(event->wheel.y) {
up -= event->wheel.y;
if(up < -128) up = -128;
if(up > 127) up = 127;
} else {
edit_rotate(currlayer, cz, cx, event->wheel.x < 0 ? 1 : 0);
return 1;
}
} else if(ctrl) {
if(event->wheel.y > 0) return edit_zoomout();
if(event->wheel.y < 0) return edit_zoomin();
} else {
oy += event->wheel.y;
if(oy < -128) oy = -128;
if(oy > 127) oy = 127;
ox += event->wheel.x;
if(ox < -128) ox = -128;
if(ox > 127) ox = 127;
}
return (lu != up || lx != ox || ly != oy);
}
/**
* Edit Area mouse over event handler
*/
void edit_mouseover(SDL_Event *event)
{
int l, k, x, y, X = cx, Z = cz;
x = (event->type == SDL_MOUSEMOTION ? event->motion.x : event->button.x) + 32;
y = (event->type == SDL_MOUSEMOTION ? event->motion.y : event->button.y) + 32;
l = (int)(y - (bg->h / 2));
if(l < 0) l -= zz;
l /= zz; l--;
k = (int)(x - 36 - (bg-> w / 2) - zx/2);
if(k < 0) k -= zx;
k -= (l & 1 ? zx/2 : 0);
k /= zx;
cz = (l - k) / 2;
cx = l - cz;
cz += 127 + oy - ox;
cx += 127 + oy + ox;
if((cx != X || cz != Z) && event->type == SDL_MOUSEMOTION && event->motion.state)
brush_place(event->motion.state == 1 ? palette[activeblock] : 0);
status = nodes[currlayer][cz][cx].param0 ? blocks[nodes[currlayer][cz][cx].param0].name : NULL;
}
/**
* Edit Area mouse down event handler
*/
void edit_mousedown(SDL_Event *event)
{
if(event->button.button != 1)
edit_rotate(currlayer, cz, cx, 1 - shift);
else
brush_place(palette[activeblock]);
}
/**
* Edit Area key event handler
*/
void edit_key(SDL_Event *event)
{
switch (event->key.keysym.sym) {
case SDLK_ESCAPE:
case SDLK_q: quitting = 1; break;
case SDLK_l: sdldo(0); break;
case SDLK_s: if(!shift) sdldo(1); break;
case SDLK_p: sdldo(2); break;
case SDLK_r: sdldo(4 - shift); break;
case SDLK_e: sdldo(12); break;
case SDLK_g: gndlayer = currlayer; break;
case SDLK_d:
if(nodes[currlayer][cz][cx].param0) {
nodes[currlayer][cz][cx].param1 ^= 0x80;
status = blocks[nodes[currlayer][cz][cx].param0].name;
}
break;
case SDLK_SEMICOLON: grid ^= 1; break;
case SDLK_a: curronly ^= 1; break;
case SDLK_PLUS: case SDLK_EQUALS: mts_layerprob(+1); break;
case SDLK_MINUS: mts_layerprob(-1); break;
case SDLK_PAGEUP: if(currlayer < 255) currlayer++; break;
case SDLK_PAGEDOWN: if(currlayer > 0) currlayer--; break;
case SDLK_BACKQUOTE: case SDLK_0: activeblock = 0; break;
case SDLK_1: case SDLK_2: case SDLK_3: case SDLK_4: case SDLK_5:
case SDLK_6: case SDLK_7: case SDLK_8: case SDLK_9:
if(palette[event->key.keysym.sym - SDLK_0])
activeblock = event->key.keysym.sym - SDLK_0;
break;
case SDLK_n: edit_pipette(currlayer, cz, cx); break;
case SDLK_x: sdldo(6 + ctrl); break;
case SDLK_c: sdldo(8 + ctrl); break;
case SDLK_v: sdldo(10 + ctrl); break;
case SDLK_h: dx--; sprintf(dstr, "dx %d dz %d", dx, dz); status = dstr; break;
case SDLK_k: dx++; sprintf(dstr, "dx %d dz %d", dx, dz); status = dstr; break;
case SDLK_u: dz--; sprintf(dstr, "dx %d dz %d", dx, dz); status = dstr; break;
case SDLK_j: dz++; sprintf(dstr, "dx %d dz %d", dx, dz); status = dstr; break;
case SDLK_UP:
if(ctrl) {
if(oy < 127) oy++;
} else if(shift) {
if(up > -128) up--;
} else {
if(cz > 0) cz--;
status = nodes[currlayer][cz][cx].param0 ?
blocks[nodes[currlayer][cz][cx].param0].name : NULL;
}
break;
case SDLK_DOWN:
if(ctrl) {
if(oy > -128) oy--;
} else if(shift) {
if(up < 127) up++;
} else {
if(cz < 255) cz++;
status = nodes[currlayer][cz][cx].param0 ?
blocks[nodes[currlayer][cz][cx].param0].name : NULL;
}
break;
case SDLK_LEFT:
if(ctrl) {
if(ox < 127) ox++;
} else if(shift) {
edit_rotate(currlayer, cz, cx, 1);
} else {
if(cx > 0) cx--;
status = nodes[currlayer][cz][cx].param0 ?
blocks[nodes[currlayer][cz][cx].param0].name : NULL;
}
break;
case SDLK_RIGHT:
if(ctrl) {
if(ox > -128) ox--;
} else if(shift) {
edit_rotate(currlayer, cz, cx, 0);
} else {
if(cx < 255) cx++;
status = nodes[currlayer][cz][cx].param0 ?
blocks[nodes[currlayer][cz][cx].param0].name : NULL;
}
break;
case SDLK_COMMA: edit_zoomout(); break;
case SDLK_PERIOD: edit_zoomin(); break;
case SDLK_BACKSPACE:
case SDLK_DELETE:
case SDLK_SPACE: brush_place(event->key.keysym.sym == SDLK_SPACE && !shift ? palette[activeblock] : 0); break;
case SDLK_RETURN: brush_floodfill(!shift ? palette[activeblock] : 0); break;
case SDLK_z: hist_undo(); break;
case SDLK_y: hist_redo(); break;
}
}
/**
* Rotate a single block
*/
void edit_rotate(int y, int z, int x, int ccw)
{
unsigned char oldparam2 = nodes[y][z][x].param2;
if(!nodes[y][z][x].param0) return;
if(ccw) {
nodes[y][z][x].param2++;
} else {
nodes[y][z][x].param2--;
}
if(oldparam2 != nodes[y][z][x].param2) {
hist_prepare(HIST_NODE, 0);
hist_add(y, z, x, nodes[y][z][x].param0, oldparam2, nodes[y][z][x].param0, nodes[y][z][x].param2);
hist_commit();
}
}