higher resolution for entity attributes

Interpret the values for entity yaw angles and clip entity sizes as
fifths or tenths of cubes or degree.

This makes some entity attributes appear as float values to the user.
The map file still only keeps integer values - but now they are
interpreted differently - for example, a health pack elevation value of 6
now means 0.6 cubes instead of 6.

Note: the scaling factors are hardcoded - use ENTSCALE5 or ENTSCALE10
whereever possible. For generic entity editing, the scaling values are
also stored in entscale[] - but that table can not be changed without
breaking the map format! This scaling is similar to the treatment of the
vdelta values of map cubes.
This commit is contained in:
ac-stef
2015-05-27 15:47:04 +02:00
parent ea480a7b0c
commit f774e4d841
9 changed files with 115 additions and 46 deletions
+1 -1
View File
@@ -660,7 +660,7 @@ void gotoplayerstart(playerent *d, entity *e)
d->o.x = e->x;
d->o.y = e->y;
d->o.z = e->z;
d->yaw = e->attr1;
d->yaw = float(e->attr1) / ENTSCALE10;
d->pitch = 0;
d->roll = 0;
}
+2 -1
View File
@@ -276,8 +276,9 @@ bool good_map() // call this function only at startmap
{
entity &e2 = ents[j];
if (e2.type < I_CLIPS || e2.type > I_AKIMBO || i == j) continue;
// only I_CLIPS, I_AMMO, I_GRENADE, I_HEALTH, I_HELMET, I_ARMOUR, I_AKIMBO
#define DIST(x) (e1.x - e2.x)
#define DIST_ATT ((e1.z + e1.attr1) - (e2.z + e2.attr1))
#define DIST_ATT ((e1.z + float(e1.attr1) / entscale[e1.type][0]) - (e2.z + float(e2.attr1) / entscale[e2.type][0]))
float r2 = DIST(x)*DIST(x) + DIST(y)*DIST(y) + DIST_ATT*DIST_ATT;
#undef DIST_ATT
#undef DIST
+7 -3
View File
@@ -105,7 +105,10 @@ char *editinfo()
if(e >= 0)
{
entity &c = ents[e];
formatstring(info)("closest entity = %s (%d, %d, %d, %d, %d, %d, %d), ", entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4, c.attr5, c.attr6, c.attr7);
int t = c.type < MAXENTTYPES ? c.type : 0;
#define AA(x) floatstr(float(c.attr##x) / entscale[t][x - 1], true)
formatstring(info)("closest entity = %s (%s, %s, %s, %s, %s, %s, %s), ", entnames[t], AA(1), AA(2), AA(3), AA(4), AA(5), AA(6), AA(7));
#undef AA
}
if(selset()) concatformatstring(info, "selection = (%d, %d)", (sels.last()).xs, (sels.last()).ys);
else concatformatstring(info, "no selection");
@@ -378,7 +381,7 @@ void gotoentity(int *n)
player1->o.x = ents[*n].x;
player1->o.y = ents[*n].y;
player1->o.z = ents[*n].z;
player1->yaw = (ents[*n].attr1 % 360 + 360) % 360; // works for most ;)
player1->yaw = ((ents[*n].attr1 / ENTSCALE10) % 360 + 360) % 360; // works for most ;)
player1->pitch = 0;
player1->resetinterp();
}
@@ -984,7 +987,7 @@ COMMANDF(selectionrotate, "i", (int *d) { selectionrotate(*d); });
COMMAND(selectionflip, "s");
COMMAND(countwalls, "i");
COMMANDF(settex, "ii", (int *texture, int *type) { settex(*texture, *type); });
#if 0 // fix clip scaling
void transformclipentities() // transforms all clip entities to tag clips, if they are big enough (so, that no player could be above or below them)
{
EDITMP;
@@ -1032,3 +1035,4 @@ void transformclipentities() // transforms all clip entities to tag clips, if t
}
COMMAND(transformclipentities, "");
#endif
+23 -23
View File
@@ -8,28 +8,28 @@ VAR(showmodelclipping, 0, 0, 1);
vector<entity> ents;
vector<int> eh_ents; // edithide entities
const char *entmdlnames[] =
{
"pistolclips", "ammobox", "nade", "health", "helmet", "kevlar", "akimbo", "nades", //FIXME
};
{
"pistolclips", "ammobox", "nade", "health", "helmet", "kevlar", "akimbo", "nades" //FIXME
};
void renderent(entity &e)
{
/* FIXME: if the item list change, this hack will be messed */
void renderent(entity &e)
{
/* FIXME: if the item list change, this hack will be messed */
defformatstring(widn)("modmdlpickup%d", e.type-3);
defformatstring(mdlname)("pickups/%s", identexists(widn)?getalias(widn):
defformatstring(widn)("modmdlpickup%d", e.type-3);
defformatstring(mdlname)("pickups/%s", identexists(widn)?getalias(widn):
entmdlnames[e.type-I_CLIPS+(m_lss && e.type==I_GRENADE ? 5:0)]);
entmdlnames[e.type-I_CLIPS+(m_lss && e.type==I_GRENADE ? 5:0)]);
float z = (float)(1+sinf(lastmillis/100.0f+e.x+e.y)/20), yaw = lastmillis/10.0f;
rendermodel(mdlname, ANIM_MAPMODEL|ANIM_LOOP|ANIM_DYNALLOC, 0, 0, vec(e.x, e.y, z+S(e.x, e.y)->floor+e.attr1), 0, yaw, 0);
}
float z = (float)(1+sinf(lastmillis/100.0f+e.x+e.y)/20), yaw = lastmillis/10.0f;
rendermodel(mdlname, ANIM_MAPMODEL|ANIM_LOOP|ANIM_DYNALLOC, 0, 0, vec(e.x, e.y, z+S(e.x, e.y)->floor + float(e.attr1) / ENTSCALE10), 0, yaw, 0);
}
void renderclip(entity &e)
{
float xradius = max(float(e.attr2), 0.1f), yradius = max(float(e.attr3), 0.1f);
vec bbmin(e.x - xradius, e.y - yradius, float(S(e.x, e.y)->floor+e.attr1)),
bbmax(e.x + xradius, e.y + yradius, bbmin.z + max(float(e.attr4), 0.1f));
float xradius = max(float(e.attr2) / ENTSCALE5, 0.1f), yradius = max(float(e.attr3) / ENTSCALE5, 0.1f);
vec bbmin(e.x - xradius, e.y - yradius, float(S(e.x, e.y)->floor + float(e.attr1) / ENTSCALE10)),
bbmax(e.x + xradius, e.y + yradius, bbmin.z + max(float(e.attr4) / ENTSCALE5, 0.1f));
glDisable(GL_TEXTURE_2D);
switch(e.type)
@@ -67,7 +67,7 @@ void rendermapmodels()
{
mapmodelinfo &mmi = getmminfo(e.attr2);
if(!&mmi) continue;
rendermodel(mmi.name, ANIM_MAPMODEL|ANIM_LOOP, e.attr4, 0, vec(e.x, e.y, (float)S(e.x, e.y)->floor+mmi.zoff+e.attr3), e.attr6, e.attr1 / 4.0f, e.attr5 / 4.0f, 10.0f);
rendermodel(mmi.name, ANIM_MAPMODEL|ANIM_LOOP, e.attr4, 0, vec(e.x, e.y, S(e.x, e.y)->floor + mmi.zoff + float(e.attr3) / ENTSCALE5), e.attr6, float(e.attr1) / ENTSCALE10, float(e.attr5) / ENTSCALE10, 10.0f);
}
}
}
@@ -218,7 +218,7 @@ void renderentities()
if(e.type==CTF_FLAG)
{
defformatstring(path)("pickups/flags/%s", team_basestring(e.attr2));
rendermodel(path, ANIM_FLAG|ANIM_LOOP, 0, 0, vec(e.x, e.y, (float)S(e.x, e.y)->floor), 0, e.attr1 / 4.0f, 0, 120.0f);
rendermodel(path, ANIM_FLAG|ANIM_LOOP, 0, 0, vec(e.x, e.y, (float)S(e.x, e.y)->floor), 0, float(e.attr1) / ENTSCALE10, 0, 120.0f);
}
else if((e.type == CLIP || e.type == PLCLIP) && showclips && !stenciling) renderclip(e);
else if(e.type == MAPMODEL && showclips && showmodelclipping && !stenciling)
@@ -228,9 +228,9 @@ void renderentities()
{
entity ce = e;
ce.type = MAPMODEL;
ce.attr1 = mmi.zoff+e.attr3;
ce.attr2 = ce.attr3 = mmi.rad;
ce.attr4 = mmi.h;
ce.attr1 = (mmi.zoff + float(e.attr3) / ENTSCALE5) * ENTSCALE10;
ce.attr2 = ce.attr3 = mmi.rad * ENTSCALE5;
ce.attr4 = mmi.h * ENTSCALE5;
renderclip(ce);
}
}
@@ -243,7 +243,7 @@ void renderentities()
{
glColor3f(0, 1, 1);
vec dir;
vecfromyawpitch(e.attr1, 0, -1, 0, dir);
vecfromyawpitch(float(e.attr1) / ENTSCALE10, 0, -1, 0, dir);
renderentarrow(e, dir, 4);
glColor3f(1, 1, 1);
}
@@ -271,7 +271,7 @@ void renderentities()
if(OUTBORD(f.pos.x, f.pos.y)) break;
entity &e = *f.flagent;
defformatstring(path)("pickups/flags/%s%s", m_ktf ? "" : team_basestring(i), m_htf ? "_htf" : m_ktf ? "ktf" : "");
if(f.flagent->spawned) rendermodel(path, ANIM_FLAG|ANIM_LOOP, 0, 0, vec(f.pos.x, f.pos.y, f.state==CTFF_INBASE ? (float)S(int(f.pos.x), int(f.pos.y))->floor : f.pos.z), 0, e.attr1 / 4.0f, 0, 120.0f);
if(f.flagent->spawned) rendermodel(path, ANIM_FLAG|ANIM_LOOP, 0, 0, vec(f.pos.x, f.pos.y, f.state==CTFF_INBASE ? (float)S(int(f.pos.x), int(f.pos.y))->floor : f.pos.z), 0, float(e.attr1) / ENTSCALE10, 0, 120.0f);
break;
}
case CTFF_IDLE:
@@ -426,7 +426,7 @@ void checkitems(playerent *d)
if(e.type==CTF_FLAG) continue;
// simple 2d collision
vec v(e.x, e.y, S(e.x, e.y)->floor+eyeheight);
if(isitem(e.type)) v.z += e.attr1;
if(isitem(e.type)) v.z += float(e.attr1) / ENTSCALE10;
if(d->o.dist(v)<2.5f) trypickup(i, d);
}
if(m_flags) loopi(2)
+5
View File
@@ -15,6 +15,11 @@ enum // static entity types
MAXENTTYPES
};
extern short entwraparound[MAXENTTYPES][7];
extern uchar entscale[MAXENTTYPES][7];
#define ENTSCALE10 10
#define ENTSCALE5 5
enum {MAP_IS_BAD, MAP_IS_EDITABLE, MAP_IS_GOOD};
extern const char *entnames[];
+3 -3
View File
@@ -139,9 +139,9 @@ bool mmcollide(physent *d, float &hi, float &lo) // collide with a map
// if(e.type==CLIP || (e.type == PLCLIP && d->type == ENT_PLAYER))
if (e.type==CLIP || (e.type == PLCLIP && (d->type == ENT_BOT || d->type == ENT_PLAYER || (d->type == ENT_BOUNCE && ((bounceent *)d)->plclipped)))) // don't allow bots to hack themselves into plclips - Bukz 2011/04/14
{
if(fabs(e.x-d->o.x) < e.attr2 + d->radius && fabs(e.y-d->o.y) < e.attr3 + d->radius)
if(fabs(e.x-d->o.x) < float(e.attr2) / ENTSCALE5 + d->radius && fabs(e.y-d->o.y) < float(e.attr3) / ENTSCALE5 + d->radius)
{
const float cz = float(S(e.x, e.y)->floor+e.attr1), ch = float(e.attr4);
const float cz = float(S(e.x, e.y)->floor + float(e.attr1) / ENTSCALE10), ch = float(e.attr4) / ENTSCALE5;
const float dz = d->o.z-d->eyeheight;
if(dz < cz - 0.42) { if(cz<hi) hi = cz; }
else if(cz+ch>lo) lo = cz+ch;
@@ -155,7 +155,7 @@ bool mmcollide(physent *d, float &hi, float &lo) // collide with a map
const float r = mmi.rad+d->radius;
if(fabs(e.x-d->o.x)<r && fabs(e.y-d->o.y)<r)
{
const float mmz = float(S(e.x, e.y)->floor+mmi.zoff+e.attr3);
const float mmz = float(S(e.x, e.y)->floor + mmi.zoff + float(e.attr3) / ENTSCALE5);
const float dz = d->o.z-eyeheight;
if(dz < mmz - 0.42) { if(mmz < hi) hi = mmz; }
else if(mmz+mmi.h>lo) lo = mmz+mmi.h;
+43
View File
@@ -451,6 +451,49 @@ const char *entnames[] =
"mapmodel", "trigger", "ladder", "ctf-flag", "sound", "clip", "plclip", ""
};
// entity attribute scaling and wraparound definitions for mapformat 10
short entwraparound[MAXENTTYPES][7] =
{ // limit angles to 0..359 degree and mapsound slot numbers to 0..255
{ 0, 0, 0, 0, 0, 0, 0 }, // deleted
{ 0, 0, 0, 0, 0, 0, 0 }, // light
{ 3600, 0, 0, 0, 0, 0, 0 }, // playerstart
{ 0, 0, 0, 0, 0, 0, 0 }, // pistol
{ 0, 0, 0, 0, 0, 0, 0 }, // ammobox
{ 0, 0, 0, 0, 0, 0, 0 }, // grenades
{ 0, 0, 0, 0, 0, 0, 0 }, // health
{ 0, 0, 0, 0, 0, 0, 0 }, // helmet
{ 0, 0, 0, 0, 0, 0, 0 }, // armour
{ 0, 0, 0, 0, 0, 0, 0 }, // akimbo
{ 3600, 0, 0, 0, 3600, 0, 0 }, // mapmodel
{ 0, 0, 0, 0, 0, 0, 0 }, // trigger
{ 0, 0, 0, 0, 0, 0, 0 }, // ladder
{ 3600, 0, 0, 0, 0, 0, 0 }, // ctf-flag
{ 256, 0, 0, 0, 0, 0, 0 }, // sound
{ 0, 0, 0, 0, 0, 0, 0 }, // clip
{ 0, 0, 0, 0, 0, 0, 0 } // plclip
};
uchar entscale[MAXENTTYPES][7] =
{ // (no zeros allowed here!)
{ 1, 1, 1, 1, 1, 1, 1 }, // deleted
{ 1, 1, 1, 1, 1, 1, 1 }, // light
{ 10, 1, 1, 1, 1, 1, 1 }, // playerstart
{ 10, 1, 1, 1, 1, 1, 1 }, // pistol
{ 10, 1, 1, 1, 1, 1, 1 }, // ammobox
{ 10, 1, 1, 1, 1, 1, 1 }, // grenades
{ 10, 1, 1, 1, 1, 1, 1 }, // health
{ 10, 1, 1, 1, 1, 1, 1 }, // helmet
{ 10, 1, 1, 1, 1, 1, 1 }, // armour
{ 10, 1, 1, 1, 1, 1, 1 }, // akimbo
{ 10, 1, 5, 1, 10, 1, 1 }, // mapmodel
{ 1, 1, 1, 1, 1, 1, 1 }, // trigger
{ 1, 1, 1, 1, 1, 1, 1 }, // ladder
{ 10, 1, 1, 1, 1, 1, 1 }, // ctf-flag
{ 1, 1, 1, 1, 1, 1, 1 }, // sound
{ 10, 5, 5, 5, 1, 1, 1 }, // clip
{ 10, 5, 5, 5, 1, 1, 1 } // plclip
};
// see entity.h:61: struct itemstat { int add, start, max, sound; };
// Please update ./ac_website/htdocs/docs/introduction.html if these figures change.
itemstat ammostats[NUMGUNS] =
+2 -2
View File
@@ -297,8 +297,8 @@ entity *newentity(int index, int x, int y, int z, char *what, int v1, int v2, in
case PLAYERSTART:
case CTF_FLAG:
e.attr2 = v1;
e.attr1 = (int)camera1->yaw;
if(type != PLAYERSTART) e.attr1 = (e.attr1 + 7 - (e.attr1 + 7) % 15) * 4;
e.attr1 = int(camera1->yaw * ENTSCALE10);
if(type != PLAYERSTART) e.attr1 = e.attr1 + 70 - (e.attr1 + 70) % 150;
break;
}
syncentchanges(true);
+29 -13
View File
@@ -551,7 +551,7 @@ void save_world(char *mname, bool skipoptimise, bool addcomfort)
ucharbuf hx = packheaderextras(addcomfort ? 0 : (1 << HX_EDITUNDO)); // if addcomfort -> add undos/redos
int writeextra = 0;
if(hx.maxlen) tmp.headersize += writeextra = clamp(hx.maxlen, 0, MAXHEADEREXTRA);
if(writeextra || skipoptimise) tmp.version = 10; // 9 and 10 are the same, but in 10 the headersize is reliable - if we don't need it, stick to 9
if(writeextra || skipoptimise || true) tmp.version = 10; // use format 10, if required
bool oldentityformat = tmp.version < 10;
tmp.maprevision += advancemaprevision;
DEBUG("version " << tmp.version << " headersize " << tmp.headersize << " entities " << tmp.numents << " factor " << tmp.sfactor << " revision " << tmp.maprevision);
@@ -568,7 +568,16 @@ void save_world(char *mname, bool skipoptimise, bool addcomfort)
{
if(!ne--) break;
persistent_entity tmp = ents[i];
if(oldentityformat && tmp.type == MAPMODEL) tmp.attr1 = tmp.attr1 / 4;
if(oldentityformat && tmp.type < MAXENTTYPES)
{
tmp.attr1 = tmp.attr1 / entscale[tmp.type][0];
tmp.attr2 = tmp.attr2 / entscale[tmp.type][1];
tmp.attr3 = tmp.attr3 / entscale[tmp.type][2];
tmp.attr4 = tmp.attr4 / entscale[tmp.type][3];
tmp.attr5 = tmp.attr5 / entscale[tmp.type][4];
tmp.attr6 = tmp.attr6 / entscale[tmp.type][5];
tmp.attr7 = tmp.attr7 / entscale[tmp.type][6];
}
lilswap((short *)&tmp, 4);
lilswap(&tmp.attr5, 1);
f->write(&tmp, oldentityformat ? 12 : sizeof(persistent_entity));
@@ -696,7 +705,7 @@ bool load_world(char *mname) // still supports all map formats that have
ents.shrink(0);
loopi(3) numspawn[i] = 0;
loopi(2) numflagspawn[i] = 0;
bool oldentityformat = hdr.version < 10;
bool oldentityformat = hdr.version < 10; // version < 10 have only 4 attributes and no scaling
loopi(hdr.numents)
{
entity &e = ents.add();
@@ -705,22 +714,29 @@ bool load_world(char *mname) // still supports all map formats that have
if(oldentityformat) e.attr5 = e.attr6 = e.attr7 = 0;
else lilswap(&e.attr5, 1);
e.spawned = false;
if(e.type == LIGHT)
if(e.type == LIGHT && e.attr1 >= 0)
{
if(!e.attr2) e.attr2 = 255; // needed for MAPVERSION<=2
if(e.attr1>32) e.attr1 = 32; // 12_03 and below
}
transformoldentitytypes(hdr.version, e.type);
if(oldentityformat)
if(oldentityformat && e.type < MAXENTTYPES)
{
switch(e.type)
{
case CTF_FLAG:
case MAPMODEL:
e.attr1 = e.attr1 + 7 - (e.attr1 + 7) % 15; // round the angle to the nearest 15-degree-step
e.attr1 = (e.attr1 % 360) * 4; // cut down to less that one rotation and set resolution to 0.25 degree
break;
}
if(e.type == CTF_FLAG || e.type == MAPMODEL) e.attr1 = e.attr1 + 7 - (e.attr1 + 7) % 15; // round the angle to the nearest 15-degree-step, like old versions did during rendering
if(e.type == LIGHT && e.attr1 < 0) e.attr1 = 0; // negative lights had no meaning before version 10
int ov, ss;
#define SCALEATTR(x) \
if((ss = entwraparound[e.type][x - 1] / entscale[e.type][x - 1])) e.attr##x = (int(e.attr##x) % ss + ss) % ss; \
e.attr##x = ov = e.attr##x * entscale[e.type][x - 1]; \
if(ov != e.attr##x) conoutf("overflow during conversion of attr%d of entity #%d (%s) - pls check before saving the map", x, i, entnames[e.type]);
SCALEATTR(1);
SCALEATTR(2);
SCALEATTR(3);
SCALEATTR(4);
SCALEATTR(5);
SCALEATTR(6);
SCALEATTR(7);
#undef SCALEATTR
}
if(e.type == PLAYERSTART && (e.attr2 == 0 || e.attr2 == 1 || e.attr2 == 100))
{