From e1ce0979d0265c9079015418cf441b776d65be2b Mon Sep 17 00:00:00 2001 From: ac-stef Date: Sun, 14 Jun 2015 17:44:59 +0200 Subject: [PATCH] rewrite calculation of map geometry bounding box and rename client-side struct "mapdims" to clmapdims (to avoid confusion with server-side data) --- source/src/clientgame.cpp | 7 +-- source/src/clients2c.cpp | 7 +-- source/src/cube.h | 2 +- source/src/entities.cpp | 8 +-- source/src/protos.h | 8 ++- source/src/rendergl.cpp | 30 +++++------ source/src/renderhud.cpp | 22 ++++---- source/src/texture.cpp | 4 +- source/src/tools.cpp | 108 ++++++++++++++++++++++++++++++++++++-- source/src/world.cpp | 39 ++------------ source/src/world.h | 41 +++++++++++++-- source/src/worldio.cpp | 43 ++++++++------- source/src/worldlight.cpp | 9 +++- 13 files changed, 223 insertions(+), 105 deletions(-) diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index a3356630..2b3e7486 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -9,6 +9,8 @@ VAR(nextGameMode, 1, 0, 0); VARP(modeacronyms, 0, 1, 1); flaginfo flaginfos[2]; +entitystats_s clentstats; +mapdim_s clmapdims; // min/max X/Y and delta X/Y and min/max Z void mode(int *n) { @@ -687,8 +689,7 @@ void findplayerstart(playerent *d, bool mapcenter, int arenaspawn) loopi(r) { - // 2013jun28:lucas: SKB suggested to use FFA spawns only in FFA modes, which seems reasonable. - spawncycle = /*m_ktf && */numspawn[2] > 4 ? findentity(PLAYERSTART, spawncycle+1, 100) : findentity(PLAYERSTART, spawncycle+1); + spawncycle = clentstats.hasffaspawns ? findentity(PLAYERSTART, spawncycle+1, 100) : findentity(PLAYERSTART, spawncycle+1); // if not enough ffa-spawns are available, use all if(spawncycle < 0) continue; float dist = nearestenemy(vec(ents[spawncycle].x, ents[spawncycle].y, ents[spawncycle].z), d->team); if(!e || dist < 0 || (bestdist >= 0 && dist > bestdist)) { e = &ents[spawncycle]; bestdist = dist; } @@ -1153,7 +1154,7 @@ void startmap(const char *name, bool reset, bool norespawn) // called just aft minutesremaining = -1; lastgametimeupdate = 0; arenaintermission = 0; - bool noflags = (m_ctf || m_ktf) && (!numflagspawn[0] || !numflagspawn[1]); + bool noflags = (m_ctf || m_ktf) && !clentstats.hasflags; if(*clientmap) conoutf("game mode is \"%s\"%s", modestr(gamemode, modeacronyms > 0), noflags ? " - \f2but there are no flag bases on this map" : ""); if(showmodedescriptions && (multiplayer(false) || m_botmode)) diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index ff0c9162..b6376fe3 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -254,15 +254,16 @@ int map_quality = MAP_IS_EDITABLE; /// TODO: many functions and variables are redundant between client and server... someone should redo the entire server code and unify client and server. bool good_map() // call this function only at startmap { + return true; if (mlayout) MA = checkarea(sfactor, mlayout); F2F = 1000 * MINFF; if(m_flags) { - flaginfo &f0 = flaginfos[0]; - flaginfo &f1 = flaginfos[1]; +// flaginfo &f0 = flaginfos[0]; + // flaginfo &f1 = flaginfos[1]; #define DIST(x) (f0.pos.x - f1.pos.x) - F2F = (!numflagspawn[0] || !numflagspawn[1]) ? 1000 * MINFF : DIST(x)*DIST(x)+DIST(y)*DIST(y); + F2F = 1000;//(!numflagspawn[0] || !numflagspawn[1]) ? 1000 * MINFF : DIST(x)*DIST(x)+DIST(y)*DIST(y); #undef DIST } diff --git a/source/src/cube.h b/source/src/cube.h index f7717060..89ef675c 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -54,7 +54,7 @@ extern bool intermission; extern int arenaintermission; extern hashtable mapinfo; extern int hwtexsize, hwmaxaniso; -extern int numspawn[3], maploaded, numflagspawn[2]; +extern int maploaded; extern int verbose; extern float waterlevel; diff --git a/source/src/entities.cpp b/source/src/entities.cpp index c816328b..35451fc5 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -301,7 +301,7 @@ void renderentities() } break; case CTFF_INBASE: - if(!numflagspawn[i]) break; + if(!clentstats.flags[i]) break; case CTFF_DROPPED: { if(OUTBORD(f.pos.x, f.pos.y)) break; @@ -469,7 +469,7 @@ void checkitems(playerent *d) { flaginfo &f = flaginfos[i]; entity &e = *f.flagent; - if(!e.spawned || !f.ack || (f.state == CTFF_INBASE && !numflagspawn[i])) continue; + if(!e.spawned || !f.ack || (f.state == CTFF_INBASE && !clentstats.flags[i])) continue; if(OUTBORD(f.pos.x, f.pos.y)) continue; if(f.state==CTFF_DROPPED) // 3d collision for dropped ctf flags { @@ -655,7 +655,7 @@ void flagidle(int flag) flaginfos[flag].flagent->spawned = false; } -void entstats(void) +void entstats_(void) { int entcnt[MAXENTTYPES] = {0}, clipents = 0, spawncnt[5] = {0}; loopv(ents) @@ -693,7 +693,7 @@ void entstats(void) conoutf("total entities: %d", ents.length()); } -COMMAND(entstats, ""); +COMMANDN(entstats, entstats_, ""); vector changedents; int lastentsync = 0; diff --git a/source/src/protos.h b/source/src/protos.h index 861244d0..f53d2294 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -454,6 +454,8 @@ bool requestmasterf(const char *fmt, ...); // for AUTH et al // clientgame extern flaginfo flaginfos[2]; +extern entitystats_s clentstats; +extern mapdim_s clmapdims; extern int sessionid; extern int gametimecurrent; extern int gametimemaximum; @@ -537,7 +539,6 @@ extern void teamflagscores(int &team1, int &team2); extern void setupworld(int factor); extern void sqrdefault(sqr *s); extern bool worldbordercheck(int x1, int x2, int y1, int y2, int z1, int z2); -extern void calcmapdims(); extern bool empty_world(int factor, bool force); extern void remip(const block &b, int level = 0); extern void remipmore(const block &b, int level = 0); @@ -741,7 +742,6 @@ extern bool addscorchmark(vec &o, float radius = 7); extern void render_particles(int time, int typemask = ~0); // worldio -extern mapdim mapdims; extern const char *setnames(const char *name); extern void save_world(char *mname, bool skipoptimise = false, bool addcomfort = false); extern bool load_world(char *mname); @@ -985,6 +985,10 @@ extern void extinfo_teamscorebuf(ucharbuf &p); extern char *votestring(int type, char *arg1, char *arg2, char *arg3); extern int wizardmain(int argc, char **argv); +// some tools +extern servsqr *createservworld(const sqr *s, int _cubicsize); +extern int calcmapdims(mapdim_s &md, const servsqr *s, int _ssize); +extern void calcentitystats(entitystats_s &es, const persistent_entity *pents, int pentsize); // demo #define DHDR_DESCCHARS 80 #define DHDR_PLISTCHARS 322 diff --git a/source/src/rendergl.cpp b/source/src/rendergl.cpp index 8c160b76..7a1bdf3b 100644 --- a/source/src/rendergl.cpp +++ b/source/src/rendergl.cpp @@ -529,9 +529,9 @@ void recomputecamera() case SM_OVERVIEW: { // TODO : fix water rendering - camera1->o.x = mapdims.xm; - camera1->o.y = mapdims.ym; - camera1->o.z = mapdims.maxceil + 1; + camera1->o.x = clmapdims.xm; + camera1->o.y = clmapdims.ym; + camera1->o.z = clmapdims.maxceil + 1; camera1->pitch = -90; camera1->yaw = 0; @@ -824,14 +824,14 @@ void drawminimap(int w, int h) physent minicam; camera1 = &minicam; camera1->type = ENT_CAMERA; - camera1->o.x = mapdims.xm; - camera1->o.y = mapdims.ym; + camera1->o.x = clmapdims.xm; + camera1->o.y = clmapdims.ym; //float gdim = max(mapdims.xspan, mapdims.yspan)+2.0f; //make 1 cube smaller to give it a black edge - float gdim = max(mapdims.xspan, mapdims.yspan); //no border + float gdim = max(clmapdims.xspan, clmapdims.yspan); //no border if(gdim < 1) gdim = ssize/2.0f; - camera1->o.z = mapdims.maxceil + 1; + camera1->o.z = clmapdims.maxceil + 1; camera1->pitch = -90; camera1->yaw = 0; @@ -846,7 +846,7 @@ void drawminimap(int w, int h) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // stencil added 2010jul22 glMatrixMode(GL_PROJECTION); glLoadIdentity(); - glOrtho(-orthd, orthd, -orthd, orthd, 0, (mapdims.maxceil - mapdims.minfloor) + 2); // depth of map +2 covered + glOrtho(-orthd, orthd, -orthd, orthd, 0, (clmapdims.maxceil - clmapdims.minfloor) + 2); // depth of map +2 covered glScalef(1, -1, 1); glMatrixMode(GL_MODELVIEW); glCullFace(GL_BACK); @@ -862,7 +862,7 @@ void drawminimap(int w, int h) renderstrips(); glDepthFunc(GL_LESS); rendermapmodels(); - renderzones(mapdims.maxceil); + renderzones(clmapdims.maxceil); //renderentities();// IMHO better done by radar itself, if at all resettmu(0); float hf = waterlevel - 0.3f; @@ -875,10 +875,10 @@ void drawminimap(int w, int h) glColor3f(0, 0, 0); glLineWidth(2.0f); glBegin(GL_LINE_LOOP); - glVertex3f(mapdims.x1 + 0.02f, mapdims.y1 + 0.02f, mapdims.maxceil); - glVertex3f(mapdims.x1 + mapdims.xspan - 0.02f, mapdims.y1 + 0.02f, mapdims.maxceil); - glVertex3f(mapdims.x1 + mapdims.xspan - 0.02f, mapdims.y1 + mapdims.yspan - 0.02f, mapdims.maxceil); - glVertex3f(mapdims.x1 + 0.02f, mapdims.y1 + mapdims.yspan - 0.02f, mapdims.maxceil); + glVertex3f(clmapdims.x1 + 0.02f, clmapdims.y1 + 0.02f, clmapdims.maxceil); + glVertex3f(clmapdims.x1 + clmapdims.xspan - 0.02f, clmapdims.y1 + 0.02f, clmapdims.maxceil); + glVertex3f(clmapdims.x1 + clmapdims.xspan - 0.02f, clmapdims.y1 + clmapdims.yspan - 0.02f, clmapdims.maxceil); + glVertex3f(clmapdims.x1 + 0.02f, clmapdims.y1 + clmapdims.yspan - 0.02f, clmapdims.maxceil); glEnd(); glLineWidth(prevLineWidth); glEnable(GL_BLEND); @@ -930,10 +930,10 @@ void setperspective(float fovy, float aspect, float nearplane, float farplane) GLdouble ydist = nearplane * tan(fovy/2*RAD), xdist = ydist * aspect; if(player1->isspectating() && player1->spectatemode == SM_OVERVIEW) { - int gdim = max(mapdims.xspan, mapdims.yspan); + int gdim = max(clmapdims.xspan, clmapdims.yspan); if(!gdim) gdim = ssize/2; int orthd = 2 + gdim/2; - glOrtho(-orthd, orthd, -orthd, orthd, 0, (mapdims.maxceil - mapdims.minfloor) + 2); // depth of map +2 covered + glOrtho(-orthd, orthd, -orthd, orthd, 0, (clmapdims.maxceil - clmapdims.minfloor) + 2); // depth of map +2 covered } else { diff --git a/source/src/renderhud.cpp b/source/src/renderhud.cpp index 8504cba2..016fed6c 100644 --- a/source/src/renderhud.cpp +++ b/source/src/renderhud.cpp @@ -482,14 +482,14 @@ void drawradar_showmap(playerent *p, int w, int h) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_BLEND); - float gdim = max(mapdims.xspan, mapdims.yspan); //no border - float offd = fabs((mapdims.yspan - mapdims.xspan) / 2.0f); + float gdim = max(clmapdims.xspan, clmapdims.yspan); //no border + float offd = fabs((clmapdims.yspan - clmapdims.xspan) / 2.0f); if(!gdim) { gdim = ssize/2.0f; offd = 0; } float coordtrans = minimapviewsize / gdim; - float offx = gdim == mapdims.yspan ? offd : 0; - float offy = gdim == mapdims.xspan ? offd : 0; + float offx = gdim == clmapdims.yspan ? offd : 0; + float offy = gdim == clmapdims.xspan ? offd : 0; - vec mdd = vec(mapdims.x1 - offx, mapdims.y1 - offy, 0); + vec mdd = vec(clmapdims.x1 - offx, clmapdims.y1 - offy, 0); vec ppv = vec(p->o).sub(mdd).mul(coordtrans); if(!(p->isspectating() && spect3rd)) drawradarent(ppv.x, ppv.y, p->yaw, (p->state==CS_ALIVE || p->state==CS_EDITING) ? (isattacking(p) ? 2 : 0) : 1, 2, iconsize, isattacking(p), "%s", colorname(p)); // local player @@ -555,18 +555,18 @@ void drawradar_vicinity(playerent *p, int w, int h) playerent *d = spect3rd ? players[p->followplayercn] : p; int p_baseteam = p->team == TEAM_SPECT && spect3rd ? team_base(players[p->followplayercn]->team) : team_base(p->team); extern GLuint minimaptex; - int gdim = max(mapdims.xspan, mapdims.yspan); + int gdim = max(clmapdims.xspan, clmapdims.yspan); float radarviewsize = min(VIRTW,VIRTH)/5; float halfviewsize = radarviewsize/2.0f; float iconsize = radarentsize/0.4f; float scaleh = radarheight/(2.0f*gdim); float scaled = radarviewsize/float(radarheight); - float offd = fabs((mapdims.yspan - mapdims.xspan) / 2.0f); + float offd = fabs((clmapdims.yspan - clmapdims.xspan) / 2.0f); if(gdim < 1) { gdim = ssize/2; offd = 0; } - float offx = gdim == mapdims.yspan ? offd : 0; - float offy = gdim==mapdims.xspan ? offd : 0; - vec rtr = vec(mapdims.x1 - offx, mapdims.y1 - offy, 0); - vec rsd = vec(mapdims.xm, mapdims.ym, 0); + float offx = gdim == clmapdims.yspan ? offd : 0; + float offy = gdim == clmapdims.xspan ? offd : 0; + vec rtr = vec(clmapdims.x1 - offx, clmapdims.y1 - offy, 0); + vec rsd = vec(clmapdims.xm, clmapdims.ym, 0); float d2s = radarheight/2.0f; glColor3f(1.0f, 1.0f, 1.0f); glPushMatrix(); diff --git a/source/src/texture.cpp b/source/src/texture.cpp index 3db20c58..527275ef 100644 --- a/source/src/texture.cpp +++ b/source/src/texture.cpp @@ -1045,7 +1045,7 @@ void deletetextureslot(int *n, char *opt, char *_replace) // delete texture slot else if(s->utex > *n) s->utex--; s++; } - block bb = { mapdims.x1 - 1, mapdims.y1 - 1, mapdims.xspan + 2, mapdims.yspan + 2 }; + block bb = { clmapdims.x1 - 1, clmapdims.y1 - 1, clmapdims.xspan + 2, clmapdims.yspan + 2 }; remip(bb); loopv(ents) if(ents[i].type == MAPMODEL) { // check, if mapmodels use the texture @@ -1260,7 +1260,7 @@ void sorttextureslots(char **args, int numargs) s->utex = newslot[s->utex]; s++; } - block bb = { mapdims.x1 - 1, mapdims.y1 - 1, mapdims.xspan + 2, mapdims.yspan + 2 }; + block bb = { clmapdims.x1 - 1, clmapdims.y1 - 1, clmapdims.xspan + 2, clmapdims.yspan + 2 }; remip(bb); loopv(ents) if(ents[i].type == MAPMODEL) { diff --git a/source/src/tools.cpp b/source/src/tools.cpp index 3fbab428..5f3226a6 100644 --- a/source/src/tools.cpp +++ b/source/src/tools.cpp @@ -79,7 +79,108 @@ int fixmapheadersize(int version, int headersize) // we can't trust hdr.header return headersize; } -mapdim mapdims; // min/max X/Y and delta X/Y and min/max Z +// map geometry statistics + +servsqr *createservworld(const sqr *s, int _cubicsize) // create a server-style floorplan on the client, to use same statistics functions on client and server +{ + servsqr *res = new servsqr[_cubicsize], *d = res; + loopirev(_cubicsize) + { + d->type = s->type == SOLID ? SOLID : ((s->type & TAGTRIGGERMASK) | (s->tag & ~TAGTRIGGERMASK)); // guaranteed to not have tagclips on SOLID cubes - so we can check for SOLID without masks + d->ceil = s->ceil; + d->floor = s->floor; + d->vdelta = s->vdelta; + d++; + s++; + } + return res; +} + +int calcmapdims(mapdim_s &md, const servsqr *s, int _ssize) +{ + int res = 0; + md.x1 = md.y1 = _ssize; + md.x2 = md.y2 = 0; + md.minfloor = 127; md.maxceil = -128; + for(int y = 0; y < _ssize; y++) for(int x = 0; x < _ssize; x++) + { + if((s->type & TAGTRIGGERMASK) != SOLID) + { + if(x < md.x1) md.x1 = x; + if(x > md.x2) md.x2 = x; + if(y < md.y1) md.y1 = y; + md.y2 = y; + if(s->floor < md.minfloor) md.minfloor = s->floor; + if(s->ceil > md.maxceil) md.maxceil = s->ceil; + } + s++; + } + if(md.x2 < md.x1 || md.y2 < md.y1) + { // map is completely solid -> default to empty map values + md.x1 = md.y1 = 2; + md.x2 = md.y2 = _ssize - 3; + md.minfloor = 0; + md.maxceil = 16; + res = -1; // reject map on servers + } + if(md.x1 < MINBORD || md.y1 < MINBORD || md.x2 >= _ssize - MINBORD || md.y2 >= _ssize - MINBORD) res = -2; // reject map because of world border violation + md.xspan = md.x2 - md.x1 + 1; + md.yspan = md.y2 - md.y1 + 1; + md.xm = md.x1 + md.xspan / 2.0f; + md.ym = md.y1 + md.yspan / 2.0f; + return res; +} + +void calcentitystats(entitystats_s &es, const persistent_entity *pents, int pentsize) +{ + memset(&es, 0, sizeof(es)); + vector picks; + loopi(pentsize) + { + const persistent_entity &e = pents[i]; + if(e.type < MAXENTTYPES) + { + es.entcnt[e.type]++; + if(e.type == PLAYERSTART) switch(e.attr2) + { + case 0: es.spawns[0]++; break; + case 1: es.spawns[1]++; break; + case 100: es.spawns[2]++; break; + default: es.unknownspawns++; break; + } + if(e.type == CTF_FLAG) switch(e.attr2) + { + case 0: + case 1: + es.flags[e.attr2]++; + es.flagents[e.attr2] = i; + break; + default: + es.unknownflags++; + break; + } + if(isitem(e.type)) picks.add(i); + } + } + es.hasffaspawns = es.spawns[2] >= MINSPAWNS; + es.hasteamspawns = es.spawns[0] >= MINSPAWNS && es.spawns[1] >= MINSPAWNS; + if(es.flags[0] == 1 && es.flags[1] == 1) + { + vec fd(pents[es.flagents[0]].x - pents[es.flagents[1]].x, pents[es.flagents[0]].y - pents[es.flagents[1]].y, 0); + es.flagentdistance = fd.magnitude(); + es.hasflags = es.flagentdistance >= MINFLAGDISTANCE; + } + int r = 0; + loopvjrev(picks) loopirev(j) + { // calculate the distances between all pickups (takes n^2/2 calculations - which should be fine for any sane map) + double d = pow2(pents[picks[i]].x - pents[picks[j]].x) + pow2(pents[picks[i]].y - pents[picks[j]].y); // deltaX^2 + deltaY^2 + frexp(d, &r); + r /= 2; // sqrt ;) + if(r > LARGEST_FACTOR) r = LARGEST_FACTOR; + es.pickupdistance[r]++; + } + es.pickups = picks.length(); +} int cmpintasc(const int *a, const int *b) { return *a - *b; } // leads to ascending sort order int cmpintdesc(const int *a, const int *b) { return *b - *a; } // leads to descending sort order @@ -212,7 +313,7 @@ mapstats *loadmapstats(const char *filename, bool getlayout) maplayout = new char[layoutsize + 256]; memcpy(maplayout, testlayout, layoutsize * sizeof(char)); - memset(&mapdims, 0, sizeof(struct mapdim)); +/* memset(&mapdims, 0, sizeof(struct mapdim_s)); mapdims.x1 = mapdims.y1 = maplayoutssize; loopk(layoutsize) if (testlayout[k] != 127) { @@ -227,7 +328,7 @@ mapstats *loadmapstats(const char *filename, bool getlayout) mapdims.yspan = mapdims.y2 - mapdims.y1; mapdims.minfloor = minfloor; mapdims.maxceil = maxceil; - } +*/ } } delete f; s.hasffaspawns = s.spawns[2] > 0; @@ -239,6 +340,7 @@ mapstats *loadmapstats(const char *filename, bool getlayout) return &s; } + ///////////////////////// debugging /////////////////////// #if defined(WIN32) && !defined(_DEBUG) && !defined(__GNUC__) diff --git a/source/src/world.cpp b/source/src/world.cpp index 928ea026..52163e24 100644 --- a/source/src/world.cpp +++ b/source/src/world.cpp @@ -620,41 +620,6 @@ bool worldbordercheck(int x1, int x2, int y1, int y2, int z1, int z2) // check return true; } -void calcmapdims() -{ - mapdims.x1 = mapdims.y1 = ssize; - mapdims.x2 = mapdims.y2 = 0; - mapdims.minfloor = 127; mapdims.maxceil = -128; // flow initialised those two with "0", but i'd guess, that was by mistake - sqr *s = world; - for(int y = 0; y < ssize; y++) for(int x = 0; x < ssize; x++) - { - if(!SOLID(s)) - { - if(OUTBORD(x, y)) conoutf("non-solid cube in world border found: x %d, y %d", x, y); // will also go fatal() below - if(x < mapdims.x1) mapdims.x1 = x; - if(x > mapdims.x2) mapdims.x2 = x; - if(y < mapdims.y1) mapdims.y1 = y; - mapdims.y2 = y; - if(s->floor < mapdims.minfloor) mapdims.minfloor = s->floor; // for some reason, flow excluded cubes with floor == -128 here - let's try without that... - if(s->ceil > mapdims.maxceil) mapdims.maxceil = s->ceil; - } - s++; - } - if(mapdims.x2 < mapdims.x1 || mapdims.y2 < mapdims.y1) - { // map is completely solid -> default to empty map values - mapdims.x1 = mapdims.y1 = 2; - mapdims.x2 = mapdims.y2 = ssize - 3; - mapdims.minfloor = 0; - mapdims.maxceil = 16; - } - if(mapdims.x1 < MINBORD || mapdims.y1 < MINBORD || mapdims.x2 >= ssize - MINBORD || mapdims.y2 >= ssize - MINBORD) - fatal("calcmapdims(): world border violation (x:%d..%d, y:%d..%d)", mapdims.x1, mapdims.x2, mapdims.y1, mapdims.y2); - mapdims.xspan = mapdims.x2 - mapdims.x1 + 1; - mapdims.yspan = mapdims.y2 - mapdims.y1 + 1; - mapdims.xm = mapdims.x1 + mapdims.xspan / 2.0f; - mapdims.ym = mapdims.y1 + mapdims.yspan / 2.0f; -} - 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; @@ -719,7 +684,9 @@ bool empty_world(int factor, bool force) // main empty world creation routine hdr.headersize = sizeof(header); hdr.sfactor = sfactor; - calcmapdims(); + servsqr *servworld = createservworld(world, cubicsize); + calcmapdims(clmapdims, servworld, ssize); + delete[] servworld; calclight(); resetmap(!copy); if(clearmap) diff --git a/source/src/world.h b/source/src/world.h index be3a868c..7ea8afb3 100644 --- a/source/src/world.h +++ b/source/src/world.h @@ -23,6 +23,13 @@ struct sqr uchar reserved[2]; }; +struct servsqr // server variant of sqr +{ + uchar type; // SOLID, etc. (also contains the two tagclip bits ORed in) + char floor, ceil; // height, in cubes + uchar vdelta; // vertex delta, used for heightfield cubes +}; + enum // hardcoded texture numbers { DEFAULT_SKY = 0, @@ -91,7 +98,7 @@ struct mapstats bool hasflags; }; -struct mapdim +struct mapdim_s { // 0 2 1 3 6 7 int x1, x2, y1, y2, minfloor, maxceil; // outer borders (points to last used cube) // 4 5 @@ -111,10 +118,6 @@ struct mapdim #define MINBORD 2 // 2 cubes from the edge of the world are always solid #define OUTBORD(x,y) ((x)=ssize-MINBORD || (y)>=ssize-MINBORD) #define OUTBORDRAD(x,y,rad) (int(x-rad)=ssize-MINBORD || (y+rad)>=ssize-MINBORD) -#define MAXMHEIGHT 30 -#define MAXMAREA 10000 -#define MAXHHITS 50000 // was 6000, which denied my fav. maps - jamz, 2012-06-12; 15000 denies sane map too - lucas -#define MINFF 2500 #define WATERLEVELSCALING 10 struct block { int x, y, xs, ys, h; short p[5]; }; @@ -123,3 +126,31 @@ struct block { int x, y, xs, ys, h; short p[5]; }; struct vertex { float u, v, x, y, z; uchar r, g, b, a; }; +// map statistics + +#define MINSPAWNS 5 // minimum number of spawns per team +#define MINFLAGDISTANCE 42 // minimum flag entity distance (2D) + +struct entitystats_s +{ + int entcnt[MAXENTTYPES]; // simple count for every basic ent type + int spawns[3]; // number of spawns with valid attr2 + int flags[2]; // number of flags with valid attr2 + int flagents[2]; // entity indices of the flag ents + int pickups; // total number of pickup entities + int pickupdistance[LARGEST_FACTOR + 1]; // entity distance values grouped logarithmically + int flagentdistance; // distance of the two flag entities, if existing + int modes_possible; // bitmask of possible gamemodes, depending on flag and spawn entities + bool hasffaspawns; // has enough ffa spawns + bool hasteamspawns; // has enough team spawns + bool hasflags; // has exactly 2 valid flags + int unknownspawns, unknownflags; // attr2 is not valid +}; + + +// legacy + +#define MAXMHEIGHT 30 +#define MAXMAREA 10000 +#define MAXHHITS 50000 // was 6000, which denied my fav. maps - jamz, 2012-06-12; 15000 denies sane map too - lucas +#define MINFF 2500 diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index 8185bd33..a8f15bf0 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -696,14 +696,14 @@ COMMANDN(savemap9, save_world9, "s"); void showmapdims() { - conoutf(" min X|Y|Z: %3d : %3d : %3d", mapdims.x1, mapdims.y1, mapdims.minfloor); - conoutf(" max X|Y|Z: %3d : %3d : %3d", mapdims.x2, mapdims.y2, mapdims.maxceil); - conoutf("delta X|Y|Z: %3d : %3d : %3d", mapdims.xspan, mapdims.yspan, mapdims.maxceil - mapdims.minfloor); + conoutf(" min X|Y|Z: %3d : %3d : %3d", clmapdims.x1, clmapdims.y1, clmapdims.minfloor); + conoutf(" max X|Y|Z: %3d : %3d : %3d", clmapdims.x2, clmapdims.y2, clmapdims.maxceil); + conoutf("delta X|Y|Z: %3d : %3d : %3d", clmapdims.xspan, clmapdims.yspan, clmapdims.maxceil - clmapdims.minfloor); } COMMAND(showmapdims, ""); extern void preparectf(bool cleanonly = false); -int numspawn[3], maploaded = 0, numflagspawn[2]; +int maploaded = 0; VAR(curmaprevision, 1, 0, 0); extern char *mlayout; @@ -782,18 +782,17 @@ bool load_world(char *mname) // still supports all map formats that have } if(hdr.version < 10) hdr.waterlevel *= WATERLEVELSCALING; setfvar("waterlevel", float(hdr.waterlevel) / WATERLEVELSCALING); - ents.shrink(0); - loopi(3) numspawn[i] = 0; - loopi(2) numflagspawn[i] = 0; + + // read and convert all entities + persistent_entity *tempents = new persistent_entity[hdr.numents]; bool oldentityformat = hdr.version < 10; // version < 10 have only 4 attributes and no scaling loopi(hdr.numents) { - entity &e = ents.add(); + persistent_entity &e = tempents[i]; f->read(&e, oldentityformat ? 12 : sizeof(persistent_entity)); lilswap((short *)&e, 4); if(oldentityformat) e.attr5 = e.attr6 = e.attr7 = 0; else lilswap(&e.attr5, 1); - e.spawned = false; if(e.type == LIGHT && e.attr1 >= 0) { if(!e.attr2) e.attr2 = 255; // needed for MAPVERSION<=2 @@ -818,15 +817,19 @@ bool load_world(char *mname) // still supports all map formats that have //SCALEATTR(7); #undef SCALEATTR } - if(e.type == PLAYERSTART && (e.attr2 == 0 || e.attr2 == 1 || e.attr2 == 100)) - { - if(e.attr2 == 100) - numspawn[2]++; - else - numspawn[e.attr2]++; - } - if(e.type == CTF_FLAG && (e.attr2 == 0 || e.attr2 == 1)) numflagspawn[e.attr2]++; } + // create entities (and update some statistics) + calcentitystats(clentstats, tempents, hdr.numents); + ents.shrink(0); + loopi(hdr.numents) + { + entity &e = ents.add(); + memcpy(&e, &tempents[i], sizeof(persistent_entity)); + e.spawned = false; + } + delete[] tempents; + + // read world geometry delete[] world; setupworld(hdr.sfactor); if(!mapinfo.numelems || (mapinfo.access(mname) && !cmpf(cgzname, mapinfo[mname]))) world = (sqr *)ents.getbuf(); @@ -845,6 +848,11 @@ bool load_world(char *mname) // still supports all map formats that have c2skeepalive(); // calculate map statistics + servsqr *smallworld = createservworld(world, cubicsize); + int we = calcmapdims(clmapdims, smallworld, ssize); + if(we) conoutf("world error %d", we); + delete[] smallworld; + DELETEA(mlayout); mlayout = new char[cubicsize + 256]; memset(mlayout, 0, cubicsize * sizeof(char)); @@ -872,7 +880,6 @@ bool load_world(char *mname) // still supports all map formats that have Mh = Ma ? (float)Mv/Ma : 0; c2skeepalive(); - calcmapdims(); calclight(); conoutf("read map %s rev %d (%d milliseconds)", cgzname, hdr.maprevision, watch.elapsed()); conoutf("%s", hdr.maptitle); diff --git a/source/src/worldlight.cpp b/source/src/worldlight.cpp index 6e74b646..b2e08587 100644 --- a/source/src/worldlight.cpp +++ b/source/src/worldlight.cpp @@ -178,7 +178,12 @@ VARF(ambient, 0, 0, 0xFFFFFF, if(!noteditmode("ambient")) { hdr.ambient = ambien void calclight() { - if(editmode) calcmapdims(); + if(editmode) + { + servsqr *servworld = createservworld(world, cubicsize); + calcmapdims(clmapdims, servworld, ssize); + delete[] servworld; + } uchar r = (hdr.ambient>>16) & 0xFF, g = (hdr.ambient>>8) & 0xFF, b = hdr.ambient & 0xFF; if(!r && !g) { @@ -205,7 +210,7 @@ void calclight() popMT(); // undo the static seedMT() from above - block bb = { mapdims.x1 - 1, mapdims.y1 - 1, mapdims.xspan + 2, mapdims.yspan + 2 }; + block bb = { clmapdims.x1 - 1, clmapdims.y1 - 1, clmapdims.xspan + 2, clmapdims.yspan + 2 }; postlightarea(bb); setvar("fullbright", 0); lastcalclight = totalmillis;