From 1d0dff7a2ead6c61b630146bad5d721d93e65519 Mon Sep 17 00:00:00 2001 From: driAn Date: Thu, 13 Apr 2006 22:03:09 +0000 Subject: [PATCH] small fixes git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@242 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/src/clientgame.cpp | 2133 ++++++++++++++++++++++++------------- source/src/menus.cpp | 666 ++++++++---- source/src/sound.cpp | 681 ++++++++---- 3 files changed, 2320 insertions(+), 1160 deletions(-) diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index 7e436618..f257636f 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -1,711 +1,1422 @@ -// 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); - -flaginfo flaginfos[2]; - -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; - }; - */ - - d->akimbo = false; -}; - -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->flagscore = 0; // EDIT: AH - 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; - d->gunselect = GUN_PISTOL; - d->onladder = false; - d->isphysent = false; - spawnstate(d); - return d; -}; - -void ctf_death() // EDIT: AH -{ - int flag = rb_opposite(rb_team_int(player1->team)); - flaginfo &f = flaginfos[flag]; - if(f.state==CTFF_STOLEN && f.thief==player1) - { - addmsg(1, 2, SV_FLAGDROP, flag); - f.flag->spawned = false; - f.state = CTFF_DROPPED; - }; -}; - -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; - }; -*/ - - //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->state == NADE_THROWED) moveplayer(p, 2, false); -// printf("physent %i: %i %i %i\n", i, lastmillis, p->millis, p->timetolife); - - if(lastmillis - p->millis >= p->timetolife) - { - if(p->state==NADE_ACTIVATED || p->state==NADE_THROWED) 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 -}; - -// EDIT: AH -int securespawndist = 15; - -// Returns -1 for a free place, if not it returns the vdist to the nearest enemy -int nearestenemy(vec *v, string team) -{ - float nearestPlayerDistSquared = -1; - loopv(players) - { - dynent *other = players[i]; - if(!other) continue; - if(isteam(team,other->team))continue; // its a teammate - vec place = {v->x, v->y, v->z}; - vdistsquared(distsquared, t, place, other->o); - if(nearestPlayerDistSquared == -1) nearestPlayerDistSquared = distsquared; // first run - else if(distsquared < nearestPlayerDistSquared) nearestPlayerDistSquared = distsquared; // if a player is closer - }; - if(nearestPlayerDistSquared >= securespawndist * securespawndist || nearestPlayerDistSquared == -1) return -1; // a distance more than securespawndist means the place is free - else return sqrt(nearestPlayerDistSquared); -}; - -int spawncycle = -1; -int fixspawn = 2; - -void spawnplayer(dynent *d, bool secure) // place at random spawn. also used by monsters! -{ - loopj(10) // EDIT: AH - { - int r = fixspawn-->0 ? 4 : rnd(10)+1; - loopi(r) spawncycle = m_ctf ? findteamplayerstart(rb_team_int(d->team), spawncycle+1) : findentity(PLAYERSTART, spawncycle+1); - if(spawncycle!=-1 && secure) - { - entity &e = ents[spawncycle]; - vec pos = { e.x, e.y, e.z }; - if(nearestenemy(&pos, d->team) == -1) break; - } else break; - }; - 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); - }; - }; - }; - // EDIT: AH - if(m_ctf) ctf_death(); - 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 preparectf() -{ - loopi(2) flaginfos[i].flag = NULL; - loopv(ents) - { - entity &e = ents[i]; - if(e.type==CTF_FLAG) - { - e.spawned = true; - if(e.attr2<0||e.attr2>2) { conoutf("invalid ctf-flag entity (%i)", i); e.attr2 = 0; }; - flaginfo &f = flaginfos[e.attr2]; - f.flag = &e; - f.state = CTFF_INBASE; - f.originalpos.x = (float) e.x; - f.originalpos.y = (float) e.y; - f.originalpos.z = (float) e.z; - }; - }; - loopi(2) // ignore missing flags - { - flaginfo &f = flaginfos[i]; - if(f.flag == NULL) f.flag = new entity(); - } - ctf_team(player1->team); // ensure valid team -}; - -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(); - if(m_ctf) preparectf(); - shotlinereset(); - spawncycle = -1; - spawnplayer(player1, true); - player1->frags = 0; - player1->flagscore = 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); - -void killdummies() -{ - loopv(players) if(strcmp(players[i]->name, "dummy") == 0) { players[i]->lastaction = lastmillis; players[i]->health=1; }; -}; COMMAND(killdummies, ARG_NONE); - -// EDIT: AH -void flagaction(int flag, int action) -{ - flaginfo &f = flaginfos[flag]; - if(!f.thief) return; - bool ownflag = flag == rb_team_int(player1->team); - switch(action) - { - case SV_FLAGPICKUP: - { - playsound(S_FLAGPICKUP); - if(f.thief==player1) - { - conoutf("you got the enemy flag"); - f.pick_ack = true; - } - else conoutf("%s got %s flag", f.thief->name, ownflag ? "your": "the enemy"); - break; - }; - case SV_FLAGDROP: - { - playsound(S_FLAGDROP); - if(f.thief==player1) conoutf("you lost the flag"); - else conoutf("%s lost %s flag", f.thief->name, ownflag ? "your" : "the enemy"); - break; - }; - case SV_FLAGRETURN: - { - playsound(S_FLAGRETURN); - if(f.thief==player1) conoutf("you returned your flag"); - else conoutf("%s returned %s flag", f.thief->name, ownflag ? "your" : "the enemy"); - break; - }; - case SV_FLAGSCORE: - { - playsound(S_FLAGSCORE); - if(f.thief==player1) - { - conoutf("you scored"); - addmsg(1, 2, SV_FLAGS, ++player1->flagscore); - } - else conoutf("%s scored for %s team", f.thief->name, ownflag ? "the enemy" : "your"); - break; - }; - default: break; - }; -}; +// 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); + + + +flaginfo flaginfos[2]; + + + +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; + + }; + + */ + + + + d->akimbo = false; + +}; + + + +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->flagscore = 0; // EDIT: AH + + 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; + + d->gunselect = GUN_PISTOL; + + d->onladder = false; + + d->isphysent = false; + + spawnstate(d); + + return d; + +}; + + + +void ctf_death() // EDIT: AH + +{ + + int flag = rb_opposite(rb_team_int(player1->team)); + + flaginfo &f = flaginfos[flag]; + + if(f.state==CTFF_STOLEN && f.thief==player1) + + { + + addmsg(1, 2, SV_FLAGDROP, flag); + + f.flag->spawned = false; + + f.state = CTFF_DROPPED; + + }; + +}; + + + +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; + + }; + +*/ + + + + //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->state == NADE_THROWED) moveplayer(p, 2, false); + +// printf("physent %i: %i %i %i\n", i, lastmillis, p->millis, p->timetolife); + + + + if(lastmillis - p->millis >= p->timetolife) + + { + + if(p->state==NADE_ACTIVATED || p->state==NADE_THROWED) 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 + +}; + + + +// EDIT: AH + +int securespawndist = 15; + + + +// Returns -1 for a free place, if not it returns the vdist to the nearest enemy + +int nearestenemy(vec *v, string team) + +{ + + float nearestPlayerDistSquared = -1; + + loopv(players) + + { + + dynent *other = players[i]; + + if(!other) continue; + + if(isteam(team,other->team))continue; // its a teammate + + vec place = {v->x, v->y, v->z}; + + vdistsquared(distsquared, t, place, other->o); + + if(nearestPlayerDistSquared == -1) nearestPlayerDistSquared = distsquared; // first run + + else if(distsquared < nearestPlayerDistSquared) nearestPlayerDistSquared = distsquared; // if a player is closer + + }; + + if(nearestPlayerDistSquared >= securespawndist * securespawndist || nearestPlayerDistSquared == -1) return -1; // a distance more than securespawndist means the place is free + + else return sqrt(nearestPlayerDistSquared); + +}; + + + +int spawncycle = -1; + +int fixspawn = 2; + + + +void spawnplayer(dynent *d, bool secure) // place at random spawn. also used by monsters! + +{ + + loopj(10) // EDIT: AH + + { + + int r = fixspawn-->0 ? 4 : rnd(10)+1; + + loopi(r) spawncycle = m_ctf ? findteamplayerstart(rb_team_int(d->team), spawncycle+1) : findentity(PLAYERSTART, spawncycle+1); + + if(spawncycle!=-1 && secure) + + { + + entity &e = ents[spawncycle]; + + vec pos = { e.x, e.y, e.z }; + + if(nearestenemy(&pos, d->team) == -1) break; + + } else break; + + }; + + 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); + + }; + + }; + + }; + + // EDIT: AH + + if(m_ctf) ctf_death(); + + 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 preparectf() + +{ + + loopi(2) flaginfos[i].flag = NULL; + + loopv(ents) + + { + + entity &e = ents[i]; + + if(e.type==CTF_FLAG) + + { + + e.spawned = true; + + if(e.attr2<0||e.attr2>2) { conoutf("invalid ctf-flag entity (%i)", i); e.attr2 = 0; }; + + flaginfo &f = flaginfos[e.attr2]; + + f.flag = &e; + + f.state = CTFF_INBASE; + + f.originalpos.x = (float) e.x; + + f.originalpos.y = (float) e.y; + + f.originalpos.z = (float) e.z; + + }; + + }; + + loopi(2) // ignore missing flags + + { + + flaginfo &f = flaginfos[i]; + + if(f.flag == NULL) f.flag = new entity(); + + } + + ctf_team(player1->team); // ensure valid team + +}; + + + +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(); + + if(m_ctf) preparectf(); + + shotlinereset(); + + spawncycle = -1; + + spawnplayer(player1, true); + + player1->frags = 0; + + player1->flagscore = 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); + + + +void killdummies() + +{ + + loopv(players) if(strcmp(players[i]->name, "dummy") == 0) { players[i]->lastaction = lastmillis; players[i]->health=1; }; + +}; COMMAND(killdummies, ARG_NONE); + + + +// EDIT: AH + +void flagaction(int flag, int action) + +{ + + flaginfo &f = flaginfos[flag]; + + if(!f.thief) return; + + bool ownflag = flag == rb_team_int(player1->team); + + switch(action) + + { + + case SV_FLAGPICKUP: + + { + + playsound(S_FLAGPICKUP); + + if(f.thief==player1) + + { + + conoutf("you got the enemy flag"); + + f.pick_ack = true; + + } + + else conoutf("%s got %s flag", (int) f.thief->name, (int) (ownflag ? "your": "the enemy")); + + break; + + }; + + case SV_FLAGDROP: + + { + + playsound(S_FLAGDROP); + + if(f.thief==player1) conoutf("you lost the flag"); + + else conoutf("%s lost %s flag", (int) f.thief->name, (int) (ownflag ? "your" : "the enemy")); + + break; + + }; + + case SV_FLAGRETURN: + + { + + playsound(S_FLAGRETURN); + + if(f.thief==player1) conoutf("you returned your flag"); + + else conoutf("%s returned %s flag", (int) f.thief->name, (int) (ownflag ? "your" : "the enemy")); + + break; + + }; + + case SV_FLAGSCORE: + + { + + playsound(S_FLAGSCORE); + + if(f.thief==player1) + + { + + conoutf("you scored"); + + addmsg(1, 2, SV_FLAGS, ++player1->flagscore); + + } + + else conoutf("%s scored for %s team", (int) f.thief->name, (int) (ownflag ? "the enemy" : "your")); + + break; + + }; + + default: break; + + }; + +}; + diff --git a/source/src/menus.cpp b/source/src/menus.cpp index e63397fc..6807a906 100644 --- a/source/src/menus.cpp +++ b/source/src/menus.cpp @@ -1,222 +1,444 @@ -// menus.cpp: ingame menu system (also used for scores and serverlist) - -#include "cube.h" - -#define FIRSTMENU 120 - -struct mitem { char *text, *action, *hoveraction; }; - -struct gmenu -{ - char *name; - vector items; - int mwidth; - int menusel; - int tex; // (optional) bg tex - char *mdl; // (optional) md2 mdl - int frame, range, rotspeed, scale; -}; - -vector menus; - -int vmenu = -1; - -ivector menustack; - -void menuset(int menu) // EDIT: AH -{ - if((vmenu = menu)>=1 && menu != 2) resetmovement(player1); - if(vmenu==1) menus[1].menusel = 0; -}; - -void showmenu(char *name) -{ - loopv(menus) if(i>1 && strcmp(menus[i].name, name)==0) - { - menuset(i); - return; - }; -}; - -int menucompare(mitem *a, mitem *b) -{ - int x = atoi(a->text); - int y = atoi(b->text); - if(x>y) return -1; - if(x0 && vmenu!=2 ? "[ %s menu ]" : "%s", m.name); // EDIT: AH - int mdisp = m.items.length(); - int w = 0; - loopi(mdisp) - { - int x = text_width(m.items[i].text); - if(x>w) w = x; - }; - int tw = text_width(title); - if(tw>w) w = tw; - int step = FONTH/4*5; - int h = (mdisp+2)*step; - int y = (VIRTH-h)/2; - int x = (VIRTW-w)/2; - blendbox(x-FONTH/2*3, y-FONTH, x+w+FONTH/2*3, y+h+FONTH, true, m.tex >= 0 ? FIRSTMENU + m.tex : -1); - draw_text(title, x, y,2); - y += FONTH*2; - if(vmenu && vmenu!=2) - { - int bh = y+m.menusel*step; - blendbox(x-FONTH, bh-10, x+w+FONTH, bh+FONTH+10, false); - }; - loopj(mdisp) - { - draw_text(m.items[j].text, x, y, 2); - y += step; - }; - return true; -}; - -void rendermenumdl() -{ - if(vmenu<0) { menustack.setsize(0); return false; }; - 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, false); - glPopMatrix(); -} - -void newmenu(char *name) -{ - gmenu &menu = menus.add(); - menu.name = newstring(name); - menu.menusel = 0; - menu.tex = -1; - menu.mdl = NULL; -}; - -void menumanual(int m, int n, char *text) -{ - if(!n) menus[m].items.setsize(0); - mitem &mitem = menus[m].items.add(); - mitem.text = text; - mitem.action = ""; -} - -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); - 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; - }; - }; -}; - - -COMMAND(menuitem, ARG_3STR); -COMMAND(showmenu, ARG_1STR); -COMMAND(newmenu, ARG_1STR); -COMMAND(menubg, ARG_1STR); -COMMAND(menumdl, ARG_5STR); -COMMAND(chmenumdl, ARG_6STR); - -bool menukey(int code, bool isdown) -{ - if(vmenu<=0 || vmenu == 2) return false; // EDIT: AH - int menusel = menus[vmenu].menusel; - if(isdown) - { - int oldmenusel = menusel; - if(code==SDLK_ESCAPE) - { - menuset(-1); - if(!menustack.empty()) menuset(menustack.pop()); - return true; - } - else if(code==SDLK_UP || code==-4) menusel--; - else if(code==SDLK_DOWN || code==-5) menusel++; - int n = menus[vmenu].items.length(); - if(menusel<0) menusel = n-1; - else if(menusel>=n) menusel = 0; - menus[vmenu].menusel = menusel; - if(menusel != oldmenusel)execute(menus[vmenu].items[menusel].hoveraction, true); - } - else - { - if(code==SDLK_RETURN || code==-2) - { - char *action = menus[vmenu].items[menusel].action; - if(vmenu==1) connects(getservername(menusel)); - menustack.add(vmenu); - menuset(-1); - execute(action, true); - }; - }; - return true; -}; +// menus.cpp: ingame menu system (also used for scores and serverlist) + + + +#include "cube.h" + + + +#define FIRSTMENU 120 + + + +struct mitem { char *text, *action, *hoveraction; }; + + + +struct gmenu + +{ + + char *name; + + vector items; + + int mwidth; + + int menusel; + + int tex; // (optional) bg tex + + char *mdl; // (optional) md2 mdl + + int frame, range, rotspeed, scale; + +}; + + + +vector menus; + + + +int vmenu = -1; + + + +ivector menustack; + + + +void menuset(int menu) // EDIT: AH + +{ + + if((vmenu = menu)>=1 && menu != 2) resetmovement(player1); + + if(vmenu==1) menus[1].menusel = 0; + +}; + + + +void showmenu(char *name) + +{ + + loopv(menus) if(i>1 && strcmp(menus[i].name, name)==0) + + { + + menuset(i); + + return; + + }; + +}; + + + +int menucompare(mitem *a, mitem *b) + +{ + + int x = atoi(a->text); + + int y = atoi(b->text); + + if(x>y) return -1; + + if(x0 && vmenu!=2 ? "[ %s menu ]" : "%s", m.name); // EDIT: AH + + int mdisp = m.items.length(); + + int w = 0; + + loopi(mdisp) + + { + + int x = text_width(m.items[i].text); + + if(x>w) w = x; + + }; + + int tw = text_width(title); + + if(tw>w) w = tw; + + int step = FONTH/4*5; + + int h = (mdisp+2)*step; + + int y = (VIRTH-h)/2; + + int x = (VIRTW-w)/2; + + blendbox(x-FONTH/2*3, y-FONTH, x+w+FONTH/2*3, y+h+FONTH, true, m.tex >= 0 ? FIRSTMENU + m.tex : -1); + + draw_text(title, x, y,2); + + y += FONTH*2; + + if(vmenu && vmenu!=2) + + { + + int bh = y+m.menusel*step; + + blendbox(x-FONTH, bh-10, x+w+FONTH, bh+FONTH+10, false); + + }; + + loopj(mdisp) + + { + + draw_text(m.items[j].text, x, y, 2); + + y += step; + + }; + + return true; + +}; + + + +void rendermenumdl() + +{ + + if(vmenu<0) { menustack.setsize(0); return; }; + + 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, false); + + glPopMatrix(); + +} + + + +void newmenu(char *name) + +{ + + gmenu &menu = menus.add(); + + menu.name = newstring(name); + + menu.menusel = 0; + + menu.tex = -1; + + menu.mdl = NULL; + +}; + + + +void menumanual(int m, int n, char *text) + +{ + + if(!n) menus[m].items.setsize(0); + + mitem &mitem = menus[m].items.add(); + + mitem.text = text; + + mitem.action = ""; + +} + + + +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); + + 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; + + }; + + }; + +}; + + + + + +COMMAND(menuitem, ARG_3STR); + +COMMAND(showmenu, ARG_1STR); + +COMMAND(newmenu, ARG_1STR); + +COMMAND(menubg, ARG_1STR); + +COMMAND(menumdl, ARG_5STR); + +COMMAND(chmenumdl, ARG_6STR); + + + +bool menukey(int code, bool isdown) + +{ + + if(vmenu<=0 || vmenu == 2) return false; // EDIT: AH + + int menusel = menus[vmenu].menusel; + + if(isdown) + + { + + int oldmenusel = menusel; + + if(code==SDLK_ESCAPE) + + { + + menuset(-1); + + if(!menustack.empty()) menuset(menustack.pop()); + + return true; + + } + + else if(code==SDLK_UP || code==-4) menusel--; + + else if(code==SDLK_DOWN || code==-5) menusel++; + + int n = menus[vmenu].items.length(); + + if(menusel<0) menusel = n-1; + + else if(menusel>=n) menusel = 0; + + menus[vmenu].menusel = menusel; + + if(menusel != oldmenusel)execute(menus[vmenu].items[menusel].hoveraction, true); + + } + + else + + { + + if(code==SDLK_RETURN || code==-2) + + { + + char *action = menus[vmenu].items[menusel].action; + + if(vmenu==1) connects(getservername(menusel)); + + menustack.add(vmenu); + + menuset(-1); + + execute(action, true); + + }; + + }; + + return true; + +}; + diff --git a/source/src/sound.cpp b/source/src/sound.cpp index 89efd71a..f3a82ca0 100644 --- a/source/src/sound.cpp +++ b/source/src/sound.cpp @@ -1,227 +1,454 @@ -// sound.cpp: uses fmod on windows and sdl_mixer on unix (both had problems on the other platform) - -#include "cube.h" - -//#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 - -VARP(soundvol, 0, 255, 255); -VARP(musicvol, 0, 128, 255); -bool nosound = false; - -#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; - void *stream = NULL; -#else - #include "fmod.h" - #define MAXVOL 255 - FMUSIC_MODULE *mod = NULL; - FSOUND_STREAM *stream = NULL; -#endif - -void stopsound() -{ - if(nosound) return; - if(mod) - { - #ifdef USE_MIXER - Mix_HaltMusic(); - Mix_FreeMusic(mod); - #else - FMUSIC_FreeSong(mod); - #endif - mod = NULL; - }; - if(stream) - { - #ifndef USE_MIXER - FSOUND_Stream_Close(stream); - #endif - stream = NULL; - }; -}; - -VAR(soundbufferlen, 128, 1024, 4096); - -void initsound() -{ - memset(soundlocs, 0, sizeof(soundloc)*MAXCHAN); - #ifdef USE_MIXER - if(Mix_OpenAudio(SOUNDFREQ, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0) - { - conoutf("sound init failed (SDL_mixer): %s", (size_t)Mix_GetError()); - nosound = true; - }; - Mix_AllocateChannels(MAXCHAN); - #else - if(FSOUND_GetVersion()=0) { FSOUND_SetVolume(chan, (musicvol*MAXVOL)/255); FSOUND_SetPaused(chan, false); }; - } - else - { - conoutf("could not play music: %s", sn); - }; - #endif - }; -}; - -COMMAND(music, ARG_1STR); - -#ifdef USE_MIXER -vector samples; -#else -vector samples; -#endif - -cvector snames; - -int registersound(char *name) -{ - loopv(snames) if(strcmp(snames[i], name)==0) return i; - snames.add(newstring(name)); - samples.add(NULL); - return samples.length()-1; -}; - -COMMAND(registersound, ARG_1EST); - -void cleansound() -{ - if(nosound) return; - stopsound(); - #ifdef USE_MIXER - Mix_CloseAudio(); - #else - FSOUND_Close(); - #endif -}; - -VAR(stereo, 0, 1, 1); - -void updatechanvol(int chan, vec *loc) -{ - int vol = soundvol, pan = 255/2; - if(loc) - { - vdist(dist, v, *loc, player1->o); - vol -= (int)(dist*3*soundvol/255); // simple mono distance attenuation - if(vol<0) vol = 0; - 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 && chan5) return; // avoid bursts of sounds with heavy packetloss and in sp - if(n<0 || n>=samples.length()) { conoutf("unregistered sound: %d", n); return; }; - - if(!samples[n]) - { - sprintf_sd(buf)("packages/audio/sounds/%s.wav", snames[n]); - - #ifdef USE_MIXER - samples[n] = Mix_LoadWAV(path(buf)); - #else - samples[n] = FSOUND_Sample_Load(n, path(buf), FSOUND_LOOP_OFF, 0, 0); - #endif - - if(!samples[n]) { conoutf("failed to load sample: %s", buf); return; }; - }; - - #ifdef USE_MIXER - int chan = Mix_PlayChannel(-1, samples[n], 0); - #else - int chan = FSOUND_PlaySoundEx(FSOUND_FREE, samples[n], NULL, true); - #endif - if(chan<0) return; - if(loc) newsoundloc(chan, loc); - updatechanvol(chan, loc); - #ifndef USE_MIXER - FSOUND_SetPaused(chan, false); - #endif -}; - -void sound(int n) { playsound(n, NULL); }; -COMMAND(sound, ARG_1INT); +// sound.cpp: uses fmod on windows and sdl_mixer on unix (both had problems on the other platform) + + + +#include "cube.h" + + + +//#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 + + + +VARP(soundvol, 0, 255, 255); + +VARP(musicvol, 0, 128, 255); + +bool nosound = false; + + + +#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; + + void *stream = NULL; + +#else + + #include "fmod.h" + + #define MAXVOL 255 + + FMUSIC_MODULE *mod = NULL; + + FSOUND_STREAM *stream = NULL; + +#endif + + + +void stopsound() + +{ + + if(nosound) return; + + if(mod) + + { + + #ifdef USE_MIXER + + Mix_HaltMusic(); + + Mix_FreeMusic(mod); + + #else + + FMUSIC_FreeSong(mod); + + #endif + + mod = NULL; + + }; + + if(stream) + + { + + #ifndef USE_MIXER + + FSOUND_Stream_Close(stream); + + #endif + + stream = NULL; + + }; + +}; + + + +VAR(soundbufferlen, 128, 1024, 4096); + + + +void initsound() + +{ + + memset(soundlocs, 0, sizeof(soundloc)*MAXCHAN); + + #ifdef USE_MIXER + + if(Mix_OpenAudio(SOUNDFREQ, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0) + + { + + conoutf("sound init failed (SDL_mixer): %s", (size_t)Mix_GetError()); + + nosound = true; + + }; + + Mix_AllocateChannels(MAXCHAN); + + #else + + if(FSOUND_GetVersion()=0) { FSOUND_SetVolume(chan, (musicvol*MAXVOL)/255); FSOUND_SetPaused(chan, false); }; + + } + + else + + { + + conoutf("could not play music: %s", sn); + + }; + + #endif + + }; + +}; + + + +COMMAND(music, ARG_1STR); + + + +#ifdef USE_MIXER + +vector samples; + +#else + +vector samples; + +#endif + + + +cvector snames; + + + +int registersound(char *name) + +{ + + loopv(snames) if(strcmp(snames[i], name)==0) return i; + + snames.add(newstring(name)); + + samples.add(NULL); + + return samples.length()-1; + +}; + + + +COMMAND(registersound, ARG_1EST); + + + +void cleansound() + +{ + + if(nosound) return; + + stopsound(); + + #ifdef USE_MIXER + + Mix_CloseAudio(); + + #else + + FSOUND_Close(); + + #endif + +}; + + + +VAR(stereo, 0, 1, 1); + + + +void updatechanvol(int chan, vec *loc) + +{ + + int vol = soundvol, pan = 255/2; + + if(loc) + + { + + vdist(dist, v, *loc, player1->o); + + vol -= (int)(dist*3*soundvol/255); // simple mono distance attenuation + + if(vol<0) vol = 0; + + 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 && chan5) return; // avoid bursts of sounds with heavy packetloss and in sp + + if(n<0 || n>=samples.length()) { conoutf("unregistered sound: %d", n); return; }; + + + + if(!samples[n]) + + { + + sprintf_sd(buf)("packages/audio/sounds/%s.wav", snames[n]); + + + + #ifdef USE_MIXER + + samples[n] = Mix_LoadWAV(path(buf)); + + #else + + samples[n] = FSOUND_Sample_Load(n, path(buf), FSOUND_LOOP_OFF, 0, 0); + + #endif + + + + if(!samples[n]) { conoutf("failed to load sample: %s", (int) buf); return; }; + + }; + + + + #ifdef USE_MIXER + + int chan = Mix_PlayChannel(-1, samples[n], 0); + + #else + + int chan = FSOUND_PlaySoundEx(FSOUND_FREE, samples[n], NULL, true); + + #endif + + if(chan<0) return; + + if(loc) newsoundloc(chan, loc); + + updatechanvol(chan, loc); + + #ifndef USE_MIXER + + FSOUND_SetPaused(chan, false); + + #endif + +}; + + + +void sound(int n) { playsound(n, NULL); }; + +COMMAND(sound, ARG_1INT); +