From e11a5f55148421a0984a09c484b81b58d8256ca8 Mon Sep 17 00:00:00 2001 From: Lee Salzman Date: Mon, 13 Oct 2008 01:17:44 +0000 Subject: [PATCH] better hitbox testing git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@2992 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- packages/models/playermodels/md2.cfg | 4 +- source/src/bot/bot_util.cpp | 6 +- source/src/cube.h | 1 - source/src/entities.cpp | 41 --------- source/src/entity.h | 7 +- source/src/md2.h | 58 +++++++++---- source/src/md3.h | 18 ++-- source/src/model.h | 7 +- source/src/protos.h | 2 +- source/src/rendergl.cpp | 2 - source/src/rendermodel.cpp | 99 +++++++++++++++++++-- source/src/vertmodel.h | 73 ++++++++++------ source/src/weapon.cpp | 125 ++++++++++++++++++++++----- 13 files changed, 306 insertions(+), 137 deletions(-) diff --git a/packages/models/playermodels/md2.cfg b/packages/models/playermodels/md2.cfg index 468ff9a9..d6140a1e 100644 --- a/packages/models/playermodels/md2.cfg +++ b/packages/models/playermodels/md2.cfg @@ -1,5 +1,7 @@ mdlscale 155 -mdltrans 0 0 24 +mdltrans 2 2 24 mdlshadowdist 20 mdlcachelimit 16 +md2tag tag_head 215 223 188 188 + diff --git a/source/src/bot/bot_util.cpp b/source/src/bot/bot_util.cpp index bc1facb9..2cf315f3 100644 --- a/source/src/bot/bot_util.cpp +++ b/source/src/bot/bot_util.cpp @@ -183,7 +183,7 @@ void TraceLine(vec from, vec to, dynent *pTracer, bool CheckPlayers, traceresult flNearestDist = 9999.0f; if(OUTBORD((int)from.x, (int)from.y)) return; - + // Check if the 'line' collides with entities like mapmodels loopv(ents) { @@ -211,7 +211,7 @@ void TraceLine(vec from, vec to, dynent *pTracer, bool CheckPlayers, traceresult // Check if the 'line' collides with players loopv(players) { - dynent *d = players[i]; + playerent *d = players[i]; if(!d || (d==pTracer) || (d->state != CS_ALIVE)) continue; // Only check valid players flDist = GetDistance(from, d->o); @@ -224,7 +224,7 @@ void TraceLine(vec from, vec to, dynent *pTracer, bool CheckPlayers, traceresult } // Check if the 'line' collides with the local player(player1) - dynent *d = player1; // Shortcut + playerent *d = player1; // Shortcut if (d && (d!=pTracer) && !BotManager.m_pBotToView && (d->state == CS_ALIVE)) { flDist = GetDistance(from, d->o); diff --git a/source/src/cube.h b/source/src/cube.h index 81a53acc..013aa314 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -32,7 +32,6 @@ extern vector bounceents; extern bool editmode; extern vector ents; // map entities extern vec worldpos, camup, camright, camdir; // current target of the crosshair in the world -extern vec hitpos; extern int lastmillis, totalmillis; // last time extern int curtime; // current frame time extern int gamemode, nextmode; diff --git a/source/src/entities.cpp b/source/src/entities.cpp index cabd04f5..0d24c17a 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -275,47 +275,6 @@ void selectnextprimary(int num) COMMANDN(nextprimary, selectnextprimary, ARG_1INT); -// Added by Rick -bool intersect(entity *e, vec &from, vec &to, vec *end) // if lineseg hits entity bounding box(entity version) -{ - mapmodelinfo &mmi = getmminfo(e->attr2); - if(!&mmi || !mmi.h) return false; - - float lo = (float)(S(e->x, e->y)->floor+mmi.zoff+e->attr3); - float hi = lo+mmi.h; - vec v = to, w(e->x, e->y, lo + (fabs(hi-lo)/2.0f)), *p; - v.sub(from); - w.sub(from); - float c1 = w.dot(v); - - if(c1<=0) p = &from; - else - { - float c2 = v.squaredlen(); - if(c2<=c1) p = &to; - else - { - float f = c1/c2; - v.mul(f); - v.add(from); - p = &v; - } - } - - if (p->x <= e->x+mmi.rad - && p->x >= e->x-mmi.rad - && p->y <= e->y+mmi.rad - && p->y >= e->y-mmi.rad - && p->z <= hi - && p->z >= lo) - { - if (end) *end = *p; - return true; - } - return false; -} -// End add by Ricks - // flag ent actions done by the local player int flagdropmillis = 0; diff --git a/source/src/entity.h b/source/src/entity.h index b4eca33b..65b78924 100644 --- a/source/src/entity.h +++ b/source/src/entity.h @@ -324,6 +324,8 @@ struct playerstate } }; +#define HEADSIZE 0.4f + struct playerent : dynent, playerstate { int clientnum, lastupdate, plag, ping; @@ -350,10 +352,13 @@ struct playerent : dynent, playerstate float deltayaw, deltapitch, newyaw, newpitch; int smoothmillis; + vec head; + playerent() : clientnum(-1), lastupdate(0), plag(0), ping(0), lifesequence(0), frags(0), flagscore(0), deaths(0), lastpain(0), lastvoicecom(0), clientrole(CR_DEFAULT), skin(0), nextskin(0), spectatemode(SM_NONE), followplayercn(-1), eardamagemillis(0), respawnoffset(0), prevweaponsel(NULL), weaponsel(NULL), nextweaponsel(NULL), primweap(NULL), nextprimweap(NULL), lastattackweapon(NULL), - smoothmillis(-1) + smoothmillis(-1), + head(-1, -1, -1) { type = ENT_PLAYER; name[0] = team[0] = 0; diff --git a/source/src/md2.h b/source/src/md2.h index 4db20399..37739fd2 100644 --- a/source/src/md2.h +++ b/source/src/md2.h @@ -179,6 +179,11 @@ struct md2 : vertmodel { if(!loaded) return; + if(a) for(int i = 0; a[i].tag; i++) + { + if(a[i].pos) link(NULL, a[i].tag, a[i].pos); + } + if(!cullface) glDisable(GL_CULL_FACE); else if(anim&ANIM_MIRROR) glCullFace(GL_BACK); @@ -208,8 +213,10 @@ struct md2 : vertmodel if(!cullface) glEnable(GL_CULL_FACE); else if(anim&ANIM_MIRROR) glCullFace(GL_FRONT); - if(a) for(int i = 0; a[i].name; i++) + if(a) for(int i = 0; a[i].tag; i++) { + if(a[i].pos) link(NULL, a[i].tag, NULL); + vertmodel *m = (vertmodel *)a[i].m; if(!m) continue; m->parts[0]->index = parts.length()+i; @@ -276,30 +283,43 @@ void md2anim(char *anim, char *frame, char *range, char *speed) loadingmd2->parts.last()->setanim(num, atoi(frame), atoi(range), atof(speed)); } -void md2tag(char *name, char *vert) +void md2tag(char *name, char *vert1, char *vert2, char *vert3, char *vert4) { if(!loadingmd2 || loadingmd2->parts.empty()) { conoutf("not loading an md2"); return; } md2::part &mdl = *loadingmd2->parts.last(); - int vnum = -1; - if(isdigit(vert[0])) vnum = ATOI(vert); - else + int indexes[4] = { -1, -1, -1, -1 }, numverts = 0; + loopi(4) { - int axis = 0, dir = 1; - for(char *s = vert; *s; s++) switch(*s) + char *vert = NULL; + switch(i) { - case '+': dir = 1; break; - case '-': dir = -1; break; - case 'x': - case 'X': axis = 0; break; - case 'y': - case 'Y': axis = 1; break; - case 'z': - case 'Z': axis = 2; break; + case 0: vert = vert1; break; + case 1: vert = vert2; break; + case 2: vert = vert3; break; + case 3: vert = vert4; break; } - if(!mdl.meshes.empty()) vnum = mdl.meshes[0]->findvert(axis, dir); - if(vnum < 0) { conoutf("could not find vertex %s", vert); return; } + if(!vert[0]) break; + if(isdigit(vert[0])) indexes[i] = ATOI(vert); + else + { + int axis = 0, dir = 1; + for(char *s = vert; *s; s++) switch(*s) + { + case '+': dir = 1; break; + case '-': dir = -1; break; + case 'x': + case 'X': axis = 0; break; + case 'y': + case 'Y': axis = 1; break; + case 'z': + case 'Z': axis = 2; break; + } + if(!mdl.meshes.empty()) indexes[i] = mdl.meshes[0]->findvert(axis, dir); + } + if(indexes[i] < 0) { conoutf("could not find vertex %s", vert); return; } + numverts = i + 1; } - if(!mdl.gentag(name, vnum)) { conoutf("could not find vertex %d", vnum); return; } + if(!mdl.gentag(name, indexes, numverts)) { conoutf("could not generate tag %s", name); return; } } void md2emit(char *tag, char *type, char *arg1, char *arg2) @@ -310,6 +330,6 @@ void md2emit(char *tag, char *type, char *arg1, char *arg2) } COMMAND(md2anim, ARG_4STR); -COMMAND(md2tag, ARG_2STR); +COMMAND(md2tag, ARG_5STR); COMMAND(md2emit, ARG_4STR); diff --git a/source/src/md3.h b/source/src/md3.h index bf2afa02..6adf1e9e 100644 --- a/source/src/md3.h +++ b/source/src/md3.h @@ -86,8 +86,7 @@ struct md3 : vertmodel // then restore it loopj(3) swap(tags[i].transform[j][0], tags[i].transform[j][1]); } - links = new part *[numtags]; - loopi(numtags) links[i] = NULL; + links = new linkedpart[numtags]; } int mesh_offset = header.ofs_meshes; @@ -156,12 +155,16 @@ struct md3 : vertmodel { if(!loaded) return; - if(a) for(int i = 0; a[i].name; i++) + if(a) for(int i = 0; a[i].tag; i++) { vertmodel *m = (vertmodel *)a[i].m; - if(!m || !a[i].tag) continue; + if(!m || !a[i].name) + { + if(a[i].pos) link(NULL, a[i].tag); + continue; + } part *p = m->parts[0]; - if(link(p, a[i].tag)) p->index = parts.length()+i; + if(link(p, a[i].tag, a[i].pos)) p->index = parts.length()+i; } if(!cullface) glDisable(GL_CULL_FACE); @@ -193,10 +196,7 @@ struct md3 : vertmodel if(!cullface) glEnable(GL_CULL_FACE); else if(anim&ANIM_MIRROR) glCullFace(GL_FRONT); - if(a) for(int i = 0; a[i].name; i++) - { - if(a[i].tag) link(NULL, a[i].tag); - } + if(a) for(int i = 0; a[i].tag; i++) link(NULL, a[i].tag); if(d) d->lastrendered = lastmillis; } diff --git a/source/src/model.h b/source/src/model.h index 50577c73..2b03e89e 100644 --- a/source/src/model.h +++ b/source/src/model.h @@ -28,10 +28,13 @@ enum { MDL_MD2 = 1, MDL_MD3 }; struct model; struct modelattach { - const char *name, *tag; + const char *tag, *name; + vec *pos; model *m; - modelattach() : name(NULL), tag(NULL) {} + modelattach() : tag(NULL), name(NULL), pos(NULL) {} + modelattach(const char *tag, const char *name) : tag(tag), name(name), pos(NULL) {} + modelattach(const char *tag, vec *pos) : tag(tag), name(NULL), pos(pos) {} }; struct dynent; diff --git a/source/src/protos.h b/source/src/protos.h index c861518f..9f192416 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -586,7 +586,7 @@ extern playerent *playerincrosshair(); extern int magsize(int gun); extern void checkweaponswitch(); extern void setscope(bool activate); -extern bool intersect(dynent *d, const vec &from, const vec &to, vec *end = NULL); +extern int intersect(playerent *d, const vec &from, const vec &to, vec *end = NULL); extern bool intersect(entity *e, const vec &from, const vec &to, vec *end = NULL); extern void damageeffect(int damage, playerent *d); extern void tryreload(playerent *p); diff --git a/source/src/rendergl.cpp b/source/src/rendergl.cpp index bb7bd2da..f073449c 100644 --- a/source/src/rendergl.cpp +++ b/source/src/rendergl.cpp @@ -951,8 +951,6 @@ void gl_drawframe(int w, int h, float changelod, float curfps) renderclients(); endmodelbatches(); - if(player1->state==CS_ALIVE) readdepth(w, h, hitpos); - startmodelbatches(); renderbounceents(); endmodelbatches(); diff --git a/source/src/rendermodel.cpp b/source/src/rendermodel.cpp index 2952ac88..458e942f 100644 --- a/source/src/rendermodel.cpp +++ b/source/src/rendermodel.cpp @@ -326,6 +326,8 @@ void endmodelbatches(bool flush) if(flush) clearmodelbatches(); } +VAR(dbgmbatch, 0, 0, 1); + void rendermodel(const char *mdl, int anim, int tex, float rad, const vec &o, float yaw, float pitch, float speed, int basetime, playerent *d, modelattach *a, float scale) { model *m = loadmodel(mdl); @@ -352,13 +354,13 @@ void rendermodel(const char *mdl, int anim, int tex, float rad, const vec &o, fl default: varseed = (int)(size_t)d + d->lastaction; break; } - if(a) for(int i = 0; a[i].name; i++) + if(a) for(int i = 0; a[i].tag; i++) { - a[i].m = loadmodel(a[i].name); + if(a[i].name) a[i].m = loadmodel(a[i].name); //if(a[i].m && a[i].m->type()!=m->type()) a[i].m = NULL; } - if(numbatches>=0) + if(numbatches>=0 && !dbgmbatch) { batchedmodel &b = addbatchedmodel(m); b.o = o; @@ -371,7 +373,7 @@ void rendermodel(const char *mdl, int anim, int tex, float rad, const vec &o, fl b.basetime = basetime; b.d = d; b.attached = a ? modelattached.length() : -1; - if(a) for(int i = 0;; i++) { modelattached.add(a[i]); if(!a[i].name) break; } + if(a) for(int i = 0;; i++) { modelattached.add(a[i]); if(!a[i].tag) break; } b.scale = scale; return; } @@ -498,6 +500,69 @@ void preload_mapmodels() } } +VAR(dbghbox, 0, 0, 1); + +void renderhbox(playerent *d) +{ + glDisable(GL_TEXTURE_2D); + glColor3f(1, 1, 1); + + float y = d->yaw*RAD, p = (d->pitch/4+90)*RAD, c = cosf(p); + vec bottom(d->o), up(sinf(y)*c, -cosf(y)*c, sinf(p)), top(up); + bottom.z -= d->eyeheight; + top.mul(d->eyeheight + d->aboveeye).add(bottom); + + if(d->head.x >= 0) + { + glBegin(GL_LINE_LOOP); + loopi(10) + { + vec pos(camright); + pos.rotate(2*M_PI*i/10.0f, camdir).mul(HEADSIZE).add(d->head); + glVertex3fv(pos.v); + } + glEnd(); + + glBegin(GL_LINES); + glVertex3fv(bottom.v); + glVertex3fv(d->head.v); + glEnd(); + } + + vec spoke; + spoke.orthogonal(up); + spoke.normalize().mul(d->radius); + + glBegin(GL_LINE_LOOP); + loopi(10) + { + vec pos(spoke); + pos.rotate(2*M_PI*i/10.0f, up).add(top); + glVertex3fv(pos.v); + } + glEnd(); + glBegin(GL_LINE_LOOP); + loopi(10) + { + vec pos(spoke); + pos.rotate(2*M_PI*i/10.0f, up).add(bottom); + glVertex3fv(pos.v); + } + glEnd(); + glBegin(GL_LINES); + loopi(10) + { + vec pos(spoke); + pos.rotate(2*M_PI*i/10.0f, up).add(bottom); + glVertex3fv(pos.v); + pos.sub(bottom).add(top); + glVertex3fv(pos.v); + } + glEnd(); + + glEnable(GL_TEXTURE_2D); +} + void renderclient(playerent *d, const char *mdlname, const char *vwepname, int tex) { int varseed = (int)(size_t)d; @@ -534,14 +599,26 @@ void renderclient(playerent *d, const char *mdlname, const char *vwepname, int t else if(d->weaponsel==d->lastattackweapon && lastmillis-d->lastaction<300 && d->lastpain < d->lastaction) { anim = d->crouching ? ANIM_CROUCH_ATTACK : ANIM_ATTACK; speed = 300.0f/8; basetime = d->lastaction; } else if(!d->move && !d->strafe) { anim = (d->crouching ? ANIM_CROUCH_IDLE : ANIM_IDLE)|ANIM_LOOP; } else { anim = (d->crouching ? ANIM_CROUCH_WALK : ANIM_RUN)|ANIM_LOOP; speed = 1860/d->maxspeed; } - modelattach a[2]; + modelattach a[3]; + int numattach = 0; if(vwepname) { - a[0].name = vwepname; - a[0].tag = "tag_weapon"; + a[numattach].name = vwepname; + a[numattach].tag = "tag_weapon"; + numattach++; + } - if(!stenciling && !reflecting && !refracting && d->weaponsel==d->lastattackweapon && lastmillis-d->lastaction < d->weaponsel->flashtime()) + if(!stenciling && !reflecting && !refracting) + { + if(d->weaponsel==d->lastattackweapon && lastmillis-d->lastaction < d->weaponsel->flashtime()) anim |= ANIM_PARTICLE; + if(d != player1 && d->state==CS_ALIVE) + { + d->head = vec(-1, -1, -1); + a[numattach].tag = "tag_head"; + a[numattach].pos = &d->head; + numattach++; + } } // FIXME: while networked my state as spectator seems to stay CS_DEAD, not CS_SPECTATE // flowtron: I fixed this for following at least (see followplayer()) @@ -551,7 +628,11 @@ void renderclient(playerent *d, const char *mdlname, const char *vwepname, int t if(stenciling) return; } rendermodel(mdlname, anim|ANIM_DYNALLOC, tex, 1.5f, o, d->yaw+90, d->pitch/4, speed, basetime, d, a); - if(!stenciling && !reflecting && !refracting && isteam(player1->team, d->team)) renderaboveheadicon(d); + if(!stenciling && !reflecting && !refracting) + { + if(isteam(player1->team, d->team)) renderaboveheadicon(d); + if(dbghbox) renderhbox(d); + } } VARP(teamdisplaymode, 0, 1, 2); diff --git a/source/src/vertmodel.h b/source/src/vertmodel.h index c6a2935d..8fa9b6d0 100644 --- a/source/src/vertmodel.h +++ b/source/src/vertmodel.h @@ -618,6 +618,16 @@ struct vertmodel : model } }; + struct linkedpart + { + part *p; + particleemitter *emitter; + vec *pos; + + linkedpart() : p(NULL), emitter(NULL), pos(NULL) {} + ~linkedpart() { DELETEP(emitter); } + }; + struct part { char *filename; @@ -625,14 +635,13 @@ struct vertmodel : model int index, numframes; vector meshes; vector *anims; - part **links; + linkedpart *links; tag *tags; int numtags; - particleemitter *emitters; GLuint *shadows; float shadowrad; - part() : filename(NULL), anims(NULL), links(NULL), tags(NULL), numtags(0), emitters(NULL), shadows(NULL), shadowrad(0) {} + part() : filename(NULL), anims(NULL), links(NULL), tags(NULL), numtags(0), shadows(NULL), shadowrad(0) {} virtual ~part() { DELETEA(filename); @@ -640,7 +649,6 @@ struct vertmodel : model DELETEA(anims); DELETEA(links); DELETEA(tags); - DELETEA(emitters); if(shadows) glDeleteTextures(numframes, shadows); DELETEA(shadows); } @@ -655,24 +663,26 @@ struct vertmodel : model } } - bool link(part *link, const char *tag) + bool link(part *link, const char *tag, vec *pos = NULL) { loopi(numtags) if(!strcmp(tags[i].name, tag)) { - links[i] = link; + links[i].p = link; + links[i].pos = pos; return true; } return false; } - bool gentag(const char *name, int vert, mesh *m = NULL) + bool gentag(const char *name, int *verts, int numverts, mesh *m = NULL) { if(!m) { if(meshes.empty()) return false; m = meshes[0]; } - if(vert < 0 || vert > m->numverts) return false; + if(numverts < 1) return false; + loopi(numverts) if(verts[i] < 0 || verts[i] > m->numverts) return false; tag *ntags = new tag[(numtags + 1)*numframes]; ntags[numtags].name = newstring(name); @@ -681,7 +691,12 @@ struct vertmodel : model memcpy(&ntags[(numtags + 1)*i], &tags[numtags*i], numtags*sizeof(tag)); tag *t = &ntags[(numtags + 1)*i + numtags]; - t->pos = m->verts[m->numverts*i + vert]; + t->pos = m->verts[m->numverts*i + verts[0]]; + if(numverts > 1) + { + for(int j = 1; j < numverts; j++) t->pos.add(m->verts[m->numverts*i + verts[j]]); + t->pos.div(numverts); + } t->identity(); } loopi(numtags) tags[i].name = NULL; @@ -690,9 +705,10 @@ struct vertmodel : model tags = ntags; numtags++; + linkedpart *nlinks = new linkedpart[numtags]; + loopi(numtags-1) swap(links[i].emitter, nlinks[i].emitter); DELETEA(links); - links = new part *[numtags]; - loopi(numtags) links[i] = NULL; + links = nlinks; return true; } @@ -700,8 +716,8 @@ struct vertmodel : model { loopi(numtags) if(!strcmp(tags[i].name, tag)) { - if(!emitters) emitters = new particleemitter[numtags]; - particleemitter &p = emitters[i]; + if(!links[i].emitter) links[i].emitter = new particleemitter; + particleemitter &p = *links[i].emitter; p.type = type; p.args[0] = arg1; p.args[1] = arg2; @@ -840,19 +856,22 @@ struct vertmodel : model prev.setframes(d->prev[index]); ai_t = (lastmillis-d->lastanimswitchtime[index])/(float)animationinterpolationtime; } - + loopv(meshes) meshes[i]->render(as, cur, doai ? &prev : NULL, ai_t); - loopi(numtags) if(links[i] || (anim&ANIM_PARTICLE && emitters && emitters[i].type>=0)) // render the linked models - interpolate rotation and position of the 'link-tags' + loopi(numtags) { + linkedpart &link = links[i]; + if(!(link.p || link.pos || (anim&ANIM_PARTICLE && link.emitter))) continue; + + // render the linked models - interpolate rotation and position of the 'link-tags' glmatrixf linkmat; gentagmatrix(cur, doai ? &prev : NULL, ai_t, i, linkmat.v); matrixpos++; matrixstack[matrixpos].mul(matrixstack[matrixpos-1].v, linkmat.v); - part *link = links[i]; - if(link) + if(link.p) { vec oldshadowdir, oldshadowpos; @@ -865,7 +884,7 @@ struct vertmodel : model } glLoadMatrixf(matrixstack[matrixpos].v); - link->render(anim, varseed, speed, basetime, d); + link.p->render(anim, varseed, speed, basetime, d); if(stenciling) { @@ -874,15 +893,19 @@ struct vertmodel : model } } - if(anim&ANIM_PARTICLE && emitters && emitters[i].type>=0) + if(link.pos) *link.pos = matrixstack[matrixpos].gettranslation(); + + if(anim&ANIM_PARTICLE && link.emitter) { - if(emitters[i].lastemit!=basetime) + particleemitter &p = *link.emitter; + + if(p.lastemit!=basetime) { - emitters[i].seed = rnd(0x1000000); - emitters[i].lastemit = basetime; + p.seed = rnd(0x1000000); + p.lastemit = basetime; } - particle_emit(emitters[i].type, emitters[i].args, basetime, emitters[i].seed, matrixstack[matrixpos].gettranslation()); + particle_emit(p.type, p.args, basetime, p.seed, matrixstack[matrixpos].gettranslation()); } matrixpos--; @@ -1160,9 +1183,9 @@ struct vertmodel : model loopv(parts) parts[i]->cleanup(); } - bool link(part *link, const char *tag) + bool link(part *link, const char *tag, vec *pos = NULL) { - loopv(parts) if(parts[i]->link(link, tag)) return true; + loopv(parts) if(parts[i]->link(link, tag, pos)) return true; return false; } diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index f84336f7..8a2ab048 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -133,7 +133,7 @@ void createrays(vec &from, vec &to) // create random spread of rays } } -static inline bool intersect(const vec &o, const vec &rad, const vec &from, const vec &to, vec *end) // if lineseg hits entity bounding box +static inline bool intersectbox(const vec &o, const vec &rad, const vec &from, const vec &to, vec *end) // if lineseg hits entity bounding box { const vec *p; vec v = to, w = o; @@ -167,12 +167,93 @@ static inline bool intersect(const vec &o, const vec &rad, const vec &from, cons return false; } -bool intersect(dynent *d, const vec &from, const vec &to, vec *end) +static inline bool intersectsphere(const vec &from, const vec &to, vec center, float radius, float &dist) { + vec ray(to); + ray.sub(from); + center.sub(from); + float v = center.dot(ray), + inside = radius*radius - center.squaredlen(); + if(inside < 0 && v < 0) return false; + float raysq = ray.squaredlen(), d = inside*raysq + v*v; + if(d < 0) return false; + dist = (v - sqrtf(d)) / raysq; + return dist >= 0 && dist <= 1; +} + +static inline bool intersectcylinder(const vec &from, const vec &to, const vec &start, const vec &end, float radius, float &dist) +{ + vec d(end), m(from), n(to); + d.sub(start); + m.sub(start); + n.sub(from); + float md = m.dot(d), + nd = n.dot(d), + dd = d.squaredlen(); + if(md < 0 && md + nd < 0) return false; + if(md > dd && md + nd > dd) return false; + float nn = n.squaredlen(), + mn = m.dot(n), + a = dd*nn - nd*nd, + k = m.squaredlen() - radius*radius, + c = dd*k - md*md; + if(fabs(a) < 1e-9f) + { + if(c > 0) return false; + if(md < 0) dist = -mn / nn; + else if(md > dd) dist = (nd - mn) / nn; + else dist = 0; + return true; + } + float b = dd*mn - nd*md, + discrim = b*b - a*c; + if(discrim < 0) return false; + dist = (-b - sqrtf(discrim)) / a; + if(dist < 0 || dist > 1) return false; + float offset = md + dist*nd; + if(offset < 0) + { + if(nd < 0) return false; + dist = -md / nd; + return k + 2*dist*(mn + dist*nn) <= 0; + } + else if(offset > dd) + { + if(nd >= 0) return 0; + dist = (dd - md) / nd; + return k + dd - 2*md + dist*(2*(mn-nd) + dist*nn) <= 0; + } + return true; +} + +int intersect(playerent *d, const vec &from, const vec &to, vec *end) +{ + float dist; + if(d->head.x >= 0) + { + if(intersectsphere(from, to, d->head, HEADSIZE, dist)) + { + if(end) (*end = to).sub(from).mul(dist).add(from); + return 2; + } + } + float y = d->yaw*RAD, p = (d->pitch/4+90)*RAD, c = cosf(p); + vec bottom(d->o), top(sinf(y)*c, -cosf(y)*c, sinf(p)); + bottom.z -= d->eyeheight; + top.mul(d->eyeheight + d->aboveeye).add(bottom); + if(intersectcylinder(from, to, bottom, top, d->radius, dist)) + { + if(end) (*end = to).sub(from).mul(dist).add(from); + return 1; + } + return 0; + +#if 0 const float eyeheight = d->eyeheight; vec o(d->o); o.z += (d->aboveeye - eyeheight)/2; - return intersect(o, vec(d->radius, d->radius, (d->aboveeye + eyeheight)/2), from, to, end); + return intersectbox(o, vec(d->radius, d->radius, (d->aboveeye + eyeheight)/2), from, to, end) ? 1 : 0; +#endif } bool intersect(entity *e, const vec &from, const vec &to, vec *end) @@ -181,28 +262,30 @@ bool intersect(entity *e, const vec &from, const vec &to, vec *end) if(!&mmi || !mmi.h) return false; float lo = float(S(e->x, e->y)->floor+mmi.zoff+e->attr3); - return intersect(vec(e->x, e->y, lo+mmi.h/2.0f), vec(mmi.rad, mmi.rad, mmi.h/2.0f), from, to, end); + return intersectbox(vec(e->x, e->y, lo+mmi.h/2.0f), vec(mmi.rad, mmi.rad, mmi.h/2.0f), from, to, end); } -playerent *intersectclosest(vec &from, vec &to, playerent *at) +playerent *intersectclosest(const vec &from, const vec &to, playerent *at, int &hitzone) { playerent *best = NULL; float bestdist = 1e16f; - if(at!=player1 && player1->state==CS_ALIVE && intersect(player1, from, to)) + int zone; + if(at!=player1 && player1->state==CS_ALIVE && (zone = intersect(player1, from, to))) { best = player1; bestdist = at->o.dist(player1->o); + hitzone = zone; } loopv(players) { playerent *o = players[i]; if(!o || o==at || o->state!=CS_ALIVE) continue; - if(!intersect(o, from, to)) continue; float dist = at->o.dist(o->o); - if(disttype==ENT_PLAYER) { - return intersectclosest(camera1->o, worldpos, (playerent *)camera1); + int hitzone; + return intersectclosest(camera1->o, worldpos, (playerent *)camera1, hitzone); } else return NULL; } @@ -389,12 +473,9 @@ void shorten(vec &from, vec &to, vec &target) target.sub(from).normalize().mul(from.dist(to)).add(from); } -const float HEADSIZE = 0.8f; -vec hitpos; - void raydamage(vec &from, vec &to, playerent *d) { - int gdam = d->weaponsel->info.damage; + int gdam = d->weaponsel->info.damage, hitzone = -1; playerent *o = NULL; if(d->weaponsel->type==GUN_SHOTGUN) { @@ -405,7 +486,7 @@ void raydamage(vec &from, vec &to, playerent *d) bool raysleft = false; int hitrays = 0; o = NULL; - loop(r, SGRAYS) if((done&(1<weaponsel->type==GUN_KNIFE) gib = true; - else if(d==player1 && d->weaponsel->type==GUN_SNIPER - && worldpos!=hitpos && hitpos.z>=o->o.z+o->aboveeye-HEADSIZE - && intersect(o, from, hitpos)) + else if(d==player1 && d->weaponsel->type==GUN_SNIPER && hitzone==2) { gdam *= 3; gib = true; @@ -825,7 +904,7 @@ void grenades::onownerdies() // gun base class -gun::gun(playerent *owner, int type) : weapon(owner, type) {}; +gun::gun(playerent *owner, int type) : weapon(owner, type) {} bool gun::attack(vec &targ) { @@ -882,7 +961,7 @@ void gun::attackfx(const vec &from, const vec &to, int millis) attacksound(); } -int gun::modelanim() { return modelattacking() ? ANIM_GUN_SHOOT|ANIM_LOOP : ANIM_GUN_IDLE; }; +int gun::modelanim() { return modelattacking() ? ANIM_GUN_SHOOT|ANIM_LOOP : ANIM_GUN_IDLE; } void gun::checkautoreload() { if(autoreload && owner==player1 && !mag) reload(); } @@ -1049,13 +1128,13 @@ bool knife::attack(vec &targ) sendshoot(from, to); gunwait = info.attackdelay; return true; -}; +} -int knife::modelanim() { return modelattacking() ? ANIM_GUN_SHOOT : ANIM_GUN_IDLE; }; +int knife::modelanim() { return modelattacking() ? ANIM_GUN_SHOOT : ANIM_GUN_IDLE; } void knife::drawstats() {} void knife::attackfx(const vec &from, const vec &to, int millis) { attacksound(); } -void knife::renderstats() { }; +void knife::renderstats() { } void setscope(bool enable)