diff --git a/source/src/ac_changelog.txt b/source/src/ac_changelog.txt index a0bdd4b5..16f9dfe2 100644 --- a/source/src/ac_changelog.txt +++ b/source/src/ac_changelog.txt @@ -1,38 +1,38 @@ -Action Cube Change-Log -//hopefully this will be the last one ;) -Up to date as of 5-29-05 - -Done: -*addition of checkgame for special crap -*added new entities, pick ups for weapons -*advanced map version, while still mantaining compatibility -*added game modes dm, tdm, s, ts, ctf(ctf not implemented yet) with tdm being the default mode -*new guns pistol, shotgun, subgun, assult rifle, sniper rifle (ready to add grenade) -*removed single player modes and monsters -*removed jump-pads, teleports, and someother entities -*auto-force to 'T' or 'CT' when in team mode -*64 cube radius lights -*suicide -*physics changes -slowed down 16 normaly 14/15 with armour -*falling damage -*added ladder entity -*spread and recoil (needs balancing) - -ToDo: -*PLAYTESTING - -*reloading -*automatic and semiauto weapons -*weapon categories -*(D)new package tree -*Ricks bots -*(D)ctf -*new sound cfg -*(D)team spawns -*(D)team player models -*(D)merge new media -*update hud sprites, font?, etc -*add altfire functions (sniper=zoom, shotgun=reload 1 shell?) -*ati fix -*finish ladders +Action Cube Change-Log +//hopefully this will be the last one ;) +Up to date as of 5-29-05 + +Done: +*addition of checkgame for special crap +*added new entities, pick ups for weapons +*advanced map version, while still mantaining compatibility +*added game modes dm, tdm, s, ts, ctf(ctf not implemented yet) with tdm being the default mode +*new guns pistol, shotgun, subgun, assult rifle, sniper rifle (ready to add grenade) +*removed single player modes and monsters +*removed jump-pads, teleports, and someother entities +*auto-force to 'T' or 'CT' when in team mode +*64 cube radius lights +*suicide +*physics changes -slowed down 16 normaly 14/15 with armour +*falling damage +*added ladder entity +*spread and recoil (needs balancing) + +ToDo: +*PLAYTESTING + +*reloading +*automatic and semiauto weapons +*weapon categories +*(D)new package tree +*Ricks bots +*(D)ctf +*new sound cfg +*(D)team spawns +*(D)team player models +*(D)merge new media +*update hud sprites, font?, etc +*add altfire functions (sniper=zoom, shotgun=reload 1 shell?) +*ati fix +*finish ladders *change shooting visual effects \ No newline at end of file diff --git a/source/src/clientextras.cpp b/source/src/clientextras.cpp index 97835e3b..84ad9842 100644 --- a/source/src/clientextras.cpp +++ b/source/src/clientextras.cpp @@ -16,7 +16,7 @@ void renderclient(dynent *d, bool team, char *mdlname, bool vwep, float scale) float mz = d->o.z-d->eyeheight+1.55f*scale; int basetime = -((int)d&0xFFF); if(d->state==CS_DEAD) - { + { if(vwep) mz = S((int)d->o.x, (int)d->o.y)->floor; int r; n = (int)d%3; r = range[n]; @@ -27,11 +27,11 @@ void renderclient(dynent *d, bool team, char *mdlname, bool vwep, float scale) if(mz<-1000) return; //mdl = (((int)d>>6)&1)+1; //mz = d->o.z-d->eyeheight+0.2f; - //scale = 1.2f; - if(vwep) - { - rendermodel(mdlname, 0, 1, 0, 1.5f, d->o.x, mz, d->o.y, -90, 90, team, scale, speed, 0, basetime); - return; + //scale = 1.2f; + if(vwep) + { + rendermodel(mdlname, 0, 1, 0, 1.5f, d->o.x, mz, d->o.y, -90, 90, team, scale, speed, 0, basetime); + return; }; } else if(d->state==CS_EDITING) { n = 16; } @@ -40,7 +40,7 @@ void renderclient(dynent *d, bool team, char *mdlname, bool vwep, float scale) else if(d->monsterstate==M_PAIN) { n = 10; } else if((!d->move && !d->strafe) || !d->moving) { n = 12; } else if(!d->onfloor && d->timeinair>100) { n = 18; } - else { n = 14; speed = 1200/d->maxspeed*scale; }; + else { n = 14; speed = 1200/d->maxspeed*scale; }; rendermodel(mdlname, frame[n], range[n], 0, 1.5f, d->o.x, mz, d->o.y, d->yaw+90, d->pitch/2, team, scale, speed, 0, basetime); }; diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index cde5ff4e..1fd68707 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -1,592 +1,592 @@ -// clientgame.cpp: core game related stuff - -#include "cube.h" -#define sleep my_sleep -int nextmode = 0; // nextmode becomes gamemode after next map load -VAR(gamemode, 1, 0, 0); - -void mode(int n) { addmsg(1, 2, SV_GAMEMODE, nextmode = n); }; -COMMAND(mode, ARG_1INT); - -bool intermission = false; - -dynent *player1 = newdynent(); // our client -dvector players; // other clients - -vector physents; - -VAR(sensitivity, 0, 10, 1000); -VAR(sensitivityscale, 1, 1, 100); -VAR(invmouse, 0, 0, 1); - -int lastmillis = 0; -int curtime; -string clientmap; - -extern int framesinmap; - -char *getclientmap() { return clientmap; }; - -void resetmovement(dynent *d) -{ - d->k_left = false; - d->k_right = false; - d->k_up = false; - d->k_down = false; - d->jumpnext = false; - d->strafe = 0; - d->move = 0; -}; - -void spawnstate(dynent *d) // reset player state not persistent accross spawns -{ - resetmovement(d); - d->vel.x = d->vel.y = d->vel.z = 0; - d->onfloor = false; - d->timeinair = 0; - d->health = 100; - d->armour = 0; - //d->hasarmour = false; - //d->armourtype = A_BLUE; - //d->quadmillis = 0; - d->gunselect = GUN_PISTOL; - d->mag[6]=10; - d->gunwait = 0; - d->attacking = false; - d->lastaction = 0; - - - radd(d); - - - //loopi(NUMGUNS) if(d->nextprimary!=i) d->ammo[i] = 0; - //d->mag[GUN_KNIFE] = 1; - //equp with default pistol - - //d->mag[GUN_PISTOL] = 8; - //if(d->nextprimary!=GUN_PISTOL) - // d->ammo[GUN_PISTOL] = 16; - //else - // d->ammo[GUN_PISTOL] = 32; - //temp for testing guns - //d->ammo[GUN_SHOTGUN] = 100; - //d->ammo[GUN_SUBGUN] = 400; - //d->ammo[GUN_SNIPER] = 100; - //d->ammo[GUN_ASSULT] = 400; - - //end tmp - - /* - if(m_noitems) - { - d->gunselect = GUN_RIFLE; - d->armour = 0; - if(m_noitemsrail) - { - d->health = 1; - d->ammo[GUN_RIFLE] = 100; - } - else - { - if(gamemode==12) { d->gunselect = GUN_FIST; return; }; // eihruls secret "instafist" mode - d->health = 256; - if(m_tarena) - { - int gun1 = rnd(4)+1; - baseammo(d->gunselect = gun1); - for(;;) - { - int gun2 = rnd(4)+1; - if(gun1!=gun2) { baseammo(gun2); break; }; - }; - } - else if(m_arena) // insta arena - { - d->ammo[GUN_RIFLE] = 100; - } - else // efficiency - { - loopi(4) baseammo(i+1); - d->gunselect = GUN_CG; - }; - d->ammo[GUN_CG] /= 2; - }; - } - else - { - d->ammo[GUN_SG] = 5; - }; - */ -}; - -dynent *newdynent() // create a new blank player or monster -{ - dynent *d = (dynent *)gp()->alloc(sizeof(dynent)); - d->o.x = 0; - d->o.y = 0; - d->o.z = 0; - d->yaw = 270; - d->pitch = 0; - d->roll = 0; - d->maxspeed = 16; //16 max speed, 14 max with armour - d->outsidemap = false; - d->inwater = false; - d->radius = 1.1f; - d->eyeheight = 4.25f; - d->aboveeye = 0.7f; - d->frags = 0; - d->plag = 0; - d->ping = 0; - d->lastupdate = lastmillis; - d->enemy = NULL; - d->monsterstate = 0; - d->name[0] = d->team[0] = 0; - d->blocked = false; - d->lifesequence = 0; - d->state = CS_ALIVE; - d->shots = 0; - d->reloading = false; - d->nextprimary = 1; - d->hasarmour = false; - spawnstate(d); - return d; -}; -void respawnself() -{ - spawnplayer(player1); - showscores(false); -}; - -void respawn() -{ - if(player1->state==CS_DEAD) - { - player1->attacking = false; - if(m_arena) { conoutf("waiting for new round to start..."); return; }; - respawnself(); - }; -}; - -void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, char *&lastname, bool &oneteam) -//void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam) -{ - if(d->state!=CS_DEAD) - { - alive++; - if(lastteam && strcmp(lastteam, d->team)) oneteam = false; - lastteam = d->team; - lastname = d->name; - } - else - { - dead++; - }; -}; - -int arenarespawnwait = 0; -int arenadetectwait = 0; - -void arenarespawn() -{ - if (!m_arena) return; - - if(arenarespawnwait) - { - if(arenarespawnwait0 && (alive<=1 || (m_teammode && oneteam))) - { - conoutf("arena round is over! next round in 5 seconds..."); - if(alive) - { - if(m_teammode) - conoutf("team %s has won the round", (int)lastteam); - else - conoutf("%s is the survior!", (int)lastname); - } - else conoutf("everyone died!"); - arenarespawnwait = lastmillis+5000; - arenadetectwait = lastmillis+10000; - player1->roll = 0; - }; - }; -}; - - -void checkquad(int time) -{ -/* if(player1->quadmillis && (player1->quadmillis -= time)<0) - { - player1->quadmillis = 0; - playsoundc(S_PUPOUT); - conoutf("quad damage is over"); - }; -*/ -}; - -//generic function to check all kinds of crazy gamemode specific things -void checkgame(int time) -{ - - //allow faster movements when editing -/* if (editmode) - { - player1->maxspeed=22; - return; - } - else //otherwise max speed is 16 and 14 with armour - { - if(player1->armour>50) - player1->maxspeed = 14; - if(player1->armour>0) - player1->maxspeed = 15; - player1->maxspeed = 16; - }; -*/ - - //force to a team if team mode - if (m_teammode) - { - if (strcmp(player1->team,"T") && strcmp(player1->team,"CT")) - { - player1->state = CS_DEAD; - //conoutf("set team to T or CT to joing game"); - if (rnd(2)<1) strcpy_s(player1->team,"T"); - else strcpy_s(player1->team,"CT"); - } - - //add checks here to force-drop flag if switching team with flag - - //add checks for end conditions of ctf - }; - - - //reset zoom if dead - - //etc - //checkquad(time); - - if (player1->state==CS_DEAD) - { - player1->primary = player1->nextprimary; - } -}; - -void zapdynent(dynent *&d) -{ - if(d) gp()->dealloc(d, sizeof(dynent)); - d = NULL; -}; - -extern int democlientnum; - -void otherplayers() -{ - loopv(players) if(players[i]) - { - const int lagtime = lastmillis-players[i]->lastupdate; - if(lagtime>1000 && players[i]->state==CS_ALIVE) - { - players[i]->state = CS_LAGGED; - continue; - }; - if(lagtime && players[i]->state != CS_DEAD && (!demoplayback || i!=democlientnum)) moveplayer(players[i], 2, false); // use physics to extrapolate player position - }; -}; - -extern void explode_nade(physent *i); - -void mphysents() -{ - loopv(physents) if(physents[i]) - { - physent *p = physents[i]; - if(p->throwed) moveplayer(p, 2, false); - if(lastmillis - p->millis >= p->lifetime) - { - explode_nade(physents[i]); - physents.remove(i); - i--; - }; - }; -}; - -int sleepwait = 0; -string sleepcmd; -void sleep(char *msec, char *cmd) { sleepwait = atoi(msec)+lastmillis; strcpy_s(sleepcmd, cmd); }; -COMMAND(sleep, ARG_2STR); - -void updateworld(int millis) // main game update loop -{ - if(lastmillis) - { - curtime = millis - lastmillis; - if(sleepwait && lastmillis>sleepwait) { execute(sleepcmd); sleepwait = 0; }; - physicsframe(); - checkgame(curtime); - //if(m_arena) arenarespawn(); - arenarespawn(); - moveprojectiles((float)curtime); - demoplaybackstep(); - if(!demoplayback) - { - if(getclientnum()>=0) shoot(player1, worldpos); // only shoot when connected to server - gets2c(); // do this first, so we have most accurate information when our player moves - }; - mphysents(); - otherplayers(); - if(!demoplayback) - { - //put game mode extra call here - if(player1->state==CS_DEAD) - { - if(lastmillis-player1->lastaction<2000) - { - player1->move = player1->strafe = 0; - moveplayer(player1, 10, false); - }; - } - else if(!intermission) - { - moveplayer(player1, 20, true); - checkitems(); - }; - c2sinfo(player1); // do this last, to reduce the effective frame lag - }; - }; - lastmillis = millis; -}; - -void entinmap(dynent *d) // brute force but effective way to find a free spawn spot in the map -{ - loopi(100) // try max 100 times - { - float dx = (rnd(21)-10)/10.0f*i; // increasing distance - float dy = (rnd(21)-10)/10.0f*i; - d->o.x += dx; - d->o.y += dy; - if(collide(d, true, 0, 0)) return; - d->o.x -= dx; - d->o.y -= dy; - }; - conoutf("can't find entity spawn spot! (%d, %d)", (int)d->o.x, (int)d->o.y); - // leave ent at original pos, possibly stuck -}; - -int spawncycle = -1; -int fixspawn = 2; - -void spawnplayer(dynent *d) // place at random spawn. also used by monsters! -{ - int r = fixspawn-->0 ? 4 : rnd(10)+1; - loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1); - if(spawncycle!=-1) - { - d->o.x = ents[spawncycle].x; - d->o.y = ents[spawncycle].y; - d->o.z = ents[spawncycle].z; - d->yaw = ents[spawncycle].attr1; - d->pitch = 0; - d->roll = 0; - } - else - { - d->o.x = d->o.y = (float)ssize/2; - d->o.z = 4; - }; - entinmap(d); - spawnstate(d); - d->state = CS_ALIVE; -}; - -// movement input code - -#define dir(name,v,d,s,os) void name(bool isdown) { player1->s = isdown; player1->v = isdown ? d : (player1->os ? -(d) : 0); player1->lastmove = lastmillis; }; - -dir(backward, move, -1, k_down, k_up); -dir(forward, move, 1, k_up, k_down); -dir(left, strafe, 1, k_left, k_right); -dir(right, strafe, -1, k_right, k_left); - -void attack(bool on) -{ - if(intermission) return; - if(editmode) editdrag(on); - else if(player1->attacking = on) respawn(); -}; - -void jumpn(bool on) { if(!intermission && (player1->jumpnext = on) ) respawn(); }; - -COMMAND(backward, ARG_DOWN); -COMMAND(forward, ARG_DOWN); -COMMAND(left, ARG_DOWN); -COMMAND(right, ARG_DOWN); -COMMANDN(jump, jumpn, ARG_DOWN); -COMMAND(attack, ARG_DOWN); -COMMAND(showscores, ARG_DOWN); - -void fixplayer1range() -{ - const float MAXPITCH = 90.0f; - if(player1->pitch>MAXPITCH) player1->pitch = MAXPITCH; - if(player1->pitch<-MAXPITCH) player1->pitch = -MAXPITCH; - while(player1->yaw<0.0f) player1->yaw += 360.0f; - while(player1->yaw>=360.0f) player1->yaw -= 360.0f; -}; - -void mousemove(int dx, int dy) -{ - if(player1->state==CS_DEAD || intermission) return; - const float SENSF = 33.0f; // try match quake sens - player1->yaw += (dx/SENSF)*(sensitivity/(float)sensitivityscale); - player1->pitch -= (dy/SENSF)*(sensitivity/(float)sensitivityscale)*(invmouse ? -1 : 1); - fixplayer1range(); -}; - -// damage arriving from the network, monsters, yourself, all ends up here. - -void selfdamage(int damage, int actor, dynent *act) -{ - if(player1->state!=CS_ALIVE || editmode || intermission) return; - damageblend(damage); - demoblend(damage); - int ad = damage*30/100;//int ad = damage*(player1->armourtype+1)*20/100; // let armour absorb when possible - if(ad>player1->armour) ad = player1->armour; - player1->armour -= ad; - damage -= ad; - float droll = damage/0.5f; - player1->roll += player1->roll>0 ? droll : (player1->roll<0 ? -droll : (rnd(2) ? droll : -droll)); // give player a kick depending on amount of damage - if((player1->health -= damage)<=0) - { - if(actor==-2) - { - conoutf("you got killed by %s!", (int)&act->name); - } - else if(actor==-1) - { - actor = getclientnum(); - conoutf("you suicided!"); - addmsg(1, 2, SV_FRAGS, --player1->frags); - } - else - { - dynent *a = getclient(actor); - if(a) - { - if(isteam(a->team, player1->team)) - { - conoutf("you got fragged by a teammate (%s)", (int)a->name); - } - else - { - conoutf("you got fragged by %s", (int)a->name); - }; - }; - }; - showscores(true); - addmsg(1, 2, SV_DIED, actor); - player1->lifesequence++; - player1->attacking = false; - player1->state = CS_DEAD; - player1->oldpitch = player1->pitch; - player1->pitch = 0; - player1->roll = 60; - playsound(S_DIE1+rnd(2)); - spawnstate(player1); - player1->lastaction = lastmillis; - } - else - { - playsound(S_PAIN6); - }; -}; - -void timeupdate(int timeremain) -{ - if(!timeremain) - { - intermission = true; - player1->attacking = false; - conoutf("intermission:"); - conoutf("game has ended!"); - showscores(true); - } - else - { - conoutf("time remaining: %d minutes", timeremain); - }; -}; - -dynent *getclient(int cn) // ensure valid entity -{ - if(cn<0 || cn>=MAXCLIENTS) - { - neterr("clientnum"); - return NULL; - }; - while(cn>=players.length()) players.add(NULL); - return players[cn] ? players[cn] : (players[cn] = newdynent()); -}; - -void addc() { strcpy(getclient(1)->team, "CT"); getclient(1)->o = player1->o; strcpy(getclient(1)->name, "sucker"); } COMMAND(addc, ARG_NONE); // FIXME - -void initclient() -{ - clientmap[0] = 0; - initclientnet(); -}; - -void startmap(char *name) // called just after a map load -{ - //if(netmapstart()) { gamemode = 0;}; //needs fixed to switch modes? - netmapstart(); //should work - sleepwait = 0; - //put call to clear/restart game mode extras here - projreset(); - shotlinereset(); - spawncycle = -1; - spawnplayer(player1); - player1->frags = 0; - loopv(players) if(players[i]) players[i]->frags = 0; - resetspawns(); - strcpy_s(clientmap, name); - if(editmode) toggleedit(); - setvar("gamespeed", 100); - setvar("fog", 180); - setvar("fogcolour", 0x8099B3); - showscores(false); - intermission = false; - framesinmap = 0; - conoutf("game mode is %s", (int)modestr(gamemode)); -}; - -COMMANDN(map, changemap, ARG_1STR); - -void suicide() -{ - if (multiplayer()) conoutf("there are others willing to kill you..."); - else conoutf("this is not the answer"); - selfdamage(1000, -1, player1); - //if(d==player1) selfdamage(dm, -1, d); - //else { addmsg(1, 4, SV_DAMAGE, d, dm, d->lifesequence); playsound(S_FALL1+rnd(5), &d->o); }; - //particle_splash(3, 1000, 1000, player1->o); //edit out? - demodamage(1000, player1->o); - playsound(S_SUICIDE, &player1->o); - -}; - -COMMAND(suicide, ARG_NONE); +// clientgame.cpp: core game related stuff + +#include "cube.h" +#define sleep my_sleep +int nextmode = 0; // nextmode becomes gamemode after next map load +VAR(gamemode, 1, 0, 0); + +void mode(int n) { addmsg(1, 2, SV_GAMEMODE, nextmode = n); }; +COMMAND(mode, ARG_1INT); + +bool intermission = false; + +dynent *player1 = newdynent(); // our client +dvector players; // other clients + +vector physents; + +VAR(sensitivity, 0, 10, 1000); +VAR(sensitivityscale, 1, 1, 100); +VAR(invmouse, 0, 0, 1); + +int lastmillis = 0; +int curtime; +string clientmap; + +extern int framesinmap; + +char *getclientmap() { return clientmap; }; + +void resetmovement(dynent *d) +{ + d->k_left = false; + d->k_right = false; + d->k_up = false; + d->k_down = false; + d->jumpnext = false; + d->strafe = 0; + d->move = 0; +}; + +void spawnstate(dynent *d) // reset player state not persistent accross spawns +{ + resetmovement(d); + d->vel.x = d->vel.y = d->vel.z = 0; + d->onfloor = false; + d->timeinair = 0; + d->health = 100; + d->armour = 0; + //d->hasarmour = false; + //d->armourtype = A_BLUE; + //d->quadmillis = 0; + d->gunselect = GUN_PISTOL; + d->mag[6]=10; + d->gunwait = 0; + d->attacking = false; + d->lastaction = 0; + + + radd(d); + + + //loopi(NUMGUNS) if(d->nextprimary!=i) d->ammo[i] = 0; + //d->mag[GUN_KNIFE] = 1; + //equp with default pistol + + //d->mag[GUN_PISTOL] = 8; + //if(d->nextprimary!=GUN_PISTOL) + // d->ammo[GUN_PISTOL] = 16; + //else + // d->ammo[GUN_PISTOL] = 32; + //temp for testing guns + //d->ammo[GUN_SHOTGUN] = 100; + //d->ammo[GUN_SUBGUN] = 400; + //d->ammo[GUN_SNIPER] = 100; + //d->ammo[GUN_ASSULT] = 400; + + //end tmp + + /* + if(m_noitems) + { + d->gunselect = GUN_RIFLE; + d->armour = 0; + if(m_noitemsrail) + { + d->health = 1; + d->ammo[GUN_RIFLE] = 100; + } + else + { + if(gamemode==12) { d->gunselect = GUN_FIST; return; }; // eihruls secret "instafist" mode + d->health = 256; + if(m_tarena) + { + int gun1 = rnd(4)+1; + baseammo(d->gunselect = gun1); + for(;;) + { + int gun2 = rnd(4)+1; + if(gun1!=gun2) { baseammo(gun2); break; }; + }; + } + else if(m_arena) // insta arena + { + d->ammo[GUN_RIFLE] = 100; + } + else // efficiency + { + loopi(4) baseammo(i+1); + d->gunselect = GUN_CG; + }; + d->ammo[GUN_CG] /= 2; + }; + } + else + { + d->ammo[GUN_SG] = 5; + }; + */ +}; + +dynent *newdynent() // create a new blank player or monster +{ + dynent *d = (dynent *)gp()->alloc(sizeof(dynent)); + d->o.x = 0; + d->o.y = 0; + d->o.z = 0; + d->yaw = 270; + d->pitch = 0; + d->roll = 0; + d->maxspeed = 16; //16 max speed, 14 max with armour + d->outsidemap = false; + d->inwater = false; + d->radius = 1.1f; + d->eyeheight = 4.25f; + d->aboveeye = 0.7f; + d->frags = 0; + d->plag = 0; + d->ping = 0; + d->lastupdate = lastmillis; + d->enemy = NULL; + d->monsterstate = 0; + d->name[0] = d->team[0] = 0; + d->blocked = false; + d->lifesequence = 0; + d->state = CS_ALIVE; + d->shots = 0; + d->reloading = false; + d->nextprimary = 1; + d->hasarmour = false; + spawnstate(d); + return d; +}; +void respawnself() +{ + spawnplayer(player1); + showscores(false); +}; + +void respawn() +{ + if(player1->state==CS_DEAD) + { + player1->attacking = false; + if(m_arena) { conoutf("waiting for new round to start..."); return; }; + respawnself(); + }; +}; + +void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, char *&lastname, bool &oneteam) +//void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam) +{ + if(d->state!=CS_DEAD) + { + alive++; + if(lastteam && strcmp(lastteam, d->team)) oneteam = false; + lastteam = d->team; + lastname = d->name; + } + else + { + dead++; + }; +}; + +int arenarespawnwait = 0; +int arenadetectwait = 0; + +void arenarespawn() +{ + if (!m_arena) return; + + if(arenarespawnwait) + { + if(arenarespawnwait0 && (alive<=1 || (m_teammode && oneteam))) + { + conoutf("arena round is over! next round in 5 seconds..."); + if(alive) + { + if(m_teammode) + conoutf("team %s has won the round", (int)lastteam); + else + conoutf("%s is the survior!", (int)lastname); + } + else conoutf("everyone died!"); + arenarespawnwait = lastmillis+5000; + arenadetectwait = lastmillis+10000; + player1->roll = 0; + }; + }; +}; + + +void checkquad(int time) +{ +/* if(player1->quadmillis && (player1->quadmillis -= time)<0) + { + player1->quadmillis = 0; + playsoundc(S_PUPOUT); + conoutf("quad damage is over"); + }; +*/ +}; + +//generic function to check all kinds of crazy gamemode specific things +void checkgame(int time) +{ + + //allow faster movements when editing +/* if (editmode) + { + player1->maxspeed=22; + return; + } + else //otherwise max speed is 16 and 14 with armour + { + if(player1->armour>50) + player1->maxspeed = 14; + if(player1->armour>0) + player1->maxspeed = 15; + player1->maxspeed = 16; + }; +*/ + + //force to a team if team mode + if (m_teammode) + { + if (strcmp(player1->team,"T") && strcmp(player1->team,"CT")) + { + player1->state = CS_DEAD; + //conoutf("set team to T or CT to joing game"); + if (rnd(2)<1) strcpy_s(player1->team,"T"); + else strcpy_s(player1->team,"CT"); + } + + //add checks here to force-drop flag if switching team with flag + + //add checks for end conditions of ctf + }; + + + //reset zoom if dead + + //etc + //checkquad(time); + + if (player1->state==CS_DEAD) + { + player1->primary = player1->nextprimary; + } +}; + +void zapdynent(dynent *&d) +{ + if(d) gp()->dealloc(d, sizeof(dynent)); + d = NULL; +}; + +extern int democlientnum; + +void otherplayers() +{ + loopv(players) if(players[i]) + { + const int lagtime = lastmillis-players[i]->lastupdate; + if(lagtime>1000 && players[i]->state==CS_ALIVE) + { + players[i]->state = CS_LAGGED; + continue; + }; + if(lagtime && players[i]->state != CS_DEAD && (!demoplayback || i!=democlientnum)) moveplayer(players[i], 2, false); // use physics to extrapolate player position + }; +}; + +extern void explode_nade(physent *i); + +void mphysents() +{ + loopv(physents) if(physents[i]) + { + physent *p = physents[i]; + if(p->throwed) moveplayer(p, 2, false); + if(lastmillis - p->millis >= p->lifetime) + { + explode_nade(physents[i]); + physents.remove(i); + i--; + }; + }; +}; + +int sleepwait = 0; +string sleepcmd; +void sleep(char *msec, char *cmd) { sleepwait = atoi(msec)+lastmillis; strcpy_s(sleepcmd, cmd); }; +COMMAND(sleep, ARG_2STR); + +void updateworld(int millis) // main game update loop +{ + if(lastmillis) + { + curtime = millis - lastmillis; + if(sleepwait && lastmillis>sleepwait) { execute(sleepcmd); sleepwait = 0; }; + physicsframe(); + checkgame(curtime); + //if(m_arena) arenarespawn(); + arenarespawn(); + moveprojectiles((float)curtime); + demoplaybackstep(); + if(!demoplayback) + { + if(getclientnum()>=0) shoot(player1, worldpos); // only shoot when connected to server + gets2c(); // do this first, so we have most accurate information when our player moves + }; + mphysents(); + otherplayers(); + if(!demoplayback) + { + //put game mode extra call here + if(player1->state==CS_DEAD) + { + if(lastmillis-player1->lastaction<2000) + { + player1->move = player1->strafe = 0; + moveplayer(player1, 10, false); + }; + } + else if(!intermission) + { + moveplayer(player1, 20, true); + checkitems(); + }; + c2sinfo(player1); // do this last, to reduce the effective frame lag + }; + }; + lastmillis = millis; +}; + +void entinmap(dynent *d) // brute force but effective way to find a free spawn spot in the map +{ + loopi(100) // try max 100 times + { + float dx = (rnd(21)-10)/10.0f*i; // increasing distance + float dy = (rnd(21)-10)/10.0f*i; + d->o.x += dx; + d->o.y += dy; + if(collide(d, true, 0, 0)) return; + d->o.x -= dx; + d->o.y -= dy; + }; + conoutf("can't find entity spawn spot! (%d, %d)", (int)d->o.x, (int)d->o.y); + // leave ent at original pos, possibly stuck +}; + +int spawncycle = -1; +int fixspawn = 2; + +void spawnplayer(dynent *d) // place at random spawn. also used by monsters! +{ + int r = fixspawn-->0 ? 4 : rnd(10)+1; + loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1); + if(spawncycle!=-1) + { + d->o.x = ents[spawncycle].x; + d->o.y = ents[spawncycle].y; + d->o.z = ents[spawncycle].z; + d->yaw = ents[spawncycle].attr1; + d->pitch = 0; + d->roll = 0; + } + else + { + d->o.x = d->o.y = (float)ssize/2; + d->o.z = 4; + }; + entinmap(d); + spawnstate(d); + d->state = CS_ALIVE; +}; + +// movement input code + +#define dir(name,v,d,s,os) void name(bool isdown) { player1->s = isdown; player1->v = isdown ? d : (player1->os ? -(d) : 0); player1->lastmove = lastmillis; }; + +dir(backward, move, -1, k_down, k_up); +dir(forward, move, 1, k_up, k_down); +dir(left, strafe, 1, k_left, k_right); +dir(right, strafe, -1, k_right, k_left); + +void attack(bool on) +{ + if(intermission) return; + if(editmode) editdrag(on); + else if(player1->attacking = on) respawn(); +}; + +void jumpn(bool on) { if(!intermission && (player1->jumpnext = on) ) respawn(); }; + +COMMAND(backward, ARG_DOWN); +COMMAND(forward, ARG_DOWN); +COMMAND(left, ARG_DOWN); +COMMAND(right, ARG_DOWN); +COMMANDN(jump, jumpn, ARG_DOWN); +COMMAND(attack, ARG_DOWN); +COMMAND(showscores, ARG_DOWN); + +void fixplayer1range() +{ + const float MAXPITCH = 90.0f; + if(player1->pitch>MAXPITCH) player1->pitch = MAXPITCH; + if(player1->pitch<-MAXPITCH) player1->pitch = -MAXPITCH; + while(player1->yaw<0.0f) player1->yaw += 360.0f; + while(player1->yaw>=360.0f) player1->yaw -= 360.0f; +}; + +void mousemove(int dx, int dy) +{ + if(player1->state==CS_DEAD || intermission) return; + const float SENSF = 33.0f; // try match quake sens + player1->yaw += (dx/SENSF)*(sensitivity/(float)sensitivityscale); + player1->pitch -= (dy/SENSF)*(sensitivity/(float)sensitivityscale)*(invmouse ? -1 : 1); + fixplayer1range(); +}; + +// damage arriving from the network, monsters, yourself, all ends up here. + +void selfdamage(int damage, int actor, dynent *act) +{ + if(player1->state!=CS_ALIVE || editmode || intermission) return; + damageblend(damage); + demoblend(damage); + int ad = damage*30/100;//int ad = damage*(player1->armourtype+1)*20/100; // let armour absorb when possible + if(ad>player1->armour) ad = player1->armour; + player1->armour -= ad; + damage -= ad; + float droll = damage/0.5f; + player1->roll += player1->roll>0 ? droll : (player1->roll<0 ? -droll : (rnd(2) ? droll : -droll)); // give player a kick depending on amount of damage + if((player1->health -= damage)<=0) + { + if(actor==-2) + { + conoutf("you got killed by %s!", (int)&act->name); + } + else if(actor==-1) + { + actor = getclientnum(); + conoutf("you suicided!"); + addmsg(1, 2, SV_FRAGS, --player1->frags); + } + else + { + dynent *a = getclient(actor); + if(a) + { + if(isteam(a->team, player1->team)) + { + conoutf("you got fragged by a teammate (%s)", (int)a->name); + } + else + { + conoutf("you got fragged by %s", (int)a->name); + }; + }; + }; + showscores(true); + addmsg(1, 2, SV_DIED, actor); + player1->lifesequence++; + player1->attacking = false; + player1->state = CS_DEAD; + player1->oldpitch = player1->pitch; + player1->pitch = 0; + player1->roll = 60; + playsound(S_DIE1+rnd(2)); + spawnstate(player1); + player1->lastaction = lastmillis; + } + else + { + playsound(S_PAIN6); + }; +}; + +void timeupdate(int timeremain) +{ + if(!timeremain) + { + intermission = true; + player1->attacking = false; + conoutf("intermission:"); + conoutf("game has ended!"); + showscores(true); + } + else + { + conoutf("time remaining: %d minutes", timeremain); + }; +}; + +dynent *getclient(int cn) // ensure valid entity +{ + if(cn<0 || cn>=MAXCLIENTS) + { + neterr("clientnum"); + return NULL; + }; + while(cn>=players.length()) players.add(NULL); + return players[cn] ? players[cn] : (players[cn] = newdynent()); +}; + +void addc() { strcpy(getclient(1)->team, "CT"); getclient(1)->o = player1->o; strcpy(getclient(1)->name, "sucker"); } COMMAND(addc, ARG_NONE); // FIXME + +void initclient() +{ + clientmap[0] = 0; + initclientnet(); +}; + +void startmap(char *name) // called just after a map load +{ + //if(netmapstart()) { gamemode = 0;}; //needs fixed to switch modes? + netmapstart(); //should work + sleepwait = 0; + //put call to clear/restart game mode extras here + projreset(); + shotlinereset(); + spawncycle = -1; + spawnplayer(player1); + player1->frags = 0; + loopv(players) if(players[i]) players[i]->frags = 0; + resetspawns(); + strcpy_s(clientmap, name); + if(editmode) toggleedit(); + setvar("gamespeed", 100); + setvar("fog", 180); + setvar("fogcolour", 0x8099B3); + showscores(false); + intermission = false; + framesinmap = 0; + conoutf("game mode is %s", (int)modestr(gamemode)); +}; + +COMMANDN(map, changemap, ARG_1STR); + +void suicide() +{ + if (multiplayer()) conoutf("there are others willing to kill you..."); + else conoutf("this is not the answer"); + selfdamage(1000, -1, player1); + //if(d==player1) selfdamage(dm, -1, d); + //else { addmsg(1, 4, SV_DAMAGE, d, dm, d->lifesequence); playsound(S_FALL1+rnd(5), &d->o); }; + //particle_splash(3, 1000, 1000, player1->o); //edit out? + demodamage(1000, player1->o); + playsound(S_SUICIDE, &player1->o); + +}; + +COMMAND(suicide, ARG_NONE); diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 1fde141a..44be9a80 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -181,8 +181,8 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th e.x = getint(p)/DMF; e.y = getint(p)/DMF; e.z = getint(p)/DMF; - if(gun==GUN_SHOTGUN) createrays(s, e); - int nadesec = getint(p); + if(gun==GUN_SHOTGUN) createrays(s, e); + int nadesec = getint(p); printf("nadesec: %i\n", nadesec); shootv(gun, s, e, d, nadesec); break; diff --git a/source/src/command.cpp b/source/src/command.cpp index 99dd971c..e9a924ec 100644 --- a/source/src/command.cpp +++ b/source/src/command.cpp @@ -165,7 +165,7 @@ int execute(char *p, bool isdown) // all evaluation happens here, case ARG_1STR: if(isdown) ((void (__cdecl *)(char *))id->fun)(w[1]); break; case ARG_2STR: if(isdown) ((void (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; case ARG_3STR: if(isdown) ((void (__cdecl *)(char *, char *, char*))id->fun)(w[1], w[2], w[3]); break; - case ARG_5STR: if(isdown) ((void (__cdecl *)(char *, char *, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5]); break; + case ARG_5STR: if(isdown) ((void (__cdecl *)(char *, char *, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5]); break; case ARG_6STR: if(isdown) ((void (__cdecl *)(char *, char *, char*, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5], w[6]); break; case ARG_DOWN: ((void (__cdecl *)(bool))id->fun)(isdown); break; case ARG_DWN1: ((void (__cdecl *)(bool, char *))id->fun)(isdown, w[1]); break; diff --git a/source/src/cube.h b/source/src/cube.h index 0e270faf..3b7b0207 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -165,18 +165,18 @@ struct dynent // players & monsters bool reloading, hasarmour; int primary; //primary gun int nextprimary; - md3state animstate[3]; - - int gravity; + md3state animstate[3]; + + int gravity; bool bounce; -}; - -struct physent : dynent // inheritance: tons of unused stuff, but makes life easier -{ - int millis, lifetime; - bool throwed; - bool enabled; - dynent *owner; +}; + +struct physent : dynent // inheritance: tons of unused stuff, but makes life easier +{ + int millis, lifetime; + bool throwed; + bool enabled; + dynent *owner; }; #define SAVEGAMEVERSION 6 // bump if dynent/netprotocol changes or any other savegame/demo data bumped from 5 @@ -243,7 +243,7 @@ extern header hdr; // current map header extern int sfactor, ssize; // ssize = 2^sfactor extern int cubicsize, mipsize; // cubicsize = ssize^2 extern dynent *player1; // special client ent that receives input and acts as camera -extern dvector players; // all the other clients (in multiplayer) +extern dvector players; // all the other clients (in multiplayer) extern vectorphysents; extern bool editmode; extern vector ents; // map entities diff --git a/source/src/entities.cpp b/source/src/entities.cpp index 1207f368..d5b7c4f2 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -1,257 +1,257 @@ -// entities.cpp: map entity related functions (pickup etc.) - -#include "cube.h" - -vector ents; - -char *entmdlnames[] = -{ -//FIXME : fix the "pickups" infront - "pickups/pistolclips", "pickups/ammobox", "ammobox" /*grenade*/, "pickups/health", "pickups/kevlar", "pickups/duals" /*dual pistols*/, -}; - -int triggertime = 0; - -void renderent(entity &e, char *mdlname, float z, float yaw, int frame = 0, int numf = 1, int basetime = 0, float speed = 10.0f) -{ - rendermodel(mdlname, frame, numf, 0, 1.1f, e.x, z+S(e.x, e.y)->floor, e.y, yaw, 0, false, 1.0f, speed, 0, basetime); -}; - -void renderentities() -{ - if(lastmillis>triggertime+1000) triggertime = 0; - loopv(ents) - { - entity &e = ents[i]; - if(e.type==MAPMODEL) - { - mapmodelinfo &mmi = getmminfo(e.attr2); - if(!&mmi) continue; - rendermodel(mmi.name, 0, 1, e.attr4, (float)mmi.rad, e.x, (float)S(e.x, e.y)->floor+mmi.zoff+e.attr3, e.y, (float)((e.attr1+7)-(e.attr1+7)%15), 0, false, 1.0f, 10.0f, mmi.snap); - } - else - { - if(OUTBORD(e.x, e.y)) continue; - if(e.type!=CARROT) - { - if(!e.spawned) continue; - if(e.typeI_QUAD) continue; - renderent(e, entmdlnames[e.type-I_CLIPS], (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/10.0f); - } - else switch(e.attr2) - { - case 1: - case 3: - continue; - - case 2: - case 0: - if(!e.spawned) continue; - renderent(e, "carrot", (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/(e.attr2 ? 1.0f : 10.0f)); - break; - - case 4: renderent(e, "switch2", 3, (float)e.attr3*90, (!e.spawned && !triggertime) ? 1 : 0, (e.spawned || !triggertime) ? 1 : 2, triggertime, 1050.0f); break; - case 5: renderent(e, "switch1", -0.15f, (float)e.attr3*90, (!e.spawned && !triggertime) ? 30 : 0, (e.spawned || !triggertime) ? 1 : 30, triggertime, 35.0f); break; - }; - }; - }; -}; - -struct itemstat { int add, start, max, sound; } itemstats[] = -{ - 16, 24, 36, S_ITEMAMMO, //pistol - 7, 7, 14, S_ITEMAMMO, //shotgun - 30, 60, 90, S_ITEMAMMO, //subgun - 5, 10, 15, S_ITEMAMMO, //sniper - 20, 20, 40, S_ITEMAMMO, //assult - 1, 0, 2, S_ITEMAMMO, //grenade - 33, 100, 100, S_ITEMHEALTH, //health - 50, 100, 100, S_ITEMARMOUR, //armour - 20000, 0, 30000, S_ITEMPUP, //powerup -}; - -void baseammo(int gun) { player1->ammo[gun] = itemstats[gun-1].add*2; }; - -// these two functions are called when the server acknowledges that you really -// picked up the item (in multiplayer someone may grab it before you). - -/* -void radditem(int i, int &v) -{ - itemstat &is = itemstats[ents[i].type-I_CLIPS]; - ents[i].spawned = false; - v += is.add; - if(v>is.max) v = is.max; - playsoundc(is.sound); -}; -*/ - -void radditem(int i, int &v, int t) -{ - itemstat &is = itemstats[t-1]; - ents[i].spawned = false; - v += is.add; - if(v>is.max) v = is.max; - playsoundc(is.sound); -}; - -void realpickup(int n, dynent *d) -{ - switch(ents[n].type) - { - //case I_PISTOL: radditem(n, d->ammo[1]); break; - //case I_SHOTGUN: radditem(n, d->ammo[2]); break; - //case I_SUBGUN: radditem(n, d->ammo[3]); break; - //case I_SNIPER: radditem(n, d->ammo[4]); break; - //case I_ASSULT: radditem(n, d->ammo[5]); break; - case I_CLIPS: radditem(n, d->ammo[1], 1); break; - case I_AMMO: radditem(n, d->ammo[d->primary], d->primary); break; - case I_GRENADE: radditem(n, d->ammo[6], 6); break; - case I_HEALTH: radditem(n, d->health, 7); break; - - case I_ARMOUR: - radditem(n, d->armour, 8); - //d->hasarmour = true; - break; - - case I_QUAD: - //radditem(n, d->quadmillis, 9); - conoutf("a lesser man would use a single pistol"); - break; - }; -}; - -// these functions are called when the client touches the item - -/* -void additem(int i, int &v, int spawnsec) -{ - if(v4 ? 2 : 3); // spawn times are dependent on number of players - int ammo = np*2; - switch(ents[n].type) - { - //case I_PISTOL: additem(n, d->ammo[1], ammo); break; - //case I_SHOTGUN: additem(n, d->ammo[2], ammo); break; - //case I_SUBGUN: additem(n, d->ammo[3], ammo); break; - //case I_SNIPER: additem(n, d->ammo[4], ammo); break; - //case I_ASSULT: additem(n, d->ammo[5], ammo); break; - case I_CLIPS: additem(n, d->ammo[1], ammo, 1); break; - case I_AMMO: additem(n, d->ammo[d->primary], ammo, d->primary); break; - case I_GRENADE: additem(n, d->ammo[6], ammo, 6); break; - case I_HEALTH: additem(n, d->health, np*5, 7); break; - - case I_ARMOUR: - additem(n, d->armour, 20, 8); - break; - - case I_QUAD: - //additem(n, d->quadmillis, 60, 9); - break; - - case CARROT: - ents[n].spawned = false; - triggertime = lastmillis; - trigger(ents[n].attr1, ents[n].attr2, false); // needs to go over server for multiplayer - break; - - }; -}; - -void checkitems() -{ - if(editmode) return; - loopv(ents) - { - entity &e = ents[i]; - if(e.type==NOTUSED) continue; - if(!ents[i].spawned) continue; - if(OUTBORD(e.x, e.y)) continue; - vec v = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }; - vdist(dist, t, player1->o, v); - if(dist<2.5) pickup(i, player1); - }; -}; - -void putitems(uchar *&p) // puts items in network stream and also spawns them locally -{ - loopv(ents) if((ents[i].type>=I_CLIPS && ents[i].type<=I_QUAD) || ents[i].type==CARROT) - { - putint(p, i); - ents[i].spawned = true; - }; -}; - -void resetspawns() { loopv(ents) ents[i].spawned = false; }; -void setspawn(int i, bool on) { if(inextprimary!=i) d->ammo[i] = 0; - d->mag[GUN_KNIFE] = 1; - - d->mag[GUN_PISTOL] = 8; - d->ammo[GUN_PISTOL] = 24; - - if (m_pistol || d->primary==GUN_PISTOL) // || pistol only mode! - { - d->primary = GUN_PISTOL; - d->ammo[d->primary] = 72; - } - else if (d->primary>GUN_PISTOL && d->primaryammo[d->primary] = itemstats[d->primary-1].max; - else - d->ammo[d->primary] = itemstats[d->primary-1].start; - - d->mag[d->primary] = itemstats[d->primary-1].add; - } - -/* - if (d->primary==GUN_GRENADE) - { - //conoutf("you don't have to worry about blowing your hand off just yet..."); - } -*/ - if (d->hasarmour) - { - if(gamemode==m_arena) - d->armour = 100; - } - - d->gunselect = d->primary; //draw main weapon -}; - -void item(int num) -{ - if (num>0 && num<7) player1->nextprimary = num; - - //if (num==6) conoutf("sorry, no nades yet"); - - if (num==7) { player1->hasarmour=!player1->hasarmour; /*toggle armour*/ }; - -}; - -COMMAND(item,ARG_1INT); +// entities.cpp: map entity related functions (pickup etc.) + +#include "cube.h" + +vector ents; + +char *entmdlnames[] = +{ +//FIXME : fix the "pickups" infront + "pickups/pistolclips", "pickups/ammobox", "ammobox" /*grenade*/, "pickups/health", "pickups/kevlar", "pickups/duals" /*dual pistols*/, +}; + +int triggertime = 0; + +void renderent(entity &e, char *mdlname, float z, float yaw, int frame = 0, int numf = 1, int basetime = 0, float speed = 10.0f) +{ + rendermodel(mdlname, frame, numf, 0, 1.1f, e.x, z+S(e.x, e.y)->floor, e.y, yaw, 0, false, 1.0f, speed, 0, basetime); +}; + +void renderentities() +{ + if(lastmillis>triggertime+1000) triggertime = 0; + loopv(ents) + { + entity &e = ents[i]; + if(e.type==MAPMODEL) + { + mapmodelinfo &mmi = getmminfo(e.attr2); + if(!&mmi) continue; + rendermodel(mmi.name, 0, 1, e.attr4, (float)mmi.rad, e.x, (float)S(e.x, e.y)->floor+mmi.zoff+e.attr3, e.y, (float)((e.attr1+7)-(e.attr1+7)%15), 0, false, 1.0f, 10.0f, mmi.snap); + } + else + { + if(OUTBORD(e.x, e.y)) continue; + if(e.type!=CARROT) + { + if(!e.spawned) continue; + if(e.typeI_QUAD) continue; + renderent(e, entmdlnames[e.type-I_CLIPS], (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/10.0f); + } + else switch(e.attr2) + { + case 1: + case 3: + continue; + + case 2: + case 0: + if(!e.spawned) continue; + renderent(e, "carrot", (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/(e.attr2 ? 1.0f : 10.0f)); + break; + + case 4: renderent(e, "switch2", 3, (float)e.attr3*90, (!e.spawned && !triggertime) ? 1 : 0, (e.spawned || !triggertime) ? 1 : 2, triggertime, 1050.0f); break; + case 5: renderent(e, "switch1", -0.15f, (float)e.attr3*90, (!e.spawned && !triggertime) ? 30 : 0, (e.spawned || !triggertime) ? 1 : 30, triggertime, 35.0f); break; + }; + }; + }; +}; + +struct itemstat { int add, start, max, sound; } itemstats[] = +{ + 16, 24, 36, S_ITEMAMMO, //pistol + 7, 7, 14, S_ITEMAMMO, //shotgun + 30, 60, 90, S_ITEMAMMO, //subgun + 5, 10, 15, S_ITEMAMMO, //sniper + 20, 20, 40, S_ITEMAMMO, //assult + 1, 0, 2, S_ITEMAMMO, //grenade + 33, 100, 100, S_ITEMHEALTH, //health + 50, 100, 100, S_ITEMARMOUR, //armour + 20000, 0, 30000, S_ITEMPUP, //powerup +}; + +void baseammo(int gun) { player1->ammo[gun] = itemstats[gun-1].add*2; }; + +// these two functions are called when the server acknowledges that you really +// picked up the item (in multiplayer someone may grab it before you). + +/* +void radditem(int i, int &v) +{ + itemstat &is = itemstats[ents[i].type-I_CLIPS]; + ents[i].spawned = false; + v += is.add; + if(v>is.max) v = is.max; + playsoundc(is.sound); +}; +*/ + +void radditem(int i, int &v, int t) +{ + itemstat &is = itemstats[t-1]; + ents[i].spawned = false; + v += is.add; + if(v>is.max) v = is.max; + playsoundc(is.sound); +}; + +void realpickup(int n, dynent *d) +{ + switch(ents[n].type) + { + //case I_PISTOL: radditem(n, d->ammo[1]); break; + //case I_SHOTGUN: radditem(n, d->ammo[2]); break; + //case I_SUBGUN: radditem(n, d->ammo[3]); break; + //case I_SNIPER: radditem(n, d->ammo[4]); break; + //case I_ASSULT: radditem(n, d->ammo[5]); break; + case I_CLIPS: radditem(n, d->ammo[1], 1); break; + case I_AMMO: radditem(n, d->ammo[d->primary], d->primary); break; + case I_GRENADE: radditem(n, d->ammo[6], 6); break; + case I_HEALTH: radditem(n, d->health, 7); break; + + case I_ARMOUR: + radditem(n, d->armour, 8); + //d->hasarmour = true; + break; + + case I_QUAD: + //radditem(n, d->quadmillis, 9); + conoutf("a lesser man would use a single pistol"); + break; + }; +}; + +// these functions are called when the client touches the item + +/* +void additem(int i, int &v, int spawnsec) +{ + if(v4 ? 2 : 3); // spawn times are dependent on number of players + int ammo = np*2; + switch(ents[n].type) + { + //case I_PISTOL: additem(n, d->ammo[1], ammo); break; + //case I_SHOTGUN: additem(n, d->ammo[2], ammo); break; + //case I_SUBGUN: additem(n, d->ammo[3], ammo); break; + //case I_SNIPER: additem(n, d->ammo[4], ammo); break; + //case I_ASSULT: additem(n, d->ammo[5], ammo); break; + case I_CLIPS: additem(n, d->ammo[1], ammo, 1); break; + case I_AMMO: additem(n, d->ammo[d->primary], ammo, d->primary); break; + case I_GRENADE: additem(n, d->ammo[6], ammo, 6); break; + case I_HEALTH: additem(n, d->health, np*5, 7); break; + + case I_ARMOUR: + additem(n, d->armour, 20, 8); + break; + + case I_QUAD: + //additem(n, d->quadmillis, 60, 9); + break; + + case CARROT: + ents[n].spawned = false; + triggertime = lastmillis; + trigger(ents[n].attr1, ents[n].attr2, false); // needs to go over server for multiplayer + break; + + }; +}; + +void checkitems() +{ + if(editmode) return; + loopv(ents) + { + entity &e = ents[i]; + if(e.type==NOTUSED) continue; + if(!ents[i].spawned) continue; + if(OUTBORD(e.x, e.y)) continue; + vec v = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }; + vdist(dist, t, player1->o, v); + if(dist<2.5) pickup(i, player1); + }; +}; + +void putitems(uchar *&p) // puts items in network stream and also spawns them locally +{ + loopv(ents) if((ents[i].type>=I_CLIPS && ents[i].type<=I_QUAD) || ents[i].type==CARROT) + { + putint(p, i); + ents[i].spawned = true; + }; +}; + +void resetspawns() { loopv(ents) ents[i].spawned = false; }; +void setspawn(int i, bool on) { if(inextprimary!=i) d->ammo[i] = 0; + d->mag[GUN_KNIFE] = 1; + + d->mag[GUN_PISTOL] = 8; + d->ammo[GUN_PISTOL] = 24; + + if (m_pistol || d->primary==GUN_PISTOL) // || pistol only mode! + { + d->primary = GUN_PISTOL; + d->ammo[d->primary] = 72; + } + else if (d->primary>GUN_PISTOL && d->primaryammo[d->primary] = itemstats[d->primary-1].max; + else + d->ammo[d->primary] = itemstats[d->primary-1].start; + + d->mag[d->primary] = itemstats[d->primary-1].add; + } + +/* + if (d->primary==GUN_GRENADE) + { + //conoutf("you don't have to worry about blowing your hand off just yet..."); + } +*/ + if (d->hasarmour) + { + if(gamemode==m_arena) + d->armour = 100; + } + + d->gunselect = d->primary; //draw main weapon +}; + +void item(int num) +{ + if (num>0 && num<7) player1->nextprimary = num; + + //if (num==6) conoutf("sorry, no nades yet"); + + if (num==7) { player1->hasarmour=!player1->hasarmour; /*toggle armour*/ }; + +}; + +COMMAND(item,ARG_1INT); diff --git a/source/src/main.cpp b/source/src/main.cpp index 4a27e4c8..85cbea38 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -70,8 +70,8 @@ void screenshot() COMMAND(screenshot, ARG_NONE); COMMAND(quit, ARG_NONE); - -int minfps, maxfps; + +int minfps, maxfps; void fpsrange(int low, int high) { @@ -180,9 +180,9 @@ int main(int argc, char **argv) exec("config/sounds.cfg"); exec("config/servers.cfg"); exec("config/autoexec.cfg"); - //exec("config/default_map_settings.cfg"); - - log("base models"); + //exec("config/default_map_settings.cfg"); + + log("base models"); loadweapons(); log("localconnect"); @@ -203,7 +203,7 @@ int main(int argc, char **argv) fps = (1000.0f/curtime+fps*50)/51; computeraytable(player1->o.x, player1->o.y); readdepth(scr_w, scr_h); - SDL_GL_SwapBuffers(); + SDL_GL_SwapBuffers(); extern void updatevol(); updatevol(); if(framesinmap++<5) // cheap hack to get rid of initial sparklies, even when triple buffering etc. { diff --git a/source/src/menus.cpp b/source/src/menus.cpp index cff6dc56..e5dbf2e8 100644 --- a/source/src/menus.cpp +++ b/source/src/menus.cpp @@ -1,7 +1,7 @@ // menus.cpp: ingame menu system (also used for scores and serverlist) -#include "cube.h" - +#include "cube.h" + #define FIRSTMENU 90 struct mitem { char *text, *action, *hoveraction; }; @@ -11,9 +11,9 @@ struct gmenu char *name; vector items; int mwidth; - int menusel; - int tex; // (optional) bg tex - char *mdl; // (optional) md2 mdl + int menusel; + int tex; // (optional) bg tex + char *mdl; // (optional) md2 mdl int frame, range, rotspeed, scale; }; @@ -87,34 +87,34 @@ bool rendermenu() y += step; }; return true; -}; - -void rendermenumdl() // nasty s*** -{ +}; + +void rendermenumdl() // nasty s*** +{ if(vmenu<0) { menustack.setsize(0); return false; }; - if(vmenu==1) refreshservers(); - gmenu &m = menus[vmenu]; - if(!m.mdl) return; - - glPushMatrix (); - + if(vmenu==1) refreshservers(); + gmenu &m = menus[vmenu]; + if(!m.mdl) return; + + glPushMatrix (); + glTranslatef(player1->o.x, player1->o.z, player1->o.y); glRotatef(player1->yaw+90+180, 0, -1, 0); - glRotatef(player1->pitch, 0, 0, 1); - - glTranslatef(2, 0, 0); - if(m.rotspeed) glRotatef(lastmillis/5.0f/100.0f*m.rotspeed, 0, 1, 0); - - rendermodel(m.mdl, m.frame, m.range, 0, 5.0f, 0, 0, 0, 0, 0, false, (float)(m.scale ? m.scale/25.0f : 1), 100, 0, 0); - glPopMatrix(); + glRotatef(player1->pitch, 0, 0, 1); + + glTranslatef(2, 0, 0); + if(m.rotspeed) glRotatef(lastmillis/5.0f/100.0f*m.rotspeed, 0, 1, 0); + + rendermodel(m.mdl, m.frame, m.range, 0, 5.0f, 0, 0, 0, 0, 0, false, (float)(m.scale ? m.scale/25.0f : 1), 100, 0, 0); + glPopMatrix(); } void newmenu(char *name) { gmenu &menu = menus.add(); menu.name = newstring(name); - menu.menusel = 0; - menu.tex = -1; + menu.menusel = 0; + menu.tex = -1; menu.mdl = NULL; }; @@ -131,59 +131,59 @@ void menuitem(char *text, char *action, char *hoveraction) gmenu &menu = menus.last(); mitem &mi = menu.items.add(); mi.text = newstring(text); - mi.action = action[0] ? newstring(action) : mi.text; - if(hoveraction) mi.hoveraction = newstring(hoveraction); + mi.action = action[0] ? newstring(action) : mi.text; + if(hoveraction) mi.hoveraction = newstring(hoveraction); else mi.hoveraction = NULL; }; - -void menubg(char *tex) -{ - gmenu &menu = menus.last(); - int curmtex = 0; - loopv(menus) curmtex = max(curmtex, menus[i].tex); - if(curmtex>=100) return; - sprintf_sd(path)("packages/%s", tex); - int xs, ys; - if(installtex(FIRSTMENU + curmtex, path, xs, ys, false, true)) - menu.tex = curmtex; -}; - -void menumdl(char *mdl, char *frame, char *range, char *rotspeed, char *scale) -{ - if(!mdl || !frame || !range) return; - gmenu &menu = menus.last(); - menu.mdl = newstring(mdl, _MAXDEFSTR); - menu.frame = max(0, atoi(frame)); - menu.range = max(1, atoi(range)); - menu.rotspeed = max(0, min(atoi(rotspeed), 100)); - menu.scale = max(0, min(atoi(scale), 100)); -}; - -void chmenumdl(char *menu, char *mdl, char *frame, char *range, char *rotspeed, char *scale) -{ - if(!menu || !mdl) return; - loopv(menus) - { - gmenu &m = menus[i]; - if(strcmp(m.name, menu) == 0) - { - if(m.mdl) strcpy_s(m.mdl, mdl); - else m.mdl = newstring(mdl, _MAXDEFSTR); - m.frame = atoi(frame); - m.range = atoi(range); - m.rotspeed = max(0, min(atoi(rotspeed), 100)); - m.scale = max(0, min(atoi(scale), 100)); - return; - }; - }; -}; - + +void menubg(char *tex) +{ + gmenu &menu = menus.last(); + int curmtex = 0; + loopv(menus) curmtex = max(curmtex, menus[i].tex); + if(curmtex>=100) return; + sprintf_sd(path)("packages/%s", tex); + int xs, ys; + if(installtex(FIRSTMENU + curmtex, path, xs, ys, false, true)) + menu.tex = curmtex; +}; + +void menumdl(char *mdl, char *frame, char *range, char *rotspeed, char *scale) +{ + if(!mdl || !frame || !range) return; + gmenu &menu = menus.last(); + menu.mdl = newstring(mdl, _MAXDEFSTR); + menu.frame = max(0, atoi(frame)); + menu.range = max(1, atoi(range)); + menu.rotspeed = max(0, min(atoi(rotspeed), 100)); + menu.scale = max(0, min(atoi(scale), 100)); +}; + +void chmenumdl(char *menu, char *mdl, char *frame, char *range, char *rotspeed, char *scale) +{ + if(!menu || !mdl) return; + loopv(menus) + { + gmenu &m = menus[i]; + if(strcmp(m.name, menu) == 0) + { + if(m.mdl) strcpy_s(m.mdl, mdl); + else m.mdl = newstring(mdl, _MAXDEFSTR); + m.frame = atoi(frame); + m.range = atoi(range); + m.rotspeed = max(0, min(atoi(rotspeed), 100)); + m.scale = max(0, min(atoi(scale), 100)); + return; + }; + }; +}; + COMMAND(menuitem, ARG_3STR); COMMAND(showmenu, ARG_1STR); -COMMAND(newmenu, ARG_1STR); -COMMAND(menubg, ARG_1STR); -COMMAND(menumdl, ARG_5STR); +COMMAND(newmenu, ARG_1STR); +COMMAND(menubg, ARG_1STR); +COMMAND(menumdl, ARG_5STR); COMMAND(chmenumdl, ARG_6STR); bool menukey(int code, bool isdown) @@ -191,7 +191,7 @@ bool menukey(int code, bool isdown) if(vmenu<=0) return false; int menusel = menus[vmenu].menusel; if(isdown) - { + { int oldmenusel = menusel; if(code==SDLK_ESCAPE) { @@ -204,7 +204,7 @@ bool menukey(int code, bool isdown) int n = menus[vmenu].items.length(); if(menusel<0) menusel = n-1; else if(menusel>=n) menusel = 0; - menus[vmenu].menusel = menusel; + menus[vmenu].menusel = menusel; if(menusel != oldmenusel)execute(menus[vmenu].items[menusel].hoveraction, true); } else diff --git a/source/src/physics.cpp b/source/src/physics.cpp index 6ffbd38c..1d2ee5db 100644 --- a/source/src/physics.cpp +++ b/source/src/physics.cpp @@ -132,9 +132,9 @@ bool collide(dynent *d, bool spawn, float drop, float rise) const float space = d->o.z-d->eyeheight-lo; if(space<0) { - if(space>-0.01) - { - d->o.z = lo+d->eyeheight; // stick on step + if(space>-0.01) + { + d->o.z = lo+d->eyeheight; // stick on step } else if(space>-1.26f) d->o.z += rise; // rise thru stair else return false; @@ -200,7 +200,7 @@ void moveplayer(dynent *pl, int moveres, bool local, int curtime) d.x = (float)(pl->move*cos(rad(pl->yaw-90))); d.y = (float)(pl->move*sin(rad(pl->yaw-90))); - d.z = (float) pl->bounce ? pl->vel.z : 0; + d.z = (float) pl->bounce ? pl->vel.z : 0; if(floating || water) { @@ -249,8 +249,8 @@ void moveplayer(dynent *pl, int moveres, bool local, int curtime) if(pl->jumpnext) { - pl->jumpnext = false; - pl->vel.z = 1.7f; // physics impulse upwards + pl->jumpnext = false; + pl->vel.z = 1.7f; // physics impulse upwards if(water) { pl->vel.x /= 8; pl->vel.y /= 8; }; // dampen velocity change even harder, gives correct water feel if(local) playsoundc(S_JUMP); else if(pl->monsterstate) playsound(S_JUMP, &pl->o); @@ -260,7 +260,7 @@ void moveplayer(dynent *pl, int moveres, bool local, int curtime) if(local) playsoundc(S_LAND); else if(pl->monsterstate) playsound(S_LAND, &pl->o); }; - pl->timeinair = 0; + pl->timeinair = 0; if(pl->bounce) pl->vel.z *= 0.7f; } else @@ -270,11 +270,10 @@ void moveplayer(dynent *pl, int moveres, bool local, int curtime) const float gravity = pl->bounce ? pl->gravity : 20; const float f = 1.0f/moveres; - float dropf = ((gravity-1)+pl->timeinair/15.0f); // incorrect, but works fine + float dropf = pl->bounce ? ((gravity-1)+pl->timeinair/14.0f) : ((gravity-1)+pl->timeinair/15.0f); // incorrect, but works fine if(water) { dropf = 5; pl->timeinair = 0; }; // float slowly down in water - const float drop = dropf*curtime/gravity/100/moveres; // at high fps, gravity kicks in too fast - const float rise = speed/moveres/1.2f; // extra smoothness when lifting up stairs - + float drop = dropf*curtime/gravity/100/moveres; // at high fps, gravity kicks in too fast + const float rise = speed/moveres/1.2f; // extra smoothness when lifting up stairs loopi(moveres) // discrete steps collision detection & sliding { // try move forward @@ -285,30 +284,30 @@ void moveplayer(dynent *pl, int moveres, bool local, int curtime) // player stuck, try slide along y axis pl->blocked = true; pl->o.x -= f*d.x; - if(collide(pl, false, drop, rise)) - { - d.x = 0; - if(pl->bounce) pl->vel.x = -pl->vel.x; - continue; + if(collide(pl, false, drop, rise)) + { + d.x = 0; + if(pl->bounce) pl->vel.x = -pl->vel.x; + continue; }; pl->o.x += f*d.x; // still stuck, try x axis pl->o.y -= f*d.y; - if(collide(pl, false, drop, rise)) - { - d.y = 0; - if(pl->bounce) pl->vel.y = -pl->vel.y; - continue; + if(collide(pl, false, drop, rise)) + { + d.y = 0; + if(pl->bounce) pl->vel.y = -pl->vel.y; + continue; }; pl->o.y += f*d.y; // try just dropping down pl->moving = false; pl->o.x -= f*d.x; pl->o.y -= f*d.y; - if(collide(pl, false, drop, rise)) - { - d.y = d.x = 0; - continue; + if(collide(pl, false, drop, rise)) + { + d.y = d.x = 0; + continue; }; pl->o.z -= f*d.z; break; @@ -347,27 +346,27 @@ void moveplayer(dynent *pl, int moveres, bool local, int curtime) if(!pl->inwater && water) { playsound(S_SPLASH2, &pl->o); pl->vel.z = 0; } else if(pl->inwater && !water) playsound(S_SPLASH1, &pl->o); pl->inwater = water; -}; +}; void moveplayer(dynent *pl, int moveres, bool local) { loopi(physicsrepeat) moveplayer(pl, moveres, local, i ? curtime/physicsrepeat : curtime-curtime/physicsrepeat*(physicsrepeat-1)); }; - -void movephysent(physent *pl) -{ - int moveres = 2; - bool local=true; - loopj(physicsrepeat) + +void movephysent(physent *pl) +{ + int moveres = 2; + bool local=true; + loopj(physicsrepeat) { - const bool water = hdr.waterlevel>pl->o.z-0.5f; + const bool water = hdr.waterlevel>pl->o.z-0.5f; const bool floating = (editmode && local); vec d; // vector of direction we ideally want to move in - - d = pl->vel; - - + + d = pl->vel; + + const float speed = curtime/(water ? 2000.0f : 1000.0f)*pl->maxspeed; const float friction = water ? 20.0f : (pl->onfloor || floating ? 6.0f : 30.0f); @@ -377,7 +376,7 @@ void movephysent(physent *pl) vadd(pl->vel, d); vdiv(pl->vel, fpsfric); d = pl->vel; - vmul(d, speed); // d is now frametime based velocity vector + vmul(d, speed); // d is now frametime based velocity vector /* if(floating || water) @@ -386,7 +385,7 @@ void movephysent(physent *pl) d.y *= (float)cos(rad(pl->pitch)); d.z = (float)(pl->move*sin(rad(pl->pitch))); };*/ - + /*if(pl->onfloor || water) { if(pl->jumpnext) @@ -406,33 +405,33 @@ void movephysent(physent *pl) else { pl->timeinair += curtime; - };*/ - - if(pl->onfloor) - { - pl->timeinair = 0; - vmul(pl->vel, 0.8f); - } - else - { - pl->timeinair += curtime; - pl->pitch = (lastmillis-pl->millis) % 360; - pl->yaw = pl->pitch; - }; - + };*/ + + if(pl->onfloor) + { + pl->timeinair = 0; + vmul(pl->vel, 0.8f); + } + else + { + pl->timeinair += curtime; + pl->pitch = (lastmillis-pl->millis) % 360; + pl->yaw = pl->pitch; + }; + //const float speed = curtime/(water ? 2000.0f : 1000.0f)*pl->maxspeed; - + const float gravity = 20; const float f = 1.0f/moveres; float dropf = pl->timeinair/15.0f; // incorrect, but works fine if(water) { dropf = 5; pl->timeinair = 0; }; // float slowly down in water const float drop = dropf*curtime/gravity/100/moveres; // at high fps, gravity kicks in too fast const float rise = speed/moveres/1.2f; // extra smoothness when lifting up stairs -// if(pl->mtype = M_NADE)printf("%i\n", pl->onfloor); -/* d.x -= d.x/50.0f*curtime; - d.y -= d.y/50.0f*curtime;*/ - +// if(pl->mtype = M_NADE)printf("%i\n", pl->onfloor); +/* d.x -= d.x/50.0f*curtime; + d.y -= d.y/50.0f*curtime;*/ + loopi(moveres) // discrete steps collision detection & sliding { @@ -460,8 +459,8 @@ void movephysent(physent *pl) }; // detect wether player is outside map, used for skipping zbuffer clear mostly - - + + if(pl->o.x < 0 || pl->o.x >= ssize || pl->o.y <0 || pl->o.y > ssize) { @@ -479,6 +478,6 @@ void movephysent(physent *pl) if(!pl->inwater && water) { playsound(S_SPLASH2, &pl->o); pl->vel.z = 0; } else if(pl->inwater && !water) playsound(S_SPLASH1, &pl->o); - pl->inwater = water; - }; + pl->inwater = water; + }; }; diff --git a/source/src/protos.h b/source/src/protos.h index a73d7ae1..64473745 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -26,7 +26,7 @@ extern void menuset(int menu); extern void menumanual(int m, int n, char *text); extern void sortmenu(int start, int num); extern bool menukey(int code, bool isdown); -extern void newmenu(char *name); +extern void newmenu(char *name); extern void rendermenumdl(); // serverbrowser @@ -160,10 +160,10 @@ extern void renderspheres(int time); extern void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater); extern void readdepth(int w, int h); extern void blendbox(int x1, int y1, int x2, int y2, bool border, int tex = -1); -extern void damageblend(int n); -extern void addshotline(dynent *d, vec &from, vec &to); -extern void rendershotlines(); -extern void shotlinereset(); +extern void damageblend(int n); +extern void addshotline(dynent *d, vec &from, vec &to); +extern void rendershotlines(); +extern void shotlinereset(); extern void renderphysents(); // renderparticles @@ -190,7 +190,7 @@ extern void moveplayer(dynent *pl, int moveres, bool local); extern bool collide(dynent *d, bool spawn, float drop, float rise); extern void entinmap(dynent *d); extern void setentphysics(int mml, int mmr); -extern void physicsframe(); +extern void physicsframe(); extern void movephysent(physent *pl); // sound @@ -204,7 +204,7 @@ extern void rendermodel(char *mdl, int frame, int range, int tex, float rad, flo extern mapmodelinfo &getmminfo(int i); // rendermd3 -extern void rendermd3gun(); +extern void rendermd3gun(); extern void loadweapons(); // server @@ -234,8 +234,8 @@ extern void createrays(vec &from, vec &to); extern void moveprojectiles(float time); extern void projreset(); extern dynent *playerincrosshair(); -extern int reloadtime(int gun); -extern int attackdelay(int gun); +extern int reloadtime(int gun); +extern int attackdelay(int gun); extern int magsize(int gun); extern int kick_rot(int gun); extern int kick_back(int gun); diff --git a/source/src/renderextras.cpp b/source/src/renderextras.cpp index e6fdcd35..2bd355bf 100644 --- a/source/src/renderextras.cpp +++ b/source/src/renderextras.cpp @@ -45,18 +45,18 @@ void dot(int x, int y, float z) void blendbox(int x1, int y1, int x2, int y2, bool border, int tex=-1) { glDepthMask(GL_FALSE); - glDisable(GL_TEXTURE_2D); - if(tex>=0) - { - glBindTexture(GL_TEXTURE_2D, tex); - glEnable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - } - else - { + glDisable(GL_TEXTURE_2D); + if(tex>=0) + { + glBindTexture(GL_TEXTURE_2D, tex); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + } + else + { if(border) glColor3d(0.7, 0.7, 0.7); //glColor3d(0.5, 0.3, 0.4); - else glColor3d(1.0, 1.0, 1.0); - glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + else glColor3d(1.0, 1.0, 1.0); + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); }; glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2i(x1, y1); @@ -64,7 +64,7 @@ void blendbox(int x1, int y1, int x2, int y2, bool border, int tex=-1) glTexCoord2f(1, 1); glVertex2i(x2, y2); glTexCoord2f(0, 1); glVertex2i(x1, y2); glEnd(); - glDisable(GL_BLEND); + glDisable(GL_BLEND); if(tex>=0) glDisable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glBegin(GL_POLYGON); @@ -272,10 +272,10 @@ int dblend = 0; void damageblend(int n) { dblend += n; }; VAR(hidestats, 0, 0, 1); -VAR(crosshairfx, 0, 1, 1); +VAR(crosshairfx, 0, 1, 1); void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater) -{ +{ readmatrices(); if(editmode) @@ -315,12 +315,12 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat dynent *player = playerincrosshair(); if(command) draw_textf("> %s_", 20, 1570, 2, (int)command); else if(closeent[0]) draw_text(closeent, 20, 1570, 2); - else if(player) - { - string plinfo; - if(isteam(player->team, player1->team)) sprintf_s(plinfo)("%s (health:%i armour:%i)", player->name, player->health, player->armour); - else sprintf_s(plinfo)("%s", player->name); - draw_text(plinfo, 20, 1570, 2); + else if(player) + { + string plinfo; + if(isteam(player->team, player1->team)) sprintf_s(plinfo)("%s (health:%i armour:%i)", player->name, player->health, player->armour); + else sprintf_s(plinfo)("%s", player->name); + draw_text(plinfo, 20, 1570, 2); }; renderscores(); @@ -384,29 +384,29 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat glPopMatrix(); glPushMatrix(); glOrtho(0, VIRTW, VIRTH, 0, -1, 1); - glDisable(GL_BLEND); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(1.0f, 1.0f, 1.0f, (float) 0.2f+(sin(lastmillis/100)+1)/2); - - if(player1->health <= 20) - { - glEnable(GL_BLEND); - drawicon(64, 0, 20, 1650); //drawicon(128, 128, 20, 1650); - glDisable(GL_BLEND); + glDisable(GL_BLEND); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1.0f, 1.0f, 1.0f, (float) 0.2f+(sin(lastmillis/100)+1)/2); + + if(player1->health <= 20) + { + glEnable(GL_BLEND); + drawicon(64, 0, 20, 1650); //drawicon(128, 128, 20, 1650); + glDisable(GL_BLEND); } else drawicon(64, 0, 20, 1650); //drawicon(128, 128, 20, 1650); if(player1->armour) drawicon((float)(128), 0, 620, 1650); //if(player1->armour) drawicon((float)(player1->armourtype*64), 0, 620, 1650); int g = player1->gunselect; int r = 64; - if(g>2) { g -= 3; r = 128; }; + if(g>2) { g -= 3; r = 128; }; - if(!player1->mag[player1->gunselect] && player1->gunselect != GUN_KNIFE && player1->gunselect != GUN_GRENADE) - { - glEnable(GL_BLEND); - drawicon((float)(g*64), (float)r, 1220, 1650); - glDisable(GL_BLEND); - } + if(!player1->mag[player1->gunselect] && player1->gunselect != GUN_KNIFE && player1->gunselect != GUN_GRENADE) + { + glEnable(GL_BLEND); + drawicon((float)(g*64), (float)r, 1220, 1650); + glDisable(GL_BLEND); + } else drawicon((float)(g*64), (float)r, 1220, 1650); glPopMatrix(); }; @@ -414,86 +414,86 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); -}; - -#define MAXSHOTLINES 100 -struct shotline -{ - vec from, to; - bool inuse; - int millis; -}; -shotline shotlines[MAXSHOTLINES]; - -void shotlinereset() { loopi(MAXSHOTLINES) shotlines[i].inuse = false; }; - -void addshotline(dynent *d, vec &from, vec &to) -{ - if(d == player1) return; - if(rnd(3) != 0) return; - loopi(MAXSHOTLINES) - { - shotline *s = &shotlines[i]; - if(s->inuse) continue; - int start = 10; - vdist(_dist, _unitv, player1->o, to); - if(_dist <= 10.0f) start = 8; - else start = 5; - - vdist(dist, unitv, from, to); - vdiv(unitv, dist); - s->inuse = true; - s->from = unitv; - vmul(s->from, dist/10*start); - vadd(s->from, from); - s->to = unitv; - vmul(s->to, dist/10*-(10-start-2)); - vadd(s->to, to); - s->millis = lastmillis; - return; - }; -}; - -VAR(shotline_duration, 0, 75, 10000); - -void rendershotlines() -{ - loopi(MAXSHOTLINES) - { - shotline *s = &shotlines[i]; - if(!s->inuse) continue; - if(lastmillis-s->millis > shotline_duration) { s->inuse = false; continue; } - glPushMatrix(); - glDisable(GL_TEXTURE_2D); - +}; + +#define MAXSHOTLINES 100 +struct shotline +{ + vec from, to; + bool inuse; + int millis; +}; +shotline shotlines[MAXSHOTLINES]; + +void shotlinereset() { loopi(MAXSHOTLINES) shotlines[i].inuse = false; }; + +void addshotline(dynent *d, vec &from, vec &to) +{ + if(d == player1) return; + if(rnd(3) != 0) return; + loopi(MAXSHOTLINES) + { + shotline *s = &shotlines[i]; + if(s->inuse) continue; + int start = 10; + vdist(_dist, _unitv, player1->o, to); + if(_dist <= 10.0f) start = 8; + else start = 5; + + vdist(dist, unitv, from, to); + vdiv(unitv, dist); + s->inuse = true; + s->from = unitv; + vmul(s->from, dist/10*start); + vadd(s->from, from); + s->to = unitv; + vmul(s->to, dist/10*-(10-start-2)); + vadd(s->to, to); + s->millis = lastmillis; + return; + }; +}; + +VAR(shotline_duration, 0, 75, 10000); + +void rendershotlines() +{ + loopi(MAXSHOTLINES) + { + shotline *s = &shotlines[i]; + if(!s->inuse) continue; + if(lastmillis-s->millis > shotline_duration) { s->inuse = false; continue; } + glPushMatrix(); + glDisable(GL_TEXTURE_2D); + glDepthMask(GL_FALSE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); - glDisable(GL_FOG); - - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glBegin(GL_LINES); - glColor4f(1.0f, 1.0f, 0.7f, 0.5f); - glVertex3f(s->from.x, s->from.z, s->from.y); - glVertex3f(s->to.x, s->to.z, s->to.y); - glEnd(); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - + glDisable(GL_FOG); + + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glBegin(GL_LINES); + glColor4f(1.0f, 1.0f, 0.7f, 0.5f); + glVertex3f(s->from.x, s->from.z, s->from.y); + glVertex3f(s->to.x, s->to.z, s->to.y); + glEnd(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glEnable(GL_FOG); glDisable(GL_BLEND); - glDepthMask(GL_TRUE); - - glEnable(GL_TEXTURE_2D); - glPopMatrix(); - }; -}; - -void renderphysents() -{ - loopv(physents) - { - physent *d = physents[i]; - if(d && d->throwed) rendermodel("vwep/grenade_static", 0, 1, 0, 0, d->o.x, d->o.z, d->o.y, d->yaw, d->pitch, false, 2.0f, 100.0f, 0, 0); - }; + glDepthMask(GL_TRUE); + + glEnable(GL_TEXTURE_2D); + glPopMatrix(); + }; +}; + +void renderphysents() +{ + loopv(physents) + { + physent *d = physents[i]; + if(d && d->throwed) rendermodel("vwep/grenade_static", 0, 1, 0, 0, d->o.x, d->o.z, d->o.y, d->yaw, d->pitch, false, 2.0f, 100.0f, 0, 0); + }; }; diff --git a/source/src/rendergl.cpp b/source/src/rendergl.cpp index be0d844f..f19ff21e 100644 --- a/source/src/rendergl.cpp +++ b/source/src/rendergl.cpp @@ -245,25 +245,25 @@ VARF(gamma, 30, 100, 300, void transplayer() { glLoadIdentity(); - - if(player1->state==CS_DEAD) - { - float t = (lastmillis-player1->lastaction)/1000.0f; - if(t >= 1.6f) t = 1.6f; - + + if(player1->state==CS_DEAD) + { + float t = (lastmillis-player1->lastaction)/1000.0f; + if(t >= 1.6f) t = 1.6f; + player1->pitch = (float) sin(t)*(-70.0f-player1->oldpitch)+player1->oldpitch; glRotated(player1->pitch,-1.0,0.0,0.0); glRotated(player1->yaw,0.0,1.0,0.0); - glTranslated(-player1->o.x, player1->eyeheight-sin(t)*7.0f-player1->o.z, -player1->o.y); - } - else + glTranslated(-player1->o.x, player1->eyeheight-sin(t)*7.0f-player1->o.z, -player1->o.y); + } + else { glRotated(player1->roll,0.0,0.0,1.0); glRotated(player1->pitch,-1.0,0.0,0.0); glRotated(player1->yaw,0.0,1.0,0.0); - glTranslated(-player1->o.x, (player1->state==CS_DEAD ? player1->eyeheight-0.2f : 0)-player1->o.z, -player1->o.y); + glTranslated(-player1->o.x, (player1->state==CS_DEAD ? player1->eyeheight-0.2f : 0)-player1->o.z, -player1->o.y); }; }; @@ -295,7 +295,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); - rendermd3gun(); + rendermd3gun(); rendermenumdl(); // lazy bastard glMatrixMode(GL_PROJECTION); @@ -379,10 +379,10 @@ void gl_drawframe(int w, int h, float changelod, float curfps) renderentities(); renderspheres(curtime); - renderents(); - - renderphysents(); - + renderents(); + + renderphysents(); + rendershotlines(); glDisable(GL_CULL_FACE); diff --git a/source/src/rendermd2.cpp b/source/src/rendermd2.cpp index cad941eb..74d6340e 100644 --- a/source/src/rendermd2.cpp +++ b/source/src/rendermd2.cpp @@ -124,8 +124,8 @@ void md2::scale(int frame, float scale, int sn) }; void md2::render(vec &light, int frame, int range, float x, float y, float z, float yaw, float pitch, float sc, float speed, int snap, int basetime) -{ - loopi(range) if(!mverts[frame+i]) scale(frame+i, sc, snap); +{ + loopi(range) if(!mverts[frame+i]) scale(frame+i, sc, snap); glPushMatrix (); glTranslatef(x, y, z); @@ -186,7 +186,7 @@ void md2::render(vec &light, int frame, int range, float x, float y, float z, fl glEndList(); displaylistverts = xtraverts-displaylistverts; }; - }; + }; glPopMatrix(); } diff --git a/source/src/rendermd3.cpp b/source/src/rendermd3.cpp index af26b53f..655f22ae 100644 --- a/source/src/rendermd3.cpp +++ b/source/src/rendermd3.cpp @@ -1,461 +1,517 @@ -// code for loading, linking and rendering md3 models -// See http://www.draekko.org/documentation/md3format.html for informations about the md3 format - -#include "cube.h" - -const int FIRSTMD3 = 20; -enum { MDL_LOWER = 0, MDL_UPPER, MDL_HEAD }; - -#define MD3_DEFAULT_SCALE 0.08f - -struct vec2 +// code for loading, linking and rendering md3 models +// See http://www.draekko.org/documentation/md3format.html for informations about the md3 format + +#include "cube.h" + +const int FIRSTMD3 = 20; +enum { MDL_LOWER = 0, MDL_UPPER, MDL_HEAD }; + +#define MD3_DEFAULT_SCALE 0.08f + +struct vec2 +{ + float x, y; +}; + +struct md3tag +{ + char name[64]; + float pos[3]; + float rotation[3][3]; +}; + +struct md3vertex +{ + signed short vertex[3]; + uchar normal[2]; +}; + +struct md3triangle +{ + int vertexindices[3]; +}; + +struct md3animinfo +{ + int start; + int end; + bool loop; + int fps; +}; + +struct md3header +{ + char id[4]; + int version; + char name[64]; + int flags; + int numframes, numtags, nummeshes, numskins; + int ofs_frames, ofs_tags, ofs_meshes, ofs_eof; // offsets +}; + +struct md3meshheader +{ + char id[4]; + char name[64]; + int flags; + int numframes, numshaders, numvertices, numtriangles; + int ofs_triangles, ofs_shaders, ofs_uv, ofs_vertices, meshsize; // offsets +}; + +struct md3mesh +{ + char name[64]; + md3triangle *triangles; + vec *vertices; + vec2 *uv; + int numtriangles, numvertices; // numvertices == numUV + int tex; +}; + +struct md3model +{ + vector meshes; + vector animations; + md3state *animstate; + md3model **links; + md3tag *tags; + int numframes, numtags; + bool loaded; + vec scale; + bool link(md3model *link, char *tag); + bool load(char *path); + void render(); + void draw(float x, float y, float z, float yaw, float pitch, float rad, vec &light); + md3model(); + ~md3model(); +}; + +md3model::md3model() +{ + loaded = false; + scale.x = scale.y = scale.z = MD3_DEFAULT_SCALE; + animstate = NULL; +}; + +md3model::~md3model() +{ + loopv(meshes){ + if(meshes[i].vertices) delete [] meshes[i].vertices; + if(meshes[i].triangles) delete [] meshes[i].triangles; + if(meshes[i].uv) delete [] meshes[i].uv; + }; + if(links) free(links); + if(tags) delete [] tags; +}; + +bool md3model::link(md3model *link, char *tag) +{ + loopi(numtags) + if(strcmp(tags[i].name, tag) == 0) + { + links[i] = link; + return true; + }; + return false; +}; + +bool md3model::load(char *path) +{ + if(!path) return false; + FILE *f = fopen(path, "rb"); + if(!f) fatal("could not load md3 model: %s", path); + md3header header; + fread(&header, sizeof(md3header), 1, f); + if(header.id[0] != 'I' || header.id[1] != 'D' || header.id[2] != 'P' || header.id[3] != '3' || header.version != 15) // header check + fatal("corruped header in md3 model: %s", path); + + tags = new md3tag[header.numframes * header.numtags]; + fseek(f, header.ofs_tags, SEEK_SET); + fread(tags, sizeof(md3tag), header.numframes * header.numtags, f); + numframes = header.numframes; + numtags = header.numtags; + + links = (md3model **) malloc(sizeof(md3model) * header.numtags); + loopi(header.numtags) links[i] = NULL; + + int mesh_offset = ftell(f); + + loopi(header.nummeshes) + { + md3mesh mesh; + md3meshheader mheader; + fseek(f, mesh_offset, SEEK_SET); + fread(&mheader, sizeof(md3meshheader), 1, f); + strn0cpy(mesh.name, mheader.name, 64); + + mesh.triangles = new md3triangle[mheader.numtriangles]; + fseek(f, mesh_offset + mheader.ofs_triangles, SEEK_SET); + fread(mesh.triangles, sizeof(md3triangle), mheader.numtriangles, f); // read the triangles + mesh.numtriangles = mheader.numtriangles; + + mesh.uv = new vec2[mheader.numvertices]; + fseek(f, mesh_offset + mheader.ofs_uv , SEEK_SET); + fread(mesh.uv, sizeof(vec2), mheader.numvertices, f); // read the UV data + + md3vertex *vertices = new md3vertex[mheader.numframes * mheader.numvertices]; + fseek(f, mesh_offset + mheader.ofs_vertices, SEEK_SET); + fread(vertices, sizeof(md3vertex), mheader.numframes * mheader.numvertices, f); // read the vertices + mesh.vertices = new vec[mheader.numframes * mheader.numvertices]; // transform to our own structure + loopj(mheader.numframes * mheader.numvertices) + { + mesh.vertices[j].x = vertices[j].vertex[0] / 64.0f; + mesh.vertices[j].y = vertices[j].vertex[1] / 64.0f; + mesh.vertices[j].z = vertices[j].vertex[2] / 64.0f; + }; + mesh.numvertices = mheader.numvertices; + + meshes.add(mesh); + mesh_offset += mheader.meshsize; + delete [] vertices; + }; + + loaded = true; + return true; +}; + +VAR(anim, 0, 0, 100); + +void md3model::render() +{ + if(!loaded || meshes.length() <= 0) return; + float t = 0.0f; + int nextfrm; + md3state *a = animstate; + + if(a && animations.length() > a->anim) + { + md3animinfo *info = &animations[a->anim]; + if(a->frm < info->start || a->frm > info->end) // we switched to a new animation, jump to the start + a->frm = info->start; + t = (float) (lastmillis - a->lastTime) / (1000.0f / info->fps); // t has a value from 0.0f to 1.0f - used for interpolation + if(t >= 1.0f) // jump to the next keyframe + { + a->frm++; + a->lastTime = lastmillis; + t = 0.0f; + }; + if(a->frm >= info->end) + { + if(info->loop) + a->frm = info->start; + else + { + a->frm = info->end; + t = 0.0f; // stops the animation + }; + }; + } + else + { + a = new md3state(); + a->frm = 0; + } + a->frm = anim; //fixme + nextfrm = a->frm + 1; + + #define interpolate(p1,p2) ((p1) + t * ((p2) - (p1))) + + loopv(meshes) + { + md3mesh *mesh = &meshes[i]; + + glBindTexture(GL_TEXTURE_2D, FIRSTMD3 + mesh->tex); + + glBegin(GL_TRIANGLES); + loopj(mesh->numtriangles) // triangles + { + loopk(3) // vertices + { + int index = mesh->triangles[j].vertexindices[k]; + vec *point1 = &mesh->vertices[index + a->frm * mesh->numvertices]; + vec *point2 = &mesh->vertices[index + nextfrm * mesh->numvertices]; + + if(mesh->uv) + glTexCoord2f(mesh->uv[index].x, mesh->uv[index].y); + + glVertex3f( interpolate(point1->x, point2->x), + interpolate(point1->y, point2->y), + interpolate(point1->z, point2->z)); + }; + }; + glEnd(); + }; + + loopi(numtags) // render the linked models - interpolate rotation and position of the 'link-tags' + { + md3model *link = links[i]; + if(!link) continue; + GLfloat matrix[16] = {0}; // fixme: un-obfuscate it! + loopj(3) matrix[j] = interpolate(tags[a->frm * numtags + i].rotation[0][j], tags[nextfrm * numtags + i].rotation[0][j]); // rotation + loopj(3) matrix[4 + j] = interpolate(tags[a->frm * numtags + i].rotation[1][j], tags[nextfrm * numtags + i].rotation[1][j]); + loopj(3) matrix[8 + j] = interpolate(tags[a->frm * numtags + i].rotation[2][j], tags[nextfrm * numtags + i].rotation[2][j]); + loopj(3) matrix[12 + j] = interpolate(tags[a->frm * numtags + i].pos[j] , tags[nextfrm * numtags + i].pos[j]); // position + matrix[15] = 1.0f; + glPushMatrix(); + glMultMatrixf(matrix); + link->render(); + glPopMatrix(); + }; +}; + +void md3model::draw(float x, float y, float z, float yaw, float pitch, float rad, vec &light) +{ + glPushMatrix(); + + glColor3fv((float *)&light); + + glTranslatef(x, y, z); + + glRotatef(yaw+180, 0, -1, 0); + glRotatef(pitch, 0, 0, 1); + + glRotatef(-90, 0, 1, 0); + glRotatef(-90, 1, 0, 0); + + glScalef( scale.x, scale.y, scale.z); + + render(); + + glPopMatrix(); +}; + +void md3setanim(dynent *d, int anim) +{ + if(anim <= BOTH_DEAD3) // assign to both, legs and torso + { + d->animstate[MDL_LOWER].anim = anim; + d->animstate[MDL_UPPER].anim = anim; + } + else if(anim <= TORSO_STAND2) + d->animstate[MDL_UPPER].anim = anim; + else + d->animstate[MDL_LOWER].anim = anim - 7; +}; + +vector models; +vector tmp_animations; +int firstweapon = -1; +int numskins = 0; +char basedir[_MAXDEFSTR]; // necessary for relative path's + +void md3skin(char *objname, char *skin) // called by the {lower|upper|head}.cfg (originally .skin in Q3A) +{ + md3model *mdl = models.last(); + loopv(mdl->meshes) + { + md3mesh *mesh = &mdl->meshes[i]; + if(strcmp(mesh->name, objname) == 0) + { + int xs, ys; + sprintf_sd(path)("%s/%s", basedir, skin); // 'skin' is a relative url + bool highqual = strstr(basedir, "weapons") != NULL; + installtex(FIRSTMD3 + numskins, path, xs, ys, true, highqual); + mesh->tex = numskins; + numskins++; + }; + }; +}; + +COMMAND(md3skin, ARG_2STR); + +void md3animation(char *first, char *nums, char *loopings, char *fps) /* configurable animations - use hardcoded instead ? */ +{ + md3animinfo &a = tmp_animations.add(); + a.start = atoi(first); + a.end = a.start + atoi(nums) - 1; + (atoi(loopings) > 0) ? a.loop = true : a.loop = false; + a.fps = atoi(fps); +}; + +COMMAND(md3animation, ARG_5STR); + +void loadplayermdl(char *model) +{ + char pl_objects[3][6] = {"lower", "upper", "head"}; + tmp_animations.setsize(0); + sprintf(basedir, "packages/models/players/%s", model); + md3model *mdls[3]; + loopi(3) // load lower,upper and head models + { + sprintf_sd(path)("%s/%s", basedir, pl_objects[i]); + mdls[i] = new md3model(); + models.add(mdls[i]); + sprintf_sd(mdl_path)("%s.md3", path); + sprintf_sd(cfg_path)("%s.cfg", path); + mdls[i]->load(mdl_path); + exec(cfg_path); + }; + mdls[MDL_LOWER]->link(mdls[MDL_UPPER], "tag_torso"); + mdls[MDL_UPPER]->link(mdls[MDL_HEAD], "tag_head"); + sprintf_sd(modelcfg)("%s/animation.cfg", basedir); + exec(modelcfg); // load animations to tmp_animation + int skip_gap = tmp_animations[LEGS_WALKCR].start - tmp_animations[TORSO_GESTURE].start; + for(int i = LEGS_WALKCR; i < tmp_animations.length(); i++) // the upper and lower mdl frames are independent + { + tmp_animations[i].start -= skip_gap; + tmp_animations[i].end -= skip_gap; + }; + loopv(tmp_animations) // assign the loaded animations to the lower,upper or head model + { + md3animinfo &anim = tmp_animations[i]; + if(i <= BOTH_DEAD3) + { + mdls[MDL_UPPER]->animations.add(anim); + mdls[MDL_LOWER]->animations.add(anim); + } + else if(i <= TORSO_STAND2) + mdls[MDL_UPPER]->animations.add(anim); + else if(i <= LEGS_TURN) + mdls[MDL_LOWER]->animations.add(anim); + }; +}; + +COMMAND(loadplayermdl, ARG_1STR); + +enum {MDL_GUN_IDLE, MDL_GUN_RELOAD, NUM_MDL_GUN}; +/*md3animinfo weaponsanim[] = { {0, 8, 8, 25}, + {8, 15, 0, 25}, + {16,25, 0, 25} };*/ + +md3animinfo weaponsanim[] = { {0, 8, 8, 25}, + {8, 25, 0, 25} }; + +void loadweapons() +{ + firstweapon = models.length(); + loopi(NUMGUNS) + { + sprintf(basedir, "packages/models/weapons/%s", hudgunnames[i]); + md3model *mdl = new md3model(); + models.add(mdl); + sprintf_sd(mdl_path)("%s/tris_high.md3", basedir); + sprintf_sd(cfg_path)("%s/default.skin", basedir); + mdl->load(mdl_path); + exec(cfg_path); + loopj(NUM_MDL_GUN) mdl->animations.add(weaponsanim[j]); + } +}; + +void rotatevec(vec &point, vec &rot) { - float x, y; -}; + vec tmp(point); + + point.x=tmp.z*sin(rot.x)+tmp.x*cos(rot.x); + point.y=tmp.y; + point.z=tmp.z*cos(rot.x)-tmp.x*sin(rot.x); + + point.x=tmp.x; + point.y=tmp.y*cos(rot.y)-tmp.z*sin(rot.y); + point.z=tmp.y*sin(rot.y)+tmp.z*cos(rot.y); + + point.x=tmp.y*sin(rot.z)+tmp.x*cos(rot.z); + point.y=tmp.y*cos(rot.z)-tmp.x*sin(rot.z); + point.z=tmp.z; +}; -struct md3tag -{ - char name[64]; - float pos[3]; - float rotation[3][3]; -}; - -struct md3vertex -{ - signed short vertex[3]; - uchar normal[2]; -}; - -struct md3triangle -{ - int vertexindices[3]; -}; - -struct md3animinfo -{ - int start; - int end; - bool loop; - int fps; -}; - -struct md3header -{ - char id[4]; - int version; - char name[64]; - int flags; - int numframes, numtags, nummeshes, numskins; - int ofs_frames, ofs_tags, ofs_meshes, ofs_eof; // offsets -}; - -struct md3meshheader -{ - char id[4]; - char name[64]; - int flags; - int numframes, numshaders, numvertices, numtriangles; - int ofs_triangles, ofs_shaders, ofs_uv, ofs_vertices, meshsize; // offsets -}; - -struct md3mesh -{ - char name[64]; - md3triangle *triangles; - vec *vertices; - vec2 *uv; - int numtriangles, numvertices; // numvertices == numUV - int tex; -}; - -struct md3model -{ - vector meshes; - vector animations; - md3state *animstate; - md3model **links; - md3tag *tags; - int numframes, numtags; - bool loaded; - vec scale; - bool link(md3model *link, char *tag); - bool load(char *path); - void render(); - void draw(float x, float y, float z, float yaw, float pitch, float rad); - md3model(); - ~md3model(); -}; - -md3model::md3model() -{ - loaded = false; - scale.x = scale.y = scale.z = MD3_DEFAULT_SCALE; - animstate = NULL; -}; - -md3model::~md3model() -{ - loopv(meshes){ - if(meshes[i].vertices) delete [] meshes[i].vertices; - if(meshes[i].triangles) delete [] meshes[i].triangles; - if(meshes[i].uv) delete [] meshes[i].uv; - }; - if(links) free(links); - if(tags) delete [] tags; -}; - -bool md3model::link(md3model *link, char *tag) -{ - loopi(numtags) - if(strcmp(tags[i].name, tag) == 0) - { - links[i] = link; - return true; - }; - return false; -}; - -bool md3model::load(char *path) -{ - if(!path) return false; - FILE *f = fopen(path, "rb"); - if(!f) fatal("could not load md3 model: %s", path); - md3header header; - fread(&header, sizeof(md3header), 1, f); - if(header.id[0] != 'I' || header.id[1] != 'D' || header.id[2] != 'P' || header.id[3] != '3' || header.version != 15) // header check - fatal("corruped header in md3 model: %s", path); - - tags = new md3tag[header.numframes * header.numtags]; - fseek(f, header.ofs_tags, SEEK_SET); - fread(tags, sizeof(md3tag), header.numframes * header.numtags, f); - numframes = header.numframes; - numtags = header.numtags; - - links = (md3model **) malloc(sizeof(md3model) * header.numtags); - loopi(header.numtags) links[i] = NULL; - - int mesh_offset = ftell(f); - - loopi(header.nummeshes) - { - md3mesh mesh; - md3meshheader mheader; - fseek(f, mesh_offset, SEEK_SET); - fread(&mheader, sizeof(md3meshheader), 1, f); - strn0cpy(mesh.name, mheader.name, 64); - - mesh.triangles = new md3triangle[mheader.numtriangles]; - fseek(f, mesh_offset + mheader.ofs_triangles, SEEK_SET); - fread(mesh.triangles, sizeof(md3triangle), mheader.numtriangles, f); // read the triangles - mesh.numtriangles = mheader.numtriangles; - - mesh.uv = new vec2[mheader.numvertices]; - fseek(f, mesh_offset + mheader.ofs_uv , SEEK_SET); - fread(mesh.uv, sizeof(vec2), mheader.numvertices, f); // read the UV data - - md3vertex *vertices = new md3vertex[mheader.numframes * mheader.numvertices]; - fseek(f, mesh_offset + mheader.ofs_vertices, SEEK_SET); - fread(vertices, sizeof(md3vertex), mheader.numframes * mheader.numvertices, f); // read the vertices - mesh.vertices = new vec[mheader.numframes * mheader.numvertices]; // transform to our own structure - loopj(mheader.numframes * mheader.numvertices) - { - mesh.vertices[j].x = vertices[j].vertex[0] / 64.0f; - mesh.vertices[j].y = vertices[j].vertex[1] / 64.0f; - mesh.vertices[j].z = vertices[j].vertex[2] / 64.0f; - }; - mesh.numvertices = mheader.numvertices; - - meshes.add(mesh); - mesh_offset += mheader.meshsize; - delete [] vertices; - }; - - loaded = true; - return true; -}; - -VAR(anim, 0, 0, 100); - -void md3model::render() -{ - if(!loaded || meshes.length() <= 0) return; - float t = 0.0f; - int nextfrm; - md3state *a = animstate; - - if(a && animations.length() > a->anim) - { - md3animinfo *info = &animations[a->anim]; - if(a->frm < info->start || a->frm > info->end) // we switched to a new animation, jump to the start - a->frm = info->start; - t = (float) (lastmillis - a->lastTime) / (1000.0f / info->fps); // t has a value from 0.0f to 1.0f - used for interpolation - if(t >= 1.0f) // jump to the next keyframe - { - a->frm++; - a->lastTime = lastmillis; - t = 0.0f; - }; - if(a->frm >= info->end) - { - if(info->loop) - a->frm = info->start; - else - { - a->frm = info->end; - t = 0.0f; // stops the animation - }; - }; - } - else - { - a = new md3state(); - a->frm = 0; - } - a->frm = anim; //fixme - nextfrm = a->frm + 1; - - #define interpolate(p1,p2) ((p1) + t * ((p2) - (p1))) - - loopv(meshes) - { - md3mesh *mesh = &meshes[i]; - - glBindTexture(GL_TEXTURE_2D, FIRSTMD3 + mesh->tex); - - glBegin(GL_TRIANGLES); - loopj(mesh->numtriangles) // triangles - { - loopk(3) // vertices - { - int index = mesh->triangles[j].vertexindices[k]; - vec *point1 = &mesh->vertices[index + a->frm * mesh->numvertices]; - vec *point2 = &mesh->vertices[index + nextfrm * mesh->numvertices]; - - if(mesh->uv) - glTexCoord2f(mesh->uv[index].x, mesh->uv[index].y); - - glVertex3f( interpolate(point1->x, point2->x), - interpolate(point1->y, point2->y), - interpolate(point1->z, point2->z)); - }; - }; - glEnd(); - }; - - loopi(numtags) // render the linked models - interpolate rotation and position of the 'link-tags' - { - md3model *link = links[i]; - if(!link) continue; - GLfloat matrix[16] = {0}; // fixme: un-obfuscate it! - loopj(3) matrix[j] = interpolate(tags[a->frm * numtags + i].rotation[0][j], tags[nextfrm * numtags + i].rotation[0][j]); // rotation - loopj(3) matrix[4 + j] = interpolate(tags[a->frm * numtags + i].rotation[1][j], tags[nextfrm * numtags + i].rotation[1][j]); - loopj(3) matrix[8 + j] = interpolate(tags[a->frm * numtags + i].rotation[2][j], tags[nextfrm * numtags + i].rotation[2][j]); - loopj(3) matrix[12 + j] = interpolate(tags[a->frm * numtags + i].pos[j] , tags[nextfrm * numtags + i].pos[j]); // position - matrix[15] = 1.0f; - glPushMatrix(); - glMultMatrixf(matrix); - link->render(); - glPopMatrix(); - }; -}; - -void md3model::draw(float x, float y, float z, float yaw, float pitch, float rad) -{ - glPushMatrix(); - - glTranslatef(x, y, z); - - glRotatef(yaw+180, 0, -1, 0); - glRotatef(pitch, 0, 0, 1); - - glRotatef(-90, 0, 1, 0); - glRotatef(-90, 1, 0, 0); - - glScalef( scale.x, scale.y, scale.z); - - render(); - - glPopMatrix(); -}; - -void md3setanim(dynent *d, int anim) -{ - if(anim <= BOTH_DEAD3) // assign to both, legs and torso - { - d->animstate[MDL_LOWER].anim = anim; - d->animstate[MDL_UPPER].anim = anim; - } - else if(anim <= TORSO_STAND2) - d->animstate[MDL_UPPER].anim = anim; - else - d->animstate[MDL_LOWER].anim = anim - 7; -}; - -vector models; -vector tmp_animations; -int firstweapon = -1; -int numskins = 0; -char basedir[_MAXDEFSTR]; // necessary for relative path's - -void md3skin(char *objname, char *skin) // called by the {lower|upper|head}.cfg (originally .skin in Q3A) -{ - md3model *mdl = models.last(); - loopv(mdl->meshes) - { - md3mesh *mesh = &mdl->meshes[i]; - if(strcmp(mesh->name, objname) == 0) - { - int xs, ys; - sprintf_sd(path)("%s/%s", basedir, skin); // 'skin' is a relative url - bool highqual = strstr(basedir, "weapons") != NULL; - installtex(FIRSTMD3 + numskins, path, xs, ys, true, highqual); - mesh->tex = numskins; - numskins++; - }; - }; -}; - -COMMAND(md3skin, ARG_2STR); - -void md3animation(char *first, char *nums, char *loopings, char *fps) /* configurable animations - use hardcoded instead ? */ -{ - md3animinfo &a = tmp_animations.add(); - a.start = atoi(first); - a.end = a.start + atoi(nums) - 1; - (atoi(loopings) > 0) ? a.loop = true : a.loop = false; - a.fps = atoi(fps); -}; - -COMMAND(md3animation, ARG_5STR); - -void loadplayermdl(char *model) -{ - char pl_objects[3][6] = {"lower", "upper", "head"}; - tmp_animations.setsize(0); - sprintf(basedir, "packages/models/players/%s", model); - md3model *mdls[3]; - loopi(3) // load lower,upper and head models - { - sprintf_sd(path)("%s/%s", basedir, pl_objects[i]); - mdls[i] = new md3model(); - models.add(mdls[i]); - sprintf_sd(mdl_path)("%s.md3", path); - sprintf_sd(cfg_path)("%s.cfg", path); - mdls[i]->load(mdl_path); - exec(cfg_path); - }; - mdls[MDL_LOWER]->link(mdls[MDL_UPPER], "tag_torso"); - mdls[MDL_UPPER]->link(mdls[MDL_HEAD], "tag_head"); - sprintf_sd(modelcfg)("%s/animation.cfg", basedir); - exec(modelcfg); // load animations to tmp_animation - int skip_gap = tmp_animations[LEGS_WALKCR].start - tmp_animations[TORSO_GESTURE].start; - for(int i = LEGS_WALKCR; i < tmp_animations.length(); i++) // the upper and lower mdl frames are independent - { - tmp_animations[i].start -= skip_gap; - tmp_animations[i].end -= skip_gap; - }; - loopv(tmp_animations) // assign the loaded animations to the lower,upper or head model - { - md3animinfo &anim = tmp_animations[i]; - if(i <= BOTH_DEAD3) - { - mdls[MDL_UPPER]->animations.add(anim); - mdls[MDL_LOWER]->animations.add(anim); - } - else if(i <= TORSO_STAND2) - mdls[MDL_UPPER]->animations.add(anim); - else if(i <= LEGS_TURN) - mdls[MDL_LOWER]->animations.add(anim); - }; -}; - -COMMAND(loadplayermdl, ARG_1STR); - -enum {MDL_GUN_IDLE, MDL_GUN_RELOAD, NUM_MDL_GUN}; -/*md3animinfo weaponsanim[] = { {0, 8, 8, 25}, - {8, 15, 0, 25}, - {16,25, 0, 25} };*/ - -md3animinfo weaponsanim[] = { {0, 8, 8, 25}, - {8, 25, 0, 25} }; - -void loadweapons() -{ - firstweapon = models.length(); - loopi(NUMGUNS) - { - sprintf(basedir, "packages/models/weapons/%s", hudgunnames[i]); - md3model *mdl = new md3model(); - models.add(mdl); - sprintf_sd(mdl_path)("%s/tris_high.md3", basedir); - sprintf_sd(cfg_path)("%s/default.skin", basedir); - mdl->load(mdl_path); - exec(cfg_path); - loopj(NUM_MDL_GUN) mdl->animations.add(weaponsanim[j]); - } -}; - -void rendermd3gun() -{ - if(firstweapon >= 0) - { - md3model *weapon = models[firstweapon + player1->gunselect]; - int rtime = reloadtime(player1->gunselect); - if(player1->reloading) - { - weapon->animstate = new md3state(); - weapon->animstate->anim = MDL_GUN_RELOAD; - float percent_done = (float)(lastmillis-player1->lastaction)*100.0f/reloadtime(player1->gunselect); - if(percent_done >= 100) percent_done = 100; - weapon->draw(player1->o.x, player1->o.z, player1->o.y, player1->yaw + 90, player1->pitch-(sin((float)(percent_done*2/100.0f*90.0f)*PI/180.0f)*90), 1.0f); - } - else - { - weapon->animstate = new md3state(); - weapon->animstate->anim = MDL_GUN_IDLE; - float kick = 0.0f; - if(player1->gunselect==player1->lastattackgun) - { - int percent_done = (lastmillis-player1->lastaction)*100/attackdelay(player1->gunselect); - if(percent_done > 100) percent_done = 100.0; - // f(x) = -sin(x-1.5)^3 - kick = -sin(pow((1.5f/100*percent_done)-1.5f,3)); - }; +VAR(swayspeeddiv, 1, 200, 1000); +VAR(swaymovediv, 1, 10, 1000); + +void rendermd3gun() +{ + if(firstweapon >= 0) + { + int ix = (int)player1->o.x; + int iy = (int)player1->o.y; + vec light = { 1.0f, 1.0f, 1.0f }; + + if(!OUTBORD(ix, iy)) + { + sqr *s = S(ix,iy); + float ll = 256.0f; // 0.96f; + float of = 0.0f; // 0.1f; + light.x = s->r/ll+of; + light.y = s->g/ll+of; + light.z = s->b/ll+of; + }; + + md3model *weapon = models[firstweapon + player1->gunselect]; + int rtime = reloadtime(player1->gunselect); + if(player1->reloading) + { + weapon->animstate = new md3state(); + weapon->animstate->anim = MDL_GUN_RELOAD; + float percent_done = (float)(lastmillis-player1->lastaction)*100.0f/reloadtime(player1->gunselect); + if(percent_done >= 100) percent_done = 100; + weapon->draw(player1->o.x, player1->o.z, player1->o.y, player1->yaw + 90, player1->pitch-(sin((float)(percent_done*2/100.0f*90.0f)*PI/180.0f)*90), 1.0f, light); + } + else + { + weapon->animstate = new md3state(); + weapon->animstate->anim = MDL_GUN_IDLE; + float kick = 0.0f; + if(player1->gunselect==player1->lastattackgun) + { + int percent_done = (lastmillis-player1->lastaction)*100/attackdelay(player1->gunselect); + if(percent_done > 100) percent_done = 100.0; + // f(x) = -sin(x-1.5)^3 + kick = -sin(pow((1.5f/100*percent_done)-1.5f,3)); + }; vdist(dist, unitv, player1->o, worldpos); - vdiv(unitv, dist); - float k_rot = kick_rot(player1->gunselect)*kick; - float k_back = kick_back(player1->gunselect)*kick/10; - weapon->draw(player1->o.x-unitv.x*k_back, player1->o.z-unitv.z*k_back, player1->o.y-unitv.y*k_back, player1->yaw + 90, player1->pitch+k_rot, 1.0f); - }; - }; -}; - -void rendermd3player(dynent *d) -{ - if(models.length() >= 3) - { - loopi(3) models[i]->animstate = &d->animstate[i]; - - float mz = d->o.z-d->eyeheight+1.55f; - - if(d->state == CS_DEAD) { md3setanim(d, BOTH_DEATH1); } - else if(d->state == CS_EDITING) { md3setanim(d, LEGS_IDLECR); md3setanim(d, TORSO_GESTURE); } - else if(d->state == CS_LAGGED) { md3setanim(d, LEGS_IDLECR); md3setanim(d, TORSO_DROP); } - else - { - if(!d->onfloor && d->timeinair>100) { md3setanim(d, LEGS_JUMP); } - else if((!d->move && !d->strafe)) { md3setanim(d, LEGS_IDLE); } - else { md3setanim(d, LEGS_RUN); } - - if(d->attacking) { md3setanim(d, TORSO_ATTACK2); } - else { md3setanim(d, TORSO_STAND); } - }; - if(d->gunselect >= models.length()) - models[MDL_UPPER]->link(NULL, "tag_weapon"); // no weapon model - else - models[MDL_UPPER]->link(models[d->gunselect], "tag_weapon"); // show current weapon + vdiv(unitv, dist); + float k_rot = kick_rot(player1->gunselect)*kick; + float k_back = kick_back(player1->gunselect)*kick/10; + + vec sway(unitv); + int swayt = (lastmillis+1)/10 % 100; + float swayspeed = (float) sin((float)lastmillis/swayspeeddiv)/swaymovediv; - models[MDL_LOWER]->draw(d->o.x, mz, d->o.y, d->yaw+90, d->pitch/2, d->radius); - }; -}; - + float plspeed = min(1.0f, 0.1f + sqrt(player1->vel.x*player1->vel.x + player1->vel.y*player1->vel.y)); + swayspeed *= plspeed/2; + + vec rot = {0.0f, 0.0f, 1.0f}; + + rotatevec(sway, rot); + + sway.x*=swayspeed; + sway.y*=swayspeed; + sway.z*=swayspeed; + + + weapon->draw( player1->o.x-unitv.x*k_back+sway.x, + player1->o.z-unitv.z*k_back+sway.z, + player1->o.y-unitv.y*k_back+sway.y, + player1->yaw + 90, player1->pitch+k_rot, 1.0f, light); + }; + }; +}; + +void rendermd3player(dynent *d) +{ + /*if(models.length() >= 3) + { + loopi(3) models[i]->animstate = &d->animstate[i]; + + float mz = d->o.z-d->eyeheight+1.55f; + + if(d->state == CS_DEAD) { md3setanim(d, BOTH_DEATH1); } + else if(d->state == CS_EDITING) { md3setanim(d, LEGS_IDLECR); md3setanim(d, TORSO_GESTURE); } + else if(d->state == CS_LAGGED) { md3setanim(d, LEGS_IDLECR); md3setanim(d, TORSO_DROP); } + else + { + if(!d->onfloor && d->timeinair>100) { md3setanim(d, LEGS_JUMP); } + else if((!d->move && !d->strafe)) { md3setanim(d, LEGS_IDLE); } + else { md3setanim(d, LEGS_RUN); } + + if(d->attacking) { md3setanim(d, TORSO_ATTACK2); } + else { md3setanim(d, TORSO_STAND); } + }; + if(d->gunselect >= models.length()) + models[MDL_UPPER]->link(NULL, "tag_weapon"); // no weapon model + else + models[MDL_UPPER]->link(models[d->gunselect], "tag_weapon"); // show current weapon + + models[MDL_LOWER]->draw(d->o.x, mz, d->o.y, d->yaw+90, d->pitch/2, d->radius); + };*/ +}; + diff --git a/source/src/renderparticles.cpp b/source/src/renderparticles.cpp index 4c498b37..1601e7d5 100644 --- a/source/src/renderparticles.cpp +++ b/source/src/renderparticles.cpp @@ -4,13 +4,13 @@ const int MAXPARTICLES = 10500; const int NUMPARTCUTOFF = 20; -struct particle { vec o, d; int fade, type; int millis; particle *next; }; +struct particle { vec o, d; int fade, type; int millis; particle *next; int tex; }; // EDIT: AH particle particles[MAXPARTICLES], *parlist = NULL, *parempty = NULL; bool parinit = false; VAR(maxparticles, 100, 2000, MAXPARTICLES-500); -void newparticle(vec &o, vec &d, int fade, int type) +void newparticle(vec &o, vec &d, int fade, int type, int tex = -1) { if(!parinit) { @@ -31,6 +31,7 @@ void newparticle(vec &o, vec &d, int fade, int type) p->type = type; p->millis = lastmillis; p->next = parlist; + p->tex = tex; parlist = p; }; }; @@ -54,7 +55,7 @@ void render_particles(int time) glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); glDisable(GL_FOG); - + struct parttype { float r, g, b; int gr, tex; float sz; } parttypes[] = { { 0.7f, 0.6f, 0.3f, 2, 3, 0.06f }, // yellow: sparks @@ -66,12 +67,14 @@ void render_particles(int time) { 1.0f, 1.0f, 1.0f, 20, 8, 1.2f }, // blue: fireball2 { 1.0f, 1.0f, 1.0f, 20, 9, 1.2f }, // green: fireball3 { 1.0f, 0.1f, 0.1f, 0, 7, 0.2f }, // red: demotrack + { 1.0f, 1.0f, 1.0f, 2, 3, 0.06f }, // }; int numrender = 0; for(particle *p, **pp = &parlist; p = *pp;) { + if(p->type==9 && p->tex!=-1) parttypes[9].tex = p->tex; // hack, AH parttype *pt = &parttypes[p->type]; glBindTexture(GL_TEXTURE_2D, pt->tex); @@ -125,7 +128,7 @@ void particle_splash(int type, int num, int fade, vec &p) vec d = { (float)x, (float)y, (float)z }; newparticle(p, d, rnd(fade*3), type); }; -}; +}; void particle_trail(int type, int fade, vec &s, vec &e) { @@ -139,5 +142,5 @@ void particle_trail(int type, int fade, vec &s, vec &e) newparticle(p, d, rnd(fade)+fade, type); }; }; - + diff --git a/source/src/sound.cpp b/source/src/sound.cpp index 04a52e0d..0d29917f 100644 --- a/source/src/sound.cpp +++ b/source/src/sound.cpp @@ -4,16 +4,16 @@ //#ifndef WIN32 // NOTE: fmod not being supported for the moment as it does not allow stereo pan/vol updating during playback #define USE_MIXER -//#endif +//#endif bool nosound = false; -#define MAXCHAN 32 -#define SOUNDFREQ 22050 - -struct soundloc { vec loc; bool inuse; } soundlocs[MAXCHAN]; - -#ifdef USE_MIXER +#define MAXCHAN 32 +#define SOUNDFREQ 22050 + +struct soundloc { vec loc; bool inuse; } soundlocs[MAXCHAN]; + +#ifdef USE_MIXER #include "SDL_mixer.h" #define MAXVOL MIX_MAX_VOLUME Mix_Music *mod = NULL; @@ -23,13 +23,13 @@ struct soundloc { vec loc; bool inuse; } soundlocs[MAXCHAN]; #define MAXVOL 255 FMUSIC_MODULE *mod = NULL; FSOUND_STREAM *stream = NULL; -#endif +#endif -#ifdef USE_MIXER -VARFP(musicvol, 0, 128, 255, Mix_VolumeMusic((musicvol*MAXVOL)/255)); -#else -VARP(musicvol, 0, 128, 255); -#endif +#ifdef USE_MIXER +VARFP(musicvol, 0, 128, 255, Mix_VolumeMusic((musicvol*MAXVOL)/255)); +#else +VARP(musicvol, 0, 128, 255); +#endif VARP(soundvol, 0, 255, 255); void stopsound() @@ -64,7 +64,7 @@ void initsound() { conoutf("sound init failed (SDL_mixer): %s", (size_t)Mix_GetError()); nosound = true; - }; + }; Mix_AllocateChannels(MAXCHAN); #else if(FSOUND_GetVersion()o); - vol -= (int)(dist*3*soundvol/255); // simple mono distance attenuation - if(stereo && (v.x != 0 || v.y != 0)) - { - float yaw = -atan2(v.x, v.y) - player1->yaw*(PI / 180.0f); // relative angle of sound along X-Y axis - pan = int(255.9f*(0.5*sin(yaw)+0.5f)); // range is from 0 (left) to 255 (right) - }; - }; - vol = (vol*MAXVOL)/255; - #ifdef USE_MIXER - Mix_Volume(chan, vol); - Mix_SetPanning(chan, 255-pan, pan); - #else - FSOUND_SetVolume(chan, vol); - FSOUND_SetPan(chan, pan); - #endif -}; - -void newsoundloc(int chan, vec *loc) -{ - assert(chan>=0 && chano); + vol -= (int)(dist*3*soundvol/255); // simple mono distance attenuation + if(stereo && (v.x != 0 || v.y != 0)) + { + float yaw = -atan2(v.x, v.y) - player1->yaw*(PI / 180.0f); // relative angle of sound along X-Y axis + pan = int(255.9f*(0.5*sin(yaw)+0.5f)); // range is from 0 (left) to 255 (right) + }; + }; + vol = (vol*MAXVOL)/255; + #ifdef USE_MIXER + Mix_Volume(chan, vol); + Mix_SetPanning(chan, 255-pan, pan); + #else + FSOUND_SetVolume(chan, vol); + FSOUND_SetPan(chan, pan); + #endif +}; + +void newsoundloc(int chan, vec *loc) +{ + assert(chan>=0 && chano); }; -const float RL_RADIUS = 10; +const float RL_RADIUS = 7; const float RL_DAMRAD = 10; // hack void radialeffect(dynent *o, vec &v, int cn, int qdam, dynent *at) @@ -302,107 +302,107 @@ void moveprojectiles(float time) p->o = v; }; }; - -extern float rad(float x); - -bool nade_activated = false; -physent *curnade; - -void thrownade(dynent *d, vec &to, physent *nade = NULL, int nadesec = 0) -{ - physent *p; - if(nade) p = nade; - else p = (physent *)gp()->alloc(sizeof(physent)); // allocate new nade - - p->owner = d; - p->yaw = 270; - p->pitch = 0; - p->roll = 0; - p->maxspeed = 35; //16 max speed, 14 max with armour - p->outsidemap = false; - p->inwater = false; - p->radius = 0.2f; - p->eyeheight = 0.3f; - p->aboveeye = 0.0f; - p->frags = 0; - p->plag = 0; - p->ping = 0; - p->lastupdate = lastmillis; - p->enemy = NULL; - p->monsterstate = 0; - p->name[0] = p->team[0] = 0; - p->blocked = false; - p->lifesequence = 0; - p->state = CS_ALIVE; - p->shots = 0; - p->reloading = false; - p->nextprimary = 1; - p->hasarmour = false; - - p->k_left = false; - p->k_right = false; - p->k_up = false; - p->k_down = false; - p->jumpnext = false; - p->strafe = 0; - p->move = 0; - - p->bounce = true; - p->gravity = 20; - - p->timeinair = 0; - p->throwed = true; - p->onfloor = false; - p->lifetime = 3000; - - p->o = d->o; - -// float speed = sin(rad((d->pitch+90)/2.0f)); - p->vel.z = sin(rad(d->pitch)); - float speed = cos(rad(d->pitch)); - p->vel.x = sin(rad(d->yaw))*speed; - p->vel.y = -cos(rad(d->yaw))*speed; - - vmul(p->vel, 1.5f); - - vec throwdir = p->vel; - vmul(throwdir, d->radius); - vadd(p->o, throwdir); - vadd(p->o, throwdir); - - if(!nade) // add new one - { - p->millis = lastmillis; - physents.add(p); - }; -}; - -void activate_nade() -{ - physent *p = (physent *)gp()->alloc(sizeof(physent)); - p->owner = player1; - p->millis = lastmillis; - p->lifetime = 3000; - p->throwed = false; - p->enabled = true; - curnade = p; - physents.add(p); - - nade_activated = true; -}; - -extern void explode_nade(physent *i) -{ - if(!i) return; - if(!i->throwed) - { - vec o = i->owner->o; - vec dist = { 0.1f, 0.1f, 0.1f }; - vadd(o, dist); - thrownade(i->owner, o, i); - }; - newprojectile(i->o, i->o, 1, true, i->owner, GUN_GRENADE); -}; + +extern float rad(float x); + +bool nade_activated = false; +physent *curnade; + +void thrownade(dynent *d, vec &to, physent *nade = NULL, int nadesec = 0) +{ + physent *p; + if(nade) p = nade; + else p = (physent *)gp()->alloc(sizeof(physent)); // allocate new nade + + p->owner = d; + p->yaw = 270; + p->pitch = 0; + p->roll = 0; + p->maxspeed = 40; //16 max speed, 14 max with armour + p->outsidemap = false; + p->inwater = false; + p->radius = 0.2f; + p->eyeheight = 0.3f; + p->aboveeye = 0.0f; + p->frags = 0; + p->plag = 0; + p->ping = 0; + p->lastupdate = lastmillis; + p->enemy = NULL; + p->monsterstate = 0; + p->name[0] = p->team[0] = 0; + p->blocked = false; + p->lifesequence = 0; + p->state = CS_ALIVE; + p->shots = 0; + p->reloading = false; + p->nextprimary = 1; + p->hasarmour = false; + + p->k_left = false; + p->k_right = false; + p->k_up = false; + p->k_down = false; + p->jumpnext = false; + p->strafe = 0; + p->move = 0; + + p->bounce = true; + p->gravity = 20; + + p->timeinair = 0; + p->throwed = true; + p->onfloor = false; + p->lifetime = 3000; + + p->o = d->o; + +// float speed = sin(rad((d->pitch+90)/2.0f)); + p->vel.z = sin(rad(d->pitch)); + float speed = cos(rad(d->pitch)); + p->vel.x = sin(rad(d->yaw))*speed; + p->vel.y = -cos(rad(d->yaw))*speed; + + vmul(p->vel, 1.7f); + + vec throwdir = p->vel; + vmul(throwdir, d->radius); + vadd(p->o, throwdir); + vadd(p->o, throwdir); + + if(!nade) // add new one + { + p->millis = lastmillis; + physents.add(p); + }; +}; + +void activate_nade() +{ + physent *p = (physent *)gp()->alloc(sizeof(physent)); + p->owner = player1; + p->millis = lastmillis; + p->lifetime = 3000; + p->throwed = false; + p->enabled = true; + curnade = p; + physents.add(p); + + nade_activated = true; +}; + +extern void explode_nade(physent *i) +{ + if(!i) return; + if(!i->throwed) + { + vec o = i->owner->o; + vec dist = { 0.1f, 0.1f, 0.1f }; + vadd(o, dist); + thrownade(i->owner, o, i); + }; + newprojectile(i->o, i->o, 1, true, i->owner, GUN_GRENADE); +}; void shootv(int gun, vec &from, vec &to, dynent *d, bool local, int nadesec) // create visual effect from a shot { @@ -421,22 +421,22 @@ void shootv(int gun, vec &from, vec &to, dynent *d, bool local, int nadesec) case GUN_PISTOL: case GUN_SUBGUN: - case GUN_ASSULT: + case GUN_ASSULT: addshotline(d, from, to); - particle_splash(0, 100, 250, to); + particle_splash(9, 5, 250, to); //particle_trail(1, 10, from, to); break; case GUN_GRENADE: pspeed = guns[gun].projspeed; - //newprojectile(from, to, (float)pspeed, local, d, gun); - if(d==player1 && curnade) - thrownade(d, to, curnade); - else + //newprojectile(from, to, (float)pspeed, local, d, gun); + if(d==player1 && curnade) + thrownade(d, to, curnade); + else thrownade(d, to, NULL, nadesec); break; - case GUN_SNIPER: + case GUN_SNIPER: addshotline(d, from, to); particle_splash(0, 50, 200, to); particle_trail(1, 500, from, to); @@ -477,8 +477,8 @@ void spreadandrecoil(vec & from, vec & to, dynent * d) //recoil int rcl = guns[d->gunselect].recoil*-0.01f; if (d->gunselect==GUN_ASSULT) - { - if(d->shots > 3) + { + if(d->shots > 3) spd = 70; rcl += (rnd(8)*-0.01f); }; @@ -488,7 +488,7 @@ void spreadandrecoil(vec & from, vec & to, dynent * d) rcl = rcl / 3; }; if (d->gunselect!=GUN_SHOTGUN) //no spread on shotgun - { + { #define RNDD (rnd(spd)-spd/2)*f vec r = { RNDD, RNDD, RNDD }; vadd(to, r); @@ -537,76 +537,76 @@ void spreadandrecoil(vec & from, vec & to, dynent * d) vadd(d->vel, recoil); if(d->pitch<80.0f) d->pitch += guns[d->gunselect].recoil*0.05f; */ -}; - -void shoot(dynent *d, vec &targ) -{ - bool grenade_throw = d->gunselect==GUN_GRENADE && !d->attacking && nade_activated; - int attacktime = lastmillis-d->lastaction; - if(d->reloading && (attacktimegunwait) && !grenade_throw) return; - else if(attacktimegunwait) return; - d->gunwait = 0; - d->reloading = false; - if(grenade_throw) - { - d->attacking = true; - nade_activated = false; - d->gunwait = guns[d->gunselect].attackdelay; - } - else if(d->gunselect==GUN_GRENADE && d->attacking && nade_activated) return; - if(!d->attacking) - { - d->shots = 0; - return; - }; - if(!(d->gunselect==GUN_SUBGUN || d->gunselect==GUN_ASSULT)) d->attacking = false; //makes sub/assult autos - else - { - d->shots++; - }; - d->lastaction = lastmillis; - d->lastattackgun = d->gunselect; - if(!d->mag[d->gunselect]) { playsoundc(S_NOAMMO); d->gunwait = 250; d->lastattackgun = -1; return; }; - - if(d->gunselect==GUN_GRENADE && !grenade_throw) - { - activate_nade(); - d->attacking = true; - return; - } - - if(d->gunselect) d->mag[d->gunselect]--; - - vec from = d->o; - vec to = targ; - from.z -= 0.2f; // below eye - - spreadandrecoil(from,to,d); - - vdist(dist, unitv, from, to); - vdiv(unitv, dist); - - if(d->gunselect==GUN_KNIFE) - { - vmul(unitv, 3); // punch range - to = from; - vadd(to, unitv); - }; - if(d->gunselect==GUN_SHOTGUN) createrays(from, to); - - //if(d->quadmillis && attacktime>200) playsoundc(S_ITEMPUP); - - shootv(d->gunselect, from, to, d, 0); - addmsg(1, 9, SV_SHOT, d->gunselect, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), (int) grenade_throw ? lastmillis-curnade->millis : 0 ); - d->gunwait = guns[d->gunselect].attackdelay; - - if(guns[d->gunselect].projspeed) return; - - loopv(players) - { - dynent *o = players[i]; - if(!o) continue; - raydamage(o, from, to, d, i); - }; - +}; + +void shoot(dynent *d, vec &targ) +{ + bool grenade_throw = d->gunselect==GUN_GRENADE && !d->attacking && nade_activated; + int attacktime = lastmillis-d->lastaction; + if(d->reloading && (attacktimegunwait) && !grenade_throw) return; + else if(attacktimegunwait) return; + d->gunwait = 0; + d->reloading = false; + if(grenade_throw) + { + d->attacking = true; + nade_activated = false; + d->gunwait = guns[d->gunselect].attackdelay; + } + else if(d->gunselect==GUN_GRENADE && d->attacking && nade_activated) return; + if(!d->attacking) + { + d->shots = 0; + return; + }; + if(!(d->gunselect==GUN_SUBGUN || d->gunselect==GUN_ASSULT)) d->attacking = false; //makes sub/assult autos + else + { + d->shots++; + }; + d->lastaction = lastmillis; + d->lastattackgun = d->gunselect; + if(!d->mag[d->gunselect]) { playsoundc(S_NOAMMO); d->gunwait = 250; d->lastattackgun = -1; return; }; + + if(d->gunselect==GUN_GRENADE && !grenade_throw) + { + activate_nade(); + d->attacking = true; + return; + } + + if(d->gunselect) d->mag[d->gunselect]--; + + vec from = d->o; + vec to = targ; + from.z -= 0.2f; // below eye + + spreadandrecoil(from,to,d); + + vdist(dist, unitv, from, to); + vdiv(unitv, dist); + + if(d->gunselect==GUN_KNIFE) + { + vmul(unitv, 3); // punch range + to = from; + vadd(to, unitv); + }; + if(d->gunselect==GUN_SHOTGUN) createrays(from, to); + + //if(d->quadmillis && attacktime>200) playsoundc(S_ITEMPUP); + + shootv(d->gunselect, from, to, d, 0); + addmsg(1, 9, SV_SHOT, d->gunselect, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), (int) grenade_throw ? lastmillis-curnade->millis : 0 ); + d->gunwait = guns[d->gunselect].attackdelay; + + if(guns[d->gunselect].projspeed) return; + + loopv(players) + { + dynent *o = players[i]; + if(!o) continue; + raydamage(o, from, to, d, i); + }; + }; diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index debfda43..f88a33ab 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -135,7 +135,7 @@ void save_world(char *mname) backup(cgzname, bakname); gzFile f = gzopen(cgzname, "wb9"); if(!f) { conoutf("could not write map to %s", (int)cgzname); return; }; - hdr.version = MAPVERSION; + hdr.version = 5; //MAPVERSION; hdr.numents = 0; loopv(ents) if(ents[i].type!=NOTUSED) hdr.numents++; header tmp = hdr;