OctaCore/src/game/render.cc

532 lines
18 KiB
C++

#include "game.hh"
namespace game
{
vector<gameent *> bestplayers;
vector<int> bestteams;
VARP(ragdoll, 0, 1, 1);
VARP(ragdollmillis, 0, 10000, 300000);
VARP(ragdollfade, 0, 100, 5000);
VARP(forceplayermodels, 0, 0, 1);
VARP(hidedead, 0, 0, 1);
extern int playermodel;
vector<gameent *> ragdolls;
void saveragdoll(gameent *d)
{
if(!d->ragdoll || !ragdollmillis || (!ragdollfade && lastmillis > d->lastpain + ragdollmillis)) return;
gameent *r = new gameent(*d);
r->lastupdate = ragdollfade && lastmillis > d->lastpain + max(ragdollmillis - ragdollfade, 0) ? lastmillis - max(ragdollmillis - ragdollfade, 0) : d->lastpain;
r->edit = NULL;
if(d==player1) r->playermodel = playermodel;
ragdolls.add(r);
d->ragdoll = NULL;
}
void clearragdolls()
{
ragdolls.deletecontents();
}
void moveragdolls()
{
loopv(ragdolls)
{
gameent *d = ragdolls[i];
if(lastmillis > d->lastupdate + ragdollmillis)
{
delete ragdolls.remove(i--);
continue;
}
moveragdoll(d);
}
}
static const int playercolors[] =
{
0xA12020,
0xA15B28,
0xB39D52,
0x3E752F,
0x3F748C,
0x214C85,
0xB3668C,
0x523678,
0xB3ADA3
};
static const int playercolorsazul[] =
{
0x27508A,
0x3F748C,
0x3B3B80,
0x5364B5
};
static const int playercolorsrojo[] =
{
0xAC2C2A,
0x992417,
0x802438,
0xA3435B
};
extern void changedplayercolor();
VARFP(playercolor, 0, 4, sizeof(playercolors)/sizeof(playercolors[0])-1, changedplayercolor());
VARFP(playercolorazul, 0, 0, sizeof(playercolorsazul)/sizeof(playercolorsazul[0])-1, changedplayercolor());
VARFP(playercolorrojo, 0, 0, sizeof(playercolorsrojo)/sizeof(playercolorsrojo[0])-1, changedplayercolor());
static const playermodelinfo playermodels[] =
{
{ { "player/bones", "player/bones", "player/bones" }, { "hudgun", "hudgun", "hudgun" }, { "player", "player_azul", "player_rojo" }, true }
};
extern void changedplayermodel();
VARFP(playermodel, 0, 0, sizeof(playermodels)/sizeof(playermodels[0])-1, changedplayermodel());
int chooserandomplayermodel(int seed)
{
return (seed&0xFFFF)%(sizeof(playermodels)/sizeof(playermodels[0]));
}
const playermodelinfo *getplayermodelinfo(int n)
{
if(size_t(n) >= sizeof(playermodels)/sizeof(playermodels[0])) return NULL;
return &playermodels[n];
}
const playermodelinfo &getplayermodelinfo(gameent *d)
{
const playermodelinfo *mdl = getplayermodelinfo(d==player1 || forceplayermodels ? playermodel : d->playermodel);
if(!mdl) mdl = getplayermodelinfo(playermodel);
return *mdl;
}
int getplayercolor(int team, int color)
{
#define GETPLAYERCOLOR(playercolors) \
return playercolors[color%(sizeof(playercolors)/sizeof(playercolors[0]))];
switch(team)
{
case 1: GETPLAYERCOLOR(playercolorsazul)
case 2: GETPLAYERCOLOR(playercolorsrojo)
default: GETPLAYERCOLOR(playercolors)
}
}
ICOMMAND(getplayercolor, "ii", (int *color, int *team), intret(getplayercolor(*team, *color)));
int getplayercolor(gameent *d, int team)
{
if(d==player1) switch(team)
{
case 1: return getplayercolor(1, playercolorazul);
case 2: return getplayercolor(2, playercolorrojo);
default: return getplayercolor(0, playercolor);
}
else return getplayercolor(team, (d->playercolor>>(5*team))&0x1F);
}
void changedplayermodel()
{
if(player1->clientnum < 0) player1->playermodel = playermodel;
if(player1->ragdoll) cleanragdoll(player1);
loopv(ragdolls)
{
gameent *d = ragdolls[i];
if(!d->ragdoll) continue;
if(!forceplayermodels)
{
const playermodelinfo *mdl = getplayermodelinfo(d->playermodel);
if(mdl) continue;
}
cleanragdoll(d);
}
loopv(players)
{
gameent *d = players[i];
if(d == player1 || !d->ragdoll) continue;
if(!forceplayermodels)
{
const playermodelinfo *mdl = getplayermodelinfo(d->playermodel);
if(mdl) continue;
}
cleanragdoll(d);
}
}
void changedplayercolor()
{
if(player1->clientnum < 0) player1->playercolor = playercolor | (playercolorazul<<5) | (playercolorrojo<<10);
}
void syncplayer()
{
if(player1->playermodel != playermodel)
{
player1->playermodel = playermodel;
addmsg(N_SWITCHMODEL, "ri", player1->playermodel);
}
int col = playercolor | (playercolorazul<<5) | (playercolorrojo<<10);
if(player1->playercolor != col)
{
player1->playercolor = col;
addmsg(N_SWITCHCOLOR, "ri", player1->playercolor);
}
}
void preloadplayermodel()
{
loopi(sizeof(playermodels)/sizeof(playermodels[0]))
{
const playermodelinfo *mdl = getplayermodelinfo(i);
if(!mdl) break;
if(i != playermodel && (!multiplayer(false) || forceplayermodels)) continue;
if(m_teammode)
{
loopj(MAXTEAMS) preloadmodel(mdl->model[1+j]);
}
else preloadmodel(mdl->model[0]);
}
}
int numanims() { return NUMANIMS; }
void findanims(const char *pattern, vector<int> &anims)
{
loopi(sizeof(animnames)/sizeof(animnames[0])) if(matchanim(animnames[i], pattern)) anims.add(i);
}
VAR(animoverride, -1, 0, NUMANIMS-1);
VAR(testanims, 0, 0, 1);
VAR(testpitch, -90, 0, 90);
void renderplayer(gameent *d, const playermodelinfo &mdl, int color, int team, float fade, int flags = 0, bool mainpass = true)
{
int lastaction = d->lastaction, anim = ANIM_IDLE|ANIM_LOOP, attack = 0, delay = 0;
if(d->lastattack >= 0)
{
attack = attacks[d->lastattack].anim;
delay = attacks[d->lastattack].attackdelay+50;
}
if(intermission && d->state!=CS_DEAD)
{
anim = attack = ANIM_LOSE|ANIM_LOOP;
if(validteam(team) ? bestteams.htfind(team)>=0 : bestplayers.find(d)>=0) anim = attack = ANIM_WIN|ANIM_LOOP;
}
else if(d->state==CS_ALIVE && d->lasttaunt && lastmillis-d->lasttaunt<1000 && lastmillis-d->lastaction>delay)
{
lastaction = d->lasttaunt;
anim = attack = ANIM_TAUNT;
delay = 1000;
}
modelattach a[5];
int ai = 0;
if(guns[d->gunselect].vwep)
{
int vanim = ANIM_VWEP_IDLE|ANIM_LOOP, vtime = 0;
if(lastaction && d->lastattack >= 0 && attacks[d->lastattack].gun==d->gunselect && lastmillis < lastaction + delay)
{
vanim = attacks[d->lastattack].vwepanim;
vtime = lastaction;
}
a[ai++] = modelattach("tag_weapon", guns[d->gunselect].vwep, vanim, vtime);
}
if(mainpass && !(flags&MDL_ONLYSHADOW))
{
d->muzzle = vec(-1, -1, -1);
if(guns[d->gunselect].vwep) a[ai++] = modelattach("tag_muzzle", &d->muzzle);
}
const char *mdlname = mdl.model[validteam(team) ? team : 0];
float yaw = testanims && d==player1 ? 0 : d->yaw,
pitch = testpitch && d==player1 ? testpitch : d->pitch;
vec o = d->feetpos();
int basetime = 0;
if(animoverride) anim = (animoverride<0 ? ANIM_ALL : animoverride)|ANIM_LOOP;
else if(d->state==CS_DEAD)
{
anim = ANIM_DYING|ANIM_NOPITCH;
basetime = d->lastpain;
if(ragdoll && mdl.ragdoll) anim |= ANIM_RAGDOLL;
else if(lastmillis-basetime>1000) anim = ANIM_DEAD|ANIM_LOOP|ANIM_NOPITCH;
}
else if(d->state==CS_EDITING || d->state==CS_SPECTATOR) anim = ANIM_EDIT|ANIM_LOOP;
else if(d->state==CS_LAGGED) anim = ANIM_LAG|ANIM_LOOP;
else if(!intermission)
{
if(lastmillis-d->lastpain < 300)
{
anim = ANIM_PAIN;
basetime = d->lastpain;
}
else if(d->lastpain < lastaction && lastmillis-lastaction < delay)
{
anim = attack;
basetime = lastaction;
}
if(d->inwater && d->physstate<=PHYS_FALL) anim |= (((game::allowmove(d) && (d->move || d->strafe)) || d->vel.z+d->falling.z>0 ? ANIM_SWIM : ANIM_SINK)|ANIM_LOOP)<<ANIM_SECONDARY;
else
{
static const int dirs[9] =
{
ANIM_RUN_SE, ANIM_RUN_S, ANIM_RUN_SW,
ANIM_RUN_E, 0, ANIM_RUN_W,
ANIM_RUN_NE, ANIM_RUN_N, ANIM_RUN_NW
};
int dir = dirs[(d->move+1)*3 + (d->strafe+1)];
if(d->timeinair>100) anim |= ((dir ? dir+ANIM_JUMP_N-ANIM_RUN_N : ANIM_JUMP) | ANIM_END) << ANIM_SECONDARY;
else if(dir && game::allowmove(d)) anim |= (dir | ANIM_LOOP) << ANIM_SECONDARY;
}
if(d->crouching) switch((anim>>ANIM_SECONDARY)&ANIM_INDEX)
{
case ANIM_IDLE: anim &= ~(ANIM_INDEX<<ANIM_SECONDARY); anim |= ANIM_CROUCH<<ANIM_SECONDARY; break;
case ANIM_JUMP: anim &= ~(ANIM_INDEX<<ANIM_SECONDARY); anim |= ANIM_CROUCH_JUMP<<ANIM_SECONDARY; break;
case ANIM_SWIM: anim &= ~(ANIM_INDEX<<ANIM_SECONDARY); anim |= ANIM_CROUCH_SWIM<<ANIM_SECONDARY; break;
case ANIM_SINK: anim &= ~(ANIM_INDEX<<ANIM_SECONDARY); anim |= ANIM_CROUCH_SINK<<ANIM_SECONDARY; break;
case 0: anim |= (ANIM_CROUCH|ANIM_LOOP)<<ANIM_SECONDARY; break;
case ANIM_RUN_N: case ANIM_RUN_NE: case ANIM_RUN_E: case ANIM_RUN_SE: case ANIM_RUN_S: case ANIM_RUN_SW: case ANIM_RUN_W: case ANIM_RUN_NW:
anim += (ANIM_CROUCH_N - ANIM_RUN_N) << ANIM_SECONDARY;
break;
case ANIM_JUMP_N: case ANIM_JUMP_NE: case ANIM_JUMP_E: case ANIM_JUMP_SE: case ANIM_JUMP_S: case ANIM_JUMP_SW: case ANIM_JUMP_W: case ANIM_JUMP_NW:
anim += (ANIM_CROUCH_JUMP_N - ANIM_JUMP_N) << ANIM_SECONDARY;
break;
}
if((anim&ANIM_INDEX)==ANIM_IDLE && (anim>>ANIM_SECONDARY)&ANIM_INDEX) anim >>= ANIM_SECONDARY;
}
if(!((anim>>ANIM_SECONDARY)&ANIM_INDEX)) anim |= (ANIM_IDLE|ANIM_LOOP)<<ANIM_SECONDARY;
if(d!=player1) flags |= MDL_CULL_VFC | MDL_CULL_OCCLUDED | MDL_CULL_QUERY;
if(d->type==ENT_PLAYER) flags |= MDL_FULLBRIGHT;
else flags |= MDL_CULL_DIST;
if(!mainpass) flags &= ~(MDL_FULLBRIGHT | MDL_CULL_VFC | MDL_CULL_OCCLUDED | MDL_CULL_QUERY | MDL_CULL_DIST);
float trans = d->state == CS_LAGGED ? 0.5f : 1.0f;
rendermodel(mdlname, anim, o, yaw, pitch, 0, flags, d, a[0].tag ? a : NULL, basetime, 0, fade, vec4(vec::hexcolor(color), trans));
}
static inline void renderplayer(gameent *d, float fade = 1, int flags = 0)
{
int team = m_teammode && validteam(d->team) ? d->team : 0;
renderplayer(d, getplayermodelinfo(d), getplayercolor(d, team), team, fade, flags);
}
void rendergame()
{
if(intermission)
{
bestteams.shrink(0);
bestplayers.shrink(0);
}
bool third = isthirdperson();
gameent *f = followingplayer(), *exclude = third ? NULL : f;
loopv(players)
{
gameent *d = players[i];
if(d == player1 || d->state==CS_SPECTATOR || d->state==CS_SPAWNING || d->lifesequence < 0 || d == exclude || (d->state==CS_DEAD && hidedead)) continue;
renderplayer(d);
copystring(d->info, colorname(d));
if(d->state!=CS_DEAD)
{
int team = m_teammode && validteam(d->team) ? d->team : 0;
particle_text(d->abovehead(), d->info, PART_TEXT, 1, teamtextcolor[team], 2.0f);
}
}
loopv(ragdolls)
{
gameent *d = ragdolls[i];
float fade = 1.0f;
if(ragdollmillis && ragdollfade)
fade -= clamp(float(lastmillis - (d->lastupdate + max(ragdollmillis - ragdollfade, 0)))/min(ragdollmillis, ragdollfade), 0.0f, 1.0f);
renderplayer(d, fade);
}
if(exclude)
renderplayer(exclude, 1, MDL_ONLYSHADOW);
else if(!f && (player1->state==CS_ALIVE || (player1->state==CS_EDITING && third) || (player1->state==CS_DEAD && !hidedead)))
renderplayer(player1, 1, third ? 0 : MDL_ONLYSHADOW);
entities::renderentities();
renderbouncers();
renderprojectiles();
if(cmode) cmode->rendergame();
}
VARP(hudgun, 0, 1, 1);
VARP(hudgunsway, 0, 1, 1);
FVAR(swaystep, 1, 35.0f, 100);
FVAR(swayside, 0, 0.10f, 1);
FVAR(swayup, -1, 0.15f, 1);
float swayfade = 0, swayspeed = 0, swaydist = 0;
vec swaydir(0, 0, 0);
void swayhudgun(int curtime)
{
gameent *d = hudplayer();
if(d->state != CS_SPECTATOR)
{
if(d->physstate >= PHYS_SLOPE)
{
swayspeed = min(sqrtf(d->vel.x*d->vel.x + d->vel.y*d->vel.y), d->maxspeed);
swaydist += swayspeed*curtime/1000.0f;
swaydist = fmod(swaydist, 2*swaystep);
swayfade = 1;
}
else if(swayfade > 0)
{
swaydist += swayspeed*swayfade*curtime/1000.0f;
swaydist = fmod(swaydist, 2*swaystep);
swayfade -= 0.5f*(curtime*d->maxspeed)/(swaystep*1000.0f);
}
float k = pow(0.7f, curtime/10.0f);
swaydir.mul(k);
vec vel(d->vel);
vel.add(d->falling);
swaydir.add(vec(vel).mul((1-k)/(15*max(vel.magnitude(), d->maxspeed))));
}
}
struct hudent : dynent
{
hudent() { type = ENT_CAMERA; }
} guninterp;
void drawhudmodel(gameent *d, int anim, int basetime)
{
const char *file = guns[d->gunselect].file;
if(!file) return;
vec sway;
vecfromyawpitch(d->yaw, 0, 0, 1, sway);
float steps = swaydist/swaystep*M_PI;
sway.mul(swayside*cosf(steps));
sway.z = swayup*(fabs(sinf(steps)) - 1);
sway.add(swaydir).add(d->o);
if(!hudgunsway) sway = d->o;
const playermodelinfo &mdl = getplayermodelinfo(d);
int team = m_teammode && validteam(d->team) ? d->team : 0,
color = getplayercolor(d, team);
defformatstring(gunname, "%s/%s", mdl.hudguns[team], file);
modelattach a[2];
d->muzzle = vec(-1, -1, -1);
a[0] = modelattach("tag_muzzle", &d->muzzle);
rendermodel(gunname, anim, sway, d->yaw, d->pitch, 0, MDL_NOBATCH, NULL, a, basetime, 0, 1, vec4(vec::hexcolor(color), 1));
if(d->muzzle.x >= 0) d->muzzle = calcavatarpos(d->muzzle, 12);
}
void drawhudgun()
{
gameent *d = hudplayer();
if(d->state==CS_SPECTATOR || d->state==CS_EDITING || !hudgun || editmode)
{
d->muzzle = player1->muzzle = vec(-1, -1, -1);
return;
}
int anim = ANIM_GUN_IDLE|ANIM_LOOP, basetime = 0;
if(d->lastaction && d->lastattack >= 0 && attacks[d->lastattack].gun==d->gunselect && lastmillis-d->lastaction<attacks[d->lastattack].attackdelay)
{
anim = attacks[d->lastattack].hudanim;
basetime = d->lastaction;
}
drawhudmodel(d, anim, basetime);
}
void renderavatar()
{
drawhudgun();
}
void renderplayerpreview(int model, int color, int team, int weap)
{
static gameent *previewent = NULL;
if(!previewent)
{
previewent = new gameent;
loopi(NUMGUNS) previewent->ammo[i] = 1;
}
float height = previewent->eyeheight + previewent->aboveeye,
zrad = height/2;
vec2 xyrad = vec2(previewent->xradius, previewent->yradius).max(height/4);
previewent->o = calcmodelpreviewpos(vec(xyrad, zrad), previewent->yaw).addz(previewent->eyeheight - zrad);
previewent->gunselect = validgun(weap) ? weap : GUN_RAIL;
const playermodelinfo *mdlinfo = getplayermodelinfo(model);
if(!mdlinfo) return;
renderplayer(previewent, *mdlinfo, getplayercolor(team, color), team, 1, 0, false);
}
vec hudgunorigin(int gun, const vec &from, const vec &to, gameent *d)
{
if(d->muzzle.x >= 0) return d->muzzle;
vec offset(from);
if(d!=hudplayer() || isthirdperson())
{
vec front, right;
vecfromyawpitch(d->yaw, d->pitch, 1, 0, front);
offset.add(front.mul(d->radius));
offset.z += (d->aboveeye + d->eyeheight)*0.75f - d->eyeheight;
vecfromyawpitch(d->yaw, 0, 0, -1, right);
offset.add(right.mul(0.5f*d->radius));
offset.add(front);
return offset;
}
offset.add(vec(to).sub(from).normalize().mul(2));
if(hudgun)
{
offset.sub(vec(camup).mul(1.0f));
offset.add(vec(camright).mul(0.8f));
}
else offset.sub(vec(camup).mul(0.8f));
return offset;
}
void preloadweapons()
{
const playermodelinfo &mdl = getplayermodelinfo(player1);
loopi(NUMGUNS)
{
const char *file = guns[i].file;
if(!file) continue;
string fname;
if(m_teammode)
{
loopj(MAXTEAMS)
{
formatstring(fname, "%s/%s", mdl.hudguns[1+j], file);
preloadmodel(fname);
}
}
else
{
formatstring(fname, "%s/%s", mdl.hudguns[0], file);
preloadmodel(fname);
}
formatstring(fname, "worldgun/%s", file);
preloadmodel(fname);
}
}
void preloadsounds()
{
//for(int i = S_JUMP; i <= S_DIE2; i++) preloadsound(i);
}
void preload()
{
if(hudgun) preloadweapons();
preloadbouncers();
preloadplayermodel();
preloadsounds();
entities::preloadentities();
}
}