1
// editing.cpp: most map editing commands go here, entity editing commands are in world.cpp
8
// the current selection, used by almost all editing commands
9
// invariant: all code assumes that these are kept inside MINBORD distance of the edge of the map
13
variable("selx", 0, 0, 4096, &sel.x, NULL, false),
14
variable("sely", 0, 0, 4096, &sel.y, NULL, false),
15
variable("selxs", 0, 0, 4096, &sel.xs, NULL, false),
16
variable("selys", 0, 0, 4096, &sel.ys, NULL, false),
22
#define loopselxy(b) { makeundo(); loop(x,sel.xs) loop(y,sel.ys) { sqr *s = S(sel.x+x, sel.y+y); b; } remip(sel); }
26
int curedittex[] = { -1, -1, -1 };
28
bool dragging = false;
29
int lastx, lasty, lasth;
31
int lasttype = 0, lasttex = 0;
34
VAR(editing, 1, 0, 0);
36
void toggleedit(bool force)
38
if(player1->state==CS_DEAD) return; // do not allow dead players to edit to avoid state confusion
39
if(!force && !editmode && !allowedittoggle()) return; // not in most multiplayer modes
40
if(!(editmode = !editmode))
42
entinmap(player1); // find spawn closest to current floating pos
46
//player1->health = 100; // illusion only (client-side) and unwanted anyway (bug found by grenadier)
47
//put call to clear/restart gamemode
48
player1->attacking = false;
52
editing = editmode ? 1 : 0;
53
player1->state = editing ? CS_EDITING : CS_ALIVE;
54
if(!force) addmsg(SV_EDITMODE, "ri", editing);
57
void edittoggle() { toggleedit(false); }
59
COMMAND(edittoggle, ARG_NONE);
64
if(!editmode) return NULL;
68
s_sprintf(info)("closest entity = %s (%d, %d, %d, %d), selection = (%d, %d)", entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4, sel.xs, sel.ys);
72
void correctsel() // ensures above invariant
74
selset = !OUTBORD(sel.x, sel.y);
75
int bsize = ssize-MINBORD;
76
if(sel.xs+sel.x>bsize) sel.xs = bsize-sel.x;
77
if(sel.ys+sel.y>bsize) sel.ys = bsize-sel.y;
78
if(sel.xs<=0 || sel.ys<=0) selset = false;
84
if(!editmode) conoutf("this function is only allowed in edit mode");
90
if(!selset) conoutf("no selection");
94
#define EDITSEL if(noteditmode() || noselection()) return;
95
#define EDITSELMP if(noteditmode() || noselection() || multiplayer()) return;
96
#define EDITMP if(noteditmode() || multiplayer()) return;
98
void selectpos(int x, int y, int xs, int ys)
100
block s = { x, y, xs, ys };
108
block s = { min(lastx,cx), min(lasty,cy), abs(lastx-cx)+1, abs(lasty-cy)+1 };
110
selh = max(lasth,ch);
112
if(selset) rtex = *S(sel.x, sel.y);
116
VAR(editaxis,0,0,13);
118
// VC8 optimizer screws up rendering somehow if this is an actual function
119
#define sheight(s,t,z) (!flrceil ? (s->type==FHF ? s->floor-t->vdelta/4.0f : (float)s->floor) : (s->type==CHF ? s->ceil+t->vdelta/4.0f : (float)s->ceil))
121
void cursorupdate() // called every frame from hud
123
flrceil = ((int)(camera1->pitch>=0))*2;
124
int cyaw = ((int) camera1->yaw) % 180;
125
editaxis = editmode ? (fabs(camera1->pitch) > 65 ? 13 : (cyaw < 45 || cyaw > 135 ? 12 : 11)) : 0;
127
volatile float x = worldpos.x; // volatile needed to prevent msvc7 optimizer bug?
128
volatile float y = worldpos.y;
129
volatile float z = worldpos.z;
134
if(OUTBORD(cx, cy)) return;
137
if(fabs(sheight(s,s,z)-z)>1) // selected wall
139
x += x>camera1->o.x ? 0.5f : -0.5f; // find right wall cube
140
y += y>camera1->o.y ? 0.5f : -0.5f;
145
if(OUTBORD(cx, cy)) return;
148
if(dragging) makesel();
150
const int GRIDSIZE = 5;
151
const float GRIDW = 0.5f;
152
const float GRID8 = 2.0f;
153
const float GRIDS = 2.0f;
154
const int GRIDM = 0x7;
156
// render editing grid
158
for(int ix = cx-GRIDSIZE; ix<=cx+GRIDSIZE; ix++) for(int iy = cy-GRIDSIZE; iy<=cy+GRIDSIZE; iy++)
161
if(OUTBORD(ix, iy)) continue;
163
if(SOLID(s)) continue;
164
float h1 = sheight(s, s, z);
165
float h2 = sheight(s, SWS(s,1,0,sfactor), z);
166
float h3 = sheight(s, SWS(s,1,1,sfactor), z);
167
float h4 = sheight(s, SWS(s,0,1,sfactor), z);
168
if(s->tag) linestyle(GRIDW, 0xFF, 0x40, 0x40);
169
else if(s->type==FHF || s->type==CHF) linestyle(GRIDW, 0x80, 0xFF, 0x80);
170
else linestyle(GRIDW, 0x80, 0x80, 0x80);
171
block b = { ix, iy, 1, 1 };
172
box(b, h1, h2, h3, h4);
173
linestyle(GRID8, 0x40, 0x40, 0xFF);
174
if(!(ix&GRIDM)) line(ix, iy, h1, ix, iy+1, h4);
175
if(!((ix+1)&GRIDM)) line(ix+1, iy, h2, ix+1, iy+1, h3);
176
if(!(iy&GRIDM)) line(ix, iy, h1, ix+1, iy, h2);
177
if(!((iy+1)&GRIDM)) line(ix, iy+1, h4, ix+1, iy+1, h3);
182
float ih = sheight(s, s, z);
183
linestyle(GRIDS, 0xFF, 0xFF, 0xFF);
184
block b = { cx, cy, 1, 1 };
185
box(b, ih, sheight(s, SWS(s,1,0,sfactor), z), sheight(s, SWS(s,1,1,sfactor), z), sheight(s, SWS(s,0,1,sfactor), z));
186
linestyle(GRIDS, 0xFF, 0x00, 0x00);
193
linestyle(GRIDS, 0xFF, 0x40, 0x40);
194
box(sel, (float)selh, (float)selh, (float)selh, (float)selh);
200
vector<block *> undos; // unlimited undo
201
VAR(undomegs, 0, 1, 10); // bounded by n megs
203
void pruneundos(int maxremain) // bound memory
208
t += undos[i]->xs*undos[i]->ys*sizeof(sqr);
209
if(t>maxremain) delete[] (uchar *)undos.remove(i);
215
undos.add(blockcopy(sel));
216
pruneundos(undomegs<<20);
222
if(undos.empty()) { conoutf("nothing more to undo"); return; }
223
block *p = undos.pop();
228
block *copybuf = NULL;
234
copybuf = blockcopy(sel);
240
if(!copybuf) { conoutf("nothing to paste"); return; }
241
sel.xs = copybuf->xs;
242
sel.ys = copybuf->ys;
244
if(!selset || sel.xs!=copybuf->xs || sel.ys!=copybuf->ys) { conoutf("incorrect selection"); return; }
248
blockpaste(*copybuf);
251
void tofronttex() // maintain most recently used of the texture lists when applying texture
255
int c = curedittex[i];
258
uchar *p = hdr.texlists[i];
260
for(int a = c-1; a>=0; a--) p[a+1] = p[a];
267
void editdrag(bool isdown)
269
if((dragging = isdown))
280
// the core editing function. all the *xy functions perform the core operations
281
// and are also called directly from the network, the function below it is strictly
282
// triggered locally. They all have very similar structure.
284
void editheightxy(bool isfloor, int amount, block &sel)
286
loopselxy(if(isfloor)
289
if(s->floor>=s->ceil) s->floor = s->ceil-1;
294
if(s->ceil<=s->floor) s->ceil = s->floor+1;
298
void editheight(int flr, int amount)
301
bool isfloor = flr==0;
302
editheightxy(isfloor, amount, sel);
303
addmsg(SV_EDITH, "ri6", sel.x, sel.y, sel.xs, sel.ys, isfloor, amount);
306
COMMAND(editheight, ARG_2INT);
308
void edittexxy(int type, int t, block &sel)
310
loopselxy(switch(type)
312
case 0: s->ftex = t; break;
313
case 1: s->wtex = t; break;
314
case 2: s->ctex = t; break;
315
case 3: s->utex = t; break;
319
void edittex(int type, int dir)
322
if(type<0 || type>3) return;
323
if(type!=lasttype) { tofronttex(); lasttype = type; }
324
int atype = type==3 ? 1 : type;
325
int i = curedittex[atype];
327
curedittex[atype] = i = min(max(i, 0), 255);
328
int t = lasttex = hdr.texlists[atype][i];
329
edittexxy(type, t, sel);
330
addmsg(SV_EDITT, "ri6", sel.x, sel.y, sel.xs, sel.ys, type, t);
336
loop(x,ssize) loop(y,ssize)
341
case 0: if(s->ftex == rtex.ftex) s->ftex = lasttex; break;
342
case 1: if(s->wtex == rtex.wtex) s->wtex = lasttex; break;
343
case 2: if(s->ctex == rtex.ctex) s->ctex = lasttex; break;
344
case 3: if(s->utex == rtex.utex) s->utex = lasttex; break;
347
block b = { 0, 0, ssize, ssize };
351
void edittypexy(int type, block &sel)
353
loopselxy(s->type = type);
356
void edittype(int type)
359
if(type==CORNER && (sel.xs!=sel.ys || sel.xs==3 || (sel.xs>4 && sel.xs!=8)
360
|| sel.x&~-sel.xs || sel.y&~-sel.ys))
361
{ conoutf("corner selection must be power of 2 aligned"); return; }
362
edittypexy(type, sel);
363
addmsg(SV_EDITS, "ri5", sel.x, sel.y, sel.xs, sel.ys, type);
366
void heightfield(int t) { edittype(t==0 ? FHF : CHF); }
367
void solid(int t) { edittype(t==0 ? SPACE : SOLID); }
368
void corner() { edittype(CORNER); }
370
COMMAND(heightfield, ARG_1INT);
371
COMMAND(solid, ARG_1INT);
372
COMMAND(corner, ARG_NONE);
374
void editequalisexy(bool isfloor, block &sel)
376
int low = 127, hi = -128;
379
if(s->floor<low) low = s->floor;
380
if(s->ceil>hi) hi = s->ceil;
384
if(isfloor) s->floor = low; else s->ceil = hi;
385
if(s->floor>=s->ceil) s->floor = s->ceil-1;
389
void equalize(int flr)
391
bool isfloor = flr==0;
393
editequalisexy(isfloor, sel);
394
addmsg(SV_EDITE, "ri5", sel.x, sel.y, sel.xs, sel.ys, isfloor);
397
COMMAND(equalize, ARG_1INT);
399
void setvdeltaxy(int delta, block &sel)
401
loopselxy(s->vdelta = max(s->vdelta+delta, 0));
405
void setvdelta(int delta)
408
setvdeltaxy(delta, sel);
409
addmsg(SV_EDITD, "ri5", sel.x, sel.y, sel.xs, sel.ys, delta);
412
const int MAXARCHVERT = 50;
413
int archverts[MAXARCHVERT][MAXARCHVERT];
414
bool archvinit = false;
416
void archvertex(int span, int vert, int delta)
421
loop(s,MAXARCHVERT) loop(v,MAXARCHVERT) archverts[s][v] = 0;
423
if(span>=MAXARCHVERT || vert>=MAXARCHVERT || span<0 || vert<0) return;
424
archverts[span][vert] = delta;
427
void arch(int sidedelta, int _a)
432
if(sel.xs>MAXARCHVERT) sel.xs = MAXARCHVERT;
433
if(sel.ys>MAXARCHVERT) sel.ys = MAXARCHVERT;
434
loopselxy(s->vdelta =
436
? (archverts[sel.xs-1][x] + (y==0 || y==sel.ys-1 ? sidedelta : 0))
437
: (archverts[sel.ys-1][y] + (x==0 || x==sel.xs-1 ? sidedelta : 0)));
441
void slope(int xd, int yd)
445
if(xd<0) off -= xd*sel.xs;
446
if(yd<0) off -= yd*sel.ys;
449
loopselxy(s->vdelta = xd*x+yd*y+off);
453
void perlin(int scale, int seed, int psize)
461
perlinarea(sel, scale, seed, psize);
469
VARF(fullbright, 0, 0, 1,
472
if(noteditmode()) return;
478
void edittag(int tag)
481
loopselxy(s->tag = tag);
484
void newent(char *what, char *a1, char *a2, char *a3, char *a4)
487
newentity(-1, sel.x, sel.y, (int)camera1->o.z, what, ATOI(a1), ATOI(a2), ATOI(a3), ATOI(a4));
490
COMMANDN(select, selectpos, ARG_4INT);
491
COMMAND(edittag, ARG_1INT);
492
COMMAND(replace, ARG_NONE);
493
COMMAND(archvertex, ARG_3INT);
494
COMMAND(arch, ARG_2INT);
495
COMMAND(slope, ARG_2INT);
496
COMMANDN(vdelta, setvdelta, ARG_1INT);
497
COMMANDN(undo, editundo, ARG_NONE);
498
COMMAND(copy, ARG_NONE);
499
COMMAND(paste, ARG_NONE);
500
COMMAND(edittex, ARG_2INT);
501
COMMAND(newent, ARG_5STR);
502
COMMAND(perlin, ARG_3INT);