home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2007 September / maximum-cd-2007-09.iso / Assets / data / AssaultCube_v0.93.exe / source / src / editing.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-04-05  |  12.8 KB  |  495 lines

  1. // editing.cpp: most map editing commands go here, entity editing commands are in world.cpp
  2.  
  3. #include "cube.h"
  4.  
  5. bool editmode = false; 
  6.  
  7. // the current selection, used by almost all editing commands
  8. // invariant: all code assumes that these are kept inside MINBORD distance of the edge of the map
  9.  
  10. block sel =
  11. {
  12.     variable("selx",  0, 0, 4096, &sel.x,  NULL, false),
  13.     variable("sely",  0, 0, 4096, &sel.y,  NULL, false),
  14.     variable("selxs", 0, 0, 4096, &sel.xs, NULL, false),
  15.     variable("selys", 0, 0, 4096, &sel.ys, NULL, false),
  16. };
  17.  
  18. int selh = 0;
  19. bool selset = false;
  20.  
  21. #define loopselxy(b) { makeundo(); loop(x,sel.xs) loop(y,sel.ys) { sqr *s = S(sel.x+x, sel.y+y); b; } remip(sel); }
  22.  
  23. int cx, cy, ch;
  24.  
  25. int curedittex[] = { -1, -1, -1 };
  26.  
  27. bool dragging = false;
  28. int lastx, lasty, lasth;
  29.  
  30. int lasttype = 0, lasttex = 0;
  31. sqr rtex;
  32.  
  33. VAR(editing, 1, 0, 0);
  34.  
  35. void toggleedit()
  36. {
  37.     if(player1->state==CS_DEAD) return;                 // do not allow dead players to edit to avoid state confusion
  38.     if(!editmode && !allowedittoggle()) return;         // not in most multiplayer modes
  39.     if(!(editmode = !editmode))
  40.     {
  41.         entinmap(player1);                              // find spawn closest to current floating pos
  42.     }
  43.     else
  44.     {
  45.         player1->health = 100;
  46.         //put call to clear/restart gamemode
  47.         projreset();
  48.         player1->attacking = false;
  49.     }
  50.     keyrepeat(editmode);
  51.     selset = false;
  52.     editing = editmode;
  53. }
  54.  
  55. COMMANDN(edittoggle, toggleedit, ARG_NONE);
  56.  
  57. char *editinfo()
  58. {
  59.     static string info;
  60.     if(!editmode) return NULL;
  61.     int e = closestent();
  62.     if(e<0) return NULL;
  63.     entity &c = ents[e];
  64.     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);
  65.     return info;
  66. }
  67.  
  68. void correctsel()                                       // ensures above invariant
  69. {
  70.     selset = !OUTBORD(sel.x, sel.y);
  71.     int bsize = ssize-MINBORD;
  72.     if(sel.xs+sel.x>bsize) sel.xs = bsize-sel.x;
  73.     if(sel.ys+sel.y>bsize) sel.ys = bsize-sel.y;
  74.     if(sel.xs<=0 || sel.ys<=0) selset = false;
  75. }
  76.  
  77. bool noteditmode()
  78. {
  79.     correctsel();
  80.     if(!editmode) conoutf("this function is only allowed in edit mode");
  81.     return !editmode;
  82. }
  83.  
  84. bool noselection()
  85. {
  86.     if(!selset) conoutf("no selection");
  87.     return !selset;
  88. }
  89.  
  90. #define EDITSEL   if(noteditmode() || noselection()) return;
  91. #define EDITSELMP if(noteditmode() || noselection() || multiplayer()) return;
  92. #define EDITMP    if(noteditmode() || multiplayer()) return;
  93.  
  94. void selectpos(int x, int y, int xs, int ys)
  95. {
  96.     block s = { x, y, xs, ys };
  97.     sel = s;
  98.     selh = 0;
  99.     correctsel();
  100. }
  101.  
  102. void makesel()
  103. {
  104.     block s = { min(lastx,cx), min(lasty,cy), abs(lastx-cx)+1, abs(lasty-cy)+1 };
  105.     sel = s;
  106.     selh = max(lasth,ch);
  107.     correctsel();
  108.     if(selset) rtex = *S(sel.x, sel.y);
  109. }
  110.  
  111. VAR(flrceil,0,0,2);
  112.  
  113. // VC8 optimizer screws up rendering somehow if this is an actual function
  114. #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))
  115.  
  116. void cursorupdate()                                     // called every frame from hud
  117. {
  118.     flrceil = ((int)(camera1->pitch>=0))*2;
  119.  
  120.     volatile float x = worldpos.x;                      // volatile needed to prevent msvc7 optimizer bug?
  121.     volatile float y = worldpos.y;
  122.     volatile float z = worldpos.z;
  123.     
  124.     cx = (int)x;
  125.     cy = (int)y;
  126.  
  127.     if(OUTBORD(cx, cy)) return;
  128.     sqr *s = S(cx,cy);
  129.     
  130.     if(fabs(sheight(s,s,z)-z)>1)                        // selected wall
  131.     {
  132.         x += x>camera1->o.x ? 0.5f : -0.5f;             // find right wall cube
  133.         y += y>camera1->o.y ? 0.5f : -0.5f;
  134.  
  135.         cx = (int)x;
  136.         cy = (int)y;
  137.  
  138.         if(OUTBORD(cx, cy)) return;
  139.     }
  140.         
  141.     if(dragging) makesel();
  142.  
  143.     const int GRIDSIZE = 5;
  144.     const float GRIDW = 0.5f;
  145.     const float GRID8 = 2.0f;
  146.     const float GRIDS = 2.0f;
  147.     const int GRIDM = 0x7;
  148.     
  149.     // render editing grid
  150.  
  151.     for(int ix = cx-GRIDSIZE; ix<=cx+GRIDSIZE; ix++) for(int iy = cy-GRIDSIZE; iy<=cy+GRIDSIZE; iy++)
  152.     {
  153.  
  154.         if(OUTBORD(ix, iy)) continue;
  155.         sqr *s = S(ix,iy);
  156.         if(SOLID(s)) continue;
  157.         float h1 = sheight(s, s, z);
  158.         float h2 = sheight(s, SWS(s,1,0,ssize), z);
  159.         float h3 = sheight(s, SWS(s,1,1,ssize), z);
  160.         float h4 = sheight(s, SWS(s,0,1,ssize), z);
  161.         if(s->tag) linestyle(GRIDW, 0xFF, 0x40, 0x40);
  162.         else if(s->type==FHF || s->type==CHF) linestyle(GRIDW, 0x80, 0xFF, 0x80);
  163.         else linestyle(GRIDW, 0x80, 0x80, 0x80);
  164.         block b = { ix, iy, 1, 1 };
  165.         box(b, h1, h2, h3, h4);
  166.         linestyle(GRID8, 0x40, 0x40, 0xFF);
  167.         if(!(ix&GRIDM))   line(ix,   iy,   h1, ix,   iy+1, h4);
  168.         if(!(ix+1&GRIDM)) line(ix+1, iy,   h2, ix+1, iy+1, h3);
  169.         if(!(iy&GRIDM))   line(ix,   iy,   h1, ix+1, iy,   h2);
  170.         if(!(iy+1&GRIDM)) line(ix,   iy+1, h4, ix+1, iy+1, h3);
  171.     }
  172.  
  173.     if(!SOLID(s))
  174.     {
  175.         float ih = sheight(s, s, z);
  176.         linestyle(GRIDS, 0xFF, 0xFF, 0xFF);
  177.         block b = { cx, cy, 1, 1 };
  178.         box(b, ih, sheight(s, SWS(s,1,0,ssize), z), sheight(s, SWS(s,1,1,ssize), z), sheight(s, SWS(s,0,1,ssize), z));
  179.         linestyle(GRIDS, 0xFF, 0x00, 0x00);
  180.         dot(cx, cy, ih);
  181.         ch = (int)ih;
  182.     }
  183.  
  184.     if(selset)
  185.     {
  186.         linestyle(GRIDS, 0xFF, 0x40, 0x40);
  187.         box(sel, (float)selh, (float)selh, (float)selh, (float)selh);
  188.     }
  189. }
  190.  
  191. vector<block *> undos;                                  // unlimited undo
  192. VAR(undomegs, 0, 1, 10);                                // bounded by n megs
  193.  
  194. void pruneundos(int maxremain)                          // bound memory
  195. {
  196.     int t = 0;
  197.     loopvrev(undos)
  198.     {
  199.         t += undos[i]->xs*undos[i]->ys*sizeof(sqr);
  200.         if(t>maxremain) delete undos.remove(i);
  201.     }
  202. }
  203.  
  204. void makeundo()
  205. {
  206.     undos.add(blockcopy(sel));
  207.     pruneundos(undomegs<<20);
  208. }
  209.  
  210. void editundo()
  211. {
  212.     EDITMP;
  213.     if(undos.empty()) { conoutf("nothing more to undo"); return; }
  214.     block *p = undos.pop();
  215.     blockpaste(*p);
  216.     freeblock(p);
  217. }
  218.  
  219. block *copybuf = NULL;
  220.  
  221. void copy()
  222. {
  223.     EDITSELMP;
  224.     freeblock(copybuf);
  225.     copybuf = blockcopy(sel);
  226. }
  227.  
  228. void paste()
  229. {
  230.     EDITMP;
  231.     if(!copybuf) { conoutf("nothing to paste"); return; }
  232.     sel.xs = copybuf->xs;
  233.     sel.ys = copybuf->ys;
  234.     correctsel();
  235.     if(!selset || sel.xs!=copybuf->xs || sel.ys!=copybuf->ys) { conoutf("incorrect selection"); return; }
  236.     makeundo();
  237.     copybuf->x = sel.x;
  238.     copybuf->y = sel.y;
  239.     blockpaste(*copybuf);
  240. }
  241.  
  242. void tofronttex()                                       // maintain most recently used of the texture lists when applying texture
  243. {
  244.     loopi(3)
  245.     {
  246.         int c = curedittex[i];
  247.         if(c>=0)
  248.         {
  249.             uchar *p = hdr.texlists[i];
  250.             int t = p[c];
  251.             for(int a = c-1; a>=0; a--) p[a+1] = p[a];
  252.             p[0] = t;
  253.             curedittex[i] = -1;
  254.         }
  255.     }
  256. }
  257.  
  258. void editdrag(bool isdown)
  259. {
  260.     if(dragging = isdown)
  261.     {
  262.         lastx = cx;
  263.         lasty = cy;
  264.         lasth = ch;
  265.         selset = false;
  266.         tofronttex();
  267.     }
  268.     makesel();
  269. }
  270.  
  271. // the core editing function. all the *xy functions perform the core operations
  272. // and are also called directly from the network, the function below it is strictly
  273. // triggered locally. They all have very similar structure.
  274.  
  275. void editheightxy(bool isfloor, int amount, block &sel)
  276. {
  277.     loopselxy(if(isfloor)
  278.     {
  279.         s->floor += amount;
  280.         if(s->floor>=s->ceil) s->floor = s->ceil-1;
  281.     }
  282.     else
  283.     {
  284.         s->ceil += amount;
  285.         if(s->ceil<=s->floor) s->ceil = s->floor+1;
  286.     });
  287. }
  288.  
  289. void editheight(int flr, int amount)
  290. {
  291.     EDITSEL;
  292.     bool isfloor = flr==0;
  293.     editheightxy(isfloor, amount, sel);
  294.     addmsg(SV_EDITH, "ri6", sel.x, sel.y, sel.xs, sel.ys, isfloor, amount);
  295. }
  296.  
  297. COMMAND(editheight, ARG_2INT);
  298.  
  299. void edittexxy(int type, int t, block &sel)            
  300. {
  301.     loopselxy(switch(type)
  302.     {
  303.         case 0: s->ftex = t; break;
  304.         case 1: s->wtex = t; break;
  305.         case 2: s->ctex = t; break;
  306.         case 3: s->utex = t; break;
  307.     });
  308. }
  309.  
  310. void edittex(int type, int dir)
  311. {
  312.     EDITSEL;
  313.     if(type<0 || type>3) return;
  314.     if(type!=lasttype) { tofronttex(); lasttype = type; }
  315.     int atype = type==3 ? 1 : type;
  316.     int i = curedittex[atype];
  317.     i = i<0 ? 0 : i+dir;
  318.     curedittex[atype] = i = min(max(i, 0), 255);
  319.     int t = lasttex = hdr.texlists[atype][i];
  320.     edittexxy(type, t, sel);
  321.     addmsg(SV_EDITT, "ri6", sel.x, sel.y, sel.xs, sel.ys, type, t);
  322. }
  323.  
  324. void replace()
  325. {
  326.     EDITSELMP;
  327.     loop(x,ssize) loop(y,ssize)
  328.     {
  329.         sqr *s = S(x, y);
  330.         switch(lasttype)
  331.         {
  332.             case 0: if(s->ftex == rtex.ftex) s->ftex = lasttex; break;
  333.             case 1: if(s->wtex == rtex.wtex) s->wtex = lasttex; break;
  334.             case 2: if(s->ctex == rtex.ctex) s->ctex = lasttex; break;
  335.             case 3: if(s->utex == rtex.utex) s->utex = lasttex; break;
  336.         }
  337.     }
  338.     block b = { 0, 0, ssize, ssize }; 
  339.     remip(b);
  340. }
  341.  
  342. void edittypexy(int type, block &sel)
  343. {
  344.     loopselxy(s->type = type);
  345. }
  346.  
  347. void edittype(int type)
  348. {
  349.     EDITSEL;
  350.     if(type==CORNER && (sel.xs!=sel.ys || sel.xs==3 || sel.xs>4 && sel.xs!=8
  351.                    || sel.x&~-sel.xs || sel.y&~-sel.ys))
  352.                    { conoutf("corner selection must be power of 2 aligned"); return; }
  353.     edittypexy(type, sel);
  354.     addmsg(SV_EDITS, "ri5", sel.x, sel.y, sel.xs, sel.ys, type);
  355. }
  356.  
  357. void heightfield(int t) { edittype(t==0 ? FHF : CHF); }
  358. void solid(int t)       { edittype(t==0 ? SPACE : SOLID); }
  359. void corner()           { edittype(CORNER); }
  360.  
  361. COMMAND(heightfield, ARG_1INT);
  362. COMMAND(solid, ARG_1INT);
  363. COMMAND(corner, ARG_NONE);
  364.  
  365. void editequalisexy(bool isfloor, block &sel)
  366. {
  367.     int low = 127, hi = -128;
  368.     loopselxy(
  369.     {
  370.         if(s->floor<low) low = s->floor;
  371.         if(s->ceil>hi) hi = s->ceil;
  372.     });
  373.     loopselxy(
  374.     {
  375.         if(isfloor) s->floor = low; else s->ceil = hi;
  376.         if(s->floor>=s->ceil) s->floor = s->ceil-1;
  377.     });
  378. }
  379.  
  380. void equalize(int flr)
  381. {
  382.     bool isfloor = flr==0;
  383.     EDITSEL;
  384.     editequalisexy(isfloor, sel);
  385.     addmsg(SV_EDITE, "ri5", sel.x, sel.y, sel.xs, sel.ys, isfloor);
  386. }
  387.  
  388. COMMAND(equalize, ARG_1INT);
  389.  
  390. void setvdeltaxy(int delta, block &sel)
  391. {
  392.     loopselxy(s->vdelta = max(s->vdelta+delta, 0));    
  393.     remipmore(sel);
  394. }
  395.  
  396. void setvdelta(int delta)
  397. {
  398.     EDITSEL;
  399.     setvdeltaxy(delta, sel);
  400.     addmsg(SV_EDITD, "ri5", sel.x, sel.y, sel.xs, sel.ys, delta);
  401. }
  402.  
  403. const int MAXARCHVERT = 50;
  404. int archverts[MAXARCHVERT][MAXARCHVERT];
  405. bool archvinit = false;
  406.  
  407. void archvertex(int span, int vert, int delta)
  408. {
  409.     if(!archvinit)
  410.     {
  411.         archvinit = true;
  412.         loop(s,MAXARCHVERT) loop(v,MAXARCHVERT) archverts[s][v] = 0;
  413.     }
  414.     if(span>=MAXARCHVERT || vert>=MAXARCHVERT || span<0 || vert<0) return;
  415.     archverts[span][vert] = delta;
  416. }
  417.  
  418. void arch(int sidedelta, int _a)
  419. {
  420.     EDITSELMP;
  421.     sel.xs++;
  422.     sel.ys++;
  423.     if(sel.xs>MAXARCHVERT) sel.xs = MAXARCHVERT;
  424.     if(sel.ys>MAXARCHVERT) sel.ys = MAXARCHVERT;
  425.     loopselxy(s->vdelta =
  426.         sel.xs>sel.ys
  427.             ? (archverts[sel.xs-1][x] + (y==0 || y==sel.ys-1 ? sidedelta : 0))
  428.             : (archverts[sel.ys-1][y] + (x==0 || x==sel.xs-1 ? sidedelta : 0)));
  429.     remipmore(sel);
  430. }
  431.  
  432. void slope(int xd, int yd)
  433. {
  434.     EDITSELMP;
  435.     int off = 0;
  436.     if(xd<0) off -= xd*sel.xs;
  437.     if(yd<0) off -= yd*sel.ys;
  438.     sel.xs++;
  439.     sel.ys++;
  440.     loopselxy(s->vdelta = xd*x+yd*y+off);
  441.     remipmore(sel);
  442. }
  443.  
  444. void perlin(int scale, int seed, int psize)
  445. {
  446.     EDITSELMP;
  447.     sel.xs++;
  448.     sel.ys++;
  449.     makeundo();
  450.     sel.xs--;
  451.     sel.ys--;
  452.     perlinarea(sel, scale, seed, psize);
  453.     sel.xs++;
  454.     sel.ys++;
  455.     remipmore(sel);
  456.     sel.xs--;
  457.     sel.ys--;
  458. }
  459.  
  460. VARF(fullbright, 0, 0, 1,
  461.     if(fullbright)
  462.     {
  463.         if(noteditmode()) return;
  464.         loopi(mipsize) world[i].r = world[i].g = world[i].b = 176;
  465.     }
  466. );
  467.  
  468. void edittag(int tag)
  469. {
  470.     EDITSELMP;
  471.     loopselxy(s->tag = tag);
  472. }
  473.  
  474. void newent(char *what, char *a1, char *a2, char *a3, char *a4)
  475. {
  476.     EDITSEL;
  477.     newentity(sel.x, sel.y, (int)camera1->o.z, what, ATOI(a1), ATOI(a2), ATOI(a3), ATOI(a4));
  478. }
  479.  
  480. COMMANDN(select, selectpos, ARG_4INT);
  481. COMMAND(edittag, ARG_1INT);
  482. COMMAND(replace, ARG_NONE);
  483. COMMAND(archvertex, ARG_3INT);
  484. COMMAND(arch, ARG_2INT);
  485. COMMAND(slope, ARG_2INT);
  486. COMMANDN(vdelta, setvdelta, ARG_1INT);
  487. COMMANDN(undo, editundo, ARG_NONE);
  488. COMMAND(copy, ARG_NONE);
  489. COMMAND(paste, ARG_NONE);
  490. COMMAND(edittex, ARG_2INT);
  491. COMMAND(newent, ARG_5STR);
  492. COMMAND(perlin, ARG_3INT);
  493.  
  494.  
  495.