added clip entities for easier clipping

added entset command for easier ent editing
revised command completion system


git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@3182 e016b1c5-ed68-44aa-a2f7-31cac88cffde
This commit is contained in:
Lee Salzman
2008-10-22 03:28:44 +00:00
parent 3f2873259b
commit 8472dd3ba2
12 changed files with 263 additions and 49 deletions
+15
View File
@@ -1,3 +1,18 @@
// standard completions
alias mapcomplete [complete $arg1 "packages/maps" cgz]
mapcommands = "map tdm coop dm lms ts ctf pf lss osok tosok htf vip tktf ktf"
loop i (listlen $mapcommands) [
mapcomplete (at $mapcommands $i)
]
complete demo "demos" dmo
alias entcomplete [listcomplete $arg1 "light playerstart clips ammobox grenades health armour akimbo mapmodel trigger ladder ctf-flag sound clip"]
entcomplete newent
// standard menu definitions
newmenu main
+7
View File
@@ -1,3 +1,10 @@
alias editent [
alias enttype (getenttype)
if (! (strcmp enttype "")) [
saycommand "/entset" $enttype (getentattr 0) (getentattr 1) (getentattr 2) (getentattr 3)
]
]
newmenu editing
menuitem "Mapmodels..." "showmenu Mapmodels"
+1 -1
View File
@@ -40,7 +40,7 @@ bind 7 ""
bind 8 "vdelta -1"
bind 9 "vdelta 1"
bind COLON ""
bind SEMICOLON ""
bind SEMICOLON "editent"
bind LESS ""
bind EQUALS ""
bind GREATER ""
+1
View File
@@ -536,6 +536,7 @@ void parsemessages(int cn, playerent *d, ucharbuf &p)
ents[i].attr4 = getint(p);
ents[i].spawned = false;
if(ents[i].type==LIGHT || to==LIGHT) calclight();
if(ents[i].type==SOUND) preloadmapsound(ents[i]);
break;
}
+113 -24
View File
@@ -440,14 +440,12 @@ int execute(const char *p)
// tab-completion of all idents
static int completesize = 0, completeidx = 0;
static int completesize = -1, completeidx = 0;
static playerent *completeplayer = NULL;
static cvector maplist;
static cvector newentlist;
void resetcomplete()
{
completesize = 0;
completesize = -1;
completeplayer = NULL;
}
@@ -458,9 +456,9 @@ bool nickcomplete(char *s)
if(!players.length()) return false;
char *cp = s;
for(int i = (int)strlen(s) - 2; i > 0; i--)
for(int i = (int)strlen(s) - 1; i > 0; i--)
if(s[i] == ' ') { cp = s + i + 1; break; }
if(!completesize) { completesize = (int)strlen(cp); completeidx = 0; }
if(completesize < 0) { completesize = (int)strlen(cp); completeidx = 0; }
int idx = 0;
if(completeplayer!=NULL)
@@ -483,6 +481,86 @@ bool nickcomplete(char *s)
return false;
}
enum { COMPLETE_FILE = 0, COMPLETE_LIST, COMPLETE_NICK };
struct completekey
{
int type;
const char *dir, *ext;
completekey() {}
completekey(int type, const char *dir, const char *ext) : type(type), dir(dir), ext(ext) {}
};
struct completeval
{
int type;
char *dir, *ext;
vector<char *> list;
completeval(int type, const char *dir, const char *ext) : type(type), dir(dir && dir[0] ? newstring(dir) : NULL), ext(ext && ext[0] ? newstring(ext) : NULL) {}
~completeval() { DELETEA(dir); DELETEA(ext); list.deletecontentsa(); }
};
static inline bool htcmp(const completekey &x, const completekey &y)
{
return x.type==y.type && (x.dir == y.dir || (x.dir && y.dir && !strcmp(x.dir, y.dir))) && (x.ext == y.ext || (x.ext && y.ext && !strcmp(x.ext, y.ext)));
}
static inline uint hthash(const completekey &k)
{
return k.dir ? hthash(k.dir) + k.type : k.type;
}
static hashtable<completekey, completeval *> completedata;
static hashtable<char *, completeval *> completions;
void addcomplete(char *command, int type, char *dir, char *ext)
{
if(type==COMPLETE_FILE)
{
int dirlen = (int)strlen(dir);
while(dirlen > 0 && (dir[dirlen-1] == '/' || dir[dirlen-1] == '\\'))
dir[--dirlen] = '\0';
if(ext)
{
if(strchr(ext, '*')) ext[0] = '\0';
if(!ext[0]) ext = NULL;
}
}
completekey key(type, dir, ext);
completeval **val = completedata.access(key);
if(!val)
{
completeval *f = new completeval(type, dir, ext);
if(type==COMPLETE_LIST) explodelist(dir, f->list);
val = &completedata[completekey(type, f->dir, f->ext)];
*val = f;
}
completeval **hascomplete = completions.access(command);
if(hascomplete) *hascomplete = *val;
else completions[newstring(command)] = *val;
}
void addfilecomplete(char *command, char *dir, char *ext)
{
addcomplete(command, COMPLETE_FILE, dir, ext);
}
void addlistcomplete(char *command, char *list)
{
addcomplete(command, COMPLETE_LIST, list, NULL);
}
void addnickcomplete(char *command)
{
addcomplete(command, COMPLETE_NICK, NULL, NULL);
}
COMMANDN(complete, addfilecomplete, ARG_3STR);
COMMANDN(listcomplete, addlistcomplete, ARG_2STR);
COMMANDN(nickcomplete, addnickcomplete, ARG_1STR);
void commandcomplete(char *s)
{
if(*s!='/')
@@ -494,23 +572,31 @@ void commandcomplete(char *s)
}
if(!s[1]) return;
char *cp = s;
for(int i = (int)strlen(s) - 2; i > 0; i--)
for(int i = (int)strlen(s) - 1; i > 0; i--)
if(s[i] == ';' || s[i] == ' ') { cp = s + i; break; }
if(!completesize)
bool init = false;
if(completesize < 0)
{
completesize = (int)strlen(cp)-1;
completeidx = 0;
if(*cp == ' ')
{
maplist.setsize(0);
listfiles("packages/maps", "cgz", maplist);
if(!newentlist.length())
{
const char *neargs[] = { "light", "health", "armour", "akimbo", "grenades", "clips", "ammobox", "playerstart", "mapmodel", "ctf-flag", "ladder", "sound", "" };
for(int i = 0; strlen(neargs[i]); i++) newentlist.add(newstring(neargs[i]));
}
}
if(*cp == ' ') init = true;
}
completeval *cdata = NULL;
char *end = strchr(s, ' ');
if(end && end <= cp)
{
string command;
s_strncpy(command, s+1, min(size_t(end-s), sizeof(command)));
completeval **hascomplete = completions.access(command);
if(hascomplete) cdata = *hascomplete;
}
if(init && cdata && cdata->type==COMPLETE_FILE)
{
cdata->list.deletecontentsa();
listfiles(cdata->dir, cdata->ext, cdata->list);
}
if(*cp == '/' || *cp == ';')
{ // commandname completion
int idx = 0;
@@ -524,20 +610,23 @@ void commandcomplete(char *s)
completeidx++;
if(completeidx>=idx) completeidx = 0;
}
else
else if(!cdata) return;
else if(cdata->type==COMPLETE_NICK) nickcomplete(s);
else
{ // argument completion
cvector &list = strncmp(s, "/newent", 7) ? maplist : newentlist;
loopv(list)
loopv(cdata->list)
{
int j = (i + completeidx) % list.length();
if(!_strncmpi(list[j], cp + 1, completesize))
int j = (i + completeidx) % cdata->list.length();
if(!_strncmpi(cdata->list[j], cp + 1, completesize))
{
cp[1] = '\0';
s_strcat(s, list[j]);
s_strcat(s, cdata->list[j]);
completeidx = j + 1;
break;
}
}
completeidx++;
if(completeidx >= cdata->list.length()) completeidx = 0;
}
}
+1 -1
View File
@@ -481,7 +481,7 @@ void edittag(int tag)
void newent(char *what, char *a1, char *a2, char *a3, char *a4)
{
EDITSEL;
newentity(sel.x, sel.y, (int)camera1->o.z, what, ATOI(a1), ATOI(a2), ATOI(a3), ATOI(a4));
newentity(-1, sel.x, sel.y, (int)camera1->o.z, what, ATOI(a1), ATOI(a2), ATOI(a3), ATOI(a4));
}
COMMANDN(select, selectpos, ARG_4INT);
+39 -5
View File
@@ -11,7 +11,8 @@ const char *entnames[] =
"clips", "ammobox","grenades",
"health", "armour", "akimbo",
"mapmodel", "trigger",
"ladder", "ctf-flag", "sound", "?", "?",
"ladder", "ctf-flag",
"sound", "clip", "", ""
};
const char *entmdlnames[] =
@@ -27,6 +28,34 @@ void renderent(entity &e)
rendermodel(mdlname, ANIM_MAPMODEL|ANIM_LOOP|ANIM_DYNALLOC, 0, 0, vec(e.x, e.y, z+S(e.x, e.y)->floor+e.attr1), yaw, 0);
}
void renderclip(entity &e)
{
float radius = max(float(e.attr2), 1.0f);
vec bbmin(e.x - radius, e.y - radius, float(S(e.x, e.y)->floor+e.attr1)),
bbmax(e.x + radius, e.y + radius, bbmin.z + max(float(e.attr3), 1.0f));
glDisable(GL_TEXTURE_2D);
linestyle(1, 0xFF, 0xFF, 0);
glBegin(GL_LINES);
glVertex3f(bbmin.x, bbmin.y, bbmin.z);
loopi(2) glVertex3f(bbmax.x, bbmin.y, bbmin.z);
loopi(2) glVertex3f(bbmax.x, bbmax.y, bbmin.z);
loopi(2) glVertex3f(bbmin.x, bbmax.y, bbmin.z);
glVertex3f(bbmin.x, bbmin.y, bbmin.z);
glVertex3f(bbmin.x, bbmin.y, bbmax.z);
loopi(2) glVertex3f(bbmax.x, bbmin.y, bbmax.z);
loopi(2) glVertex3f(bbmax.x, bbmax.y, bbmax.z);
loopi(2) glVertex3f(bbmin.x, bbmax.y, bbmax.z);
glVertex3f(bbmin.x, bbmin.y, bbmax.z);
loopi(8) glVertex3f(i&2 ? bbmax.x : bbmin.x, i&4 ? bbmax.y : bbmin.y, i&1 ? bbmax.z : bbmin.z);
glEnd();
glEnable(GL_TEXTURE_2D);
}
void renderentities()
{
if(editmode && !reflecting && !refracting && !stenciling)
@@ -35,13 +64,14 @@ void renderentities()
if(lastmillis - lastsparkle >= 20)
{
lastsparkle = lastmillis - (lastmillis%20);
int closest = closestent();
loopv(ents)
{
entity &e = ents[i];
if(e.type==NOTUSED) continue;
vec v(e.x, e.y, e.z);
if(vec(v).sub(camera1->o).dot(camdir) < 0) continue;
particle_splash(2, 2, 40, v);
particle_splash(i == closest ? 12 : 2, 2, 40, v);
}
}
}
@@ -61,10 +91,14 @@ void renderentities()
renderent(e);
}
}
else if(e.type==CTF_FLAG && editmode)
else if(editmode)
{
s_sprintfd(path)("pickups/flags/%s", team_string(e.attr2));
rendermodel(path, ANIM_FLAG|ANIM_LOOP, 0, 0, vec(e.x, e.y, (float)S(e.x, e.y)->floor), (float)((e.attr1+7)-(e.attr1+7)%15), 0, 120.0f);
if(e.type==CTF_FLAG)
{
s_sprintfd(path)("pickups/flags/%s", team_string(e.attr2));
rendermodel(path, ANIM_FLAG|ANIM_LOOP, 0, 0, vec(e.x, e.y, (float)S(e.x, e.y)->floor), (float)((e.attr1+7)-(e.attr1+7)%15), 0, 120.0f);
}
else if(e.type==CLIP) renderclip(e);
}
}
if(m_flags) loopi(2)
+2 -1
View File
@@ -3,13 +3,14 @@ enum // static entity types
NOTUSED = 0, // entity slot not in use in map
LIGHT, // lightsource, attr1 = radius, attr2 = intensity
PLAYERSTART, // attr1 = angle, attr2 = team
I_CLIPS, I_AMMO,I_GRENADE,
I_CLIPS, I_AMMO, I_GRENADE,
I_HEALTH, I_ARMOUR, I_AKIMBO,
MAPMODEL, // attr1 = angle, attr2 = idx
CARROT, // attr1 = tag, attr2 = type
LADDER,
CTF_FLAG, // attr1 = angle, attr2 = red/blue
SOUND,
CLIP,
MAXENTTYPES
};
+24 -10
View File
@@ -114,17 +114,31 @@ bool mmcollide(physent *d, float &hi, float &lo) // collide with a map
loopv(ents)
{
entity &e = ents[i];
if(e.type!=MAPMODEL) continue;
mapmodelinfo &mmi = getmminfo(e.attr2);
if(!&mmi || !mmi.h) continue;
const float r = mmi.rad+d->radius;
if(fabs(e.x-d->o.x)<r && fabs(e.y-d->o.y)<r)
if(e.type==CLIP)
{
const float mmz = (float)(S(e.x, e.y)->floor+mmi.zoff+e.attr3);
const float dz = d->o.z-eyeheight;
if(dz<mmz) { if(mmz<hi) hi = mmz; }
else if(mmz+mmi.h>lo) lo = mmz+mmi.h;
if(hi-lo < playerheight) return false;
const float r = max(float(e.attr2), 1.0f) + d->radius;
if(fabs(e.x-d->o.x)<r && fabs(e.y-d->o.y)<r)
{
const float cz = float(S(e.x, e.y)->floor+e.attr1), ch = max(float(e.attr3), 1.0f);
const float dz = d->o.z-d->eyeheight;
if(dz<cz) { if(cz<hi) hi = cz; }
else if(cz+ch>lo) lo = cz+ch;
if(hi-lo < playerheight) return false;
}
}
else if(e.type==MAPMODEL)
{
mapmodelinfo &mmi = getmminfo(e.attr2);
if(!&mmi || !mmi.h) continue;
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 dz = d->o.z-eyeheight;
if(dz<mmz) { if(mmz<hi) hi = mmz; }
else if(mmz+mmi.h>lo) lo = mmz+mmi.h;
if(hi-lo < playerheight) return false;
}
}
}
return true;
+1 -1
View File
@@ -383,7 +383,7 @@ extern void remipmore(const block &b, int level = 0);
extern int closestent();
extern int findentity(int type, int index = 0);
extern int findentity(int type, int index, uchar attr2);
extern entity *newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v4);
extern entity *newentity(int index, int x, int y, int z, char *what, int v1, int v2, int v3, int v4);
// worldlight
extern int lastcalclight;
+2 -1
View File
@@ -185,7 +185,7 @@ void cleanupexplosion()
}
}
#define MAXPARTYPES 12
#define MAXPARTYPES 13
struct particle { vec o, d; int fade, type; int millis; particle *next; };
particle *parlist[MAXPARTYPES], *parempty = NULL;
@@ -263,6 +263,7 @@ static struct parttype { int type; float r, g, b; int gr, tex; float sz; } partt
{ PT_DECAL, 1.0f, 1.0f, 1.0f, 0, 5, 1.5f }, // scorch decal
{ PT_HUDFLASH, 1.0f, 1.0f, 1.0f, 0, 6, 0.7f }, // hudgun muzzle flash
{ PT_FLASH, 1.0f, 1.0f, 1.0f, 0, 6, 0.7f }, // muzzle flash
{ PT_PART, 1.0f, 0.5f, 0.2f, 20, 0, 0.08f }, // orange: edit mode closest ent
};
VAR(particlesize, 20, 100, 500);
+57 -5
View File
@@ -158,6 +158,31 @@ void entproperty(int prop, int amount)
hashtable<char *, enet_uint32> 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]);
}
int getentattr(int attr)
{
int e = closestent();
if(e>=0) switch(attr)
{
case 0: return ents[e].attr1;
case 1: return ents[e].attr2;
case 2: return ents[e].attr3;
case 3: return ents[e].attr4;
}
return 0;
}
COMMAND(getenttype, ARG_NONE);
COMMAND(getentattr, ARG_1EXP);
void delent()
{
int e = closestent();
@@ -179,7 +204,7 @@ int findtype(char *what)
return NOTUSED;
}
entity *newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v4)
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;
@@ -188,8 +213,13 @@ entity *newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v
switch(type)
{
case CLIP:
if(v2<=0) e.attr2 = 1;
if(v3<=0) e.attr3 = 1;
break;
case LIGHT:
if(v1>64) v1 = 64;
if(v1>64) e.attr1 = 64;
if(!v1) e.attr1 = 16;
if(!v2 && !v3 && !v4) e.attr2 = 255;
break;
@@ -204,17 +234,39 @@ entity *newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v
e.attr1 = (int)camera1->yaw;
break;
}
addmsg(SV_EDITENT, "ri9", ents.length(), type, e.x, e.y, e.z, e.attr1, e.attr2, e.attr3, e.attr4);
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;
ents.add(e);
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: preloadmapsound(e); break;
}
return &ents.last();
return index<0 ? &ents.last() : &ents[index];
}
void entset(char *what, char *a1, char *a2, char *a3, char *a4)
{
int n = closestent();
if(n>=0)
{
entity &e = ents[n];
newentity(n, e.x, e.y, e.z, what, ATOI(a1), ATOI(a2), ATOI(a3), ATOI(a4));
}
}
COMMAND(entset, ARG_5STR);
void clearents(char *name)
{
int type = findtype(name);