From fd2ef397a2ec0fa83379c84d71b3831de2b735cb Mon Sep 17 00:00:00 2001 From: driAn Date: Mon, 14 May 2007 18:43:59 +0000 Subject: [PATCH] demo git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@997 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/src/client.cpp | 6 ++ source/src/clientgame.cpp | 4 +- source/src/clients2c.cpp | 19 ++++++- source/src/docs.cpp | 4 +- source/src/main.cpp | 8 ++- source/src/physics.cpp | 6 +- source/src/protos.h | 5 ++ source/src/rendergl.cpp | 33 ++++++++++- source/src/renderhud.cpp | 63 +++++++++++++-------- source/src/rendermodel.cpp | 2 +- source/src/savegamedemo.cpp | 106 +++++++++++++++++++++++++++++------- source/src/sound.cpp | 7 ++- source/src/weapon.cpp | 7 ++- 13 files changed, 213 insertions(+), 57 deletions(-) diff --git a/source/src/client.cpp b/source/src/client.cpp index 69a970b9..c2fb0185 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -127,6 +127,12 @@ void disconnect(int onlyclean, int async) conoutf("disconnected"); cleanup = true; } + else if(demoplayback) + { + // todo: additional cleanups + cleanup = true; + } + if(cleanup) { stop(); diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index 1ee54323..5a6dcb77 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -564,14 +564,14 @@ void flagmsg(int flag, int action) case SV_FLAGDROP: { playsound(S_FLAGDROP); - if(f.actor==player1) conoutf("you lost the flag"); + if(f.actor==player1) conoutf("\f2you lost the flag"); else conoutf("\f2%s lost %s flag", f.actor->name, (own ? "your" : "the enemy")); break; } case SV_FLAGRETURN: { playsound(S_FLAGRETURN); - if(f.actor==player1) conoutf("you returned your flag"); + if(f.actor==player1) conoutf("\f2you returned your flag"); else conoutf("\f2%s returned %s flag", f.actor->name, (own ? "your" : "the enemy")); break; } diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index de711968..646c5413 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -3,6 +3,7 @@ #include "cube.h" #include "bot/bot.h" +extern int democlientnum; extern bool c2sinit, senditemstoserver; extern string clientpassword; @@ -40,6 +41,10 @@ void updatepos(playerent *d) if(fxo.y += dy<0 ? r-fy : -(r-fy); // push aside else d->o.x += dx<0 ? r-fx : -(r-fx); } +} + +void updatelagtime(playerent *d) +{ int lagtime = lastmillis-d->lastupdate; if(lagtime) { @@ -89,6 +94,7 @@ void parsepositions(ucharbuf &p) f >>= 3; d->onladder = f&1; if(!demoplayback) updatepos(d); + updatelagtime(d); break; } @@ -131,7 +137,7 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) } case SV_SOUND: - playsound(getint(p), d ? &d->o : NULL); + playsound(getint(p), !d || (demoplayback && cn == democlientnum) ? NULL : &d->o); break; @@ -272,7 +278,7 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) conoutf("\f2%s suicided", d->name); act = d; } - else if(actor==getclientnum()) + else if(actor==getclientnum() || (demoplayback && actor==democlientnum)) { act = player1; int frags; @@ -553,6 +559,15 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) break; default: + if(demoplayback) // filter demo messages + { + int size = msgsizelookup(type); + if(size) + { + loopi(size) getint(p); + break; + } + } neterr("type"); return; } diff --git a/source/src/docs.cpp b/source/src/docs.cpp index c3f2e5f1..30991018 100644 --- a/source/src/docs.cpp +++ b/source/src/docs.cpp @@ -400,7 +400,7 @@ void renderdoc(int x, int y) if(ident->examples.length()) // examples { - doclines.add(ident->examples.length() == 1 ? "Example:" : "Examples:"); + doclines.add(newstring(ident->examples.length() == 1 ? "Example:" : "Examples:")); loopvj(ident->examples) { cvector lines; @@ -413,7 +413,7 @@ void renderdoc(int x, int y) if(ident->keys.length()) // default keys { - doclines.add(ident->keys.length() == 1 ? "Default key:" : "Default keys:"); + doclines.add(newstring(ident->keys.length() == 1 ? "Default key:" : "Default keys:")); loopvj(ident->keys) { dockey &k = ident->keys[j]; diff --git a/source/src/main.cpp b/source/src/main.cpp index 7b947fda..dc934bcb 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -306,7 +306,13 @@ int main(int argc, char **argv) else if(curtime<1) curtime = 1; cleardlights(); - if(lastmillis) updateworld(curtime, lastmillis); + + if(demoplayback && demopaused) + { + curtime = 0; + millis = lastmillis; + } + else if(lastmillis) updateworld(curtime, lastmillis); lastmillis += curtime; curmillis = millis; diff --git a/source/src/physics.cpp b/source/src/physics.cpp index c0cfd04f..29c1cea8 100644 --- a/source/src/physics.cpp +++ b/source/src/physics.cpp @@ -433,6 +433,7 @@ void attack(bool on) { if(intermission) return; if(editmode) editdrag(on); + else if(demoplayback && on) shiftdemoplayer(1); else if(player1->state==CS_DEAD) respawn(); else player1->attacking = on; } @@ -440,7 +441,8 @@ void attack(bool on) void jumpn(bool on) { if(intermission) return; - if(player1->state==CS_DEAD) + if(demoplayback && on) demopaused = !demopaused; + else if(player1->state==CS_DEAD) { if(on) respawn(); } @@ -474,7 +476,7 @@ void mousemove(int dx, int dy) camera1->yaw += (dx/SENSF)*(sensitivity/(float)sensitivityscale); camera1->pitch -= (dy/SENSF)*(sensitivity/(float)sensitivityscale)*(invmouse ? -1 : 1); fixcamerarange(); - if(camera1!=player1 && player1->state!=CS_DEAD) + if(camera1!=player1 && player1->state!=CS_DEAD && !demoplayback) { player1->yaw = camera1->yaw; player1->pitch = camera1->pitch; diff --git a/source/src/protos.h b/source/src/protos.h index a5fda8f7..c9bd8d47 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -225,6 +225,11 @@ extern void stop(); extern void stopifrecording(); extern void demodamage(int damage, vec &o); extern void demoblend(int damage); +extern bool demopaused; +extern playerent *demoplayer; +extern void shiftdemoplayer(int i); +extern bool localdemoplayer1st(); +extern int demomillis(); // physics extern float raycube(const vec &o, const vec &ray, vec &surface); diff --git a/source/src/rendergl.cpp b/source/src/rendergl.cpp index 9cea66be..0cfa5532 100644 --- a/source/src/rendergl.cpp +++ b/source/src/rendergl.cpp @@ -228,7 +228,36 @@ COMMAND(trypitch, ARG_1INT); void recomputecamera() { - if(editmode || player1->state!=CS_DEAD) + if(demoplayback && demoplayer) + { + static physent democam; + extern bool firstpersondemo; + + if(demoplayer == player1 && firstpersondemo) + { + democam = *(physent *)player1; + } + else + { + static playerent *lastplayer = NULL; + if(lastplayer != demoplayer) + { + lastplayer = demoplayer; + democam = *(physent *)demoplayer; + democam.type = ENT_CAMERA; + democam.reset(); + democam.roll = 0; + democam.move = -1; + camera1 = &democam; + } + democam.o = demoplayer->o; + democam.o.x -= (float)(cosf(RAD*(demoplayer->yaw-90)))*demoplayer->radius*1.5f; + democam.o.y -= (float)(sinf(RAD*(demoplayer->yaw-90)))*demoplayer->radius*1.5f; + democam.o.z += demoplayer->eyeheight/3.0f; + if(!demopaused) democam.yaw = demoplayer->yaw; + } + } + else if(editmode || player1->state!=CS_DEAD) { camera1 = player1; } @@ -505,7 +534,7 @@ void drawhudgun(int w, int h, float aspect, int farplane) gluPerspective((float)100.0f*h/w, aspect, 0.3f, farplane); // fov fixed at 100° glMatrixMode(GL_MODELVIEW); - if(player1->state!=CS_DEAD) renderhudgun(); + if(player1->state!=CS_DEAD && (!demoplayback || localdemoplayer1st())) renderhudgun(); rendermenumdl(); glMatrixMode(GL_PROJECTION); diff --git a/source/src/renderhud.cpp b/source/src/renderhud.cpp index 854c2903..64fcb4a1 100644 --- a/source/src/renderhud.cpp +++ b/source/src/renderhud.cpp @@ -50,6 +50,7 @@ VARP(radarres, 1, 64, 1024); VARP(radarentsize, 1, 4, 64); VARP(hidectfhud, 0, 0, 1); VARP(hideteamhud, 0, 0, 1); +VARP(hidedemohud, 0, 0, 1); VAR(showmap, 0, 0, 1); @@ -259,31 +260,35 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat glEnable(GL_TEXTURE_2D); + bool demo3rd = demoplayback && !localdemoplayer1st(); playerent *targetplayer = playerincrosshair(); bool didteamkill = player1->lastteamkill && player1->lastteamkill + 5000 > lastmillis; bool menuvisible = rendermenu(); - - if(player1->state==CS_ALIVE && !player1->reloading && !didteamkill && !menuvisible) - { - bool drawteamwarning = targetplayer ? (isteam(targetplayer->team, player1->team) && targetplayer->state!=CS_DEAD) : false; - if(player1->gunselect==GUN_SNIPER && scoped) drawscope(); - else if((player1->gunselect!=GUN_SNIPER || drawteamwarning)) drawcrosshair(drawteamwarning); - } - - drawequipicons(); - - if(!hideradar) - { - glMatrixMode(GL_MODELVIEW); - drawradar(w, h); - glMatrixMode(GL_PROJECTION); - } - - char *infostr = editinfo(); bool command = getcurcommand() ? true : false; - if(command) rendercommand(20, 1570); - else if(infostr) draw_text(infostr, 20, 1570); - else if(targetplayer) draw_text(targetplayer->name, 20, 1570); + + if(!demo3rd) + { + if(player1->state==CS_ALIVE && !player1->reloading && !didteamkill && !menuvisible) + { + bool drawteamwarning = targetplayer ? (isteam(targetplayer->team, player1->team) && targetplayer->state!=CS_DEAD) : false; + if(player1->gunselect==GUN_SNIPER && scoped) drawscope(); + else if((player1->gunselect!=GUN_SNIPER || drawteamwarning)) drawcrosshair(drawteamwarning); + } + + drawequipicons(); + + if(!hideradar) + { + glMatrixMode(GL_MODELVIEW); + drawradar(w, h); + glMatrixMode(GL_PROJECTION); + } + + char *infostr = editinfo(); + if(command) rendercommand(20, 1570); + else if(infostr) draw_text(infostr, 20, 1570); + else if(targetplayer) draw_text(targetplayer->name, 20, 1570); + } glLoadIdentity(); glOrtho(0, VIRTW*2, VIRTH*2, 0, -1, 1); @@ -299,8 +304,22 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat draw_textf("wvt %d", left, top+240, curvert); draw_textf("evt %d", left, top+320, xtraverts); } + + if(!hidedemohud && demoplayback) + { + const int left = VIRTW*2/80, top = VIRTH*2*3/4; + int dmillis = demomillis(); + s_sprintfd(time)("%d:%02d", dmillis/1000/60, dmillis/1000%60); + s_sprintfd(following)("\f0Following %s", demoplayer->name); - if(player1->state==CS_ALIVE) + draw_text(time, left, top-FONTH); + draw_text(following, left, top); + draw_text("jump to pause", left, top+2*FONTH); + draw_text("attack to change view", left, top+3*FONTH); + draw_text("reload for slow-motion", left, top+4*FONTH); + } + + if(player1->state==CS_ALIVE && !demo3rd) { glLoadIdentity(); glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1); diff --git a/source/src/rendermodel.cpp b/source/src/rendermodel.cpp index 488dc84b..641eff93 100644 --- a/source/src/rendermodel.cpp +++ b/source/src/rendermodel.cpp @@ -259,6 +259,6 @@ void renderclients() { playerent *d; loopv(players) if((d = players[i]) && (!demoplayback || i!=democlientnum)) renderclient(d); - if(player1->state==CS_DEAD || (reflecting && !refracting)) renderclient(player1); + if(player1->state==CS_DEAD || (reflecting && !refracting) || (demoplayback && !localdemoplayer1st())) renderclient(player1); } diff --git a/source/src/savegamedemo.cpp b/source/src/savegamedemo.cpp index 3f8fb695..f3a93396 100644 --- a/source/src/savegamedemo.cpp +++ b/source/src/savegamedemo.cpp @@ -33,6 +33,7 @@ void stop() demoloading = false; loopv(playerhistory) zapplayer(playerhistory[i]); playerhistory.setsize(0); + setvar("gamespeed", 100); } void stopifrecording() { if(demorecording) stop(); } @@ -116,9 +117,10 @@ void loadgamerest() int nplayers = gzgeti(); loopi(nplayers) if(!gzget()) { - playerent *d = getclient(i); + playerent *d = newclient(i); ASSERT(d); gzread(f, d, sizeof(playerent)); + d->lastaction = d->lastpain = lastmillis; } conoutf("savegame restored"); @@ -132,6 +134,8 @@ int playbacktime = 0; int ddamage, bdamage; vec dorig; +int demomillis() { return lastmillis-starttime; } + void record(char *name) { int cn = getclientnum(); @@ -160,14 +164,32 @@ void incomingdemodata(int chan, uchar *buf, int len, bool extras) { gzput(player1->gunselect); gzput(player1->lastattackgun); - gzputi(player1->lastaction-starttime); - gzputi(player1->akimbolastaction[0]-starttime); - gzputi(player1->akimbolastaction[1]-starttime); gzputi(player1->gunwait); + gzputi(player1->lastaction-starttime); + gzputi(player1->lastanimswitchtime[0]); + gzputi(player1->lastanimswitchtime[1]); + loopi(NUMGUNS) { gzput(player1->ammo[i]); gzput(player1->mag[i]); } + gzput(player1->akimbo ? 1 : 0 | (player1->reloading ? 1 : 0) << 1 | (player1->weaponchanging ? 1 : 0) << 2); + switch(player1->gunselect) // AH,demo + { + case GUN_GRENADE: + gzputi(player1->thrownademillis); // AH,demo + gzput(player1->inhandnade ? 1 : 0); // AH,demo + break; + case GUN_PISTOL: + if(player1->akimbo) + { + gzputi(player1->akimbolastaction[0]-starttime); + gzputi(player1->akimbolastaction[1]-starttime); + } + break; + case GUN_SNIPER: + gzput(scoped ? 1 : 0); + break; + } + gzputi(player1->health); gzputi(player1->armour); - //gzput(player1->armourtype); - loopi(NUMGUNS) gzput(player1->ammo[i]); gzput(player1->state); gzputi(bdamage); bdamage = 0; @@ -187,8 +209,7 @@ void demo(char *name) void stopreset() { conoutf("demo stopped (%d msec elapsed)", lastmillis-starttime); - stop(); - loopv(players) zapplayer(players[i]); + player1 = newplayerent(); disconnect(0, 0); } @@ -205,15 +226,36 @@ void readdemotime() playbacktime = scaletime(playbacktime); } +bool demopaused = false; +void togglepause() { demopaused = !demopaused; } + +playerent *demoplayer = player1; +bool firstpersondemo = true; +bool localdemoplayer1st() { return demoplayback && demoplayer == player1 && firstpersondemo; } + +void shiftdemoplayer(int i) +{ + if(!i) return; + if(demoplayer == player1) + { + firstpersondemo = !firstpersondemo; + if(!firstpersondemo) return; + } + vector plrs; + loopv(players) if(players[i] != NULL) plrs.add(players[i]); + int cur = plrs.find(demoplayer); + if(cur >= 0) demoplayer = plrs[(cur+i) % plrs.length()]; +} + void startdemo() { democlientnum = gzgeti(); demoplayback = true; starttime = lastmillis; conoutf("now playing demo"); - dynent *d = newclient(democlientnum); - ASSERT(d); - *d = *player1; + while(democlientnum>=players.length()) players.add(NULL); + players[democlientnum] = player1; + demoplayer = player1; readdemotime(); } @@ -261,19 +303,40 @@ void demoplaybackstep() { target->gunselect = gzget(); target->lastattackgun = gzget(); - target->lastaction = scaletime(gzgeti()); - target->akimbolastaction[0] = scaletime(gzgeti()); - target->akimbolastaction[1] = scaletime(gzgeti()); target->gunwait = gzgeti(); + target->lastaction = scaletime(gzgeti()); + target->lastanimswitchtime[0] = scaletime(gzgeti()); // AH,demo + target->lastanimswitchtime[1] = scaletime(gzgeti()); // AH,demo + loopi(NUMGUNS) { target->ammo[i] = gzget(); target->mag[i] = gzget(); } // AH,demo + uchar flags = gzget(); + target->akimbo = flags&1 ? true : false; // AH, demo + target->reloading = (flags>>1)&1 ? true : false; // AH,demo + target->weaponchanging = (flags>>2)&1 ? true : false; // + switch(target->gunselect) + { + case GUN_GRENADE: + target->thrownademillis = scaletime(gzgeti()); // AH,demo + target->inhandnade = gzget() ? (bounceent *)1 : NULL; // AH,demo + break; + case GUN_PISTOL: + if(player1->akimbo) + { + target->akimbolastaction[0] = scaletime(gzgeti()); + target->akimbolastaction[1] = scaletime(gzgeti()); + } + break; + case GUN_SNIPER: + scoped = gzget() ? true : false; + break; + } + target->health = gzgeti(); target->armour = gzgeti(); - //target->armourtype = gzget(); - loopi(NUMGUNS) target->ammo[i] = gzget(); target->state = gzget(); - target->lastmove = playbacktime; if((bdamage = gzgeti())) damageblend(bdamage); - if((ddamage = gzgeti())) { gzgetv(dorig); particle_splash(3, ddamage, 1000, dorig); } + if((ddamage = gzgeti())) { gzgetv(dorig); particle_splash(3, ddamage, 1000, dorig); }; // FIXME: set more client state here + target->lastmove = playbacktime; } // insert latest copy of player into history @@ -295,13 +358,16 @@ void demoplaybackstep() if(demoplayback) { + if(playerhistory.length()) memcpy(player1, playerhistory.last(), sizeof(playerent)); int itime = lastmillis-demodelaymsec; loopvrev(playerhistory) if(playerhistory[i]->lastupdateo = b->o; + player1->yaw = b->yaw; + player1->pitch = b->pitch; if(a!=b) // interpolate pos & angles { dynent *c = b; @@ -332,6 +398,8 @@ void stopn() { if(demoplayback) stopreset(); else stop(); conoutf("demo stopped" COMMAND(record, ARG_1STR); COMMAND(demo, ARG_1STR); COMMANDN(stop, stopn, ARG_NONE); +COMMANDN(demopause, togglepause, ARG_NONE); +COMMANDN(demoplayer, shiftdemoplayer, ARG_1INT); COMMAND(savegame, ARG_1STR); COMMAND(loadgame, ARG_1STR); diff --git a/source/src/sound.cpp b/source/src/sound.cpp index 486f2f2d..474c6f8f 100644 --- a/source/src/sound.cpp +++ b/source/src/sound.cpp @@ -203,7 +203,12 @@ void updatevol() } } -void playsoundc(int n) { addmsg(SV_SOUND, "i", n); playsound(n); } +void playsoundc(int n) +{ + if(demoplayback) return; + addmsg(SV_SOUND, "i", n); + playsound(n); +} int soundsatonce = 0, lastsoundmillis = 0; diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index 36e6f8a2..dbaec8fc 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -111,6 +111,7 @@ COMMAND(setscope, ARG_1INT); void reload(playerent *d) { + if(demoplayback) { setvar("gamespeed", getvar("gamespeed") != 100 ? 100 : 35); return; } if(!d || d->state!=CS_ALIVE || d->reloading || d->weaponchanging) return; if(!reloadable_gun(d->gunselect) || d->ammo[d->gunselect]<=0) return; if(d->mag[d->gunselect] >= (has_akimbo(d) ? 2 : 1)*guns[d->gunselect].magsize) return; @@ -500,7 +501,7 @@ bounceent *new_nade(playerent *d, int millis = 0) d->inhandnade = p; d->thrownademillis = 0; - playsound(S_GRENADEPULL, (d==player1 ? NULL : &d->o)); + if(d==player1 && !demoplayback) playsoundc(S_GRENADEPULL); return p; } @@ -514,7 +515,7 @@ void explode_nade(bounceent *i) void shootv(int gun, vec &from, vec &to, playerent *d, bool local, int nademillis) // create visual effect from a shot { - playsound(guns[gun].sound, d==player1 ? NULL : &d->o); + if(guns[gun].sound) playsound(guns[gun].sound, d==player1 ? NULL : &d->o); switch(gun) { case GUN_KNIFE: @@ -543,7 +544,7 @@ void shootv(int gun, vec &from, vec &to, playerent *d, bool local, int nademilli case GUN_GRENADE: { - if(d!=player1) + if(d!=player1 || demoplayback) { bounceent *p = new_nade(d, nademillis); throw_nade(d, to, p);