// world.cpp: core map management stuff #include "cube.h" #include "bot/bot.h" sqr *world = NULL; int sfactor, ssize, cubicsize, mipsize; header hdr; // main geometric mipmapping routine, recursively rebuild mipmaps within block b. // tries to produce cube out of 4 lower level mips as well as possible, // sets defer to 0 if mipped cube is a perfect mip, i.e. can be rendered at this // mip level indistinguishable from its constituent cubes (saves considerable // rendering time if this is possible). void remip(const block &b, int level) { if(level>=SMALLEST_FACTOR) return; int lighterr = getvar("lighterror")*3; sqr *w = wmip[level]; sqr *v = wmip[level+1]; int wfactor = sfactor - level; int vfactor = sfactor - (level+1); block s = b; if(s.x&1) { s.x--; s.xs++; } if(s.y&1) { s.y--; s.ys++; } s.xs = (s.xs+1)&~1; s.ys = (s.ys+1)&~1; for(int y = s.y; ytype]++; r->type = SEMISOLID; // cube contains both solid and space, treated specially in the renderer loopk(MAXTYPE) if(nums[k]==4) r->type = k; if(!SOLID(r)) { int floor = 127, ceil = -128, num = 0; loopi(4) if(!SOLID(o[i])) { num++; int fh = o[i]->floor; int ch = o[i]->ceil; if(r->type==SEMISOLID) { if(o[i]->type==FHF) fh -= o[i]->vdelta/4+2; // crap hack, needed for rendering large mips next to hfs if(o[i]->type==CHF) ch += o[i]->vdelta/4+2; // FIXME: needs to somehow take into account middle vertices on higher mips } if(fhceil) ceil = ch; } r->floor = floor; r->ceil = ceil; } if(r->type==CORNER) goto mip; // special case: don't ever split even if textures etc are different r->defer = 1; if(SOLID(r)) { loopi(3) { if(o[i]->wtex!=o[3]->wtex) goto c; // on an all solid cube, only thing that needs to be equal for a perfect mip is the wall texture } } else { loopi(3) { if(o[i]->type!=o[3]->type || o[i]->floor!=o[3]->floor || o[i]->ceil!=o[3]->ceil || o[i]->ftex!=o[3]->ftex || o[i]->ctex!=o[3]->ctex || abs(o[i+1]->r-o[0]->r)>lighterr // perfect mip even if light is not exactly equal || abs(o[i+1]->g-o[0]->g)>lighterr || abs(o[i+1]->b-o[0]->b)>lighterr || o[i]->utex!=o[3]->utex || o[i]->wtex!=o[3]->wtex) goto c; } if(r->type==CHF || r->type==FHF) // can make a perfect mip out of a hf if slopes lie on one line { if(o[0]->vdelta-o[1]->vdelta != o[1]->vdelta-SWS(w,x+2,y,wfactor)->vdelta || o[0]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+2,y+2,wfactor)->vdelta || o[0]->vdelta-o[3]->vdelta != o[3]->vdelta-SWS(w,x,y+2,wfactor)->vdelta || o[3]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+2,y+1,wfactor)->vdelta || o[1]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+1,y+2,wfactor)->vdelta) goto c; } } { loopi(4) if(o[i]->defer) goto c; } // if any of the constituents is not perfect, then this one isn't either mip: r->defer = 0; c:; } s.x /= 2; s.y /= 2; s.xs /= 2; s.ys /= 2; remip(s, level+1); } void remipmore(const block &b, int level) { block bb = b; if(bb.x>1) bb.x--; if(bb.y>1) bb.y--; if(bb.xso); if(j) { if(ents[best].x == e.x && ents[best].y == e.y && ents[best].z == e.z) { if(j == 2 && bcnt == clentsel) return i; bcnt++; } } else if(distdrop(); } if(changedents.find(n) == -1) changedents.add(n); // apply ent changes later (reduces network traffic) } hashtable mapinfo, &resdata = mapinfo; void getenttype() { int e = closestent(); if(e<0) return; int type = ents[e].type; if(type < 0 || type >= MAXENTTYPES) return; result(entnames[type]); } void getentattr(int *attr) { int e = closestent(); if(e>=0) switch(*attr) { case 0: intret(ents[e].attr1); return; case 1: intret(ents[e].attr2); return; case 2: intret(ents[e].attr3); return; case 3: intret(ents[e].attr4); return; } intret(0); } COMMAND(getenttype, ""); COMMAND(getentattr, "i"); void delent() { int n = closestent(); if(n<0) { conoutf("no more entities"); return; } syncentchanges(true); int t = ents[n].type; conoutf("%s entity deleted", entnames[t]); entity &e = ents[n]; if (t == SOUND) //stop playing sound { entityreference entref(&e); location *loc = audiomgr.locations.find(e.attr1, &entref, mapsounds); if(loc) loc->drop(); } ents[n].type = NOTUSED; addmsg(SV_EDITENT, "ri9", n, NOTUSED, 0, 0, 0, 0, 0, 0, 0); switch(t) { case LIGHT: calclight(); break; } } int findtype(char *what) { loopi(MAXENTTYPES) if(strcmp(what, entnames[i])==0) return i; conoutf("unknown entity type \"%s\"", what); return NOTUSED; } entity *newentity(int index, int x, int y, int z, char *what, int v1, int v2, int v3, int v4) { int type = findtype(what); if(type==NOTUSED) return NULL; if (type == SOUND && index >= 0) { entity &o = ents[index]; entityreference entref(&o); location *loc = audiomgr.locations.find(o.attr1, &entref, mapsounds); if(loc) loc->drop(); } entity e(x, y, z, type, v1, v2, v3, v4); switch(type) { case LIGHT: if(v1>64) e.attr1 = 64; if(!v1) e.attr1 = 16; if(!v2 && !v3 && !v4) e.attr2 = 255; break; case MAPMODEL: e.attr4 = e.attr3; e.attr3 = e.attr2; e.attr2 = (uchar)e.attr1; case PLAYERSTART: case CTF_FLAG: e.attr2 = v1; e.attr1 = (int)camera1->yaw; break; } syncentchanges(true); addmsg(SV_EDITENT, "ri9", index<0 ? ents.length() : index, type, e.x, e.y, e.z, e.attr1, e.attr2, e.attr3, e.attr4); e.spawned = true; int oldtype = type; if(index<0) ents.add(e); else { oldtype = ents[index].type; ents[index] = e; } if(oldtype!=type) switch(oldtype) { case LIGHT: calclight(); break; } switch(type) { case LIGHT: calclight(); break; case SOUND: audiomgr.preloadmapsound(e); break; } return index<0 ? &ents.last() : &ents[index]; } void entset(char *what, int *a1, int *a2, int *a3, int *a4) { int n = closestent(); if(n>=0) { entity &e = ents[n]; newentity(n, e.x, e.y, e.z, what, *a1, *a2, *a3, *a4); } } COMMAND(entset, "siiii"); void clearents(char *name) { int type = findtype(name); if(noteditmode("clearents") || multiplayer()) return; loopv(ents) { entity &e = ents[i]; if(e.type==type) e.type = NOTUSED; } switch(type) { case LIGHT: calclight(); break; } } COMMAND(clearents, "s"); void scalecomp(uchar &c, int intens) { int n = c*intens/100; if(n>255) n = 255; c = n; } void scalelights(int f, int intens) { loopv(ents) { entity &e = ents[i]; if(e.type!=LIGHT) continue; e.attr1 = e.attr1*f/100; if(e.attr1<2) e.attr1 = 2; if(e.attr1>32) e.attr1 = 32; if(intens) { scalecomp(e.attr2, intens); scalecomp(e.attr3, intens); scalecomp(e.attr4, intens); } } calclight(); } COMMANDF(scalelights, "ii", (int *f, int *i) { scalelights(*f, *i); }); int findentity(int type, int index) { for(int i = index; i= 0) { entity &e = ents[cycle]; player1->o.x = e.x; player1->o.y = e.y; player1->o.z = e.z; player1->yaw = e.attr1; player1->pitch = 0; player1->roll = 0; entinmap(player1); } } COMMAND(nextplayerstart, "i"); sqr *wmip[LARGEST_FACTOR*2]; void setupworld(int factor) { ssize = 1<<(sfactor = factor); cubicsize = ssize*ssize; mipsize = cubicsize*134/100; sqr *w = world = new sqr[mipsize]; memset(world, 0, mipsize*sizeof(sqr)); loopi(LARGEST_FACTOR*2) { wmip[i] = w; w += cubicsize>>(i*2); } } sqr *sqrdefault(sqr *s) { if(!s) return s; s->r = s->g = s->b = 150; s->ftex = DEFAULT_FLOOR; s->ctex = DEFAULT_CEIL; s->wtex = s->utex = DEFAULT_WALL; s->type = SOLID; s->floor = 0; s->ceil = 16; s->vdelta = 0; s->defer = 0; return s; } bool worldbordercheck(int x1, int x2, int y1, int y2, int z1, int z2) // check for solid world border { loop(x, ssize) loop(y, ssize) { if(x >= x1 && x < ssize - x2 && y >= y1 && y < ssize - y2) { if(S(x,y)->type != SOLID && (S(x,y)->ceil > 126 - z1 || S(x,y)->floor < -127 + z2)) return false; } else { if(S(x,y)->type != SOLID) return false; } } return true; } bool empty_world(int factor, bool force) // main empty world creation routine, if passed factor -1 will enlarge old world by 1, factor -2 will shrink old world by 1 { if(!force && noteditmode("empty world")) return false; if(factor == -2 && !worldbordercheck(ssize/4 + MINBORD, ssize/4 + MINBORD, ssize/4 + MINBORD, ssize/4 + MINBORD, 0, 0)) { conoutf("map does not fit into smaller world"); return false; } block *ow = NULL, be = { 0, 0, ssize, ssize }, bs = { ssize/4, ssize/4, ssize/2, ssize/2 }; int oldfactor = sfactor; bool copy = false; if(world && factor<0) { factor = sfactor + (factor == -2 ? -1 : 1); copy = true; } if(factorLARGEST_FACTOR) factor = LARGEST_FACTOR; if(copy && oldfactor == factor) return false; bool shrink = factor < oldfactor; bool clearmap = !copy && world; if(copy) ow = blockcopy(shrink ? bs : be); extern char *mlayout; DELETEA(world); DELETEA(mlayout); setupworld(factor); loop(x,ssize) loop(y,ssize) { sqrdefault(S(x,y)); } checkselections(); // assert no selection became invalid strncpy(hdr.head, "ACMP", 4); hdr.version = MAPVERSION; hdr.headersize = sizeof(header); hdr.sfactor = sfactor; if(copy) { ow->x = ow->y = shrink ? 0 : ssize/4; blockpaste(*ow); int moveents = shrink ? -ssize/2 : ssize/4; loopv(ents) { ents[i].x += moveents; ents[i].y += moveents; if(OUTBORD(ents[i].x, ents[i].y)) ents[i].type = NOTUSED; } player1->o.x += moveents; player1->o.y += moveents; entinmap(player1); } else { copystring(hdr.maptitle, "Untitled Map by Unknown", 128); copystring(hdr.mediareq, "", 128); // MediaPack hdr.waterlevel = -100000; hdr.ambient = 0; setwatercolor(); loopi(sizeof(hdr.reserved)/sizeof(hdr.reserved[0])) hdr.reserved[i] = 0; loopk(3) loopi(256) hdr.texlists[k][i] = i; ents.shrink(0); block b = { 8, 8, ssize-16, ssize - 16}; edittypexy(SPACE, b); } calclight(); resetmap(!copy); if(clearmap) { pushscontext(IEXC_MAPCFG); persistidents = false; neverpersist = true; execfile("config/default_map_settings.cfg"); neverpersist = false; persistidents = true; popscontext(); setvar("fullbright", 1); fullbrightlight(); } if(!copy) { findplayerstart(player1, true); startmap("", false); } else conoutf("new map size: %d", sfactor); freeblock(ow); return true; } void mapenlarge() { if(empty_world(-1, false)) addmsg(SV_NEWMAP, "ri", -1); } void mapshrink() { if(empty_world(-2, false)) addmsg(SV_NEWMAP, "ri", -2); } void newmap(int *i) { if(empty_world(*i, false)) { addmsg(SV_NEWMAP, "ri", max(*i, 0)); if(identexists("onNewMap")) execute("onNewMap"); } defformatstring(startmillis)("%d", millis_()); alias("gametimestart", startmillis); } COMMAND(mapenlarge, ""); COMMAND(mapshrink, ""); COMMAND(newmap, "i"); COMMANDN(recalc, calclight, ""); COMMAND(delent, ""); COMMANDF(entproperty, "ii", (int *p, int *a) { entproperty(*p, *a); });