This patch adds various miscellaneous enhancements to Cube (http://cubeengine.com). They are: o Various cheat codes (in the spirit of Doom's cheat codes). o Environment variables that alter the behavior of Cube (see the read_env()) function. o Ability to control what modes are allowed (see the CUBE_ALLOWED_MODE environment variable). o Various other changes. This patch can be applied with: patch -p 0 < cube-misc.diff Please send comments and questions to: Steven Elliott The latest verion of this patch can be found at: http://home.austin.rr.com/selliott4/cube This patch is subject to the same open source license as Cube (ZLIB like license). See the "LICENSE" section in the cube_source/readme.txt file in the Cube source for details. This is version 1.0 of this patch. --- src/client.cpp.orig 2005-08-13 21:16:30.000000000 -0500 +++ src/client.cpp 2005-09-23 23:14:09.000000000 -0500 @@ -21,8 +21,9 @@ bool allowedittoggle() { bool allow = !clienthost || gamemode==1; - if(!allow) conoutf("editing in multiplayer requires coopedit mode (1)"); - return allow; + if(!allow) conoutf("Editing in multiplayer requires coopedit mode (1). " + "You can do it anyway (cheating)."); + return 1; }; VARF(rate, 0, 0, 25000, if(clienthost && (!rate || rate>1000)) enet_host_bandwidth_limit (clienthost, rate, rate)); @@ -160,7 +161,7 @@ void server_err() { - conoutf("server network error, disconnecting..."); + conoutf("server network error, disconnecting at %s", time_str()); disconnect(); }; @@ -189,10 +190,22 @@ else localclienttoserver((ENetPacket *)packet); } + +VAR(updaterate, 0, 25, 10000); // Default of 25 FPS == 40 ms + void c2sinfo(dynent *d) // send update to the server { + int updatedelay; + if(clientnum<0) return; // we haven't had a welcome message from the server yet - if(lastmillis-lastupdate<40) return; // don't update faster than 25fps + + if (updaterate) + updatedelay = 1000 / updaterate; + else + updatedelay = 0; + if((!updatedelay) || (lastmillis-lastupdate < updatedelay)) + return; // don't update faster than the specified rate. + ENetPacket *packet = enet_packet_create (NULL, MAXTRANS, 0); uchar *start = packet->data; uchar *p = start+2; --- src/clientextras.cpp.orig 2005-08-13 21:16:32.000000000 -0500 +++ src/clientextras.cpp 2005-09-24 22:31:19.747246000 -0500 @@ -35,7 +35,16 @@ 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; if(hellpig) speed = 300/d->maxspeed; }; - if(hellpig) { n++; scale *= 32; mz -= 1.9f; }; + if(hellpig) + { + n++; + scale *= 32; + if (cube_scale_monsters) + mz -= cube_scale_monsters * (scale / 38.4) * 1.9f; + else + mz -= 1.9f; + } + 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); }; --- src/clientgame.cpp.orig 2005-08-14 18:49:56.000000000 -0500 +++ src/clientgame.cpp 2005-09-23 23:43:30.000000000 -0500 @@ -2,7 +2,6 @@ #include "cube.h" -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); }; @@ -12,6 +11,7 @@ dynent *player1 = newdynent(); // our client dvector players; // other clients +int starting_a_map; VARP(sensitivity, 0, 10, 10000); VARP(sensitivityscale, 1, 1, 10000); @@ -118,13 +118,14 @@ d->blocked = false; d->lifesequence = 0; d->state = CS_ALIVE; + d->friendly = false; spawnstate(d); return d; }; void respawnself() { - spawnplayer(player1); + spawnplayer(player1, true); showscores(false); }; @@ -273,7 +274,7 @@ int spawncycle = -1; int fixspawn = 2; -void spawnplayer(dynent *d) // place at random spawn. also used by monsters! +void spawnplayer(dynent *d, bool new_game) // place at random spawn. also used by monsters! { int r = fixspawn-->0 ? 4 : rnd(10)+1; loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1); @@ -292,10 +293,35 @@ d->o.z = 4; }; entinmap(d); - spawnstate(d); - d->state = CS_ALIVE; + + if (new_game) { + spawnstate(d); + d->state = CS_ALIVE; + } }; +void rnd_start() +{ + spawnplayer(player1, false); + conoutf("moved to random player start (cheating)"); +} + +COMMAND(rnd_start, ARG_NONE); + +void show_time() +{ + time_t now; + char *now_str; + + time(&now); + now_str = ctime(&now); + now_str[strlen(now_str) - 1] = 0; /* get rid of the \n */ + + conoutf("%s", ctime(&now)); +} + +COMMAND(show_time, ARG_NONE); + // 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; }; @@ -372,11 +398,13 @@ { if(isteam(a->team, player1->team)) { - conoutf("you got fragged by a teammate (%s)", a->name); + if (frag_mlevel >= 1) + conoutf("you got fragged by a teammate (%s)", a->name); } else { - conoutf("you got fragged by %s", a->name); + if (frag_mlevel >= 1) + conoutf("you got fragged by %s", a->name); }; }; }; @@ -433,11 +461,12 @@ void startmap(char *name) // called just after a map load { if(netmapstart() && m_sp) { gamemode = 0; conoutf("coop sp not supported yet"); }; - sleepwait = 0; + // sleepwait = 0; // Why get rid of commands? + starting_a_map = 5; monsterclear(); projreset(); spawncycle = -1; - spawnplayer(player1); + spawnplayer(player1, true); player1->frags = 0; loopv(players) if(players[i]) players[i]->frags = 0; resetspawns(); --- src/clients2c.cpp.orig 2005-08-22 17:10:26.000000000 -0500 +++ src/clients2c.cpp 2005-09-24 23:00:29.663218336 -0500 @@ -49,6 +49,27 @@ }; }; +/* Update various globals for the message level. The three digits of the + * message level have the following meaning: + * CFG + * C - Chat level + * F - Frag level + * S - Server level + * where 2 is the highest level and 0 results in no messages. So 222 shows + * all messages and 0 shows none. + */ +static void set_msglevel(int new_mlevel) +{ + chat_mlevel = (new_mlevel / 100) % 10; + frag_mlevel = (new_mlevel / 10) % 10; + server_mlevel = new_mlevel % 10; + + conoutf("new message levels: chat=%d frag=%d server=%d", + chat_mlevel, frag_mlevel, server_mlevel); +} + +VARF(msglevel, 0, 222, 222, set_msglevel(msglevel)); + void localservertoclient(uchar *buf, int len) // processes any updates from the server { if(ENET_NET_TO_HOST_16(*(ushort *)buf)!=len) neterr("packet length"); @@ -60,8 +81,16 @@ int cn = -1, type; dynent *d = NULL; bool mapchanged = false; + int count; + int key_count; + int challenge; + int response; - while(pname, text); + if (chat_mlevel >= 1) + conoutf("%s:\f %s", d->name, text); break; case SV_MAPCHANGE: @@ -213,12 +243,14 @@ if(isteam(player1->team, d->team)) { frags = -1; - conoutf("you fragged a teammate (%s)", d->name); + if (frag_mlevel >= 1) + conoutf("you fragged a teammate (%s)", d->name); } else { frags = 1; - conoutf("you fragged %s", d->name); + if (frag_mlevel >= 1) + conoutf("you fragged %s", d->name); }; addmsg(1, 2, SV_FRAGS, player1->frags += frags); } @@ -229,11 +261,14 @@ { if(isteam(a->team, d->name)) { - conoutf("%s fragged his teammate (%s)", a->name, d->name); + if (frag_mlevel >= 2) + conoutf("%s fragged his teammate (%s)", a->name, + d->name); } else { - conoutf("%s fragged %s", a->name, d->name); + if (frag_mlevel >= 2) + conoutf("%s fragged %s", a->name, d->name); }; }; }; @@ -315,7 +350,8 @@ break; case SV_CLIENTPING: - players[cn]->ping = getint(p); + if ((cn >= 0) && (cn < players.length())) + players[cn]->ping = getint(p); break; case SV_GAMEMODE: @@ -339,17 +375,33 @@ case SV_SERVMSG: sgetstr(); - conoutf("%s", text); + if (server_mlevel >= 1) + conoutf("%s", text); break; case SV_EXT: // so we can messages without breaking previous clients/servers, if necessary { - for(int n = getint(p); n; n--) getint(p); + for(int n = getint(p); n; n--) + { + conoutf("ext: n=%d: %d\n", n, getint(p)); + } break; }; default: - neterr("type"); + conoutf("Type %d not understood in clients2c.cpp", type); + + /* Dump out the data remaining until we abort. */ + count = 1; + while (p < end) + { + type = getint(p); + conoutf("Bad int #%d: %d", count, type); + count++; + } + + // We no longer want to abort, so neterr() is not called. return; }; + } }; --- src/cube.h.orig 2005-08-22 17:10:26.000000000 -0500 +++ src/cube.h 2005-09-23 23:14:09.000000000 -0500 @@ -127,6 +127,7 @@ vec attacktarget; // delayed attacks int anger; // how many times already hit by fellow monster string name, team; + bool friendly; // True if this is a friend. }; #define SAVEGAMEVERSION 4 // bump if dynent/netprotocol changes or any other savegame/demo data @@ -216,6 +217,35 @@ #define PI (3.1415927f) #define PI2 (2*PI) +// externs of variables that correspond to environment variables. +extern int cube_allowed_mode; +extern char *cube_base_map; +extern char *cube_greeting; +extern float cube_max_fps; +extern float cube_monst_mult; +extern float cube_player_mult; +extern float cube_scale_monsters; +extern int cube_fat_monsters; +extern int cube_friendly; +extern int cube_gun; +extern int cube_jumpy; +extern int cube_minutes; +extern int cube_monster; +extern int cube_no_blue; +extern int cube_no_splash; +extern int cube_num_monst; +extern int cube_screenshots; +extern int cube_sp_insta; +extern int cube_tsunami; + +// Globals that are not set by environment variables. +extern int chat_mlevel; +extern int frag_mlevel; +extern int server_mlevel; + +// other things that have to be extern as a result of the above +extern int nextmode; + // simplistic vector ops #define dotprod(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z) #define vmul(u,f) { (u).x *= (f); (u).y *= (f); (u).z *= (f); } --- src/entities.cpp.orig 2005-08-11 03:47:40.000000000 -0500 +++ src/entities.cpp 2005-09-24 21:58:24.077593440 -0500 @@ -34,21 +34,21 @@ if(OUTBORD(e.x, e.y)) continue; if(e.type!=CARROT) { - if(!e.spawned && e.type!=TELEPORT) continue; - if(e.typeTELEPORT) continue; - renderent(e, entmdlnames[e.type-I_SHELLS], (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/10.0f); + if(!e.spawned && e.type!=TELEPORT) continue; + if(e.typeTELEPORT) continue; + renderent(e, entmdlnames[e.type-I_SHELLS], (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/10.0f); // Spinning items. } - else switch(e.attr2) + else switch(e.attr2) { - case 1: - case 3: - continue; + 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; + 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; @@ -112,6 +112,47 @@ }; }; +void fake_quad() +{ + itemstat &is = itemstats[I_QUAD - I_SHELLS]; + + player1->quadmillis += is.add; + playsoundc(is.sound); + conoutf("you got the fake quad (cheating)"); +} + +COMMAND(fake_quad, ARG_NONE); + +void get_ammo() +{ + int ammo_type; + + for (ammo_type = I_SHELLS; ammo_type <= I_ROUNDS; ammo_type++) + { + itemstat &is = itemstats[ammo_type - I_SHELLS]; + + player1->ammo[ammo_type - I_SHELLS + 1] += is.add; + + if (ammo_type == I_ROUNDS) + playsoundc(is.sound); + } + + conoutf("got some ammo (cheating)"); +} + +COMMAND(get_ammo, ARG_NONE); + +void get_health() +{ + itemstat &is = itemstats[I_HEALTH - I_SHELLS]; + + player1->health += is.add; + playsoundc(is.sound); + conoutf("you got some health (cheating)"); +} + +COMMAND(get_health, ARG_NONE); + // these functions are called when the client touches the item void additem(int i, int &v, int spawnsec) @@ -154,12 +195,22 @@ int ammo = np*2; switch(ents[n].type) { +#ifdef FAST_RESPAWN + // Really fast spawning + case I_SHELLS: additem(n, d->ammo[1], 1); break; + case I_BULLETS: additem(n, d->ammo[2], 1); break; + case I_ROCKETS: additem(n, d->ammo[3], 1); break; + case I_ROUNDS: additem(n, d->ammo[4], 1); break; + case I_HEALTH: additem(n, d->health, 1); break; + case I_BOOST: additem(n, d->health, 1); break; +#else case I_SHELLS: additem(n, d->ammo[1], ammo); break; case I_BULLETS: additem(n, d->ammo[2], ammo); break; case I_ROCKETS: additem(n, d->ammo[3], ammo); break; case I_ROUNDS: additem(n, d->ammo[4], ammo); break; case I_HEALTH: additem(n, d->health, np*5); break; case I_BOOST: additem(n, d->health, 60); break; +#endif case I_GREENARMOUR: // (100h/100g only absorbs 166 damage) @@ -196,7 +247,7 @@ if(lastmillis-lastjumppad<300) break; lastjumppad = lastmillis; vec v = { (int)(char)ents[n].attr3/10.0f, (int)(char)ents[n].attr2/10.0f, ents[n].attr1/10.0f }; - player1->vel.z = 0; + player1->vel.z = 0; vadd(player1->vel, v); playsoundc(S_JUMPPAD); break; --- src/main.cpp.orig 2005-08-29 01:38:52.000000000 -0500 +++ src/main.cpp 2005-09-24 19:38:03.625696704 -0500 @@ -2,6 +2,9 @@ #include "cube.h" +extern int starting_a_map; +extern string clientmap; + void cleanup(char *msg) // single program exit point; { stop(); @@ -50,6 +53,7 @@ SDL_Surface *image; SDL_Surface *temp; int idx; + if(image = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0)) { if(temp = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0)) @@ -61,7 +65,7 @@ memcpy(dest, (char *)image->pixels+3*scr_w*(scr_h-1-idx), 3*scr_w); endianswap(dest, 3, scr_w); }; - sprintf_sd(buf)("screenshots/screenshot_%d.bmp", lastmillis); + sprintf_sd(buf)("screenshots/%s.bmp", clientmap); SDL_SaveBMP(temp, path(buf)); SDL_FreeSurface(temp); }; @@ -84,6 +88,34 @@ int islittleendian = 1; int framesinmap = 0; +static char *next_ss_map() +{ + static int idx = 0; + static FILE *maps_fp = NULL; + static char map_name[100]; + static int done = 0; + + if (done) + return NULL; + + if (!maps_fp) + maps_fp = fopen("maps", "r"); + if (!maps_fp) + { + done = 1; + return NULL; + } + + if (1 != fscanf(maps_fp, "%s", map_name)) + { + done = 1; + fclose(maps_fp); + return NULL; + } + + return map_name; +} + int main(int argc, char **argv) { bool dedicated = false; @@ -91,6 +123,8 @@ char *sdesc = "", *ip = "", *master = NULL, *passwd = ""; islittleendian = *((char *)&islittleendian); + read_env(); + #define log(s) conoutf("init: %s", s) log("sdl"); @@ -174,7 +208,7 @@ log("localconnect"); localconnect(); - changemap("metl3"); // if this map is changed, also change depthcorrect() + changemap(cube_base_map); // if this map is changed, also change depthcorrect() log("mainloop"); int ignore = 5; @@ -188,7 +222,8 @@ updateworld(millis); if(!demoplayback) serverslice((int)time(NULL), 0); static float fps = 30.0f; - fps = (1000.0f/curtime+fps*50)/51; + if (curtime) + fps = (1000.0f/curtime+fps*50)/51; computeraytable(player1->o.x, player1->o.y); readdepth(scr_w, scr_h); SDL_GL_SwapBuffers(); @@ -200,6 +235,23 @@ player1->yaw -= 5; }; gl_drawframe(scr_w, scr_h, fps); + + if (cube_screenshots) + { + if (starting_a_map && (starting_a_map-- == 1)) + { + char *next_map; + + screenshot(); + + next_map = next_ss_map(); + if (next_map) + changemap(next_map); + + starting_a_map = 0; + } + } + SDL_Event event; int lasttype = 0, lastbut = 0; while(SDL_PollEvent(&event)) @@ -229,9 +281,23 @@ break; }; }; + + /* At this point the frame has been rendered and all events have been + * processed. Assuming that "fps" is reflective of time it took to + * render this frame wait however long we have to to make this frame + * the target FPS. + */ + if (fps > cube_max_fps) + { + float delay = (1 / cube_max_fps) - (1 / fps); + struct timespec ts_delay; + + ts_delay.tv_sec = 0; + ts_delay.tv_nsec = 1000000000 * delay; + + nanosleep(&ts_delay, 0); + } }; quit(); return 1; }; - - --- src/monster.cpp.orig 2005-08-14 16:16:00.000000000 -0500 +++ src/monster.cpp 2005-09-24 22:38:55.374980056 -0500 @@ -7,6 +7,8 @@ VARF(skill, 1, 3, 10, conoutf("skill is now %d", skill)); +extern float rad(float x); + dvector &getmonsters() { return monsters; }; void restoremonsterstate() { loopv(monsters) if(monsters[i]->state==CS_DEAD) numkilled++; }; // for savegames @@ -32,6 +34,57 @@ { GUN_SLIMEBALL, 15, 100, 1, 0, 200, 400, 2, 13, 10, S_PAIND, S_DEATHD, "a goblin", "monster/goblin" }, }; +static dynent * find_monster(dynent *dyn, bool friendly, bool farthest) +{ + int monsters_idx; + dynent *a_monster; + dynent *best = NULL; + float distance_sq; + float best_sq; + + if (farthest) + { + best_sq = 0.0; + } + else + { + best_sq = 1000000000.0; + } + + for (monsters_idx = 0; monsters_idx < monsters.length(); monsters_idx++) + { + a_monster = monsters[monsters_idx]; + + if ((a_monster->state == CS_DEAD) || (a_monster == dyn) || + (friendly && (!a_monster->friendly)) || + ((!friendly) && a_monster->friendly)) + { + continue; + } + + distance_sq = (((dyn->o.x - a_monster->o.x) * + (dyn->o.x - a_monster->o.x)) + + ((dyn->o.y - a_monster->o.y) * + (dyn->o.y - a_monster->o.y)) + + ((dyn->o.z - a_monster->o.z) * + (dyn->o.z - a_monster->o.z))); + + if (farthest && (distance_sq > best_sq)) + { + best_sq = distance_sq; + best = a_monster; + } + else if ((!farthest) && (distance_sq < best_sq)) + { + best_sq = distance_sq; + best = a_monster; + } + } + + // return itself if we could not find one + return best ? best : dyn; +} + dynent *basicmonster(int type, int yaw, int state, int trigger, int move) { if(type>=NUMMONSTERTYPES) @@ -43,16 +96,34 @@ monstertype *t = &monstertypes[m->mtype = type]; m->eyeheight = 2.0f; m->aboveeye = 1.9f; - m->radius *= t->bscale/10.0f; - m->eyeheight *= t->bscale/10.0f; - m->aboveeye *= t->bscale/10.0f; + + if (cube_scale_monsters) { + m->radius *= cube_scale_monsters * t->bscale/10.0f; + m->eyeheight *= cube_scale_monsters * t->bscale/10.0f; + m->aboveeye *= cube_scale_monsters * t->bscale/10.0f; + } + else { + m->radius *= t->bscale/10.0f; + m->eyeheight *= t->bscale/10.0f; + m->aboveeye *= t->bscale/10.0f; + } + m->monsterstate = state; - if(state!=M_SLEEP) spawnplayer(m); + if(state!=M_SLEEP) spawnplayer(m, true); m->trigger = lastmillis+trigger; m->targetyaw = m->yaw = (float)yaw; m->move = move; m->enemy = player1; - m->gunselect = t->gun; + + if (cube_gun >= 0) + { + m->gunselect = cube_gun; + } + else + { + m->gunselect = t->gun; + } + m->maxspeed = (float)t->speed; m->health = t->health; m->armour = 0; @@ -62,6 +133,17 @@ m->state = CS_ALIVE; m->anger = 0; strcpy_s(m->name, t->name); + + if (cube_friendly) + { + m->friendly = true; + m->enemy = find_monster(m, true, false); + if (m->enemy == m) + { + m->enemy = player1; + } + } + monsters.add(m); return m; }; @@ -69,21 +151,48 @@ void spawnmonster() // spawn a random monster according to freq distribution in DMSP { int n = rnd(TOTMFREQ), type; - for(int i = 0; ; i++) if((n -= monstertypes[i].freq)<0) { type = i; break; }; + + if (cube_monster >= 0) + { + type = cube_monster; + } + else + { + for(int i = 0; ; i++) + { + if((n -= monstertypes[i].freq)<0) + { + type = i; + break; + } + } + } + basicmonster(type, rnd(360), M_SEARCH, 1000, 1); }; void monsterclear() // called after map start of when toggling edit mode to reset/spawn all monsters to initial state { + int num_monst; loopv(monsters) gp()->dealloc(monsters[i], sizeof(dynent)); monsters.setsize(0); numkilled = 0; monstertotal = 0; spawnremain = 0; + + if (cube_num_monst == -2) + { + num_monst = skill * 10; + } + else + { + num_monst = cube_num_monst; + } if(m_dmsp) { nextmonster = mtimestart = lastmillis+10000; - monstertotal = spawnremain = gamemode<0 ? skill*10 : 0; + monstertotal = spawnremain = gamemode<0 ? num_monst : 0; + conoutf("Will spawn %d monsters", monstertotal); } else if(m_classicsp) { @@ -156,9 +265,116 @@ while(m->yaw>angle+180.0f) m->yaw -= 360.0f; }; +void make_friend() +{ + dynent *new_friend; + + new_friend = find_monster(player1, false, false); + if (new_friend == player1) + { + conoutf("there are no more friends to make"); + } + else + { + new_friend->enemy = find_monster(new_friend, false, false); + new_friend->jumpnext = true; // To make it odvious which one. + new_friend->friendly = true; + + playsound(S_ITEMHEALTH, &new_friend->o); + conoutf("you just made a new friend (cheating)"); + } +} + +COMMAND(make_friend, ARG_NONE); + +void teleport_friend(bool farthest) +{ + dynent *best_friend; + + best_friend = find_monster(player1, true, farthest); + if (best_friend == player1) + { + conoutf("there are no friends to teleport"); + } + else + { + best_friend->o = player1->o; + best_friend->o.z += 1.0; + + best_friend->o.x += 5.0 * sin(rad(player1->yaw)); + best_friend->o.y -= 5.0 * cos(rad(player1->yaw)); + + best_friend->yaw = player1->yaw; + + best_friend->enemy = find_monster(best_friend, false, false); + + playsound(S_TELEPORT, &best_friend->o); + + // The following would be more efficient if conoutf supported '%s'. + if (farthest) + conoutf("you just teleported your farthest friend (cheating)"); + else + conoutf("you just teleported your closest friend (cheating)"); + } +} + +void teleport_closest_friend() +{ + teleport_friend(false); +} + +COMMAND(teleport_closest_friend, ARG_NONE); + +void teleport_farthest_friend() +{ + teleport_friend(true); +} + +COMMAND(teleport_farthest_friend, ARG_NONE); + void monsteraction(dynent *m) // main AI thinking routine, called every frame for every monster { - if(m->enemy->state==CS_DEAD) { m->enemy = player1; m->anger = 0; }; + if (cube_jumpy && !rnd(cube_jumpy)) + { + m->jumpnext = true; + } + + if((m->enemy->state==CS_DEAD) || (m->friendly && m->enemy->friendly)) + { + if (m->friendly) + { + m->enemy = find_monster(m, false, false); + if (m->enemy == m) + { + /* Use a friend is no enemy. */ + m->enemy = find_monster(m, true, false); + } + } + else + { + m->enemy = player1; + } + m->anger = 0; + } +#ifdef RETREATING_MONSTERS + anti_targetyaw = m->targetyaw + 180.0; + if (anti_targetyaw > 360.0) + { + anti_targetyaw -= 360.0; + } + normalise(m, anti_targetyaw); + if(anti_targetyaw>m->yaw) // slowly turn monster towards his target + { + m->yaw += curtime*0.5f; + if(anti_targetyawyaw) m->yaw = anti_targetyaw; + } + else + { + m->yaw -= curtime*0.5f; + if(anti_targetyaw>m->yaw) m->yaw = anti_targetyaw; + }; + +#else normalise(m, m->targetyaw); if(m->targetyaw>m->yaw) // slowly turn monster towards his target { @@ -170,6 +386,7 @@ m->yaw -= curtime*0.5f; if(m->targetyaw>m->yaw) m->yaw = m->targetyaw; }; +#endif vdist(disttoenemy, vectoenemy, m->o, m->enemy->o); m->pitch = atan2(m->enemy->o.z-m->o.z, disttoenemy)*180/PI; @@ -183,7 +400,27 @@ } else if(m->triggermonsterstate!=M_HOME || !rnd(5))) // search for a way around (common) { - m->targetyaw += 180+rnd(180); // patented "random walk" AI pathfinding (tm) ;) +#ifdef SPREAD_OUT + if (rnd(2)) + { + dynent *closest_monster = find_monster(m, false, false); + // Move away from the closest monster. + float closest_monster_ayaw = -(float)atan2(closest_monster->o.x - m->o.x, closest_monster->o.y - m->o.y)/PI*180+360; + + if (closest_monster_ayaw > 360.0) + { + closest_monster_ayaw -= 360.0; + } + + m->targetyaw = closest_monster_ayaw; + } + else + { + m->targetyaw += 180+rnd(180); + } +#endif + // patented "random walk" AI pathfinding (tm) ;) + m->targetyaw += 180+rnd(180); transition(m, M_SEARCH, 1, 400, 1000); }; }; @@ -265,7 +502,7 @@ if(anger>=monstertypes[m->mtype].loyalty) m->enemy = d; // monster infight if very angry }; } - else // player hit us + else if(!m->friendly) // Player hit us. Ignore if friendly. { m->anger = 0; m->enemy = d; @@ -275,11 +512,28 @@ { m->state = CS_DEAD; m->lastaction = lastmillis; - numkilled++; - player1->frags = numkilled; + + if (!cube_sp_insta || (cube_sp_insta && (d == player1))) + { + numkilled++; + player1->frags = numkilled; + } + playsound(monstertypes[m->mtype].diesound, &m->o); - int remain = monstertotal-numkilled; - if(remain>0 && remain<=5) conoutf("only %d monster(s) remaining", remain); + + if(!cube_sp_insta) + { + /* The monsters will keep being replaced. */ + int remain = monstertotal-numkilled; + if(remain>0 && remain<=5) conoutf("only %d monster(s) remaining", + remain); + } + + if (cube_sp_insta) + { + /* Let another monster be spawned to make up for the dead one. */ + spawnremain++; + } } else { @@ -302,9 +556,16 @@ if(spawnremain--==monstertotal) conoutf("The invasion has begun!"); nextmonster = lastmillis+1000; spawnmonster(); + + if ((!cube_sp_insta) && (!spawnremain)) + conoutf("All %d monsters spawned", monstertotal); }; - if(monstertotal && !spawnremain && numkilled==monstertotal) endsp(true); + if((!cube_sp_insta) && monstertotal && !spawnremain && + numkilled==monstertotal) + { + endsp(true); + } loopv(ents) // equivalent of player entity touch, but only teleports are used { @@ -334,5 +595,17 @@ void monsterrender() { - loopv(monsters) renderclient(monsters[i], false, monstertypes[monsters[i]->mtype].mdlname, monsters[i]->mtype==5, monstertypes[monsters[i]->mtype].mscale/10.0f); + char *cube_scale_monsters_str; + + if (cube_scale_monsters) + loopv(monsters) renderclient(monsters[i], monsters[i]->friendly, + monstertypes[monsters[i]->mtype].mdlname, + monsters[i]->mtype==5, + cube_scale_monsters * + monstertypes[monsters[i]->mtype].mscale/10.0f); + else + loopv(monsters) renderclient(monsters[i], monsters[i]->friendly, + monstertypes[monsters[i]->mtype].mdlname, + monsters[i]->mtype==5, + monstertypes[monsters[i]->mtype].mscale/10.0f); }; --- src/physics.cpp.orig 2005-08-14 18:37:44.000000000 -0500 +++ src/physics.cpp 2005-09-23 23:14:09.000000000 -0500 @@ -62,6 +62,21 @@ // spawn is a dirty side effect used in spawning // drop & rise are supplied by the physics below to indicate gravity/push for current mini-timestep + +static int collide_always = 0; + +void collide_toggle() +{ + collide_always = !collide_always; + + if (collide_always) + conoutf("Always collide is now set (cheating)"); + else + conoutf("Always collide is now unset"); +} + +COMMAND(collide_toggle, ARG_NONE); + bool collide(dynent *d, bool spawn, float drop, float rise) { const float fx1 = d->o.x-d->radius; // figure out integer cube rectangle this entity covers in map @@ -75,6 +90,10 @@ float hi = 127, lo = -128; float minfloor = (d->monsterstate && !spawn && d->health>100) ? d->o.z-d->eyeheight-4.5f : -1000.0f; // big monsters are afraid of heights, unless angry :) + // Following only applies to players + if (collide_always && (d->monsterstate == M_NONE)) + return true; + for(int x = x1; x<=x2; x++) for(int y = y1; y<=y2; y++) // collide with map { if(OUTBORD(x,y)) return false; @@ -181,15 +200,50 @@ }; }; +void slower() +{ + char buff[50]; + + cube_player_mult /= 1.1; + cube_monst_mult /= 1.1; + + sprintf(buff, "player speed is now %g", cube_player_mult); + conoutf(buff); +} + +void faster() +{ + char buff[50]; + + cube_player_mult *= 1.1; + cube_monst_mult *= 1.1; + + sprintf(buff, "player speed is now %g", cube_player_mult); + conoutf(buff); +} + + +COMMAND(slower, ARG_NONE); +COMMAND(faster, ARG_NONE); + // main physics routine, moves a player/monster for a curtime step // moveres indicated the physics precision (which is lower for monsters and multiplayer prediction) // local is false for multiplayer prediction +extern float water_z(int v1, float v2, int v3, float t); + void moveplayer(dynent *pl, int moveres, bool local, int curtime) { - const bool water = hdr.waterlevel>pl->o.z-0.5f; + bool water; const bool floating = (editmode && local) || pl->state==CS_EDITING; + if (cube_tsunami) + water = water_z((float)pl->o.x, hdr.waterlevel, + (float)pl->o.y, + lastmillis/300.0f)>pl->o.z-0.5f; + else + water = hdr.waterlevel>pl->o.z-0.5f; + vec d; // vector of direction we ideally want to move in d.x = (float)(pl->move*cos(rad(pl->yaw-90))); @@ -250,7 +304,8 @@ }; const float gravity = 20; - const float f = 1.0f/moveres; + const float f = ((pl->monsterstate == M_NONE) ? + cube_player_mult/moveres : cube_monst_mult/moveres); float dropf = ((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 @@ -310,9 +365,24 @@ }; // play sounds on water transitions - - if(!pl->inwater && water) { playsound(S_SPLASH2, &pl->o); pl->vel.z = 0; } - else if(pl->inwater && !water) playsound(S_SPLASH1, &pl->o); + + { + if(!pl->inwater && water) + { + if (!cube_no_splash) + { + playsound(S_SPLASH2, &pl->o); + } + pl->vel.z = 0; + } + else if(pl->inwater && !water) + { + if (!cube_no_splash) + { + playsound(S_SPLASH1, &pl->o); + } + } + } pl->inwater = water; }; --- src/protos.h.orig 2005-08-16 05:25:10.000000000 -0500 +++ src/protos.h 2005-09-23 23:14:09.000000000 -0500 @@ -80,7 +80,7 @@ extern void startmap(char *name); extern void changemap(char *name); extern void initclient(); -extern void spawnplayer(dynent *d); +extern void spawnplayer(dynent *d, bool new_game); extern void selfdamage(int damage, int actor, dynent *act); extern dynent *newdynent(); extern char *getclientmap(); @@ -213,6 +213,10 @@ extern void sendmaps(int n, string mapname, int mapsize, uchar *mapdata); extern ENetPacket *recvmap(int n); +// whatever +char * time_str(); +void read_env(); + // weapon extern void selectgun(int a = -1, int b = -1, int c =-1); extern void shoot(dynent *d, vec &to); --- src/rendercubes.cpp.orig 2005-08-16 05:18:10.000000000 -0500 +++ src/rendercubes.cpp 2005-09-23 23:14:09.000000000 -0500 @@ -266,10 +266,20 @@ VAR(watersubdiv, 1, 4, 64); VARF(waterlevel, -128, -128, 127, if(!noteditmode()) hdr.waterlevel = waterlevel); +float water_z(int v1, float v2, int v3, float t) +{ + if (cube_tsunami) { + return v2-(float)sin(v1*v3*0.0002+t*0.2)*20.0f; + } + else { + return v2-(float)sin(v1*v3*0.1+t)*0.2f; + } +} + inline void vertw(int v1, float v2, int v3, sqr *c, float t1, float t2, float t) { vertcheck(); - vertf((float)v1, v2-(float)sin(v1*v3*0.1+t)*0.2f, (float)v3, c, t1, t2); + vertf((float)v1, water_z(v1, v2, v3, t), (float)v3, c, t1, t2); }; inline float dx(float x) { return x+(float)sin(x*2+lastmillis/1000.0f)*0.04f; }; --- src/renderextras.cpp.orig 2005-08-22 00:27:42.000000000 -0500 +++ src/renderextras.cpp 2005-09-24 19:52:00.288504624 -0500 @@ -336,10 +336,19 @@ glPopMatrix(); glPushMatrix(); glOrtho(0, VIRTW*3/2, VIRTH*3/2, 0, -1, 1); - draw_textf("fps %d", 3200, 2390, 2, curfps); + draw_textf(" x %d", 3200, 2320, 2, (int)player1->o.x); + draw_textf(" y %d", 3200, 2390, 2, (int)player1->o.y); + draw_textf(" z %d", 3200, 2460, 2, (int)player1->o.z); + draw_textf("yaw %d", 3200, 2530, 2, (int)player1->yaw); + draw_textf("fps %d", 3200, 2600, 2, curfps); +#if GEEKY_DISPLAY + // This stuff is not very intersting. Adjust coordinates if it is + // to be displayed with the above. + draw_textf("lod %d", 3200, 2390, 2, lod_factor()); draw_textf("wqd %d", 3200, 2460, 2, nquads); draw_textf("wvt %d", 3200, 2530, 2, curvert); draw_textf("evt %d", 3200, 2600, 2, xtraverts); +#endif }; glPopMatrix(); --- src/rendergl.cpp.orig 2005-08-16 05:25:10.000000000 -0500 +++ src/rendergl.cpp 2005-09-24 20:12:30.164535048 -0500 @@ -299,18 +299,63 @@ glDisable(GL_CULL_FACE); }; +int reveal_all_ents = 0; + +void reveal_all_ents_toggle() +{ + // Enabling and disabling the fog in this function does not seem to work. + + reveal_all_ents = !reveal_all_ents; + if (reveal_all_ents) + { + conoutf("revealing all entities (cheating)"); + } + else + { + conoutf("Not revealing all entities"); + } +} + +COMMAND(reveal_all_ents_toggle, ARG_NONE); + +extern float water_z(int v1, float v2, int v3, float t); + void gl_drawframe(int w, int h, float curfps) { float hf = hdr.waterlevel-0.3f; float fovy = (float)fov*h/w; float aspect = w/(float)h; - bool underwater = player1->o.zo.zo.x, hf, + (float)player1->o.y, + lastmillis/300.0f); + else + underwater = player1->o.z>16)/256.0f, ((fogcolour>>8)&255)/256.0f, (fogcolour&255)/256.0f, 1.0f }; glFogfv(GL_FOG_COLOR, fogc); - glClearColor(fogc[0], fogc[1], fogc[2], 1.0f); + + if (reveal_all_ents) + { + glClearColor(0.5, 0.5, 0.5, 1.0f); + } + else + { + glClearColor(fogc[0], fogc[1], fogc[2], 1.0f); + } if(underwater) { @@ -319,9 +364,13 @@ glFogi(GL_FOG_START, 0); glFogi(GL_FOG_END, (fog+96)/8); }; - - glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT); + if (reveal_all_ents) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // This is slow. + else + glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT); + + // The next 4 GL funcs don't seem to do anything. glMatrixMode(GL_PROJECTION); glLoadIdentity(); int farplane = fog*5/2; @@ -330,6 +379,7 @@ transplayer(); + // Without this everything is smooth. glEnable(GL_TEXTURE_2D); int xs, ys; @@ -346,7 +396,8 @@ setupworld(); - renderstripssky(); + if (!reveal_all_ents) + renderstripssky(); glLoadIdentity(); glRotated(player1->pitch, -1.0, 0.0, 0.0); @@ -363,7 +414,8 @@ overbright(2); - renderstrips(); + if (!reveal_all_ents) + renderstrips(); // Walls and stuff xtraverts = 0; --- src/rendermd2.cpp.orig 2004-05-12 00:18:14.000000000 -0500 +++ src/rendermd2.cpp 2005-09-23 23:14:09.000000000 -0500 @@ -113,6 +113,8 @@ mverts[frame] = new vec[numVerts]; md2_frame *cf = (md2_frame *) ((char*)frames+frameSize*frame); float sc = 16.0f/scale; + float radius; + loop(vi, numVerts) { uchar *cv = (uchar *)&cf->vertices[vi].vertex; @@ -120,6 +122,17 @@ v->x = (snap(sn, cv[0]*cf->scale[0])+cf->translate[0])/sc; v->y = -(snap(sn, cv[1]*cf->scale[1])+cf->translate[1])/sc; v->z = (snap(sn, cv[2]*cf->scale[2])+cf->translate[2])/sc; + + if (cube_fat_monsters) + { + radius = sqrt((v->x*v->x) + (v->y*v->y) + (v->z*v->z)); + if (radius) + { + v->x = (v->x + (v->x/radius)) / 2; + v->y = (v->y + (v->y/radius)) / 2; + v->z = (v->z + (v->z/radius)) / 2; + } + } }; }; @@ -263,8 +276,8 @@ light.y = s->g/ll+of; light.z = s->b/ll+of; }; - - if(teammate) + + if(!cube_no_blue && teammate) { light.x *= 0.6f; light.y *= 0.7f; --- src/savegamedemo.cpp.orig 2005-08-13 21:16:32.000000000 -0500 +++ src/savegamedemo.cpp 2005-09-23 23:14:09.000000000 -0500 @@ -66,7 +66,7 @@ void savegame(char *name) { - if(!m_classicsp) { conoutf("can only save classic sp games"); return; }; + //if(!m_classicsp) { conoutf("can only save classic sp games"); return; }; sprintf_sd(fn)("savegames/%s.csgz", name); savestate(fn); stop(); --- src/serverbrowser.cpp.orig 2004-05-09 19:20:40.000000000 -0500 +++ src/serverbrowser.cpp 2005-09-23 23:14:09.000000000 -0500 @@ -151,7 +151,7 @@ serverinfo &si = servers.insert(0, serverinfo()); strcpy_s(si.name, servername); si.full[0] = 0; - si.mode = 0; + si.mode = (cube_allowed_mode == -2) ? 0 : cube_allowed_mode; si.numplayers = 0; si.ping = 9999; si.protocol = 0; --- src/server.cpp.orig 2005-08-22 17:10:26.000000000 -0500 +++ src/server.cpp 2005-09-24 23:06:43.868330512 -0500 @@ -3,6 +3,34 @@ #include "cube.h" +// Some globals were moved here to make the visible to all exes. +int nextmode = 0; // nextmode becomes gamemode after next map load + +int cube_allowed_mode; +char *cube_base_map; +char *cube_greeting; +float cube_max_fps; +float cube_monst_mult; +float cube_player_mult; +float cube_scale_monsters; +int cube_fat_monsters; +int cube_friendly; +int cube_gun; +int cube_jumpy; +int cube_minutes; +int cube_monster; +int cube_no_blue; +int cube_no_splash; +int cube_num_monst; +int cube_screenshots; +int cube_sp_insta; +int cube_tsunami; + +// Globals that are not set by environment variables. +int chat_mlevel = 2; +int frag_mlevel = 2; +int server_mlevel = 2; + enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; struct client // server side version of "dynent" type @@ -29,7 +57,7 @@ vector sents; bool notgotitems = true; // true when map has changed and waiting for clients to send item -int mode = 0; +int mode; void restoreserverstate(vector &ents) // hack: called from savegame code, only works in SP { @@ -156,105 +184,182 @@ disconnect_client(sender, "packet length"); return; }; - + uchar *end = packet->data+packet->dataLength; uchar *p = packet->data+2; char text[MAXTRANS]; int cn = -1, type; - while(p 0) + { + minremain = cube_minutes; + } + if (!lastsec) + { + lastsec = time(NULL); + } + mapend = lastsec+minremain*60; + interm = 0; + strcpy_s(smapname, text); + resetitems(); + sender = -1; + break; + }; - case SV_ITEMLIST: - { - int n; - while((n = getint(p))!=-1) if(notgotitems) + case SV_ITEMLIST: { - server_entity se = { false, 0 }; - while(sents.length()<=n) sents.add(se); - sents[n].spawned = true; + int n; + + while(((p < end) && ((n = getint(p))!=-1))) + if(notgotitems) + { + server_entity se = { false, 0 }; + while(sents.length()<=n) sents.add(se); + sents[n].spawned = true; + }; + notgotitems = false; + break; }; - notgotitems = false; - break; - }; - case SV_ITEMPICKUP: - { - int n = getint(p); - pickup(n, getint(p), sender); - break; - }; + case SV_ITEMPICKUP: + { + int n = getint(p); + pickup(n, getint(p), sender); - case SV_PING: - send2(false, cn, SV_PONG, getint(p)); - break; + break; + }; - case SV_POS: - { - cn = getint(p); - if(cn<0 || cn>=clients.length() || clients[cn].type==ST_EMPTY) + case SV_PING: { - disconnect_client(sender, "client num"); - return; + send2(false, cn, SV_PONG, getint(p)); + break; + } + + case SV_POS: + { + cn = getint(p); + if(cn<0 || cn>=clients.length() || clients[cn].type==ST_EMPTY) + { + disconnect_client(sender, "client num"); + return; + }; + + int size = msgsizelookup(type); + assert(size!=-1); + loopi(size-2) + { +#ifdef DEBUG + printf("discarded SV_POS int: %d\n", getint(p)); +#else + getint(p); +#endif + } + break; }; - int size = msgsizelookup(type); - assert(size!=-1); - loopi(size-2) getint(p); - break; - }; - case SV_SENDMAP: - { - sgetstr(); - int mapsize = getint(p); - sendmaps(sender, text, mapsize, p); - return; - } + case SV_SENDMAP: + { + sgetstr(); + int mapsize = getint(p); + sendmaps(sender, text, mapsize, p); + return; + } - case SV_RECVMAP: - send(sender, recvmap(sender)); - return; + case SV_RECVMAP: + send(sender, recvmap(sender)); + return; - case SV_EXT: // allows for new features that require no server updates - { - for(int n = getint(p); n; n--) getint(p); - break; - }; + case SV_EXT: // allows for new features that require no server updates + { + for(int n = getint(p); n; n--) getint(p); + break; + }; - default: - { - int size = msgsizelookup(type); - if(size==-1) { disconnect_client(sender, "tag type"); return; }; - loopi(size-1) getint(p); - }; - }; + default: + { + int size = msgsizelookup(type); + if(size==-1) + { + printf("unknown type=%d size=%d\n", type, size); + + // Try to remain connected. Be robust. + // disconnect_client(sender, "tag type"); + // return; + + // Discard the remaining ints in this message. + while(pend) { disconnect_client(sender, "end of packet"); return; }; multicast(packet, sender); @@ -325,7 +430,7 @@ smapname[0] = 0; resetvotes(); resetitems(); - mode = 0; + mode = (cube_allowed_mode == -2) ? 0 : cube_allowed_mode; mapreload = false; minremain = 10; mapend = lastsec+minremain*60; @@ -348,8 +453,8 @@ }; lastsec = seconds; - - if((mode>1 || (mode==0 && nonlocalclients)) && seconds>mapend-minremain*60) checkintermission(); + + if((mode>1 || cube_sp_insta || (mode==0 && nonlocalclients)) && seconds>mapend-minremain*60) checkintermission(); if(interm && seconds>interm) { interm = 0; --- src/serverutil.cpp.orig 2005-08-22 17:10:26.000000000 -0500 +++ src/serverutil.cpp 2005-09-24 23:02:15.599113632 -0500 @@ -2,8 +2,140 @@ #include "cube.h" +#include + // all network traffic is in 32bit ints, which are then compressed using the following simple scheme (assumes that most values are small). +/* not thread safe */ +char * time_str() +{ + static char buff[100]; + struct timeval now; + + gettimeofday(&now, NULL); + sprintf(buff, "%d.%06d", now.tv_sec, now.tv_usec); + + return buff; +} + +void read_env() +{ + char *env_str; + + if (!(cube_base_map = getenv("CUBE_BASE_MAP"))) + cube_base_map = "metl3"; + + cube_sp_insta = getenv("CUBE_SP_INSTA") ? 1 : 0; + + cube_greeting = getenv("CUBE_GREETING"); + + env_str = getenv("CUBE_SCALE_MONSTERS"); + if (env_str) + { + cube_scale_monsters = atof(env_str); + } + + cube_screenshots = getenv("CUBE_SCREENSHOTS") ? 1 : 0; + + env_str = getenv("CUBE_MAX_FPS"); + if (env_str) + { + cube_max_fps = (float)atof(env_str); + } + else + { + cube_max_fps = 1000.0; + } + + env_str = getenv("CUBE_GUN"); + if (env_str) + { + cube_gun = atoi(env_str); + } + else + { + cube_gun = -1; + } + + env_str = getenv("CUBE_SCALE_MONSTERS"); + if (env_str) + { + cube_scale_monsters = atof(env_str); + } + else + { + cube_scale_monsters = 0; + } + + cube_friendly = getenv("CUBE_FRIENDLY") ? 1 : 0; + + cube_tsunami = getenv("CUBE_TSUNAMI") ? 1 : 0; + + env_str = getenv("CUBE_PLAYER_MULT"); + cube_player_mult = env_str ? atof(env_str) : 1.0; + + env_str = getenv("CUBE_MONST_MULT"); + cube_monst_mult = env_str ? atof(env_str) : 1.0; + + cube_no_splash = getenv("CUBE_NO_SPLASH") ? 1 : 0; + + cube_fat_monsters = getenv("CUBE_FAT_MONSTERS") ? 1 : 0; + + env_str = getenv("CUBE_MONSTER"); + if (env_str) + { + cube_monster = atoi(env_str); + } + else + { + cube_monster = -2; + } + + cube_no_blue = getenv("CUBE_NO_BLUE") ? 1 : 0; + + env_str = getenv("CUBE_NUM_MONST"); + if (env_str) + { + cube_num_monst = atoi(env_str); + } + else + { + cube_num_monst = -2; + } + + env_str = getenv("CUBE_MINUTES"); + if (env_str) + { + cube_minutes = atoi(env_str); + } + else + { + cube_minutes = -2; + } + + env_str = getenv("CUBE_JUMPY"); + if (env_str) + { + cube_jumpy = atoi(env_str); + } + else + { + cube_jumpy = 0; + } + + cube_sp_insta = getenv("CUBE_SP_INSTA") ? 1 : 0; + + env_str = getenv("CUBE_ALLOWED_MODE"); + if (env_str) + { + cube_allowed_mode = atoi(env_str); + } + else + { + cube_allowed_mode = -2; + } +} + void putint(uchar *&p, int n) { if(n<128 && n>-127) { *p++ = n; } @@ -97,7 +229,9 @@ { int uprate = 0, maxcl = 4; char *sdesc = "", *ip = "", *master = NULL, *passwd = ""; - + + read_env(); + for(int i = 1; i +#include +#include void backup(char *name, char *backupname) { @@ -229,11 +232,42 @@ hdr.waterlevel = -100000; }; ents.setsize(0); + + char cube_map_text_name[100]; + snprintf(cube_map_text_name, sizeof(cube_map_text_name), +#ifdef WIN32 + "\\temp\\%s.ctxt", +#else + "/tmp/%s.ctxt", +#endif + mname); + cube_map_text_name[sizeof(cube_map_text_name) - 1] = 0; + + struct stat cube_map_text_st; + FILE *cube_map_text_h = NULL; + if (stat(cube_map_text_name, &cube_map_text_st) < 0) { + cube_map_text_h = fopen(cube_map_text_name, "a"); + } + + if (cube_map_text_h) { + fprintf(cube_map_text_h, "Entities for %s:\n", mname); + } + loopi(hdr.numents) { entity &e = ents.add(); gzread(f, &e, sizeof(persistent_entity)); endianswap(&e, sizeof(short), 4); + + if (cube_map_text_h) { +#ifdef DOUBLE_IT + e.z *= 2; +#endif + fprintf(cube_map_text_h, "etype=%d x=%d y=%d z=%d " + "attr1=%d attr2=%d attr3=%d attr4=%d\n", + e.type, e.x, e.y, e.z, e.attr1, e.attr2, e.attr3, e.attr4); + } + e.spawned = false; if(e.type==LIGHT) { @@ -243,6 +277,13 @@ }; free(world); setupworld(hdr.sfactor); + + if (cube_map_text_h) { + fprintf(cube_map_text_h, "\nCubes for %s:\n", mname); + fprintf(cube_map_text_h, "sfactor=%d ssize=%d cubicsize=%d " + "mipsize=%d\n", sfactor, ssize, cubicsize, mipsize); + } + char texuse[256]; loopi(256) texuse[i] = 0; sqr *t = NULL; @@ -305,7 +346,26 @@ t = s; texuse[s->wtex] = 1; if(!SOLID(s)) texuse[s->utex] = texuse[s->ftex] = texuse[s->ctex] = 1; + +#ifdef DOUBLE_IT + s->floor *= 2; + s->ceil *= 2; +#endif + if (cube_map_text_h) { + fprintf(cube_map_text_h, "ctype=%d x=%d y=%d floor=%d ceil=%d " + "wtex=%d ftex=%d ctex=%d r=%d g=%d b=%d vdelta=%d " + "defer=%d occuled=%d utex=%d tag=%d\n", + s->type, k % ssize, k / ssize, + s->floor, s->ceil, s->wtex, s->ftex, s->ctex, + s->r, s->g, s->b, s->vdelta, s->defer, s->occluded, + s->utex, s->tag); + } }; + + if (cube_map_text_h) { + fclose(cube_map_text_h); + } + gzclose(f); calclight(); settagareas(); --- src/worldocull.cpp.orig 2004-03-04 02:34:00.000000000 -0600 +++ src/worldocull.cpp 2005-09-23 23:14:09.000000000 -0500 @@ -68,8 +68,13 @@ inline float ca(float x, float y) { return x>y ? y/x : 2-x/y; }; inline float ma(float x, float y) { return x==0 ? (y>0 ? 2 : -2) : y/x; }; +extern int reveal_all_ents; + int isoccluded(float vx, float vy, float cx, float cy, float csize) // v = viewer, c = cube to test { + if (reveal_all_ents) + return 0; // Show the ent no matter what. + if(!ocull) return 0; float nx = vx, ny = vy; // n = point on the border of the cube that is closest to v