From 482237bdca7345d1e88af05c2f911b9e2fe5ced4 Mon Sep 17 00:00:00 2001 From: driAn Date: Wed, 12 Apr 2006 18:00:57 +0000 Subject: [PATCH] *** empty log message *** git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@240 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/src/Makefile | 2 +- source/src/client.cpp | 647 +++++++++-------- source/src/clientextras.cpp | 84 ++- source/src/clientgame.cpp | 1317 +++++++++++++++++++---------------- source/src/clients2c.cpp | 84 ++- source/src/cube.h | 735 +++++++++---------- source/src/entities.cpp | 629 +++++++++-------- source/src/main.cpp | 25 +- source/src/menus.cpp | 444 ++++++------ source/src/physics.cpp | 966 ++++++++++++------------- source/src/protos.h | 19 +- source/src/renderextras.cpp | 54 +- source/src/rendergl.cpp | 818 +++++++++++----------- source/src/rendermd3.cpp | 202 +++--- source/src/server.cpp | 352 +++++++++- source/src/serverms.cpp | 7 +- source/src/serverutil.cpp | 14 +- source/src/sound.cpp | 693 ++++++------------ source/src/tools.cpp | 12 +- source/src/tools.h | 29 +- source/src/weapon.cpp | 1188 ++++++++++++++++--------------- source/src/world.cpp | 48 +- 22 files changed, 4375 insertions(+), 3994 deletions(-) diff --git a/source/src/Makefile b/source/src/Makefile index 316a06b3..17b98695 100644 --- a/source/src/Makefile +++ b/source/src/Makefile @@ -19,7 +19,7 @@ CLIENT_OBJS= \ renderextras.o \ rendergl.o \ rendermd2.o \ - rendermd3.o \ + rendermd3.o \ renderparticles.o \ rendertext.o \ rndmap.o \ diff --git a/source/src/client.cpp b/source/src/client.cpp index b992bcbf..4ad48b18 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -1,309 +1,338 @@ -// client.cpp, mostly network related client game code - -#include "cube.h" - -ENetHost *clienthost = NULL; -int connecting = 0; -int connattempts = 0; -int disconnecting = 0; -int clientnum = -1; // our client id in the game -bool c2sinit = false; // whether we need to tell the other clients our stats - -int getclientnum() { return clientnum; }; - -bool multiplayer() -{ - // check not correct on listen server? - if(clienthost) conoutf("operation not available in multiplayer"); - return clienthost!=NULL; -}; - -bool allowedittoggle() -{ - bool allow = !clienthost || gamemode==1; - if(!allow) conoutf("editing in multiplayer requires coopedit mode (1)"); - return allow; -}; - -VARF(rate, 0, 0, 25000, if(clienthost && (!rate || rate>1000)) enet_host_bandwidth_limit (clienthost, rate, rate)); - -void throttle(); - -VARF(throttle_interval, 0, 5, 30, throttle()); -VARF(throttle_accel, 0, 2, 32, throttle()); -VARF(throttle_decel, 0, 2, 32, throttle()); - -void throttle() -{ - if(!clienthost || connecting) return; - assert(ENET_PEER_PACKET_THROTTLE_SCALE==32); - enet_peer_throttle_configure(clienthost->peers, throttle_interval*1000, throttle_accel, throttle_decel); -}; - -void newname(char *name) { c2sinit = false; strn0cpy(player1->name, name, 16); }; -void newteam(char *name) { c2sinit = false; strn0cpy(player1->team, name, 5); }; - -COMMANDN(team, newteam, ARG_1STR); -COMMANDN(name, newname, ARG_1STR); - -void connects(char *servername) -{ - disconnect(1); // reset state - addserver(servername); - - conoutf("attempting to connect to %s", (int)servername); - ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT }; - if(enet_address_set_host(&address, servername) < 0) - { - conoutf("could not resolve server %s", (int) servername); - return; - }; - - clienthost = enet_host_create(NULL, 1, rate, rate); - - if(clienthost) - { - enet_host_connect(clienthost, &address, 1); - enet_host_flush(clienthost); - connecting = lastmillis; - connattempts = 0; - } - else - { - conoutf("could not connect to server"); - disconnect(); - }; -}; - -void disconnect(int onlyclean, int async) -{ - if(clienthost) - { - if(!connecting && !disconnecting) - { - enet_peer_disconnect(clienthost->peers); - enet_host_flush(clienthost); - disconnecting = lastmillis; - }; - if(clienthost->peers->state != ENET_PEER_STATE_DISCONNECTED) - { - if(async) return; - enet_peer_reset(clienthost->peers); - }; - enet_host_destroy(clienthost); - }; - - if(clienthost && !connecting) conoutf("disconnected"); - clienthost = NULL; - connecting = 0; - connattempts = 0; - disconnecting = 0; - clientnum = -1; - c2sinit = false; - player1->lifesequence = 0; - loopv(players) zapdynent(players[i]); - - localdisconnect(); - - if(!onlyclean) { stop(); localconnect(); }; -}; - -void trydisconnect() -{ - if(!clienthost) - { - conoutf("not connected"); - return; - }; - if(connecting) - { - conoutf("aborting connection attempt"); - disconnect(); - return; - }; - conoutf("attempting to disconnect..."); - disconnect(0, !disconnecting); -}; - -string ctext; -void toserver(char *text) { conoutf("%s:\f %s", (int)player1->name, (int)text); strn0cpy(ctext, text, 80); }; -void echo(char *text) { conoutf("%s", (int)text); }; - -COMMAND(echo, ARG_VARI); -COMMANDN(say, toserver, ARG_VARI); -COMMANDN(connect, connects, ARG_1STR); -COMMANDN(disconnect, trydisconnect, ARG_NONE); - -// collect c2s messages conveniently - -vector messages; - -void addmsg(int rel, int num, int type, ...) -{ - if(demoplayback) return; - if(num!=msgsizelookup(type)) { sprintf_sd(s)("inconsistant msg size for %d (%d != %d)", type, num, msgsizelookup(type)); fatal(s); }; - ivector &msg = messages.add(); - msg.add(num); - msg.add(rel); - msg.add(type); - va_list marker; - va_start(marker, type); - loopi(num-1) msg.add(va_arg(marker, int)); - va_end(marker); -}; - -void server_err() -{ - conoutf("server network error, disconnecting..."); - disconnect(); -}; - -int lastupdate = 0, lastping = 0; -string toservermap; -bool senditemstoserver = false; // after a map change, since server doesn't have map data - -string clientpassword; -void password(char *p) { strcpy_s(clientpassword, p); }; -COMMAND(password, ARG_1STR); - -bool netmapstart() { senditemstoserver = true; return clienthost!=NULL; }; - -void initclientnet() -{ - ctext[0] = 0; - toservermap[0] = 0; - clientpassword[0] = 0; - newname("unnamed"); - newteam("red"); -}; - -void sendpackettoserv(void *packet) -{ - if(clienthost) { enet_host_broadcast(clienthost, 0, (ENetPacket *)packet); enet_host_flush(clienthost); } - else localclienttoserver((ENetPacket *)packet); -} - -void c2sinfo(dynent *d) // send update to the server -{ - 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 - ENetPacket *packet = enet_packet_create (NULL, MAXTRANS, 0); - uchar *start = packet->data; - uchar *p = start+2; - bool serveriteminitdone = false; - if(toservermap[0]) // suggest server to change map - { // do this exclusively as map change may invalidate rest of update - packet->flags = ENET_PACKET_FLAG_RELIABLE; - putint(p, SV_MAPCHANGE); - sendstring(toservermap, p); - toservermap[0] = 0; - putint(p, nextmode); - } - else - { - putint(p, SV_POS); - putint(p, clientnum); - putint(p, (int)(d->o.x*DMF)); // quantize coordinates to 1/16th of a cube, between 1 and 3 bytes - putint(p, (int)(d->o.y*DMF)); - putint(p, (int)(d->o.z*DMF)); - putint(p, (int)(d->yaw*DAF)); - putint(p, (int)(d->pitch*DAF)); - putint(p, (int)(d->roll*DAF)); - putint(p, (int)(d->vel.x*DVF)); // quantize to 1/100, almost always 1 byte - putint(p, (int)(d->vel.y*DVF)); - putint(p, (int)(d->vel.z*DVF)); - // pack rest in 1 byte: strafe:2, move:2, onfloor:1, state:3 - putint(p, (d->strafe&3) | ((d->move&3)<<2) | (((int)d->onfloor)<<4) | ((editmode ? CS_EDITING : d->state)<<5) ); - - if(gun_changed) - { - putint(p, SV_WEAPCHANGE); - putint(p, player1->gunselect); - gun_changed = false; - } - if(senditemstoserver) - { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - putint(p, SV_ITEMLIST); - if(!m_noitems) putitems(p); - putint(p, -1); - senditemstoserver = false; - serveriteminitdone = true; - }; - if(ctext[0]) // player chat, not flood protected for now - { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - putint(p, SV_TEXT); - sendstring(ctext, p); - ctext[0] = 0; - }; - if(!c2sinit) // tell other clients who I am - { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - c2sinit = true; - putint(p, SV_INITC2S); - sendstring(player1->name, p); - sendstring(player1->team, p); - putint(p, player1->lifesequence); - }; - loopv(messages) // send messages collected during the previous frames - { - ivector &msg = messages[i]; - if(msg[1]) packet->flags = ENET_PACKET_FLAG_RELIABLE; - loopi(msg[0]) putint(p, msg[i+2]); - }; - messages.setsize(0); - if(lastmillis-lastping>250) - { - putint(p, SV_PING); - putint(p, lastmillis); - lastping = lastmillis; - }; - }; - *(ushort *)start = ENET_HOST_TO_NET_16(p-start); - enet_packet_resize(packet, p-start); - incomingdemodata(start, p-start, true); - if(clienthost) { enet_host_broadcast(clienthost, 0, packet); enet_host_flush(clienthost); } - else localclienttoserver(packet); - lastupdate = lastmillis; - if(serveriteminitdone) loadgamerest(); // hack -}; - -void gets2c() // get updates from the server -{ - ENetEvent event; - if(!clienthost) return; - if(connecting && lastmillis/3000 > connecting/3000) - { - conoutf("attempting to connect..."); - connecting = lastmillis; - ++connattempts; - if(connattempts > 3) - { - conoutf("could not connect to server"); - disconnect(); - return; - }; - }; - while(clienthost!=NULL && enet_host_service(clienthost, &event, 0)>0) - switch(event.type) - { - case ENET_EVENT_TYPE_CONNECT: - conoutf("connected to server"); - connecting = 0; - throttle(); - break; - - case ENET_EVENT_TYPE_RECEIVE: - if(disconnecting) conoutf("attempting to disconnect..."); - else localservertoclient(event.packet->data, event.packet->dataLength); - enet_packet_destroy(event.packet); - break; - - case ENET_EVENT_TYPE_DISCONNECT: - if(disconnecting) disconnect(); - else server_err(); - return; - } -}; - +// client.cpp, mostly network related client game code + +#include "cube.h" + +ENetHost *clienthost = NULL; +int connecting = 0; +int connattempts = 0; +int disconnecting = 0; +int clientnum = -1; // our client id in the game +bool c2sinit = false; // whether we need to tell the other clients our stats + +int getclientnum() { return clientnum; }; + +bool multiplayer() +{ + // check not correct on listen server? + if(clienthost) conoutf("operation not available in multiplayer"); + return clienthost!=NULL; +}; + +bool allowedittoggle() +{ + bool allow = !clienthost || gamemode==1; + if(!allow) conoutf("editing in multiplayer requires coopedit mode (1)"); + return allow; +}; + +VARF(rate, 0, 0, 25000, if(clienthost && (!rate || rate>1000)) enet_host_bandwidth_limit (clienthost, rate, rate)); + +void throttle(); + +VARF(throttle_interval, 0, 5, 30, throttle()); +VARF(throttle_accel, 0, 2, 32, throttle()); +VARF(throttle_decel, 0, 2, 32, throttle()); + +void throttle() +{ + if(!clienthost || connecting) return; + assert(ENET_PEER_PACKET_THROTTLE_SCALE==32); + enet_peer_throttle_configure(clienthost->peers, throttle_interval*1000, throttle_accel, throttle_decel); +}; + +void ctf_team(char *name) +{ + c2sinit = false; + + if(strcmp(name, "red") == 0 || strcmp(name, "blue") == 0) + { + if(strcmp(name, player1->team) != 0) + { + ctf_death(); + strcpy(player1->team, name); // avoid spawning on the enemy side + player1->lastaction = lastmillis; + player1->state = CS_DEAD; + }; + } + else + { + ctf_death(); + strcpy(player1->team, rb_team_string(rnd(2))); + player1->lastaction = lastmillis; + player1->state = CS_DEAD; + } +} + +void newname(char *name) { c2sinit = false; strn0cpy(player1->name, name, 16); }; +void newteam(char *name) +{ + c2sinit = false; + if(m_ctf) ctf_team(name); + else strn0cpy(player1->team, name, 5); +}; + +COMMANDN(team, newteam, ARG_1STR); +COMMANDN(name, newname, ARG_1STR); + +void connects(char *servername) +{ + disconnect(1); // reset state + addserver(servername); + + conoutf("attempting to connect to %s", (int)servername); + ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT }; + if(enet_address_set_host(&address, servername) < 0) + { + conoutf("could not resolve server %s", (int) servername); + return; + }; + + clienthost = enet_host_create(NULL, 1, rate, rate); + + if(clienthost) + { + enet_host_connect(clienthost, &address, 1); + enet_host_flush(clienthost); + connecting = lastmillis; + connattempts = 0; + } + else + { + conoutf("could not connect to server"); + disconnect(); + }; +}; + +void disconnect(int onlyclean, int async) +{ + if(clienthost) + { + if(!connecting && !disconnecting) + { + enet_peer_disconnect(clienthost->peers); + enet_host_flush(clienthost); + disconnecting = lastmillis; + }; + if(clienthost->peers->state != ENET_PEER_STATE_DISCONNECTED) + { + if(async) return; + enet_peer_reset(clienthost->peers); + }; + enet_host_destroy(clienthost); + }; + + if(clienthost && !connecting) conoutf("disconnected"); + clienthost = NULL; + connecting = 0; + connattempts = 0; + disconnecting = 0; + clientnum = -1; + c2sinit = false; + player1->lifesequence = 0; + loopv(players) zapdynent(players[i]); + + localdisconnect(); + + if(!onlyclean) { stop(); localconnect(); }; +}; + +void trydisconnect() +{ + if(!clienthost) + { + conoutf("not connected"); + return; + }; + if(connecting) + { + conoutf("aborting connection attempt"); + disconnect(); + return; + }; + conoutf("attempting to disconnect..."); + disconnect(0, !disconnecting); +}; + +string ctext; +void toserver(char *text) { conoutf("%s:\f %s", (int)player1->name, (int)text); strn0cpy(ctext, text, 80); }; +void echo(char *text) { conoutf("%s", (int)text); }; + +COMMAND(echo, ARG_VARI); +COMMANDN(say, toserver, ARG_VARI); +COMMANDN(connect, connects, ARG_1STR); +COMMANDN(disconnect, trydisconnect, ARG_NONE); + +// collect c2s messages conveniently + +vector messages; + +void addmsg(int rel, int num, int type, ...) +{ + if(demoplayback) return; + if(num!=msgsizelookup(type)) { sprintf_sd(s)("inconsistant msg size for %d (%d != %d)", type, num, msgsizelookup(type)); fatal(s); }; + if(messages.length()==100) { conoutf("command flood protection (type %d)", type); return; }; + ivector &msg = messages.add(); + msg.add(num); + msg.add(rel); + msg.add(type); + va_list marker; + va_start(marker, type); + loopi(num-1) msg.add(va_arg(marker, int)); + va_end(marker); +}; + +void server_err() +{ + conoutf("server network error, disconnecting..."); + disconnect(); +}; + +int lastupdate = 0, lastping = 0; +string toservermap; +bool senditemstoserver = false; // after a map change, since server doesn't have map data + +string clientpassword; +void password(char *p) { strcpy_s(clientpassword, p); }; +COMMAND(password, ARG_1STR); + +bool netmapstart() { senditemstoserver = true; return clienthost!=NULL; }; + +void initclientnet() +{ + ctext[0] = 0; + toservermap[0] = 0; + clientpassword[0] = 0; + newname("unnamed"); + newteam("red"); +}; + +void sendpackettoserv(void *packet) +{ + if(clienthost) { enet_host_broadcast(clienthost, 0, (ENetPacket *)packet); enet_host_flush(clienthost); } + else localclienttoserver((ENetPacket *)packet); +} + +void c2sinfo(dynent *d) // send update to the server +{ + 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 + ENetPacket *packet = enet_packet_create (NULL, MAXTRANS, 0); + uchar *start = packet->data; + uchar *p = start+2; + bool serveriteminitdone = false; + if(toservermap[0]) // suggest server to change map + { // do this exclusively as map change may invalidate rest of update + packet->flags = ENET_PACKET_FLAG_RELIABLE; + putint(p, SV_MAPCHANGE); + sendstring(toservermap, p); + toservermap[0] = 0; + putint(p, nextmode); + } + else + { + putint(p, SV_POS); + putint(p, clientnum); + putint(p, (int)(d->o.x*DMF)); // quantize coordinates to 1/16th of a cube, between 1 and 3 bytes + putint(p, (int)(d->o.y*DMF)); + putint(p, (int)(d->o.z*DMF)); + putint(p, (int)(d->yaw*DAF)); + putint(p, (int)(d->pitch*DAF)); + putint(p, (int)(d->roll*DAF)); + putint(p, (int)(d->vel.x*DVF)); // quantize to 1/100, almost always 1 byte + putint(p, (int)(d->vel.y*DVF)); + putint(p, (int)(d->vel.z*DVF)); + // pack rest in 1 byte: strafe:2, move:2, onfloor:1, state:3 + putint(p, (d->strafe&3) | ((d->move&3)<<2) | (((int)d->onfloor)<<4) | ((editmode ? CS_EDITING : d->state)<<5) ); + + if(gun_changed) + { + putint(p, SV_WEAPCHANGE); + putint(p, player1->gunselect); + gun_changed = false; + } + if(senditemstoserver) + { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + putint(p, SV_ITEMLIST); + if(!m_noitems) putitems(p); + putint(p, -1); + senditemstoserver = false; + serveriteminitdone = true; + }; + if(ctext[0]) // player chat, not flood protected for now + { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + putint(p, SV_TEXT); + sendstring(ctext, p); + ctext[0] = 0; + }; + if(!c2sinit) // tell other clients who I am + { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + c2sinit = true; + putint(p, SV_INITC2S); + sendstring(player1->name, p); + sendstring(player1->team, p); + putint(p, player1->lifesequence); + }; + loopv(messages) // send messages collected during the previous frames + { + ivector &msg = messages[i]; + if(msg[1]) packet->flags = ENET_PACKET_FLAG_RELIABLE; + loopi(msg[0]) putint(p, msg[i+2]); + }; + messages.setsize(0); + if(lastmillis-lastping>250) + { + putint(p, SV_PING); + putint(p, lastmillis); + lastping = lastmillis; + }; + }; + *(ushort *)start = ENET_HOST_TO_NET_16(p-start); + enet_packet_resize(packet, p-start); + incomingdemodata(start, p-start, true); + if(clienthost) { enet_host_broadcast(clienthost, 0, packet); enet_host_flush(clienthost); } + else localclienttoserver(packet); + lastupdate = lastmillis; + if(serveriteminitdone) loadgamerest(); // hack +}; + +void gets2c() // get updates from the server +{ + ENetEvent event; + if(!clienthost) return; + if(connecting && lastmillis/3000 > connecting/3000) + { + conoutf("attempting to connect..."); + connecting = lastmillis; + ++connattempts; + if(connattempts > 3) + { + conoutf("could not connect to server"); + disconnect(); + return; + }; + }; + while(clienthost!=NULL && enet_host_service(clienthost, &event, 0)>0) + switch(event.type) + { + case ENET_EVENT_TYPE_CONNECT: + conoutf("connected to server"); + connecting = 0; + throttle(); + break; + + case ENET_EVENT_TYPE_RECEIVE: + if(disconnecting) conoutf("attempting to disconnect..."); + else localservertoclient(event.packet->data, event.packet->dataLength); + enet_packet_destroy(event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + if(disconnecting) disconnect(); + else server_err(); + return; + } +}; + diff --git a/source/src/clientextras.cpp b/source/src/clientextras.cpp index 0a42cb45..29c515fc 100644 --- a/source/src/clientextras.cpp +++ b/source/src/clientextras.cpp @@ -16,7 +16,7 @@ void renderclient(dynent *d, bool team, char *mdlname, bool vwep, float scale) float mz = d->o.z-d->eyeheight+1.55f*scale; int basetime = -((int)d&0xFFF); if(d->state==CS_DEAD) - { + { d->pitch = 90.0f; //if(vwep) mz = S((int)d->o.x, (int)d->o.y)->floor; int r; @@ -53,12 +53,12 @@ void renderclients() loopv(players) if((d = players[i]) && (!demoplayback || i!=democlientnum)) { - //if (d->state==CS_DEAD) break; //cheap hack fix of - if(strcmp(d->name, "dummy") == 0) - { - d->gunselect = player1->gunselect; - d->moving = true; - d->health != 1 ? d->state=CS_ALIVE : d->state=CS_DEAD; + //if (d->state==CS_DEAD) break; //cheap hack fix of + if(strcmp(d->name, "dummy") == 0) + { + d->gunselect = player1->gunselect; + d->moving = true; + d->health != 1 ? d->state=CS_ALIVE : d->state=CS_DEAD; }; if (!strcmp(d->team,"CT")) @@ -68,13 +68,13 @@ void renderclients() else //if (!strcmp(d->team,"T")) { renderclient(d, isteam(player1->team, d->team), "playermodels/terrorist", false, 1.4f); - } - - if(d->gunselect>=0 && d->gunselectgunselect>=0 && d->gunselectgunselect]); - renderclient(d, isteam(player1->team, d->team), vwep, true, 1.4f); - }; + renderclient(d, isteam(player1->team, d->team), vwep, true, 1.4f); + }; }; if(player1->state==CS_DEAD) @@ -83,23 +83,23 @@ void renderclients() renderclient(player1, true, "playermodels/counterterrorist", false,1.4f); else renderclient(player1, true, "playermodels/terrorist", false, 1.4f); - + sprintf_sd(vwep)("weapons/%s/world", hudgunnames[player1->gunselect]); renderclient(player1, true, vwep, true, 1.4f); }; }; - -void spawn_dummy() -{ - dynent *d = newdynent(); - players.add(d); - d->o = player1->o; - strcpy(d->team, player1->team); - d->gunselect = player1->gunselect; - d->monsterstate = M_NONE; - d->state = CS_ALIVE; - strcpy(d->name, "dummy"); -}; COMMAND(spawn_dummy, ARG_NONE); + +void spawn_dummy() +{ + dynent *d = newdynent(); + players.add(d); + d->o = player1->o; + strcpy(d->team, player1->team); + d->gunselect = player1->gunselect; + d->monsterstate = M_NONE; + d->state = CS_ALIVE; + strcpy(d->name, "dummy"); +}; COMMAND(spawn_dummy, ARG_NONE); // creation of scoreboard pseudo-menu @@ -108,42 +108,52 @@ bool scoreson = false; void showscores(bool on) { scoreson = on; - menuset(((int)on)-1); +// menuset(((int)on)-1); + menuset(on ? (m_ctf ? 2 : 0) : -1); // EDIT: AH }; struct sline { string s; }; vector scorelines; +int menu = 0; void renderscore(dynent *d) { sprintf_sd(lag)("%d", d->plag); sprintf_sd(name) ("(%s)", d->name); - sprintf_s(scorelines.add().s)("%d\t%s\t%d\t%s\t%s", d->frags, d->state==CS_LAGGED ? "LAG" : lag, d->ping, (int)d->team, d->state==CS_DEAD ? name : d->name); - menumanual(0, scorelines.length()-1, scorelines.last().s); + if(m_ctf) sprintf_s(scorelines.add().s)("%d\t%d\t%s\t%d\t%s\t%s", d->flagscore, d->frags, d->state==CS_LAGGED ? "LAG" : lag, d->ping, d->team, d->state==CS_DEAD ? name : d->name); + else sprintf_s(scorelines.add().s)("%d\t%s\t%d\t%s\t%s", d->frags, d->state==CS_LAGGED ? "LAG" : lag, d->ping, d->team, d->state==CS_DEAD ? name : d->name); + menumanual(menu, scorelines.length()-1, scorelines.last().s); }; const int maxteams = 4; char *teamname[maxteams]; -int teamscore[maxteams], teamsused; +int teamscore[maxteams], teamflagscore[maxteams], teamsused; // EDIT: AH string teamscores; int timeremain = 0; void addteamscore(dynent *d) { if(!d) return; - loopi(teamsused) if(strcmp(teamname[i], d->team)==0) { teamscore[i] += d->frags; return; }; + loopi(teamsused) if(strcmp(teamname[i], d->team)==0) + { + teamscore[i] += d->frags; + if(m_ctf) teamflagscore[i] += d->flagscore; + return; + }; if(teamsused==maxteams) return; teamname[teamsused] = d->team; - teamscore[teamsused++] = d->frags; + teamscore[teamsused] = d->frags; + if(m_ctf) teamflagscore[teamsused++] = d->flagscore; }; void renderscores() { if(!scoreson) return; + menu = m_ctf ? 2 : 0; scorelines.setsize(0); if(!demoplayback) renderscore(player1); loopv(players) if(players[i]) renderscore(players[i]); - sortmenu(0, scorelines.length()); + sortmenu(menu, 0, scorelines.length()); if(m_teammode) { teamsused = 0; @@ -152,11 +162,13 @@ void renderscores() teamscores[0] = 0; loopj(teamsused) { - sprintf_sd(sc)("[ %s: %d ]", teamname[j], teamscore[j]); + string sc; + if(m_ctf) sprintf_s(sc)("[ %s: %d %d ]", teamname[j], teamflagscore[j], teamscore[j]); + else sprintf_s(sc)("[ %s: %d ]", teamname[j], teamscore[j]); strcat_s(teamscores, sc); }; - menumanual(0, scorelines.length(), ""); - menumanual(0, scorelines.length()+1, teamscores); + menumanual(menu, scorelines.length(), ""); + menumanual(menu, scorelines.length()+1, teamscores); }; }; diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index 0a49b070..7e436618 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -1,606 +1,711 @@ -// clientgame.cpp: core game related stuff - -#include "cube.h" -#define sleep my_sleep -int nextmode = 0; // nextmode becomes gamemode after next map load -VAR(gamemode, 1, 0, 0); - -void mode(int n) { addmsg(1, 2, SV_GAMEMODE, nextmode = n); }; -COMMAND(mode, ARG_1INT); - -bool intermission = false; - -dynent *player1 = newdynent(); // our client -dvector players; // other clients - -vector physents; - -VAR(sensitivity, 0, 10, 1000); -VAR(sensitivityscale, 1, 1, 100); -VAR(invmouse, 0, 0, 1); - -int lastmillis = 0; -int curtime; -string clientmap; - -extern int framesinmap; - -char *getclientmap() { return clientmap; }; - -void resetmovement(dynent *d) -{ - d->k_left = false; - d->k_right = false; - d->k_up = false; - d->k_down = false; - d->jumpnext = false; - d->strafe = 0; - d->move = 0; -}; - -void spawnstate(dynent *d) // reset player state not persistent accross spawns -{ - resetmovement(d); - d->vel.x = d->vel.y = d->vel.z = 0; - d->onfloor = false; - d->timeinair = 0; - d->health = 100; - d->armour = 0; - //d->hasarmour = false; - //d->armourtype = A_BLUE; - //d->quadmillis = 0; - d->gunselect = GUN_PISTOL; - d->mag[6]=10; - d->gunwait = 0; - d->attacking = false; - d->lastaction = 0; - - - radd(d); - - - //loopi(NUMGUNS) if(d->nextprimary!=i) d->ammo[i] = 0; - //d->mag[GUN_KNIFE] = 1; - //equp with default pistol - - //d->mag[GUN_PISTOL] = 8; - //if(d->nextprimary!=GUN_PISTOL) - // d->ammo[GUN_PISTOL] = 16; - //else - // d->ammo[GUN_PISTOL] = 32; - //temp for testing guns - //d->ammo[GUN_SHOTGUN] = 100; - //d->ammo[GUN_SUBGUN] = 400; - //d->ammo[GUN_SNIPER] = 100; - //d->ammo[GUN_ASSULT] = 400; - - //end tmp - - /* - if(m_noitems) - { - d->gunselect = GUN_RIFLE; - d->armour = 0; - if(m_noitemsrail) - { - d->health = 1; - d->ammo[GUN_RIFLE] = 100; - } - else - { - if(gamemode==12) { d->gunselect = GUN_FIST; return; }; // eihruls secret "instafist" mode - d->health = 256; - if(m_tarena) - { - int gun1 = rnd(4)+1; - baseammo(d->gunselect = gun1); - for(;;) - { - int gun2 = rnd(4)+1; - if(gun1!=gun2) { baseammo(gun2); break; }; - }; - } - else if(m_arena) // insta arena - { - d->ammo[GUN_RIFLE] = 100; - } - else // efficiency - { - loopi(4) baseammo(i+1); - d->gunselect = GUN_CG; - }; - d->ammo[GUN_CG] /= 2; - }; - } - else - { - d->ammo[GUN_SG] = 5; - }; - */ - - 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->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 respawnself() -{ - spawnplayer(player1); - showscores(false); -}; - -void respawn() -{ - if(player1->state==CS_DEAD) - { - player1->attacking = false; - if(m_arena) { conoutf("waiting for new round to start..."); return; }; - respawnself(); - }; -}; - -void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, char *&lastname, bool &oneteam) -//void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam) -{ - if(d->state!=CS_DEAD) - { - alive++; - if(lastteam && strcmp(lastteam, d->team)) oneteam = false; - lastteam = d->team; - lastname = d->name; - } - else - { - dead++; - }; -}; - -int arenarespawnwait = 0; -int arenadetectwait = 0; - -void arenarespawn() -{ - if (!m_arena) return; - - if(arenarespawnwait) - { - if(arenarespawnwait0 && (alive<=1 || (m_teammode && oneteam))) - { - conoutf("arena round is over! next round in 5 seconds..."); - if(alive) - { - if(m_teammode) - conoutf("team %s has won the round", (int)lastteam); - else - conoutf("%s is the survior!", (int)lastname); - } - else conoutf("everyone died!"); - arenarespawnwait = lastmillis+5000; - arenadetectwait = lastmillis+10000; - player1->roll = 0; - }; - }; -}; - - -void checkquad(int time) -{ -/* if(player1->quadmillis && (player1->quadmillis -= time)<0) - { - player1->quadmillis = 0; - playsoundc(S_PUPOUT); - conoutf("quad damage is over"); - }; -*/ -}; - -//generic function to check all kinds of crazy gamemode specific things -void checkgame(int time) -{ - - //allow faster movements when editing -/* if (editmode) - { - player1->maxspeed=22; - return; - } - else //otherwise max speed is 16 and 14 with armour - { - if(player1->armour>50) - player1->maxspeed = 14; - if(player1->armour>0) - player1->maxspeed = 15; - player1->maxspeed = 16; - }; -*/ - - //force to a team if team mode - if (m_teammode) - { - if (strcmp(player1->team,"T") && strcmp(player1->team,"CT")) - { - player1->state = CS_DEAD; - //conoutf("set team to T or CT to joing game"); - if (rnd(2)<1) strcpy_s(player1->team,"T"); - else strcpy_s(player1->team,"CT"); - } - - //add checks here to force-drop flag if switching team with flag - - //add checks for end conditions of ctf - }; - - - //reset zoom if dead - - //etc - //checkquad(time); - - if (player1->state==CS_DEAD) - { - player1->primary = player1->nextprimary; - } -}; - -void zapdynent(dynent *&d) -{ - if(d) gp()->dealloc(d, sizeof(dynent)); - d = NULL; -}; - -extern int democlientnum; - -void otherplayers() -{ - loopv(players) if(players[i]) - { - const int lagtime = lastmillis-players[i]->lastupdate; - if(lagtime>1000 && players[i]->state==CS_ALIVE) - { - players[i]->state = CS_LAGGED; - continue; - }; - if(lagtime && players[i]->state != CS_DEAD && (!demoplayback || i!=democlientnum)) moveplayer(players[i], 2, false); // use physics to extrapolate player position - }; -}; - -extern void explode_nade(physent *i); - -void mphysents() -{ - loopv(physents) if(physents[i]) - { - physent *p = physents[i]; - if(p->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 -}; - -int spawncycle = -1; -int fixspawn = 2; - -void spawnplayer(dynent *d) // place at random spawn. also used by monsters! -{ - if(d==player1) gun_changed=true; - - int r = fixspawn-->0 ? 4 : rnd(10)+1; - loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1); - if(spawncycle!=-1) - { - d->o.x = ents[spawncycle].x; - d->o.y = ents[spawncycle].y; - d->o.z = ents[spawncycle].z; - d->yaw = ents[spawncycle].attr1; - d->pitch = 0; - d->roll = 0; - } - else - { - d->o.x = d->o.y = (float)ssize/2; - d->o.z = 4; - }; - entinmap(d); - spawnstate(d); - d->state = CS_ALIVE; -}; - -// movement input code - -#define dir(name,v,d,s,os) void name(bool isdown) { player1->s = isdown; player1->v = isdown ? d : (player1->os ? -(d) : 0); player1->lastmove = lastmillis; }; - -dir(backward, move, -1, k_down, k_up); -dir(forward, move, 1, k_up, k_down); -dir(left, strafe, 1, k_left, k_right); -dir(right, strafe, -1, k_right, k_left); - -void attack(bool on) -{ - if(intermission) return; - if(editmode) editdrag(on); - else if(player1->attacking = on) respawn(); -}; - -void jumpn(bool on) { if(!intermission && (player1->jumpnext = on) ) respawn(); }; - -COMMAND(backward, ARG_DOWN); -COMMAND(forward, ARG_DOWN); -COMMAND(left, ARG_DOWN); -COMMAND(right, ARG_DOWN); -COMMANDN(jump, jumpn, ARG_DOWN); -COMMAND(attack, ARG_DOWN); -COMMAND(showscores, ARG_DOWN); - -void fixplayer1range() -{ - const float MAXPITCH = 90.0f; - if(player1->pitch>MAXPITCH) player1->pitch = MAXPITCH; - if(player1->pitch<-MAXPITCH) player1->pitch = -MAXPITCH; - while(player1->yaw<0.0f) player1->yaw += 360.0f; - while(player1->yaw>=360.0f) player1->yaw -= 360.0f; -}; - -void mousemove(int dx, int dy) -{ - if(player1->state==CS_DEAD || intermission) return; - const float SENSF = 33.0f; // try match quake sens - player1->yaw += (dx/SENSF)*(sensitivity/(float)sensitivityscale); - player1->pitch -= (dy/SENSF)*(sensitivity/(float)sensitivityscale)*(invmouse ? -1 : 1); - fixplayer1range(); -}; - -// damage arriving from the network, monsters, yourself, all ends up here. - -void selfdamage(int damage, int actor, dynent *act) -{ - if(player1->state!=CS_ALIVE || editmode || intermission) return; - damageblend(damage); - demoblend(damage); - int ad = damage*30/100;//int ad = damage*(player1->armourtype+1)*20/100; // let armour absorb when possible - if(ad>player1->armour) ad = player1->armour; - player1->armour -= ad; - damage -= ad; - float droll = damage/0.5f; - player1->roll += player1->roll>0 ? droll : (player1->roll<0 ? -droll : (rnd(2) ? droll : -droll)); // give player a kick depending on amount of damage - if((player1->health -= damage)<=0) - { - if(actor==-2) - { - conoutf("you got killed by %s!", (int)&act->name); - } - else if(actor==-1) - { - actor = getclientnum(); - conoutf("you suicided!"); - addmsg(1, 2, SV_FRAGS, --player1->frags); - } - else - { - dynent *a = getclient(actor); - if(a) - { - if(isteam(a->team, player1->team)) - { - conoutf("you got fragged by a teammate (%s)", (int)a->name); - } - else - { - conoutf("you got fragged by %s", (int)a->name); - }; - }; - }; - showscores(true); - addmsg(1, 2, SV_DIED, actor); - player1->lifesequence++; - player1->attacking = false; - player1->state = CS_DEAD; - player1->oldpitch = player1->pitch; - player1->pitch = 0; - player1->roll = 60; - playsound(S_DIE1+rnd(2)); - spawnstate(player1); - player1->lastaction = lastmillis; - } - else - { - playsound(S_PAIN6); - }; -}; - -void timeupdate(int timeremain) -{ - if(!timeremain) - { - intermission = true; - player1->attacking = false; - conoutf("intermission:"); - conoutf("game has ended!"); - showscores(true); - } - else - { - conoutf("time remaining: %d minutes", timeremain); - }; -}; - -dynent *getclient(int cn) // ensure valid entity -{ - if(cn<0 || cn>=MAXCLIENTS) - { - neterr("clientnum"); - return NULL; - }; - while(cn>=players.length()) players.add(NULL); - return players[cn] ? players[cn] : (players[cn] = newdynent()); -}; - -void addc() { strcpy(getclient(1)->team, "CT"); getclient(1)->o = player1->o; strcpy(getclient(1)->name, "sucker"); } COMMAND(addc, ARG_NONE); // FIXME - -void initclient() -{ - clientmap[0] = 0; - initclientnet(); -}; - -void startmap(char *name) // called just after a map load -{ - //if(netmapstart()) { gamemode = 0;}; //needs fixed to switch modes? - netmapstart(); //should work - sleepwait = 0; - //put call to clear/restart game mode extras here - projreset(); - shotlinereset(); - spawncycle = -1; - spawnplayer(player1); - player1->frags = 0; - loopv(players) if(players[i]) players[i]->frags = 0; - resetspawns(); - strcpy_s(clientmap, name); - if(editmode) toggleedit(); - setvar("gamespeed", 100); - setvar("fog", 180); - setvar("fogcolour", 0x8099B3); - showscores(false); - intermission = false; - framesinmap = 0; - conoutf("game mode is %s", (int)modestr(gamemode)); -}; - -COMMANDN(map, changemap, ARG_1STR); - -void suicide() -{ - if (multiplayer()) conoutf("there are others willing to kill you..."); - else conoutf("this is not the answer"); - selfdamage(1000, -1, player1); - //if(d==player1) selfdamage(dm, -1, d); - //else { addmsg(1, 4, SV_DAMAGE, d, dm, d->lifesequence); playsound(S_FALL1+rnd(5), &d->o); }; - //particle_splash(3, 1000, 1000, player1->o); //edit out? - demodamage(1000, player1->o); - playsound(S_SUICIDE, &player1->o); - -}; - -COMMAND(suicide, ARG_NONE); - -void killdummies() -{ - loopv(players) if(strcmp(players[i]->name, "dummy") == 0) { players[i]->lastaction = lastmillis; players[i]->health=1; }; -}; COMMAND(killdummies, ARG_NONE); +// clientgame.cpp: core game related stuff + +#include "cube.h" +#define sleep my_sleep +int nextmode = 0; // nextmode becomes gamemode after next map load +VAR(gamemode, 1, 0, 0); + +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; + }; +}; diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index e20410d5..aeafde93 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -83,6 +83,10 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th disconnect(); return; }; + if(getint(p)==1) + { + conoutf("server is FULL, disconnecting.."); + }; break; }; @@ -253,7 +257,7 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th case SV_ITEMSPAWN: { - int i = getint(p); + uint i = getint(p); setspawn(i, true); if(i>=ents.length()) break; vec v = { ents[i].x, ents[i].y, ents[i].z }; @@ -289,8 +293,8 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th case SV_EDITENT: // coop edit of ent { - int i = getint(p); - while(ents.length()<=i) ents.add().type = NOTUSED; + uint i = getint(p); + while((uint)ents.length()<=i) ents.add().type = NOTUSED; int to = ents[i].type; ents[i].type = getint(p); ents[i].x = getint(p); @@ -347,11 +351,85 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th conoutf("%s", (int)text); break; + // EDIT: AH + case SV_FLAGINFO: + { + int flag = getint(p); + if(flag<0||flag>1) return; + flaginfo &f = flaginfos[flag]; + f.state = getint(p); + int action = getint(p); + if(f.state==CTFF_STOLEN) + { + int thief_cn = getint(p); + f.thief = thief_cn == getclientnum() ? player1 : getclient(thief_cn); + f.flag->spawned = false; + printf("cl_sstolen by %i\n", thief_cn); + } + else if(f.state==CTFF_DROPPED) + { + printf("cl_dropped\n"); + f.flag->x = (ushort) (getint(p)/DMF); + f.flag->y = (ushort) (getint(p)/DMF); + f.flag->z = (ushort) (getint(p)/DMF); + f.flag->z -= 4.1; + float floor = (float)S(f.flag->x, f.flag->y)->floor; + if(f.flag->z > hdr.waterlevel) // above water + { + if(floor < hdr.waterlevel) + f.flag->z = hdr.waterlevel; // avoid dropping into water + else + f.flag->z = floor; + }; + f.flag->spawned = true; + } + else if(f.state==CTFF_INBASE) + { + printf("cl_inbase\n"); + if(action==SV_FLAGRETURN) + { + int returnerer_cn = getint(p); + f.thief = returnerer_cn == getclientnum() ? player1 : getclient(returnerer_cn); + printf("\tcl_return\n"); + } + f.flag->x = (ushort) f.originalpos.x; + f.flag->y = (ushort) f.originalpos.y; + f.flag->z = (ushort) f.originalpos.z; + f.flag->spawned = true; + }; + flagaction(flag, action); + break; + }; + + case SV_FLAGS: + { + players[cn]->flagscore = getint(p); + break; + }; + + case SV_FLAGPICKUP: + { + int flag = getint(p); + if(flag<0|flag>1) return; + flaginfos[flag].state=CTFF_STOLEN; + break; + }; + + case SV_FLAGDROP: + case SV_FLAGRETURN: + case SV_FLAGSCORE: + { + getint(p); + break; + }; + +/* case SV_EXT: // so we can messages without breaking previous clients/servers, if necessary { for(int n = getint(p); n; n--) getint(p); break; }; +*/ default: neterr("type"); diff --git a/source/src/cube.h b/source/src/cube.h index bf54c1d5..97e8a9f3 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -1,354 +1,381 @@ -/// one big bad include file for the whole engine... nasty! - -#include "tools.h" - -enum // block types, order matters! -{ - SOLID = 0, // entirely solid cube [only specifies wtex] - CORNER, // half full corner of a wall - FHF, // floor heightfield using neighbour vdelta values - CHF, // idem ceiling - SPACE, // entirely empty cube - SEMISOLID, // generated by mipmapping - MAXTYPE -}; - -struct sqr -{ - uchar type; // one of the above - char floor, ceil; // height, in cubes - uchar wtex, ftex, ctex; // wall/floor/ceil texture ids - uchar r, g, b; // light value at upper left vertex - uchar vdelta; // vertex delta, used for heightfield cubes - char defer; // used in mipmapping, when true this cube is not a perfect mip - char occluded; // true when occluded - uchar utex; // upper wall tex id - uchar tag; // used by triggers -}; - -enum // hardcoded texture numbers -{ - DEFAULT_SKY = 0, - DEFAULT_LIQUID, - DEFAULT_WALL, - DEFAULT_FLOOR, - DEFAULT_CEIL -}; - -enum // static entity types -{ - NOTUSED = 0, // entity slot not in use in map - LIGHT, // lightsource, attr1 = radius, attr2 = intensity - PLAYERSTART, // attr1 = angle - I_CLIPS, I_AMMO,I_GRENADE, - I_HEALTH, I_ARMOUR, I_AKIMBO, - MAPMODEL, // attr1 = angle, attr2 = idx - CARROT, // attr1 = tag, attr2 = type - LADDER, - MAXENTTYPES -}; - -struct persistent_entity // map entity -{ - short x, y, z; // cube aligned position - short attr1; - uchar type; // type is one of the above - uchar attr2, attr3, attr4; -}; - -struct entity : public persistent_entity -{ - bool spawned; // the only dynamic state of a map entity -}; - -#define MAPVERSION 6 // bump if map format changes, see worldio.cpp - -struct header // map file format header -{ - char head[4]; // "CUBE" - int version; // any >8bit quantity is a little indian - int headersize; // sizeof(header) - int sfactor; // in bits - int numents; - char maptitle[128]; - uchar texlists[3][256]; - int waterlevel; - int reserved[15]; -}; - -#define SWS(w,x,y,s) (&(w)[(y)*(s)+(x)]) -#define SW(w,x,y) SWS(w,x,y,ssize) -#define S(x,y) SW(world,x,y) // convenient lookup of a lowest mip cube -#define SMALLEST_FACTOR 6 // determines number of mips there can be -#define DEFAULT_FACTOR 8 -#define LARGEST_FACTOR 11 // 10 is already insane -#define SOLID(x) ((x)->type==SOLID) -#define MINBORD 2 // 2 cubes from the edge of the world are always solid -#define OUTBORD(x,y) ((x)=ssize-MINBORD || (y)>=ssize-MINBORD) - -struct vec { float x, y, z; }; -struct block { int x, y, xs, ys; }; -struct mapmodelinfo { int rad, h, zoff, snap; char *name; }; - -enum { GUN_KNIFE = 0, GUN_PISTOL, GUN_SHOTGUN, GUN_SUBGUN, GUN_SNIPER, GUN_ASSULT, GUN_GRENADE, NUMGUNS }; - -struct md3state -{ - int anim; - int frm; - int lastTime; -}; - -enum // md3 animations -{ - BOTH_DEATH1 = 0, - BOTH_DEAD1, - BOTH_DEATH2, - BOTH_DEAD2, - BOTH_DEATH3, - BOTH_DEAD3, - TORSO_GESTURE, - TORSO_ATTACK, - TORSO_ATTACK2, - TORSO_DROP, - TORSO_RAISE, - TORSO_STAND, - TORSO_STAND2, - LEGS_WALKCR, - LEGS_WALK, - LEGS_RUN, - LEGS_BACK, - LEGS_SWIM, - LEGS_JUMP, - LEGS_LAND, - LEGS_JUMPB, - LEGS_LANDB, - LEGS_IDLE, - LEGS_IDLECR, - LEGS_TURN -}; - -struct dynent // players & monsters -{ - vec o, vel; // origin, velocity - float yaw, pitch, roll; // used as vec in one place - float oldpitch; - float maxspeed; // cubes per second, 24 for player - bool outsidemap; // from his eyes - bool inwater; - bool onfloor, jumpnext; - int move, strafe; - bool k_left, k_right, k_up, k_down; // see input code - int timeinair; // used for fake gravity - float radius, eyeheight, aboveeye; // bounding box size - int lastupdate, plag, ping; - int lifesequence; // sequence id for each respawn, used in damage test - int state; // one of CS_* below - int frags; - int health, armour; //armourtype, quadmillis; - int gunselect, gunwait; - int lastaction, lastattackgun, lastmove; - bool attacking; - int ammo[NUMGUNS]; - int mag[NUMGUNS]; - int monsterstate; // one of M_* below, M_NONE means human - int mtype; // see monster.cpp - dynent *enemy; // monster wants to kill this entity - float targetyaw; // monster wants to look in this direction - bool blocked, moving; // used by physics to signal ai - int trigger; // millis at which transition to another monsterstate takes place - vec attacktarget; // delayed attacks - int anger; // how many times already hit by fellow monster - string name, team; - int startheight; - int shots; //keeps track of shots from auto weapons - bool reloading, hasarmour; - int primary; //primary gun - int nextprimary; - md3state animstate[3]; - - bool onladder; - int gravity; - bool isphysent; // hack - int thrownademillis; - int akimbo; //treated as a boolean -}; - -enum { PHYSENT_NONE = 0, NADE_ACTIVATED, NADE_THROWED }; - -struct physent : dynent // nades, gibs -{ - int millis, timetolife, state; // see enum above - dynent *owner; -}; - -#define SAVEGAMEVERSION 6 // bump if dynent/netprotocol changes or any other savegame/demo data bumped from 5 - -//enum { A_BLUE, A_GREEN, A_YELLOW }; // armour types... take 20/40/60 % off -enum { M_NONE = 0, M_SEARCH, M_HOME, M_ATTACKING, M_PAIN, M_SLEEP, M_AIMING, M_NADE /* bloody hack*/ }; // monster states - -#define MAXCLIENTS 256 // in a multiplayer game, can be arbitrarily changed -#define MAXTRANS 5000 // max amount of data to swallow in 1 go -#define CUBE_SERVER_PORT 28765 -#define CUBE_SERVINFO_PORT 28766 -#define PROTOCOL_VERSION 121 // bump when protocol changes - -// network messages codes, c2s, c2c, s2c -enum -{ - SV_INITS2C = 0, SV_INITC2S, SV_POS, SV_TEXT, SV_SOUND, SV_CDIS, - SV_DIED, SV_DAMAGE, SV_SHOT, SV_FRAGS, - SV_TIMEUP, SV_EDITENT, SV_MAPRELOAD, SV_ITEMACC, - SV_MAPCHANGE, SV_ITEMSPAWN, SV_ITEMPICKUP, SV_DENIED, - SV_PING, SV_PONG, SV_CLIENTPING, SV_GAMEMODE, - SV_EDITH, SV_EDITT, SV_EDITS, SV_EDITD, SV_EDITE, - SV_SENDMAP, SV_RECVMAP, SV_SERVMSG, SV_ITEMLIST, SV_WEAPCHANGE, - SV_MODELSKIN, - SV_EXT, -}; - -enum { CS_ALIVE = 0, CS_DEAD, CS_LAGGED, CS_EDITING }; - -// hardcoded sounds, defined in sounds.cfg -enum -{ - S_JUMP = 0, S_LAND, - S_KNIFE, - S_PISTOL, S_RPISTOL, - S_SHOTGUN, S_RSHOTGUN, - S_SUBGUN, S_RSUBGUN, - S_SNIPER, S_RSNIPER, - S_ASSULT, S_RASSULT, - S_GRENADE, - S_ITEMAMMO, S_ITEMHEALTH, - S_ITEMARMOUR, S_ITEMPUP, - S_NOAMMO, S_PUPOUT, - S_PAIN1, S_PAIN2, S_PAIN3, S_PAIN4, S_PAIN5, S_PAIN6, - S_DIE1, S_DIE2, S_SUICIDE, - S_FALL1, - S_FLAUNCH, S_FEXPLODE, - S_SPLASH1, S_SPLASH2, - S_GRUNT1, S_GRUNT2, S_RUMBLE, - S_NULL -}; - -// vertex array format - -struct vertex { float u, v, x, y, z; uchar r, g, b, a; }; - -typedef vector dvector; -typedef vector cvector; -typedef vector ivector; - -// globals ooh naughty - -extern sqr *world, *wmip[]; // map data, the mips are sequential 2D arrays in memory -extern header hdr; // current map header -extern int sfactor, ssize; // ssize = 2^sfactor -extern int cubicsize, mipsize; // cubicsize = ssize^2 -extern dynent *player1; // special client ent that receives input and acts as camera -extern dvector players; // all the other clients (in multiplayer) -extern vectorphysents; -extern bool editmode; -extern vector ents; // map entities -extern vec worldpos; // current target of the crosshair in the world -extern int lastmillis; // last time -extern int curtime; // current frame time -extern int gamemode, nextmode; -extern int xtraverts; -extern bool demoplayback; - - -#define DMF 16.0f -#define DAF 1.0f -#define DVF 100.0f - -#define VIRTW 2400 // virtual screen size for text & HUD -#define VIRTH 1800 -#define FONTH 64 -#define PIXELTAB (VIRTW/12) - -#define PI (3.1415927f) -#define PI2 (2*PI) - -// 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); } -#define vdiv(u,f) { (u).x /= (f); (u).y /= (f); (u).z /= (f); } -#define vadd(u,v) { (u).x += (v).x; (u).y += (v).y; (u).z += (v).z; }; -#define vsub(u,v) { (u).x -= (v).x; (u).y -= (v).y; (u).z -= (v).z; }; -#define vdist(d,v,e,s) vec v = s; vsub(v,e); float d = (float)sqrt(dotprod(v,v)); -#define vreject(v,u,max) ((v).x>(u).x+(max) || (v).x<(u).x-(max) || (v).y>(u).y+(max) || (v).y<(u).y-(max)) -#define vlinterp(v,f,u,g) { (v).x = (v).x*f+(u).x*g; (v).y = (v).y*f+(u).y*g; (v).z = (v).z*f+(u).z*g; } - -#define sgetstr() { char *t = text; do { *t = getint(p); } while(*t++); } // used by networking - -/* Gamemodes -0 - tdm -1 - coop edit -2 - dm -3 - survivor -4 - team survior -5 - ctf -6 - pistols -*/ - -#define m_noitems (m_arena) -#define m_noitemsrail (false) -#define m_arena (gamemode==3 || gamemode==4) -#define m_tarena (gamemode==4) -#define m_teammode (gamemode==0 || gamemode==4 || gamemode==5) -#define m_ctf (gamemode==5) -#define m_pistol (gamemode==6) -//#define m_sp (gamemode<0) -//#define m_dmsp (gamemode==-1) -//#define m_classicsp (gamemode==-2) -#define isteam(a,b) (m_teammode && strcmp(a, b)==0) - -enum // function signatures for script functions, see command.cpp -{ - ARG_1INT, ARG_2INT, ARG_3INT, ARG_4INT, - ARG_NONE, - ARG_1STR, ARG_2STR, ARG_3STR, ARG_5STR, ARG_6STR, - ARG_DOWN, ARG_DWN1, - ARG_1EXP, ARG_2EXP, - ARG_1EST, ARG_2EST, - ARG_VARI -}; - -// nasty macros for registering script functions, abuses globals to avoid excessive infrastructure -#define COMMANDN(name, fun, nargs) static bool __dummy_##fun = addcommand(#name, (void (*)())fun, nargs) -#define COMMAND(name, nargs) COMMANDN(name, name, nargs) -#define VARP(name, min, cur, max) int name = variable(#name, min, cur, max, &name, NULL, true) -#define VAR(name, min, cur, max) int name = variable(#name, min, cur, max, &name, NULL, false) -#define VARF(name, min, cur, max, body) void var_##name(); static int name = variable(#name, min, cur, max, &name, var_##name, false); void var_##name() { body; } -#define VARFP(name, min, cur, max, body) void var_##name(); static int name = variable(#name, min, cur, max, &name, var_##name, true); void var_##name() { body; } - -#define ATOI(s) strtol(s, NULL, 0) // supports hexadecimal numbers - -#ifdef WIN32 - #define WIN32_LEAN_AND_MEAN - #include "windows.h" - #define _WINDOWS - #define ZLIB_DLL - #define cos(x) cos((float)(x)) - #define sin(x) sin((float)(x)) -#else - #include -#endif - -#include - -#include -#include -#include - -#include -#include - -#include - -#include - -#include "protos.h" // external function decls - +/// one big bad include file for the whole engine... nasty! + +#include "tools.h" + +enum // block types, order matters! +{ + SOLID = 0, // entirely solid cube [only specifies wtex] + CORNER, // half full corner of a wall + FHF, // floor heightfield using neighbour vdelta values + CHF, // idem ceiling + SPACE, // entirely empty cube + SEMISOLID, // generated by mipmapping + MAXTYPE +}; + +struct sqr +{ + uchar type; // one of the above + char floor, ceil; // height, in cubes + uchar wtex, ftex, ctex; // wall/floor/ceil texture ids + uchar r, g, b; // light value at upper left vertex + uchar vdelta; // vertex delta, used for heightfield cubes + char defer; // used in mipmapping, when true this cube is not a perfect mip + char occluded; // true when occluded + uchar utex; // upper wall tex id + uchar tag; // used by triggers +}; + +enum // hardcoded texture numbers +{ + DEFAULT_SKY = 0, + DEFAULT_LIQUID, + DEFAULT_WALL, + DEFAULT_FLOOR, + DEFAULT_CEIL +}; + +enum // static entity types +{ + NOTUSED = 0, // entity slot not in use in map + LIGHT, // lightsource, attr1 = radius, attr2 = intensity + PLAYERSTART, // attr1 = angle + I_CLIPS, I_AMMO,I_GRENADE, + I_HEALTH, I_ARMOUR, I_QUAD, + MAPMODEL, // attr1 = angle, attr2 = idx + CARROT, // attr1 = tag, attr2 = type + LADDER, + CTF_FLAG, // attr1 = angle, attr2 = red/blue + MAXENTTYPES +}; + +struct persistent_entity // map entity +{ + short x, y, z; // cube aligned position + short attr1; + uchar type; // type is one of the above + uchar attr2, attr3, attr4; +}; + +struct entity : public persistent_entity +{ + bool spawned; // the only dynamic state of a map entity +}; + +#define MAPVERSION 6 // bump if map format changes, see worldio.cpp + +struct header // map file format header +{ + char head[4]; // "CUBE" + int version; // any >8bit quantity is a little indian + int headersize; // sizeof(header) + int sfactor; // in bits + int numents; + char maptitle[128]; + uchar texlists[3][256]; + int waterlevel; + int reserved[15]; +}; + +#define SWS(w,x,y,s) (&(w)[(y)*(s)+(x)]) +#define SW(w,x,y) SWS(w,x,y,ssize) +#define S(x,y) SW(world,x,y) // convenient lookup of a lowest mip cube +#define SMALLEST_FACTOR 6 // determines number of mips there can be +#define DEFAULT_FACTOR 8 +#define LARGEST_FACTOR 11 // 10 is already insane +#define SOLID(x) ((x)->type==SOLID) +#define MINBORD 2 // 2 cubes from the edge of the world are always solid +#define OUTBORD(x,y) ((x)=ssize-MINBORD || (y)>=ssize-MINBORD) + +struct vec { float x, y, z; }; +struct block { int x, y, xs, ys; }; +struct mapmodelinfo { int rad, h, zoff, snap; char *name; }; + +enum { GUN_KNIFE = 0, GUN_PISTOL, GUN_SHOTGUN, GUN_SUBGUN, GUN_SNIPER, GUN_ASSULT, GUN_GRENADE, NUMGUNS }; + +struct md3state +{ + int anim; + int frm; + int lastTime; +}; + +enum // md3 animations +{ + BOTH_DEATH1 = 0, + BOTH_DEAD1, + BOTH_DEATH2, + BOTH_DEAD2, + BOTH_DEATH3, + BOTH_DEAD3, + TORSO_GESTURE, + TORSO_ATTACK, + TORSO_ATTACK2, + TORSO_DROP, + TORSO_RAISE, + TORSO_STAND, + TORSO_STAND2, + LEGS_WALKCR, + LEGS_WALK, + LEGS_RUN, + LEGS_BACK, + LEGS_SWIM, + LEGS_JUMP, + LEGS_LAND, + LEGS_JUMPB, + LEGS_LANDB, + LEGS_IDLE, + LEGS_IDLECR, + LEGS_TURN +}; + +struct dynent // players & monsters +{ + vec o, vel; // origin, velocity + float yaw, pitch, roll; // used as vec in one place + float oldpitch; + float maxspeed; // cubes per second, 24 for player + bool outsidemap; // from his eyes + bool inwater; + bool onfloor, jumpnext; + int move, strafe; + bool k_left, k_right, k_up, k_down; // see input code + int timeinair; // used for fake gravity + float radius, eyeheight, aboveeye; // bounding box size + int lastupdate, plag, ping; + int lifesequence; // sequence id for each respawn, used in damage test + int state; // one of CS_* below + int frags; + int health, armour; //armourtype, quadmillis; + int gunselect, gunwait; + int lastaction, lastattackgun, lastmove; + bool attacking; + int ammo[NUMGUNS]; + int mag[NUMGUNS]; + int monsterstate; // one of M_* below, M_NONE means human + int mtype; // see monster.cpp + dynent *enemy; // monster wants to kill this entity + float targetyaw; // monster wants to look in this direction + bool blocked, moving; // used by physics to signal ai + int trigger; // millis at which transition to another monsterstate takes place + vec attacktarget; // delayed attacks + int anger; // how many times already hit by fellow monster + int flagscore; // EDIT: AH + string name, team; + int startheight; + int shots; //keeps track of shots from auto weapons + bool reloading, hasarmour; + int primary; //primary gun + int nextprimary; + md3state animstate[3]; + + bool onladder; + int gravity; + bool isphysent; // hack + int thrownademillis; + bool akimbo; +}; + +// EDIT: AH +enum { CTFF_INBASE = 0, CTFF_STOLEN, CTFF_DROPPED }; + +struct flaginfo +{ + entity *flag; + dynent *thief; + vec originalpos; + int state; // one of the types above + bool pick_ack; + flaginfo() : flag(0), thief(0), state(CTFF_INBASE), pick_ack(false) {}; +}; + +enum { PHYSENT_NONE = 0, NADE_ACTIVATED, NADE_THROWED }; + +struct physent : dynent // nades, gibs +{ + int millis, timetolife, state; // see enum above + dynent *owner; +}; + +#define SAVEGAMEVERSION 6 // bump if dynent/netprotocol changes or any other savegame/demo data bumped from 5 + +//enum { A_BLUE, A_GREEN, A_YELLOW }; // armour types... take 20/40/60 % off +enum { M_NONE = 0, M_SEARCH, M_HOME, M_ATTACKING, M_PAIN, M_SLEEP, M_AIMING, M_NADE /* bloody hack*/ }; // monster states + +#define MAXCLIENTS 256 // in a multiplayer game, can be arbitrarily changed +#define MAXTRANS 5000 // max amount of data to swallow in 1 go +#define CUBE_SERVER_PORT 28765 +#define CUBE_SERVINFO_PORT 28766 +#define PROTOCOL_VERSION 121 // bump when protocol changes + +// network messages codes, c2s, c2c, s2c +enum +{ + SV_INITS2C = 0, SV_INITC2S, SV_POS, SV_TEXT, SV_SOUND, SV_CDIS, + SV_DIED, SV_DAMAGE, SV_SHOT, SV_FRAGS, + SV_TIMEUP, SV_EDITENT, SV_MAPRELOAD, SV_ITEMACC, + SV_MAPCHANGE, SV_ITEMSPAWN, SV_ITEMPICKUP, SV_DENIED, + SV_PING, SV_PONG, SV_CLIENTPING, SV_GAMEMODE, + SV_EDITH, SV_EDITT, SV_EDITS, SV_EDITD, SV_EDITE, + SV_SENDMAP, SV_RECVMAP, SV_SERVMSG, SV_ITEMLIST, SV_WEAPCHANGE, + SV_MODELSKIN, + SV_FLAGPICKUP, SV_FLAGDROP, SV_FLAGRETURN, SV_FLAGSCORE, SV_FLAGINFO, SV_FLAGS, //EDIT: AH + SV_EXT, +}; + +enum { CS_ALIVE = 0, CS_DEAD, CS_LAGGED, CS_EDITING }; + +// hardcoded sounds, defined in sounds.cfg +enum +{ + S_JUMP = 0, S_LAND, + S_KNIFE, + S_PISTOL, S_RPISTOL, + S_SHOTGUN, S_RSHOTGUN, + S_SUBGUN, S_RSUBGUN, + S_SNIPER, S_RSNIPER, //10 + S_ASSULT, S_RASSULT, + S_GRENADE, + S_ITEMAMMO, S_ITEMHEALTH, + S_ITEMARMOUR, S_ITEMPUP, S_ITEMSPAWN, + S_NOAMMO, S_PUPOUT, //20 + S_PAIN1, S_PAIN2, S_PAIN3, S_PAIN4, S_PAIN5, S_PAIN6, + S_DIE1, S_DIE2, S_SUICIDE, + S_FALL1, S_FALL2, //31 + S_FLAUNCH, S_FEXPLODE, //33 + S_FLAGDROP, S_FLAGPICKUP, S_FLAGRETURN, S_FLAGSCORE, + S_SPLASH1, S_SPLASH2, + S_GRUNT1, S_GRUNT2, S_RUMBLE, //38 + S_NULL +}; + +// vertex array format + +struct vertex { float u, v, x, y, z; uchar r, g, b, a; }; + +typedef vector dvector; +typedef vector cvector; +typedef vector ivector; + +// globals ooh naughty + +extern sqr *world, *wmip[]; // map data, the mips are sequential 2D arrays in memory +extern header hdr; // current map header +extern int sfactor, ssize; // ssize = 2^sfactor +extern int cubicsize, mipsize; // cubicsize = ssize^2 +extern dynent *player1; // special client ent that receives input and acts as camera +extern dvector players; // all the other clients (in multiplayer) +extern vectorphysents; +extern bool editmode; +extern vector ents; // map entities +extern vec worldpos; // current target of the crosshair in the world +extern int lastmillis; // last time +extern int curtime; // current frame time +extern int gamemode, nextmode; +extern int xtraverts; +extern bool demoplayback; + + +#define DMF 16.0f +#define DAF 1.0f +#define DVF 100.0f + +#define VIRTW 2400 // virtual screen size for text & HUD +#define VIRTH 1800 +#define FONTH 64 +#define PIXELTAB (VIRTW/12) + +#define PI (3.1415927f) +#define PI2 (2*PI) + +// 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); } +#define vdiv(u,f) { (u).x /= (f); (u).y /= (f); (u).z /= (f); } +#define vadd(u,v) { (u).x += (v).x; (u).y += (v).y; (u).z += (v).z; }; +#define vsub(u,v) { (u).x -= (v).x; (u).y -= (v).y; (u).z -= (v).z; }; +#define vdist(d,v,e,s) vec v = s; vsub(v,e); float d = (float)sqrt(dotprod(v,v)); +#define vdistsquared(d,v,e,s) vec v = s; vsub(v,e); float d = (dotprod(v,v)); +#define vreject(v,u,max) ((v).x>(u).x+(max) || (v).x<(u).x-(max) || (v).y>(u).y+(max) || (v).y<(u).y-(max)) +#define vlinterp(v,f,u,g) { (v).x = (v).x*f+(u).x*g; (v).y = (v).y*f+(u).y*g; (v).z = (v).z*f+(u).z*g; } + +#define sgetstr() { char *t = text; int tlen=0; do { *t = getint(p); } while(*t++ && ++tlen +#endif + +#include + +#include +#include +#include + +#include +#include + +#include + +#include + +#include "protos.h" // external function decls + diff --git a/source/src/entities.cpp b/source/src/entities.cpp index 363f0093..a21b7509 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -1,303 +1,326 @@ -// entities.cpp: map entity related functions (pickup etc.) - -#include "cube.h" - -vector ents; - -char *entmdlnames[] = -{ -//FIXME : fix the "pickups" infront - "pickups/pistolclips", "pickups/ammobox", "pickups/nades", "pickups/health", "pickups/kevlar", "pickups/akimbo", -}; - -int triggertime = 0; - -void renderent(entity &e, char *mdlname, float z, float yaw, int frame = 0, int numf = 1, int basetime = 0, float speed = 10.0f) -{ - rendermodel(mdlname, frame, numf, 0, 1.1f, e.x, z+S(e.x, e.y)->floor, e.y, yaw, 0, false, 1.0f, speed, 0, basetime); -}; - -void renderentities() -{ - if(lastmillis>triggertime+1000) triggertime = 0; - loopv(ents) - { - entity &e = ents[i]; - if(e.type==MAPMODEL) - { - mapmodelinfo &mmi = getmminfo(e.attr2); - if(!&mmi) continue; - rendermodel(mmi.name, 0, 1, e.attr4, (float)mmi.rad, e.x, (float)S(e.x, e.y)->floor+mmi.zoff+e.attr3, e.y, (float)((e.attr1+7)-(e.attr1+7)%15), 0, false, 1.0f, 10.0f, mmi.snap); - } - else - { - if(OUTBORD(e.x, e.y)) continue; - if(e.type!=CARROT) - { - if(!e.spawned) continue; - if(e.typeI_AKIMBO) continue; - renderent(e, entmdlnames[e.type-I_CLIPS], (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/10.0f); - } - else switch(e.attr2) - { - case 1: - case 3: - continue; - - case 2: - case 0: - if(!e.spawned) continue; - renderent(e, "carrot", (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/(e.attr2 ? 1.0f : 10.0f)); - break; - - case 4: renderent(e, "switch2", 3, (float)e.attr3*90, (!e.spawned && !triggertime) ? 1 : 0, (e.spawned || !triggertime) ? 1 : 2, triggertime, 1050.0f); break; - case 5: renderent(e, "switch1", -0.15f, (float)e.attr3*90, (!e.spawned && !triggertime) ? 30 : 0, (e.spawned || !triggertime) ? 1 : 30, triggertime, 35.0f); break; - }; - }; - }; -}; - -struct itemstat { int add, start, max, sound; } itemstats[] = -{ - 16, 8, 72, S_ITEMAMMO, //pistol - 14, 7, 21, S_ITEMAMMO, //shotgun - 60, 30, 90, S_ITEMAMMO, //subgun - 10, 5, 15, S_ITEMAMMO, //sniper - 40, 20, 60, S_ITEMAMMO, //assult - 2, 0, 2, S_ITEMAMMO, //grenade - 33, 100, 100, S_ITEMHEALTH, //health - 50, 100, 100, S_ITEMARMOUR, //armour - 16, 0, 72, S_ITEMPUP, //powerup -}; - -void baseammo(int gun) { player1->ammo[gun] = itemstats[gun-1].add*2; }; - -// these two functions are called when the server acknowledges that you really -// picked up the item (in multiplayer someone may grab it before you). - -/* -void radditem(int i, int &v) -{ - itemstat &is = itemstats[ents[i].type-I_CLIPS]; - ents[i].spawned = false; - v += is.add; - if(v>is.max) v = is.max; - playsoundc(is.sound); -}; -*/ - -void radditem(int i, int &v, int t) -{ - itemstat &is = itemstats[t-1]; - ents[i].spawned = false; - v += is.add; - if(v>is.max) v = is.max; - playsoundc(is.sound); -}; - -void realpickup(int n, dynent *d) -{ - switch(ents[n].type) - { - //case I_PISTOL: radditem(n, d->ammo[1]); break; - //case I_SHOTGUN: radditem(n, d->ammo[2]); break; - //case I_SUBGUN: radditem(n, d->ammo[3]); break; - //case I_SNIPER: radditem(n, d->ammo[4]); break; - //case I_ASSULT: radditem(n, d->ammo[5]); break; - case I_CLIPS: - - radditem(n, d->ammo[1], 1); - break; - - case I_AMMO: - radditem(n, d->ammo[d->primary], d->primary); - break; - case I_GRENADE: radditem(n, d->mag[6], 6); break; - case I_HEALTH: radditem(n, d->health, 7); break; - - case I_ARMOUR: - radditem(n, d->armour, 8); - //d->hasarmour = true; - break; - - case I_AKIMBO: - d->akimbo = true; - d->mag[GUN_PISTOL] = 16; - radditem(n, d->ammo[1], 9); - conoutf("a lesser man would use a single pistol"); - break; - }; -}; - -// these functions are called when the client touches the item - -/* -void additem(int i, int &v, int spawnsec) -{ - if(v4 ? 2 : 3); // spawn times are dependent on number of players - int ammo = np*2; - switch(ents[n].type) - { - //case I_PISTOL: additem(n, d->ammo[1], ammo); break; - //case I_SHOTGUN: additem(n, d->ammo[2], ammo); break; - //case I_SUBGUN: additem(n, d->ammo[3], ammo); break; - //case I_SNIPER: additem(n, d->ammo[4], ammo); break; - //case I_ASSULT: additem(n, d->ammo[5], ammo); break; - case I_CLIPS: - additem(n, d->ammo[1], ammo, 1); - break; - case I_AMMO: - additem(n, d->ammo[d->primary], ammo, d->primary); - break; - case I_GRENADE: additem(n, d->mag[6], ammo, 6); break; - case I_HEALTH: additem(n, d->health, np*5, 7); break; - - case I_ARMOUR: - additem(n, d->armour, 20, 8); - break; - - case I_AKIMBO: - //additem(n, d->ammo[1], 60, 9); - additem(n, d->akimbo, 60, 9); - break; - - case CARROT: - ents[n].spawned = false; - triggertime = lastmillis; - trigger(ents[n].attr1, ents[n].attr2, false); // needs to go over server for multiplayer - break; - - case LADDER: - { - d->onladder = true; - break; - }; - }; -}; - -void checkitems() -{ - if(editmode) return; - player1->onladder = false; - loopv(ents) - { - entity &e = ents[i]; - if(e.type==NOTUSED) continue; - if(e.type==LADDER) - { - if(OUTBORD(e.x, e.y)) continue; - vec v = { e.x, e.y, player1->o.z }; - vdist(dist1, t, player1->o, v); - float dist2 = player1->o.z - (S(e.x, e.y)->floor+player1->eyeheight); - if(dist1<1.5 && dist2floor+player1->eyeheight }; - vdist(dist, t, player1->o, v); - if(dist<2.5) pickup(i, player1); - }; -}; - -void putitems(uchar *&p) // puts items in network stream and also spawns them locally -{ - loopv(ents) if((ents[i].type>=I_CLIPS && ents[i].type<=I_AKIMBO) || ents[i].type==CARROT) - { - putint(p, i); - ents[i].spawned = true; - }; -}; - -void resetspawns() { loopv(ents) ents[i].spawned = false; }; -void setspawn(int i, bool on) { if(inextprimary!=i) d->ammo[i] = 0; - d->mag[GUN_KNIFE] = 1; - - d->mag[GUN_PISTOL] = 8; - d->ammo[GUN_PISTOL] = 24; - - if (m_pistol || d->primary==GUN_PISTOL) // || pistol only mode! - { - d->primary = GUN_PISTOL; - d->ammo[d->primary] = 72; - } - else if (d->primary>GUN_PISTOL && d->primaryammo[d->primary] = itemstats[d->primary-1].max; - - d->mag[d->primary] = itemstats[d->primary-1].start; - } - -/* - if (d->primary==GUN_GRENADE) - { - //conoutf("you don't have to worry about blowing your hand off just yet..."); - } -*/ - if (d->hasarmour) - { - if(gamemode==m_arena) - d->armour = 100; - } - - d->gunselect = d->primary; //draw main weapon -}; - -void item(int num) -{ - if (num>0 && num<7) player1->nextprimary = num; - - //if (num==6) conoutf("sorry, no nades yet"); - - if (num==7) { player1->hasarmour=!player1->hasarmour; /*toggle armour*/ }; - -}; - -void weapon(int num) -{ - if(num>0 && num<7) - { - player1->nextprimary = num; - player1->gunselect = num; - player1->primary = num; - - radd(player1); - } - gun_changed = true; -} - -COMMAND(weapon, ARG_1INT); -COMMAND(item,ARG_1INT); - -void akimbo(void) -{ - conoutf("akimbo=%d",player1->akimbo); -}; -COMMAND(akimbo, ARG_NONE); \ No newline at end of file +// entities.cpp: map entity related functions (pickup etc.) + +#include "cube.h" + +vector ents; + +char *entmdlnames[] = +{ +//FIXME : fix the "pickups" infront + "pickups/pistolclips", "pickups/ammobox", "pickups/nades", "pickups/health", "pickups/kevlar", "pickups/akimbo", +}; + +int triggertime = 0; + +void renderent(entity &e, char *mdlname, float z, float yaw, int frame = 0, int numf = 1, int basetime = 0, float speed = 10.0f) +{ + rendermodel(mdlname, frame, numf, 0, 1.1f, e.x, z+S(e.x, e.y)->floor, e.y, yaw, 0, false, 1.0f, speed, 0, basetime); +}; + +void renderentities() +{ + if(lastmillis>triggertime+1000) triggertime = 0; + loopv(ents) + { + entity &e = ents[i]; + if(e.type==MAPMODEL) + { + mapmodelinfo &mmi = getmminfo(e.attr2); + if(!&mmi) continue; + rendermodel(mmi.name, 0, 1, e.attr4, (float)mmi.rad, e.x, (float)S(e.x, e.y)->floor+mmi.zoff+e.attr3, e.y, (float)((e.attr1+7)-(e.attr1+7)%15), 0, false, 1.0f, 10.0f, mmi.snap); + } + else if(e.type==CTF_FLAG && m_ctf) // EDIT: AH + { + flaginfo &f = flaginfos[e.attr2]; + if(f.state==CTFF_STOLEN && f.thief) + { + if(f.thief == player1) continue; + sprintf_sd(path)("pickups/flags/small_%s", rb_team_string(e.attr2)); + mapmodelinfo mmi = {10, 4, 0, 0, path}; + if(!&mmi) continue; + rendermodel(mmi.name, 0, 7, 0, 0, f.thief->o.x, f.thief->o.z+0.3f+(sin(lastmillis/100.0f)+1)/10, f.thief->o.y, lastmillis/2.5f, 0, false, 1.0f, 120.0f, mmi.snap); + } + else + { + sprintf_sd(path)("pickups/flags/%s", rb_team_string(e.attr2)); + mapmodelinfo mmi = {10, 4, 0, 0, path}; + if(!&mmi) continue; + rendermodel(mmi.name, 0, 7, 0, (float)mmi.rad, e.x, f.state==CTFF_INBASE ? (float)S(e.x, e.y)->floor : e.z, e.y, (float)((e.attr1+7)-(e.attr1+7)%15), 0, false, 1.0f, 120.0f, mmi.snap); + }; + } + else + { + if(OUTBORD(e.x, e.y)) continue; + if(e.type!=CARROT) + { + if(!e.spawned) continue; + if(e.typeI_QUAD) continue; + renderent(e, entmdlnames[e.type-I_CLIPS], (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/10.0f); + } + else switch(e.attr2) + { + case 1: + case 3: + continue; + + case 2: + case 0: + if(!e.spawned) continue; + renderent(e, "carrot", (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/(e.attr2 ? 1.0f : 10.0f)); + break; + + case 4: renderent(e, "switch2", 3, (float)e.attr3*90, (!e.spawned && !triggertime) ? 1 : 0, (e.spawned || !triggertime) ? 1 : 2, triggertime, 1050.0f); break; + case 5: renderent(e, "switch1", -0.15f, (float)e.attr3*90, (!e.spawned && !triggertime) ? 30 : 0, (e.spawned || !triggertime) ? 1 : 30, triggertime, 35.0f); break; + }; + }; + }; +}; + +struct itemstat { int add, start, max, sound; } itemstats[] = +{ + 16, 8, 72, S_ITEMAMMO, //pistol + 14, 7, 21, S_ITEMAMMO, //shotgun + 60, 30, 90, S_ITEMAMMO, //subgun + 10, 5, 15, S_ITEMAMMO, //sniper + 40, 20, 60, S_ITEMAMMO, //assult + 2, 0, 2, S_ITEMAMMO, //grenade + 33, 100, 100, S_ITEMHEALTH, //health + 50, 100, 100, S_ITEMARMOUR, //armour + 20000, 0, 30000, S_ITEMPUP, //powerup +}; + +void baseammo(int gun) { player1->ammo[gun] = itemstats[gun-1].add*2; }; + +// these two functions are called when the server acknowledges that you really +// picked up the item (in multiplayer someone may grab it before you). + +/* +void radditem(int i, int &v) +{ + itemstat &is = itemstats[ents[i].type-I_CLIPS]; + ents[i].spawned = false; + v += is.add; + if(v>is.max) v = is.max; + playsoundc(is.sound); +}; +*/ + +void radditem(int i, int &v, int t) +{ + itemstat &is = itemstats[t-1]; + ents[i].spawned = false; + v += is.add; + if(v>is.max) v = is.max; + playsoundc(is.sound); +}; + +void realpickup(int n, dynent *d) +{ + switch(ents[n].type) + { + //case I_PISTOL: radditem(n, d->ammo[1]); break; + //case I_SHOTGUN: radditem(n, d->ammo[2]); break; + //case I_SUBGUN: radditem(n, d->ammo[3]); break; + //case I_SNIPER: radditem(n, d->ammo[4]); break; + //case I_ASSULT: radditem(n, d->ammo[5]); break; + case I_CLIPS: radditem(n, d->ammo[1], 1); break; + case I_AMMO: radditem(n, d->ammo[d->primary], d->primary); break; + case I_GRENADE: radditem(n, d->mag[6], 6); break; + case I_HEALTH: radditem(n, d->health, 7); break; + + case I_ARMOUR: + radditem(n, d->armour, 8); + //d->hasarmour = true; + break; + + case I_QUAD: + //radditem(n, d->quadmillis, 9); + conoutf("a lesser man would use a single pistol"); + break; + }; +}; + +// these functions are called when the client touches the item + +/* +void additem(int i, int &v, int spawnsec) +{ + if(v4 ? 2 : 3); // spawn times are dependent on number of players + int ammo = np*2; + switch(ents[n].type) + { + //case I_PISTOL: additem(n, d->ammo[1], ammo); break; + //case I_SHOTGUN: additem(n, d->ammo[2], ammo); break; + //case I_SUBGUN: additem(n, d->ammo[3], ammo); break; + //case I_SNIPER: additem(n, d->ammo[4], ammo); break; + //case I_ASSULT: additem(n, d->ammo[5], ammo); break; + case I_CLIPS: additem(n, d->ammo[1], ammo, 1); break; + case I_AMMO: additem(n, d->ammo[d->primary], ammo, d->primary); break; + case I_GRENADE: additem(n, d->mag[6], ammo, 6); break; + case I_HEALTH: additem(n, d->health, np*5, 7); break; + + case I_ARMOUR: + additem(n, d->armour, 20, 8); + break; + + case I_QUAD: + //additem(n, d->quadmillis, 60, 9); + break; + + case CARROT: + ents[n].spawned = false; + triggertime = lastmillis; + trigger(ents[n].attr1, ents[n].attr2, false); // needs to go over server for multiplayer + break; + + case LADDER: + { + d->onladder = true; + break; + }; + + // EDIT: AH + case CTF_FLAG: + { + int team = ents[n].attr2; + flaginfo &f = flaginfos[team]; + if(f.state == CTFF_STOLEN) break; + else if(team == rb_team_int(player1->team) && f.state == CTFF_DROPPED) + { + addmsg(1, 2, SV_FLAGRETURN, team); + ents[n].spawned = false; + } + else if(team != rb_team_int(player1->team)) + { + addmsg(1, 2, SV_FLAGPICKUP, team); + ents[n].spawned = false; + f.thief = player1; // do this although we don't know if we picked the flag to avoid getting it after a possible respawn + f.state = CTFF_STOLEN; + f.pick_ack = false; + } + else if(team == rb_team_int(player1->team) && f.state == CTFF_INBASE && flaginfos[rb_opposite(team)].state == CTFF_STOLEN && flaginfos[rb_opposite(team)].thief == player1) addmsg(1, 2, SV_FLAGSCORE, rb_opposite(team)); + break; + }; + }; +}; + +void checkitems() +{ + if(editmode) return; + player1->onladder = false; + loopv(ents) + { + entity &e = ents[i]; + if(e.type==NOTUSED) continue; + if(e.type==LADDER) + { + if(OUTBORD(e.x, e.y)) continue; + vec v = { e.x, e.y, player1->o.z }; + vdist(dist1, t, player1->o, v); + float dist2 = player1->o.z - (S(e.x, e.y)->floor+player1->eyeheight); + if(dist1<1.5 && dist2floor+player1->eyeheight }; + vdist(dist, t, player1->o, v); + if(dist<2.5) pickup(i, player1); + }; +}; + +void putitems(uchar *&p) // puts items in network stream and also spawns them locally +{ + loopv(ents) if((ents[i].type>=I_CLIPS && ents[i].type<=I_QUAD) || ents[i].type==CARROT) + { + putint(p, i); + ents[i].spawned = true; + }; +}; + +void resetspawns() { loopv(ents) ents[i].spawned = false; }; +void setspawn(int i, bool on) { if(inextprimary!=i) d->ammo[i] = 0; + d->mag[GUN_KNIFE] = 1; + + d->mag[GUN_PISTOL] = 8; + d->ammo[GUN_PISTOL] = 24; + + if (m_pistol || d->primary==GUN_PISTOL) // || pistol only mode! + { + d->primary = GUN_PISTOL; + d->ammo[d->primary] = 72; + } + else if (d->primary>GUN_PISTOL && d->primaryammo[d->primary] = itemstats[d->primary-1].max; + + d->mag[d->primary] = itemstats[d->primary-1].start; + } + +/* + if (d->primary==GUN_GRENADE) + { + //conoutf("you don't have to worry about blowing your hand off just yet..."); + } +*/ + if (d->hasarmour) + { + if(gamemode==m_arena) + d->armour = 100; + } + + d->gunselect = d->primary; //draw main weapon +}; + +void item(int num) +{ + if (num>0 && num<7) player1->nextprimary = num; + + //if (num==6) conoutf("sorry, no nades yet"); + + if (num==7) { player1->hasarmour=!player1->hasarmour; /*toggle armour*/ }; + +}; + +void weapon(int num) +{ + if(num>0 && num<7) + { + player1->nextprimary = num; + player1->gunselect = num; + player1->primary = num; + + radd(player1); + } + gun_changed = true; +} + +COMMAND(weapon, ARG_1INT); +COMMAND(item,ARG_1INT); diff --git a/source/src/main.cpp b/source/src/main.cpp index ec84ee33..6b1c2de2 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -60,8 +60,8 @@ void screenshot() memcpy(dest, (char *)image->pixels+3*scr_w*(scr_h-1-idx), 3*scr_w); endianswap(dest, 3, scr_w); }; - sprintf_sd(buf)("screenshot_%d.bmp", lastmillis); - SDL_SaveBMP(temp, buf); + sprintf_sd(buf)("screenshots/screenshot_%d.bmp", lastmillis); + SDL_SaveBMP(temp, path(buf)); SDL_FreeSurface(temp); }; SDL_FreeSurface(image); @@ -95,9 +95,9 @@ int framesinmap = 0; int main(int argc, char **argv) { - bool dedicated = false, listen = false; - int fs = SDL_FULLSCREEN, par = 0, uprate = 0; - char *sdesc = "", *ip = "", *master = NULL, *passwd = ""; + bool dedicated = false; + int fs = SDL_FULLSCREEN, par = 0, uprate = 0, maxcl = 4; + char *sdesc = "", *ip = "", *master = NULL, *passwd = "", *maprot = ""; islittleendian = *((char *)&islittleendian); #define log(s) puts("init: " s) @@ -108,8 +108,7 @@ int main(int argc, char **argv) char *a = &argv[i][2]; if(argv[i][0]=='-') switch(argv[i][1]) { - case 'd': listen = dedicated = true; break; - case 'l': listen = true; break; + case 'd': dedicated = true; break; case 't': fs = 0; break; case 'w': scr_w = atoi(a); break; case 'h': scr_h = atoi(a); break; @@ -118,6 +117,8 @@ int main(int argc, char **argv) case 'i': ip = a; break; case 'm': master = a; break; case 'p': passwd = a; break; + case 'c': maxcl = atoi(a); break; + case 'r': maprot = a; break; // EDIT: AH default: conoutf("unknown commandline option"); } else conoutf("unknown commandline argument"); @@ -134,7 +135,7 @@ int main(int argc, char **argv) if(enet_initialize()<0) fatal("Unable to initialise network module"); initclient(); - initserver(dedicated, listen, uprate, sdesc, ip, master, passwd); // never returns if dedicated + initserver(dedicated, uprate, sdesc, ip, master, passwd, maxcl, maprot); // never returns if dedicated log("world"); empty_world(7, true); @@ -147,7 +148,7 @@ int main(int argc, char **argv) if(SDL_SetVideoMode(scr_w, scr_h, 0, SDL_OPENGL|fs)==NULL) fatal("Unable to create OpenGL screen"); log("video: misc"); - SDL_WM_SetCaption("action cube", NULL); + SDL_WM_SetCaption("Action Cube", NULL); SDL_WM_GrabInput(SDL_GRAB_ON); keyrepeat(false); SDL_ShowCursor(0); @@ -164,8 +165,9 @@ int main(int argc, char **argv) !installtex(8, path(newstring("packages/misc/martin/ball2.png")), xs, ys) || !installtex(9, path(newstring("packages/misc/martin/ball3.png")), xs, ys) || !installtex(4, path(newstring("packages/misc/explosion.jpg")), xs, ys) || - !installtex(5, path(newstring("packages/misc/items.png")), xs, ys) || + !installtex(5, path(newstring("packages/misc/items.png")), xs, ys) || !installtex(10, path(newstring("packages/misc/scope.png")), xs, ys) || + !installtex(11, path(newstring("packages/misc/flag_icons.png")), xs, ys) || !installtex(1, path(newstring("packages/misc/crosshairs/default.png")), xs, ys)) fatal("could not find core textures (hint: run cube from the parent of the bin directory)"); log("sound"); @@ -174,6 +176,7 @@ int main(int argc, char **argv) log("cfg"); newmenu("frags\tpj\tping\tteam\tname"); newmenu("ping\tplr\tserver"); + newmenu("flags\tfrags\tpj\tping\tteam\tname"); exec("config/keymap.cfg"); //exec("data/models.cfg"); exec("config/menus.cfg"); @@ -199,7 +202,7 @@ int main(int argc, char **argv) else if(millis-lastmillis<1) lastmillis = millis-1; cleardlights(); updateworld(millis); - if(!demoplayback) serverslice(/*enet_time_get_sec()*/time(NULL), 0); + if(!demoplayback) serverslice((int)time(NULL), 0); static float fps = 30.0f; fps = (1000.0f/curtime+fps*50)/51; computeraytable(player1->o.x, player1->o.y); diff --git a/source/src/menus.cpp b/source/src/menus.cpp index 86d33ee8..e63397fc 100644 --- a/source/src/menus.cpp +++ b/source/src/menus.cpp @@ -1,222 +1,222 @@ -// 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) -{ - if((vmenu = menu)>=1) 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(x1 ? "[ %s menu ]" : "%s", m.name); - 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) - { - 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) return false; - 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 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; +}; diff --git a/source/src/physics.cpp b/source/src/physics.cpp index c637b933..f0f79471 100644 --- a/source/src/physics.cpp +++ b/source/src/physics.cpp @@ -1,483 +1,483 @@ -// physics.cpp: no physics books were hurt nor consulted in the construction of this code. -// All physics computations and constants were invented on the fly and simply tweaked until -// they "felt right", and have no basis in reality. Collision detection is simplistic but -// very robust (uses discrete steps at fixed fps). - -#include "cube.h" - -bool plcollide(dynent *d, dynent *o, float &headspace) // collide with player or monster -{ - if(o->state!=CS_ALIVE) return true; - const float r = o->radius+d->radius; - if(fabs(o->o.x-d->o.x)o.y-d->o.y)o.z-d->o.z)aboveeye+d->eyeheight) return false; - if(d->monsterstate) return false; // hack - headspace = d->o.z-o->o.z-o->aboveeye-d->eyeheight; - if(headspace<0) headspace = 10; - }; - return true; -}; - -bool cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by, int &bs) // recursively collide with a mipmapped corner cube -{ - sqr *w = wmip[mip]; - int sz = ssize>>mip; - bool stest = SOLID(SWS(w, x+dx, y, sz)) && SOLID(SWS(w, x, y+dy, sz)); - mip++; - x /= 2; - y /= 2; - if(SWS(wmip[mip], x, y, ssize>>mip)->type==CORNER) - { - bx = x<radius; - if(fabs(e.x-d->o.x)o.y)floor+mmi.zoff+e.attr3); - if(d->o.z-d->eyeheightlo) lo = mmz+mmi.h; - }; - }; -}; - -// all collision happens here -// 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 - -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 - const float fy1 = d->o.y-d->radius; - const float fx2 = d->o.x+d->radius; - const float fy2 = d->o.y+d->radius; - const int x1 = fast_f2nat(fx1); - const int y1 = fast_f2nat(fy1); - const int x2 = fast_f2nat(fx2); - const int y2 = fast_f2nat(fy2); - 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 :) - - for(int x = x1; x<=x2; x++) for(int y = y1; y<=y2; y++) // collide with map - { - if(OUTBORD(x,y)) return false; - sqr *s = S(x,y); - float ceil = s->ceil; - float floor = s->floor; - switch(s->type) - { - case SOLID: - return false; - - case CORNER: - { - int bx = x, by = y, bs = 1; - if(x==x1 && y==y1 && cornertest(0, x, y, -1, -1, bx, by, bs) && fx1-bx+fy1-by<=bs - || x==x2 && y==y1 && cornertest(0, x, y, 1, -1, bx, by, bs) && fx2-bx>=fy1-by - || x==x1 && y==y2 && cornertest(0, x, y, -1, 1, bx, by, bs) && fx1-bx<=fy2-by - || x==x2 && y==y2 && cornertest(0, x, y, 1, 1, bx, by, bs) && fx2-bx+fy2-by>=bs) - return false; - break; - }; - - case FHF: // FIXME: too simplistic collision with slopes, makes it feels like tiny stairs - floor -= (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f; - break; - - case CHF: - ceil += (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f; - - }; - if(ceillo) lo = floor; - if(flooreyeheight+d->aboveeye) return false; - - float headspace = 10; - loopv(players) // collide with other players - { - dynent *o = players[i]; - if(!o || o==d) continue; - if(!plcollide(d, o, headspace)) return false; - }; - if(d!=player1 && d->mtype!=M_NADE) if(!plcollide(d, player1, headspace)) return false; - headspace -= 0.01f; - - mmcollide(d, hi, lo); // collide with map models - - if(spawn) - { - d->o.z = lo+d->eyeheight; // just drop to floor (sideeffect) - d->startheight = d->o.z; //no falling damage on spawn - d->onfloor = true; - } - else - { - const float space = d->o.z-d->eyeheight-lo; - if(space<0) - { - if(space>-0.01) - { - d->o.z = lo+d->eyeheight; // stick on step - } - else if(space>-1.26f && !d->isphysent) d->o.z += rise; // rise thru stair - else return false; - } - else - { - d->o.z -= min(min(drop, space), headspace); // gravity - }; - - const float space2 = hi-(d->o.z+d->aboveeye); - if(space2<0) - { - if(space2<-0.1) return false; // hack alert! - d->o.z = hi-d->aboveeye; // glue to ceiling - d->vel.z = 0; // cancel out jumping velocity - }; - - d->onfloor = d->o.z-d->eyeheight-lo<0.01f; - }; - return true; -} - -float rad(float x) { return x*3.14159f/180; }; - -VAR(maxroll, 0, 3, 20); - -int physicsfraction = 0, physicsrepeat = 0; -const int MINFRAMETIME = 20; // physics always simulated at 50fps or better - -void physicsframe() // optimally schedule physics frames inside the graphics frames -{ - if(curtime>=MINFRAMETIME) - { - int faketime = curtime+physicsfraction; - physicsrepeat = faketime/MINFRAMETIME; - physicsfraction = faketime-physicsrepeat*MINFRAMETIME; - } - else - { - physicsrepeat = 1; - }; -}; - -void selfdamage(dynent * d, int dm) -{ - 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, dm, 1000, d->o); //edit out? - demodamage(dm, d->o); -}; - - -// 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 - -void moveplayer(dynent *pl, int moveres, bool local, int curtime) -{ - const bool water = hdr.waterlevel>pl->o.z-0.5f; - const bool floating = (editmode && local) || pl->state==CS_EDITING; - - vec d; // vector of direction we ideally want to move in - - - int move = pl->onladder && !pl->onfloor && pl->move == -1 ? 0 : pl->move; // fix movement on ladder - - d.x = (float)(move*cos(rad(pl->yaw-90))); - d.y = (float)(move*sin(rad(pl->yaw-90))); - d.z = (float) pl->isphysent ? pl->vel.z : 0; - - if(floating || water) - { - d.x *= (float)cos(rad(pl->pitch)); - d.y *= (float)cos(rad(pl->pitch)); - d.z = (float)(move*sin(rad(pl->pitch))); - }; - - d.x += (float)(pl->strafe*cos(rad(pl->yaw-180))); - d.y += (float)(pl->strafe*sin(rad(pl->yaw-180))); - - const float speed = curtime/(water ? 2000.0f : 1000.0f)*pl->maxspeed; - const float friction = water ? 20.0f : (pl->onfloor || floating ? 6.0f : (pl->onladder ? 1.5f : 30.0f)); - - const float fpsfric = friction/curtime*20.0f; - - vmul(pl->vel, fpsfric-1); // slowly apply friction and direction to velocity, gives a smooth movement - vadd(pl->vel, d); - vdiv(pl->vel, fpsfric); - d = pl->vel; - vmul(d, speed); // d is now frametime based velocity vector - - if(pl->isphysent) - { - pl->pitch += speed*5.0f; - if(pl->pitch>360.0f) pl->pitch = 0.0f; - pl->yaw += speed*5.0f; - if(pl->yaw>360.0f) pl->yaw = 0.0f; - - } - - pl->blocked = false; - pl->moving = true; - - if(floating) // just apply velocity - { - vadd(pl->o, d); - if(pl->jumpnext) { pl->jumpnext = false; pl->vel.z = 2; } - } - else // apply velocity with collision - { - if(pl->onladder) - { - if(pl->k_up) pl->vel.z = 0.5; - else if(pl->k_down) pl->vel.z = -0.5; - pl->timeinair = 0; - } - else - { - - if(pl->onfloor || water) - { - if(pl->onfloor || !pl->vel.z) - { - int damage = (int)pl->startheight - (int)pl->o.z; - if (damage > 8 && local) //try adding "&& local" if falling not fixed - { - selfdamage(damage,-1,pl); - demodamage(damage, player1->o); - playsound(S_FALL1+rnd(2), &pl->o); - }; - pl->startheight=pl->o.z; - }; - - if(pl->jumpnext) - { - pl->jumpnext = false; - pl->vel.z = 1.7f; // physics impulse upwards - if(water) { pl->vel.x /= 8; pl->vel.y /= 8; }; // dampen velocity change even harder, gives correct water feel - if(local) playsoundc(S_JUMP); - else if(pl->monsterstate) playsound(S_JUMP, &pl->o); - } - /*else if(pl->timeinair>800) // if we land after long time must have been a high jump, make thud sound - { - if(local) playsoundc(S_LAND); - else if(pl->monsterstate) playsound(S_LAND, &pl->o); - };*/ - pl->timeinair = 0; - if(pl->isphysent) pl->vel.z *= 0.7f; - } - else - { - pl->timeinair += curtime; - }; - }; - - const float gravity = pl->isphysent ? pl->gravity : 20; - const float f = 1.0f/moveres; - float dropf = pl->isphysent ? ((gravity-1)+pl->timeinair/14.0f) : ((gravity-1)+pl->timeinair/15.0f); // incorrect, but works fine - if(water) { dropf = 5; pl->timeinair = 0; }; // float slowly down in water - if(pl->onladder) { dropf = 0; pl->timeinair = 0; }; - float drop = dropf*curtime/gravity/100/moveres; // at high fps, gravity kicks in too fast - const float rise = speed/moveres/1.2f; // extra smoothness when lifting up stairs - - loopi(moveres) // discrete steps collision detection & sliding - { - // try move forward - pl->o.x += f*d.x; - pl->o.y += f*d.y; - pl->o.z += f*d.z; - if(collide(pl, false, drop, rise)) continue; - // player stuck, try slide along y axis - pl->blocked = true; - pl->o.x -= f*d.x; - if(collide(pl, false, drop, rise)) - { - d.x = 0; - if(pl->isphysent) pl->vel.x = -pl->vel.x; - continue; - }; - pl->o.x += f*d.x; - // still stuck, try x axis - pl->o.y -= f*d.y; - if(collide(pl, false, drop, rise)) - { - d.y = 0; - if(pl->isphysent) pl->vel.y = -pl->vel.y; - continue; - }; - pl->o.y += f*d.y; - // try just dropping down - pl->moving = false; - pl->o.x -= f*d.x; - pl->o.y -= f*d.y; - if(collide(pl, false, drop, rise)) - { - d.y = d.x = 0; - continue; - }; - pl->o.z -= f*d.z; - break; - }; - }; - - // detect wether player is outside map, used for skipping zbuffer clear mostly - - if(pl->o.x < 0 || pl->o.x >= ssize || pl->o.y <0 || pl->o.y > ssize) - { - pl->outsidemap = true; - } - else - { - sqr *s = S((int)pl->o.x, (int)pl->o.y); - pl->outsidemap = SOLID(s) - || pl->o.z < s->floor - (s->type==FHF ? s->vdelta/4 : 0) - || pl->o.z > s->ceil + (s->type==CHF ? s->vdelta/4 : 0); - }; - - // automatically apply smooth roll when strafing - - if(pl->strafe==0) - { - pl->roll = pl->roll/(1+(float)sqrt((float)curtime)/25); - } - else - { - pl->roll += pl->strafe*curtime/-30.0f; - if(pl->roll>maxroll) pl->roll = (float)maxroll; - if(pl->roll<-maxroll) pl->roll = (float)-maxroll; - }; - - // 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); - pl->inwater = water; -}; - -void moveplayer(dynent *pl, int moveres, bool local) -{ - loopi(physicsrepeat) moveplayer(pl, moveres, local, i ? curtime/physicsrepeat : curtime-curtime/physicsrepeat*(physicsrepeat-1)); -}; - -void movephysent(physent *pl) -{ - int moveres = 2; - bool local=true; - loopj(physicsrepeat) - { - const bool water = hdr.waterlevel>pl->o.z-0.5f; - const bool floating = (editmode && local); - - vec d; // vector of direction we ideally want to move in - - d = pl->vel; - - - const float speed = curtime/(water ? 2000.0f : 1000.0f)*pl->maxspeed; - const float friction = water ? 20.0f : (pl->onfloor || floating ? 6.0f : 30.0f); - - const float fpsfric = friction/curtime*20.0f; - - vmul(pl->vel, fpsfric-1); // slowly apply friction and direction to velocity, gives a smooth movement - vadd(pl->vel, d); - vdiv(pl->vel, fpsfric); - d = pl->vel; - vmul(d, speed); // d is now frametime based velocity vector - - pl->pitch = (pl->pitch+(speed)); - if(pl->pitch>360.0f) pl->pitch = 0.0f; - - printf("pitch %f\n", pl->pitch); - - if(pl->onfloor) - { - pl->timeinair = 0; - vmul(pl->vel, 0.8f); - } - else - { - pl->timeinair += curtime; - pl->pitch = (lastmillis-pl->millis) % 360; - pl->yaw = pl->pitch; - }; - - - //const float speed = curtime/(water ? 2000.0f : 1000.0f)*pl->maxspeed; - - const float gravity = 20; - const float f = 1.0f/moveres; - float dropf = pl->timeinair/15.0f; // incorrect, but works fine - if(water) { dropf = 5; pl->timeinair = 0; }; // float slowly down in water - const float drop = dropf*curtime/gravity/100/moveres; // at high fps, gravity kicks in too fast - const float rise = speed/moveres/1.2f; // extra smoothness when lifting up stairs -// if(pl->mtype = M_NADE)printf("%i\n", pl->onfloor); -/* d.x -= d.x/50.0f*curtime; - d.y -= d.y/50.0f*curtime;*/ - - - loopi(moveres) // discrete steps collision detection & sliding - { - // try move forward - pl->o.x += f*d.x; - pl->o.y += f*d.y; - pl->o.z += f*d.z; - if(collide(pl, false, drop, rise)) continue; - // player stuck, try slide along y axis - pl->blocked = true; - pl->o.x -= f*d.x; - if(collide(pl, false, drop, rise)) { pl->vel.x = -d.x; vmul(pl->vel, 0.8f); d.x = 0; continue; }; - pl->o.x += f*d.x; - // still stuck, try x axis - pl->o.y -= f*d.y; - if(collide(pl, false, drop, rise)) { pl->vel.y = -d.y; vmul(pl->vel, 0.8f); d.y = 0; continue; }; - pl->o.y += f*d.y; - // try just dropping down - pl->moving = false; - pl->o.x -= f*d.x; - pl->o.y -= f*d.y; - if(collide(pl, false, drop, rise)) { d.y = d.x = 0; vmul(pl->vel, 0.8f); continue; }; - pl->o.z -= f*d.z; - break; - }; - - // detect wether player is outside map, used for skipping zbuffer clear mostly - - - - if(pl->o.x < 0 || pl->o.x >= ssize || pl->o.y <0 || pl->o.y > ssize) - { - pl->outsidemap = true; - } - else - { - sqr *s = S((int)pl->o.x, (int)pl->o.y); - pl->outsidemap = SOLID(s) - || pl->o.z < s->floor - (s->type==FHF ? s->vdelta/4 : 0) - || pl->o.z > s->ceil + (s->type==CHF ? s->vdelta/4 : 0); - }; - - // 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); - pl->inwater = water; - }; -}; +// physics.cpp: no physics books were hurt nor consulted in the construction of this code. +// All physics computations and constants were invented on the fly and simply tweaked until +// they "felt right", and have no basis in reality. Collision detection is simplistic but +// very robust (uses discrete steps at fixed fps). + +#include "cube.h" + +bool plcollide(dynent *d, dynent *o, float &headspace) // collide with player or monster +{ + if(o->state!=CS_ALIVE) return true; + const float r = o->radius+d->radius; + if(fabs(o->o.x-d->o.x)o.y-d->o.y)o.z-d->o.z)aboveeye+d->eyeheight) return false; + if(d->monsterstate) return false; // hack + headspace = d->o.z-o->o.z-o->aboveeye-d->eyeheight; + if(headspace<0) headspace = 10; + }; + return true; +}; + +bool cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by, int &bs) // recursively collide with a mipmapped corner cube +{ + sqr *w = wmip[mip]; + int sz = ssize>>mip; + bool stest = SOLID(SWS(w, x+dx, y, sz)) && SOLID(SWS(w, x, y+dy, sz)); + mip++; + x /= 2; + y /= 2; + if(SWS(wmip[mip], x, y, ssize>>mip)->type==CORNER) + { + bx = x<radius; + if(fabs(e.x-d->o.x)o.y)floor+mmi.zoff+e.attr3); + if(d->o.z-d->eyeheightlo) lo = mmz+mmi.h; + }; + }; +}; + +// all collision happens here +// 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 + +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 + const float fy1 = d->o.y-d->radius; + const float fx2 = d->o.x+d->radius; + const float fy2 = d->o.y+d->radius; + const int x1 = fast_f2nat(fx1); + const int y1 = fast_f2nat(fy1); + const int x2 = fast_f2nat(fx2); + const int y2 = fast_f2nat(fy2); + 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 :) + + for(int x = x1; x<=x2; x++) for(int y = y1; y<=y2; y++) // collide with map + { + if(OUTBORD(x,y)) return false; + sqr *s = S(x,y); + float ceil = s->ceil; + float floor = s->floor; + switch(s->type) + { + case SOLID: + return false; + + case CORNER: + { + int bx = x, by = y, bs = 1; + if(x==x1 && y==y1 && cornertest(0, x, y, -1, -1, bx, by, bs) && fx1-bx+fy1-by<=bs + || x==x2 && y==y1 && cornertest(0, x, y, 1, -1, bx, by, bs) && fx2-bx>=fy1-by + || x==x1 && y==y2 && cornertest(0, x, y, -1, 1, bx, by, bs) && fx1-bx<=fy2-by + || x==x2 && y==y2 && cornertest(0, x, y, 1, 1, bx, by, bs) && fx2-bx+fy2-by>=bs) + return false; + break; + }; + + case FHF: // FIXME: too simplistic collision with slopes, makes it feels like tiny stairs + floor -= (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f; + break; + + case CHF: + ceil += (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f; + + }; + if(ceillo) lo = floor; + if(flooreyeheight+d->aboveeye) return false; + + float headspace = 10; + loopv(players) // collide with other players + { + dynent *o = players[i]; + if(!o || o==d) continue; + if(!plcollide(d, o, headspace)) return false; + }; + if(d!=player1 && d->mtype!=M_NADE) if(!plcollide(d, player1, headspace)) return false; + headspace -= 0.01f; + + mmcollide(d, hi, lo); // collide with map models + + if(spawn) + { + d->o.z = lo+d->eyeheight; // just drop to floor (sideeffect) + d->startheight = d->o.z; //no falling damage on spawn + d->onfloor = true; + } + else + { + const float space = d->o.z-d->eyeheight-lo; + if(space<0) + { + if(space>-0.01) + { + d->o.z = lo+d->eyeheight; // stick on step + } + else if(space>-1.26f && !d->isphysent) d->o.z += rise; // rise thru stair + else return false; + } + else + { + d->o.z -= min(min(drop, space), headspace); // gravity + }; + + const float space2 = hi-(d->o.z+d->aboveeye); + if(space2<0) + { + if(space2<-0.1) return false; // hack alert! + d->o.z = hi-d->aboveeye; // glue to ceiling + d->vel.z = 0; // cancel out jumping velocity + }; + + d->onfloor = d->o.z-d->eyeheight-lo<0.01f; + }; + return true; +} + +float rad(float x) { return x*3.14159f/180; }; + +VAR(maxroll, 0, 3, 20); + +int physicsfraction = 0, physicsrepeat = 0; +const int MINFRAMETIME = 20; // physics always simulated at 50fps or better + +void physicsframe() // optimally schedule physics frames inside the graphics frames +{ + if(curtime>=MINFRAMETIME) + { + int faketime = curtime+physicsfraction; + physicsrepeat = faketime/MINFRAMETIME; + physicsfraction = faketime-physicsrepeat*MINFRAMETIME; + } + else + { + physicsrepeat = 1; + }; +}; + +void selfdamage(dynent * d, int dm) +{ + 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, dm, 1000, d->o); //edit out? + demodamage(dm, d->o); +}; + + +// 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 + +void moveplayer(dynent *pl, int moveres, bool local, int curtime) +{ + const bool water = hdr.waterlevel>pl->o.z-0.5f; + const bool floating = (editmode && local) || pl->state==CS_EDITING; + + vec d; // vector of direction we ideally want to move in + + + int move = pl->onladder && !pl->onfloor && pl->move == -1 ? 0 : pl->move; // fix movement on ladder + + d.x = (float)(move*cos(rad(pl->yaw-90))); + d.y = (float)(move*sin(rad(pl->yaw-90))); + d.z = (float) pl->isphysent ? pl->vel.z : 0; + + if(floating || water) + { + d.x *= (float)cos(rad(pl->pitch)); + d.y *= (float)cos(rad(pl->pitch)); + d.z = (float)(move*sin(rad(pl->pitch))); + }; + + d.x += (float)(pl->strafe*cos(rad(pl->yaw-180))); + d.y += (float)(pl->strafe*sin(rad(pl->yaw-180))); + + const float speed = curtime/(water ? 2000.0f : 1000.0f)*pl->maxspeed; + const float friction = water ? 20.0f : (pl->onfloor || floating ? 6.0f : (pl->onladder ? 1.5f : 30.0f)); + + const float fpsfric = friction/curtime*20.0f; + + vmul(pl->vel, fpsfric-1); // slowly apply friction and direction to velocity, gives a smooth movement + vadd(pl->vel, d); + vdiv(pl->vel, fpsfric); + d = pl->vel; + vmul(d, speed); // d is now frametime based velocity vector + + if(pl->isphysent) + { + pl->pitch += speed*5.0f; + if(pl->pitch>360.0f) pl->pitch = 0.0f; + pl->yaw += speed*5.0f; + if(pl->yaw>360.0f) pl->yaw = 0.0f; + + } + + pl->blocked = false; + pl->moving = true; + + if(floating) // just apply velocity + { + vadd(pl->o, d); + if(pl->jumpnext) { pl->jumpnext = false; pl->vel.z = 2; } + } + else // apply velocity with collision + { + if(pl->onladder) + { + if(pl->k_up) pl->vel.z = 0.5; + else if(pl->k_down) pl->vel.z = -0.5; + pl->timeinair = 0; + } + else + { + + if(pl->onfloor || water) + { + if(pl->onfloor || !pl->vel.z) + { + int damage = (int)pl->startheight - (int)pl->o.z; + if (damage > 8 && local) //try adding "&& local" if falling not fixed + { + selfdamage(damage,-1,pl); + demodamage(damage, player1->o); + playsound(S_FALL1+rnd(2), &pl->o); + }; + pl->startheight=pl->o.z; + }; + + if(pl->jumpnext) + { + pl->jumpnext = false; + pl->vel.z = 1.7f; // physics impulse upwards + if(water) { pl->vel.x /= 8; pl->vel.y /= 8; }; // dampen velocity change even harder, gives correct water feel + if(local) playsoundc(S_JUMP); + else if(pl->monsterstate) playsound(S_JUMP, &pl->o); + } + /*else if(pl->timeinair>800) // if we land after long time must have been a high jump, make thud sound + { + if(local) playsoundc(S_LAND); + else if(pl->monsterstate) playsound(S_LAND, &pl->o); + };*/ + pl->timeinair = 0; + if(pl->isphysent) pl->vel.z *= 0.7f; + } + else + { + pl->timeinair += curtime; + }; + }; + + const float gravity = pl->isphysent ? pl->gravity : 20; + const float f = 1.0f/moveres; + float dropf = pl->isphysent ? ((gravity-1)+pl->timeinair/14.0f) : ((gravity-1)+pl->timeinair/15.0f); // incorrect, but works fine + if(water) { dropf = 5; pl->timeinair = 0; }; // float slowly down in water + if(pl->onladder) { dropf = 0; pl->timeinair = 0; }; + float drop = dropf*curtime/gravity/100/moveres; // at high fps, gravity kicks in too fast + const float rise = speed/moveres/1.2f; // extra smoothness when lifting up stairs + + loopi(moveres) // discrete steps collision detection & sliding + { + // try move forward + pl->o.x += f*d.x; + pl->o.y += f*d.y; + pl->o.z += f*d.z; + if(collide(pl, false, drop, rise)) continue; + // player stuck, try slide along y axis + pl->blocked = true; + pl->o.x -= f*d.x; + if(collide(pl, false, drop, rise)) + { + d.x = 0; + if(pl->isphysent) pl->vel.x = -pl->vel.x; + continue; + }; + pl->o.x += f*d.x; + // still stuck, try x axis + pl->o.y -= f*d.y; + if(collide(pl, false, drop, rise)) + { + d.y = 0; + if(pl->isphysent) pl->vel.y = -pl->vel.y; + continue; + }; + pl->o.y += f*d.y; + // try just dropping down + pl->moving = false; + pl->o.x -= f*d.x; + pl->o.y -= f*d.y; + if(collide(pl, false, drop, rise)) + { + d.y = d.x = 0; + continue; + }; + pl->o.z -= f*d.z; + break; + }; + }; + + // detect wether player is outside map, used for skipping zbuffer clear mostly + + if(pl->o.x < 0 || pl->o.x >= ssize || pl->o.y <0 || pl->o.y > ssize) + { + pl->outsidemap = true; + } + else + { + sqr *s = S((int)pl->o.x, (int)pl->o.y); + pl->outsidemap = SOLID(s) + || pl->o.z < s->floor - (s->type==FHF ? s->vdelta/4 : 0) + || pl->o.z > s->ceil + (s->type==CHF ? s->vdelta/4 : 0); + }; + + // automatically apply smooth roll when strafing + + if(pl->strafe==0) + { + pl->roll = pl->roll/(1+(float)sqrt((float)curtime)/25); + } + else + { + pl->roll += pl->strafe*curtime/-30.0f; + if(pl->roll>maxroll) pl->roll = (float)maxroll; + if(pl->roll<-maxroll) pl->roll = (float)-maxroll; + }; + + // 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); + pl->inwater = water; +}; + +void moveplayer(dynent *pl, int moveres, bool local) +{ + loopi(physicsrepeat) moveplayer(pl, moveres, local, i ? curtime/physicsrepeat : curtime-curtime/physicsrepeat*(physicsrepeat-1)); +}; + +void movephysent(physent *pl) +{ + int moveres = 2; + bool local=true; + loopj(physicsrepeat) + { + const bool water = hdr.waterlevel>pl->o.z-0.5f; + const bool floating = (editmode && local); + + vec d; // vector of direction we ideally want to move in + + d = pl->vel; + + + const float speed = curtime/(water ? 2000.0f : 1000.0f)*pl->maxspeed; + const float friction = water ? 20.0f : (pl->onfloor || floating ? 6.0f : 30.0f); + + const float fpsfric = friction/curtime*20.0f; + + vmul(pl->vel, fpsfric-1); // slowly apply friction and direction to velocity, gives a smooth movement + vadd(pl->vel, d); + vdiv(pl->vel, fpsfric); + d = pl->vel; + vmul(d, speed); // d is now frametime based velocity vector + + pl->pitch = (pl->pitch+(speed)); + if(pl->pitch>360.0f) pl->pitch = 0.0f; + + printf("pitch %f\n", pl->pitch); + + if(pl->onfloor) + { + pl->timeinair = 0; + vmul(pl->vel, 0.8f); + } + else + { + pl->timeinair += curtime; + pl->pitch = (lastmillis-pl->millis) % 360; + pl->yaw = pl->pitch; + }; + + + //const float speed = curtime/(water ? 2000.0f : 1000.0f)*pl->maxspeed; + + const float gravity = 20; + const float f = 1.0f/moveres; + float dropf = pl->timeinair/15.0f; // incorrect, but works fine + if(water) { dropf = 5; pl->timeinair = 0; }; // float slowly down in water + const float drop = dropf*curtime/gravity/100/moveres; // at high fps, gravity kicks in too fast + const float rise = speed/moveres/1.2f; // extra smoothness when lifting up stairs +// if(pl->mtype = M_NADE)printf("%i\n", pl->onfloor); +/* d.x -= d.x/50.0f*curtime; + d.y -= d.y/50.0f*curtime;*/ + + + loopi(moveres) // discrete steps collision detection & sliding + { + // try move forward + pl->o.x += f*d.x; + pl->o.y += f*d.y; + pl->o.z += f*d.z; + if(collide(pl, false, drop, rise)) continue; + // player stuck, try slide along y axis + pl->blocked = true; + pl->o.x -= f*d.x; + if(collide(pl, false, drop, rise)) { pl->vel.x = -d.x; vmul(pl->vel, 0.8f); d.x = 0; continue; }; + pl->o.x += f*d.x; + // still stuck, try x axis + pl->o.y -= f*d.y; + if(collide(pl, false, drop, rise)) { pl->vel.y = -d.y; vmul(pl->vel, 0.8f); d.y = 0; continue; }; + pl->o.y += f*d.y; + // try just dropping down + pl->moving = false; + pl->o.x -= f*d.x; + pl->o.y -= f*d.y; + if(collide(pl, false, drop, rise)) { d.y = d.x = 0; vmul(pl->vel, 0.8f); continue; }; + pl->o.z -= f*d.z; + break; + }; + + // detect wether player is outside map, used for skipping zbuffer clear mostly + + + + if(pl->o.x < 0 || pl->o.x >= ssize || pl->o.y <0 || pl->o.y > ssize) + { + pl->outsidemap = true; + } + else + { + sqr *s = S((int)pl->o.x, (int)pl->o.y); + pl->outsidemap = SOLID(s) + || pl->o.z < s->floor - (s->type==FHF ? s->vdelta/4 : 0) + || pl->o.z > s->ceil + (s->type==CHF ? s->vdelta/4 : 0); + }; + + // 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); + pl->inwater = water; + }; +}; diff --git a/source/src/protos.h b/source/src/protos.h index 0707617e..86671f3c 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -13,6 +13,7 @@ extern void resetcomplete(); extern void complete(char *s); extern void alias(char *name, char *action); extern char *getalias(char *name); +extern void writecfg(); // console extern void keypress(int code, bool isdown, int cooked); @@ -24,7 +25,7 @@ extern char *getcurcommand(); extern bool rendermenu(); extern void menuset(int menu); extern void menumanual(int m, int n, char *text); -extern void sortmenu(int start, int num); +extern void sortmenu(int m, int start, int num); extern bool menukey(int code, bool isdown); extern void newmenu(char *name); extern void rendermenumdl(); @@ -72,15 +73,17 @@ extern void initclientnet(); extern bool netmapstart(); extern int getclientnum(); extern void changemapserv(char *name, int mode); +extern void ctf_team(char *name); // clientgame +extern flaginfo flaginfos[2]; extern void mousemove(int dx, int dy); extern void updateworld(int millis); extern void startmap(char *name); extern void changemap(char *name); extern void initclient(); +extern void spawnplayer(dynent *d, bool secure=false); extern void selfdamage(int damage, int actor, dynent *act); -extern void spawnplayer(dynent *d); extern dynent *newdynent(); extern char *getclientmap(); extern const char *modestr(int n); @@ -89,7 +92,8 @@ extern dynent *getclient(int cn); extern void timeupdate(int timeremain); extern void resetmovement(dynent *d); extern void fixplayer1range(); - +extern void respawnself(); // EDIT: AH +extern void ctf_death(); //game mode extras extern void arenarespawn(); extern void respawn(); @@ -105,8 +109,10 @@ extern void renderscores(); extern void setupworld(int factor); extern void empty_world(int factor, bool force); extern void remip(block &b, int level = 0); +extern void remipmore(block &b, int level = 0); extern int closestent(); extern int findentity(int type, int index = 0); +extern int findteamplayerstart(int team, int index = 0); extern void trigger(int tag, int type, bool savegame); extern void resettagareas(); extern void settagareas(); @@ -208,7 +214,7 @@ extern void rendermd3gun(); extern void loadweapons(); // server -extern void initserver(bool dedicated, bool listen, int uprate, char *sdesc, char *ip, char *master, char *passwd); +extern void initserver(bool dedicated, int uprate, char *sdesc, char *ip, char *master, char *passwd, int maxcl, char *maprot); // EDIT: AH extern void cleanupserver(); extern void localconnect(); extern void localdisconnect(); @@ -221,7 +227,7 @@ extern void startintermission(); extern void restoreserverstate(vector &ents); extern uchar *retrieveservers(uchar *buf, int buflen); extern char msgsizelookup(int msg); -extern void serverms(int mode, int numplayers, int minremain, char *smapname, int seconds); +extern void serverms(int mode, int numplayers, int minremain, char *smapname, int seconds, bool isfull); extern void servermsinit(const char *master, char *sdesc, bool listen); extern void sendmaps(int n, string mapname, int mapsize, uchar *mapdata); extern ENetPacket *recvmap(int n); @@ -239,12 +245,13 @@ extern int attackdelay(int gun); extern int magsize(int gun); extern int kick_rot(int gun); extern int kick_back(int gun); -extern bool gun_changed; +extern bool gun_changed; // entities extern void renderents(); extern void putitems(uchar *&p); extern void realpickup(int n, dynent *d); +extern void flagaction(int team, int action); extern void renderentities(); extern void resetspawns(); extern void setspawn(int i, bool on); diff --git a/source/src/renderextras.cpp b/source/src/renderextras.cpp index 92c2742a..fedf0640 100644 --- a/source/src/renderextras.cpp +++ b/source/src/renderextras.cpp @@ -114,7 +114,7 @@ void renderspheres(int time) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); glBindTexture(GL_TEXTURE_2D, 4); for(sphere *p, **pp = &slist; p = *pp;) @@ -139,7 +139,7 @@ void renderspheres(int time) } else { - p->size += time/30.0f; + p->size += time/100.0f; pp = &p->next; }; }; @@ -155,7 +155,7 @@ char *entnames[] = "clips", "ammobox","grenades", "health", "armour", "akimbo", "mapmodel", "trigger", - "ladder", "?", "?", "?", "?", + "ladder", "ctf-flag", "?", "?", "?", }; void renderents() // show sparkly thingies for map entities in edit mode @@ -206,18 +206,15 @@ void readmatrices() glGetDoublev(GL_PROJECTION_MATRIX, pm); }; -// stupid function to cater for stupid ATI drivers that return incorrect depth values +// stupid function to cater for stupid ATI linux drivers that return incorrect depth values float depthcorrect(float d) { - static int when = 0; - static float factor = 1; - //if(when++==5) factor = 0.992205f/d; // this magic value is tied to the fixed start spawn point in metl3 - return d*factor; + return (d<=1/256.0f) ? d*256 : d; }; // find out the 3d target of the crosshair in the world easily and very acurately. -// sadly many very old cards and drivers appear to fuck up on gluUnProject() and give false +// sadly many very old cards and drivers appear to fuck up on glReadPixels() and give false // coordinates, making shooting and such impossible. // also hits map entities which is unwanted. // could be replaced by a more acurate version of monster.cpp los() if needed @@ -235,14 +232,14 @@ void readdepth(int w, int h) setorient(r, u); }; -void drawicon(float tx, float ty, int x, int y, float scale=1.0f) +void drawicon(float tx, float ty, int x, int y, int tex = 5, float scaling = 1/3.0f) // EDIT: AH { - glBindTexture(GL_TEXTURE_2D, 5); + glBindTexture(GL_TEXTURE_2D, tex); glBegin(GL_QUADS); tx /= 192; ty /= 192; - float o = 1/3.0f; - int s = 120*scale; + float o = scaling; + int s = 120; glTexCoord2f(tx, ty); glVertex2i(x, y); glTexCoord2f(tx+o, ty); glVertex2i(x+s, y); glTexCoord2f(tx+o, ty+o); glVertex2i(x+s, y+s); @@ -406,6 +403,37 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat } glPopMatrix(); + + // EDIT: AH - ctf + #define ctf_scl 0.75 + if(m_ctf) + { + glPushMatrix(); + glOrtho(0, VIRTW, VIRTH, 0, -1, 1); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glColor4f(1.0f, 1.0f, 1.0f, 0.2f); + if(flaginfos[TEAM_CLA].state == CTFF_INBASE) glDisable(GL_BLEND); else glEnable(GL_BLEND); + drawicon(64*ctf_scl, 0, 1940, 1650, 11, 1.0f/4.0f); + if(flaginfos[TEAM_RSVF].state == CTFF_INBASE) glDisable(GL_BLEND); else glEnable(GL_BLEND); + drawicon(0, 0, 1820, 1650, 11, 1.0f/4.0f); + glDisable(GL_BLEND); + drawicon(rb_team_int(player1->team)*64*ctf_scl, 64*ctf_scl, VIRTW-128-10, 10, 11, 1.0f/4.0f); // shows which team you are + glPopMatrix(); + + // big flag icon + flaginfo &f = flaginfos[rb_opposite(rb_team_int(player1->team))]; + if(f.state==CTFF_STOLEN && f.thief == player1 && f.pick_ack ) + { + glPushMatrix(); + glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glColor4f(1.0f, 1.0f, 1.0f, (float)(sin(lastmillis / 100.0) + 1) / 2); + glEnable(GL_BLEND); + drawicon(rb_opposite(rb_team_int(player1->team))*128*ctf_scl, 128*ctf_scl, 1065, VIRTH/2/3*2, 11, 1.0f/2.0f); + glPopMatrix(); + }; + }; + glPushMatrix(); glOrtho(0, VIRTW, VIRTH, 0, -1, 1); glDisable(GL_BLEND); diff --git a/source/src/rendergl.cpp b/source/src/rendergl.cpp index a4cdfb99..0718607a 100644 --- a/source/src/rendergl.cpp +++ b/source/src/rendergl.cpp @@ -1,409 +1,409 @@ -// rendergl.cpp: core opengl rendering stuff - -#include "cube.h" - -#ifdef DARWIN -#define GL_COMBINE_EXT GL_COMBINE_ARB -#define GL_COMBINE_RGB_EXT GL_COMBINE_RGB_ARB -#define GL_SOURCE0_RBG_EXT GL_SOURCE0_RGB_ARB -#define GL_SOURCE1_RBG_EXT GL_SOURCE1_RGB_ARB -#define GL_RGB_SCALE_EXT GL_RGB_SCALE_ARB -#endif - -extern int curvert; - -bool hasoverbright = false; - -void purgetextures(); - -GLUquadricObj *qsphere = NULL; -int glmaxtexsize = 256; - -void gl_init(int w, int h) -{ - //#define fogvalues 0.5f, 0.6f, 0.7f, 1.0f - - glViewport(0, 0, w, h); - glClearDepth(1.0); - glDepthFunc(GL_LESS); - glEnable(GL_DEPTH_TEST); - glShadeModel(GL_SMOOTH); - - - glEnable(GL_FOG); - glFogi(GL_FOG_MODE, GL_LINEAR); - glFogf(GL_FOG_DENSITY, 0.25); - glHint(GL_FOG_HINT, GL_NICEST); - - - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); - glEnable(GL_POLYGON_OFFSET_LINE); - glPolygonOffset(-3.0, -3.0); - - glCullFace(GL_FRONT); - glEnable(GL_CULL_FACE); - - char *exts = (char *)glGetString(GL_EXTENSIONS); - - if(strstr(exts, "GL_EXT_texture_env_combine")) hasoverbright = true; - else conoutf("WARNING: cannot use overbright lighting, using old lighting model!"); - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glmaxtexsize); - - purgetextures(); - - if(!(qsphere = gluNewQuadric())) fatal("glu sphere"); - gluQuadricDrawStyle(qsphere, GLU_FILL); - gluQuadricOrientation(qsphere, GLU_INSIDE); - gluQuadricTexture(qsphere, GL_TRUE); - glNewList(1, GL_COMPILE); - gluSphere(qsphere, 1, 12, 6); - glEndList(); -}; - -void cleangl() -{ - if(qsphere) gluDeleteQuadric(qsphere); -}; - -bool installtex(int tnum, char *texname, int &xs, int &ys, bool clamp, bool highqual) -{ - SDL_Surface *s = IMG_Load(texname); - if(!s) { conoutf("couldn't load texture %s", (int)texname); return false; }; - if(s->format->BitsPerPixel!=24 && s->format->BitsPerPixel!=32) { conoutf("texture must be 24bpp or 32bpp: %s", (int)texname); return false; }; - // loopi(s->w*s->h*3) { uchar *p = (uchar *)s->pixels+i; *p = 255-*p; }; - glBindTexture(GL_TEXTURE_2D, tnum); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, highqual ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR); //NEAREST); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - xs = s->w; - ys = s->h; - while(xs>glmaxtexsize || ys>glmaxtexsize) { xs /= 2; ys /= 2; }; - void *scaledimg = s->pixels; - if(xs!=s->w) - { - conoutf("warning: quality loss: scaling %s", (int)texname); // for voodoo cards under linux - scaledimg = alloc(xs*ys*3); - gluScaleImage(GL_RGB, s->w, s->h, GL_UNSIGNED_BYTE, s->pixels, xs, ys, GL_UNSIGNED_BYTE, scaledimg); - }; - int mode = s->format->BitsPerPixel==24 ? GL_RGB : GL_RGBA; - if(gluBuild2DMipmaps(GL_TEXTURE_2D, mode, xs, ys, mode, GL_UNSIGNED_BYTE, scaledimg)) fatal("could not build mipmaps"); - if(xs!=s->w) free(scaledimg); - SDL_FreeSurface(s); - return true; -}; - -// management of texture slots -// each texture slot can have multople texture frames, of which currently only the first is used -// additional frames can be used for various shaders - -const int MAXTEX = 1000; -int texx[MAXTEX]; // ( loaded texture ) -> ( name, size ) -int texy[MAXTEX]; -string texname[MAXTEX]; -int curtex = 0; -const int FIRSTTEX = 130; // opengl id = loaded id + FIRSTTEX -// std 1+, sky 14+, md3's 30+,md2's 40+, menu 120+ - -const int MAXFRAMES = 2; // increase to allow more complex shader defs -int mapping[256][MAXFRAMES]; // ( cube texture, frame ) -> ( opengl id, name ) -string mapname[256][MAXFRAMES]; - -void purgetextures() -{ - loopi(256) loop(j,MAXFRAMES) mapping[i][j] = 0; -}; - -int curtexnum = 0; - -void texturereset() { curtexnum = 0; }; - -void texture(char *aframe, char *name) -{ - int num = curtexnum++, frame = atoi(aframe); - if(num<0 || num>=256 || frame<0 || frame>=MAXFRAMES) return; - mapping[num][frame] = 1; - char *n = mapname[num][frame]; - strcpy_s(n, name); - path(n); -}; - -COMMAND(texturereset, ARG_NONE); -COMMAND(texture, ARG_2STR); - -int lookuptexture(int tex, int &xs, int &ys) -{ - int frame = 0; // other frames? - int tid = mapping[tex][frame]; - - if(tid>=FIRSTTEX) - { - xs = texx[tid-FIRSTTEX]; - ys = texy[tid-FIRSTTEX]; - return tid; - }; - - xs = ys = 16; - if(!tid) return 1; // crosshair :) - - loopi(curtex) // lazily happens once per "texture" command, basically - { - if(strcmp(mapname[tex][frame], texname[i])==0) - { - mapping[tex][frame] = tid = i+FIRSTTEX; - xs = texx[i]; - ys = texy[i]; - return tid; - }; - }; - - if(curtex==MAXTEX) fatal("loaded too many textures"); - - int tnum = curtex+FIRSTTEX; - strcpy_s(texname[curtex], mapname[tex][frame]); - - sprintf_sd(name)("packages%ctextures%c%s", PATHDIV, PATHDIV, texname[curtex]); - - if(installtex(tnum, name, xs, ys)) - { - mapping[tex][frame] = tnum; - texx[curtex] = xs; - texy[curtex] = ys; - curtex++; - return tnum; - } - else - { - return mapping[tex][frame] = FIRSTTEX; // temp fix - }; -}; - -void setupworld() -{ - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - setarraypointers(); - - if(hasoverbright) - { - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT); - }; -}; - -int skyoglid; - -struct strip { int tex, start, num; }; -vector strips; - -void renderstripssky() -{ - glBindTexture(GL_TEXTURE_2D, skyoglid); - loopv(strips) if(strips[i].tex==skyoglid) glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num); -}; - -void renderstrips() -{ - int lasttex = -1; - loopv(strips) if(strips[i].tex!=skyoglid) - { - if(strips[i].tex!=lasttex) - { - glBindTexture(GL_TEXTURE_2D, strips[i].tex); - lasttex = strips[i].tex; - }; - glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num); - }; -}; - -void overbright(float amount) { if(hasoverbright) glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, amount ); }; - -void addstrip(int tex, int start, int n) -{ - strip &s = strips.add(); - s.tex = tex; - s.start = start; - s.num = n; -}; - -VARF(gamma, 30, 100, 300, -{ - float f = gamma/100.0f; - if(SDL_SetGamma(f,f,f)==-1) - { - conoutf("Could not set gamma (card/driver doesn't support it?)"); - conoutf("sdl: %s", (int)SDL_GetError()); - }; -}); - -void transplayer() -{ - glLoadIdentity(); - - if(player1->state==CS_DEAD) - { - float t = (lastmillis-player1->lastaction)/1000.0f; - if(t >= 1.6f) t = 1.6f; - - player1->pitch = (float) sin(t)*(-70.0f-player1->oldpitch)+player1->oldpitch; - glRotated(player1->pitch,-1.0,0.0,0.0); - glRotated(player1->yaw,0.0,1.0,0.0); - - glTranslated(-player1->o.x, player1->eyeheight-sin(t)*7.0f-player1->o.z, -player1->o.y); - } - else - { - glRotated(player1->roll,0.0,0.0,1.0); - glRotated(player1->pitch,-1.0,0.0,0.0); - glRotated(player1->yaw,0.0,1.0,0.0); - - glTranslated(-player1->o.x, (player1->state==CS_DEAD ? player1->eyeheight-0.2f : 0)-player1->o.z, -player1->o.y); - }; -}; - -VAR(fov, 10, 105, 120); - -int xtraverts; - -VAR(fog, 64, 180, 1024); -VAR(fogcolour, 0, 0x8099B3, 0xFFFFFF); - -VAR(hudgun,0,1,1); - -char *hudgunnames[] = { "knife", "pistol", "shotgun", "subgun", "sniper", "assault", "grenade" }; - -void drawhudmodel(int start, int end, float speed, int base) -{ - sprintf_sd(mdl)("hudguns/%s", hudgunnames[player1->gunselect]); - rendermodel(mdl, start, end, 0, 1.0f, player1->o.x, player1->o.z, player1->o.y, player1->yaw+90, player1->pitch, false, 1.0f, speed, 0, base); -}; - -void drawhudgun(int w, int h, float aspect, int farplane) -{ - if(!hudgun || player1->state==CS_DEAD) return; - - glEnable(GL_CULL_FACE); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective((float)100.0f*h/w, aspect, 0.3f, farplane); // fov fixed at 100° - glMatrixMode(GL_MODELVIEW); - - rendermd3gun(); - rendermenumdl(); // lazy bastard - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective((float)fov*h/w, aspect, 0.15f, farplane); - glMatrixMode(GL_MODELVIEW); - - glDisable(GL_CULL_FACE); -}; - -void gl_drawframe(int w, int h, float changelod, float curfps) -{ - float hf = hdr.waterlevel-0.3f; - float fovy = (float)fov*h/w; - float aspect = w/(float)h; - bool 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(underwater) - { - fovy += (float)sin(lastmillis/1000.0)*2.0f; - aspect += (float)sin(lastmillis/1000.0+PI)*0.1f; - glFogi(GL_FOG_START, 0); - glFogi(GL_FOG_END, (fog+96)/8); - }; - - glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - int farplane = fog*5/2; - gluPerspective(fovy, aspect, 0.15f, farplane); - glMatrixMode(GL_MODELVIEW); - - transplayer(); - - glEnable(GL_TEXTURE_2D); - - int xs, ys; - skyoglid = lookuptexture(DEFAULT_SKY, xs, ys); - - resetcubes(); - - curvert = 0; - strips.setsize(0); - - render_world(player1->o.x, player1->o.y, player1->o.z, changelod, - (int)player1->yaw, (int)player1->pitch, (float)fov, w, h); - finishstrips(); - - setupworld(); - - renderstripssky(); - - glLoadIdentity(); - glRotated(player1->pitch, -1.0, 0.0, 0.0); - glRotated(player1->yaw, 0.0, 1.0, 0.0); - glRotated(90.0, 1.0, 0.0, 0.0); - glColor3f(1.0f, 1.0f, 1.0f); - glDisable(GL_FOG); - glDepthFunc(GL_GREATER); - draw_envbox(14, fog*4/3); - glDepthFunc(GL_LESS); - glEnable(GL_FOG); - - transplayer(); - - overbright(2); - - renderstrips(); - - xtraverts = 0; - - renderclients(); - - renderentities(); - - renderspheres(curtime); - renderents(); - - renderphysents(); - - rendershotlines(); - - glDisable(GL_CULL_FACE); - - drawhudgun(w, h, aspect, farplane); - - overbright(1); - int nquads = renderwater(hf); - - overbright(2); - render_particles(curtime); - overbright(1); - - glDisable(GL_FOG); - - glDisable(GL_TEXTURE_2D); - - gl_drawhud(w, h, (int)curfps, nquads, curvert, underwater); - - glEnable(GL_CULL_FACE); - glEnable(GL_FOG); -}; - +// rendergl.cpp: core opengl rendering stuff + +#include "cube.h" + +#ifdef DARWIN +#define GL_COMBINE_EXT GL_COMBINE_ARB +#define GL_COMBINE_RGB_EXT GL_COMBINE_RGB_ARB +#define GL_SOURCE0_RBG_EXT GL_SOURCE0_RGB_ARB +#define GL_SOURCE1_RBG_EXT GL_SOURCE1_RGB_ARB +#define GL_RGB_SCALE_EXT GL_RGB_SCALE_ARB +#endif + +extern int curvert; + +bool hasoverbright = false; + +void purgetextures(); + +GLUquadricObj *qsphere = NULL; +int glmaxtexsize = 256; + +void gl_init(int w, int h) +{ + //#define fogvalues 0.5f, 0.6f, 0.7f, 1.0f + + glViewport(0, 0, w, h); + glClearDepth(1.0); + glDepthFunc(GL_LESS); + glEnable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + + + glEnable(GL_FOG); + glFogi(GL_FOG_MODE, GL_LINEAR); + glFogf(GL_FOG_DENSITY, 0.25); + glHint(GL_FOG_HINT, GL_NICEST); + + + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glEnable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(-3.0, -3.0); + + glCullFace(GL_FRONT); + glEnable(GL_CULL_FACE); + + char *exts = (char *)glGetString(GL_EXTENSIONS); + + if(strstr(exts, "GL_EXT_texture_env_combine")) hasoverbright = true; + else conoutf("WARNING: cannot use overbright lighting, using old lighting model!"); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glmaxtexsize); + + purgetextures(); + + if(!(qsphere = gluNewQuadric())) fatal("glu sphere"); + gluQuadricDrawStyle(qsphere, GLU_FILL); + gluQuadricOrientation(qsphere, GLU_INSIDE); + gluQuadricTexture(qsphere, GL_TRUE); + glNewList(1, GL_COMPILE); + gluSphere(qsphere, 1, 12, 6); + glEndList(); +}; + +void cleangl() +{ + if(qsphere) gluDeleteQuadric(qsphere); +}; + +bool installtex(int tnum, char *texname, int &xs, int &ys, bool clamp, bool highqual) +{ + SDL_Surface *s = IMG_Load(texname); + if(!s) { conoutf("couldn't load texture %s", (int)texname); return false; }; + if(s->format->BitsPerPixel!=24 && s->format->BitsPerPixel!=32) { conoutf("texture must be 24bpp or 32bpp: %s", (int)texname); return false; }; + // loopi(s->w*s->h*3) { uchar *p = (uchar *)s->pixels+i; *p = 255-*p; }; + glBindTexture(GL_TEXTURE_2D, tnum); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, highqual ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR_MIPMAP_LINEAR); //NEAREST); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + xs = s->w; + ys = s->h; + while(xs>glmaxtexsize || ys>glmaxtexsize) { xs /= 2; ys /= 2; }; + void *scaledimg = s->pixels; + if(xs!=s->w) + { + conoutf("warning: quality loss: scaling %s", (int)texname); // for voodoo cards under linux + scaledimg = alloc(xs*ys*3); + gluScaleImage(GL_RGB, s->w, s->h, GL_UNSIGNED_BYTE, s->pixels, xs, ys, GL_UNSIGNED_BYTE, scaledimg); + }; + int mode = s->format->BitsPerPixel==24 ? GL_RGB : GL_RGBA; + if(gluBuild2DMipmaps(GL_TEXTURE_2D, mode, xs, ys, mode, GL_UNSIGNED_BYTE, scaledimg)) fatal("could not build mipmaps"); + if(xs!=s->w) free(scaledimg); + SDL_FreeSurface(s); + return true; +}; + +// management of texture slots +// each texture slot can have multople texture frames, of which currently only the first is used +// additional frames can be used for various shaders + +const int MAXTEX = 1000; +int texx[MAXTEX]; // ( loaded texture ) -> ( name, size ) +int texy[MAXTEX]; +string texname[MAXTEX]; +int curtex = 0; +const int FIRSTTEX = 130; // opengl id = loaded id + FIRSTTEX +// std 1+, sky 14+, md3's 30+,md2's 40+, menu 120+ + +const int MAXFRAMES = 2; // increase to allow more complex shader defs +int mapping[256][MAXFRAMES]; // ( cube texture, frame ) -> ( opengl id, name ) +string mapname[256][MAXFRAMES]; + +void purgetextures() +{ + loopi(256) loop(j,MAXFRAMES) mapping[i][j] = 0; +}; + +int curtexnum = 0; + +void texturereset() { curtexnum = 0; }; + +void texture(char *aframe, char *name) +{ + int num = curtexnum++, frame = atoi(aframe); + if(num<0 || num>=256 || frame<0 || frame>=MAXFRAMES) return; + mapping[num][frame] = 1; + char *n = mapname[num][frame]; + strcpy_s(n, name); + path(n); +}; + +COMMAND(texturereset, ARG_NONE); +COMMAND(texture, ARG_2STR); + +int lookuptexture(int tex, int &xs, int &ys) +{ + int frame = 0; // other frames? + int tid = mapping[tex][frame]; + + if(tid>=FIRSTTEX) + { + xs = texx[tid-FIRSTTEX]; + ys = texy[tid-FIRSTTEX]; + return tid; + }; + + xs = ys = 16; + if(!tid) return 1; // crosshair :) + + loopi(curtex) // lazily happens once per "texture" command, basically + { + if(strcmp(mapname[tex][frame], texname[i])==0) + { + mapping[tex][frame] = tid = i+FIRSTTEX; + xs = texx[i]; + ys = texy[i]; + return tid; + }; + }; + + if(curtex==MAXTEX) fatal("loaded too many textures"); + + int tnum = curtex+FIRSTTEX; + strcpy_s(texname[curtex], mapname[tex][frame]); + + sprintf_sd(name)("packages%ctextures%c%s", PATHDIV, PATHDIV, texname[curtex]); + + if(installtex(tnum, name, xs, ys)) + { + mapping[tex][frame] = tnum; + texx[curtex] = xs; + texy[curtex] = ys; + curtex++; + return tnum; + } + else + { + return mapping[tex][frame] = FIRSTTEX; // temp fix + }; +}; + +void setupworld() +{ + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + setarraypointers(); + + if(hasoverbright) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT); + }; +}; + +int skyoglid; + +struct strip { int tex, start, num; }; +vector strips; + +void renderstripssky() +{ + glBindTexture(GL_TEXTURE_2D, skyoglid); + loopv(strips) if(strips[i].tex==skyoglid) glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num); +}; + +void renderstrips() +{ + int lasttex = -1; + loopv(strips) if(strips[i].tex!=skyoglid) + { + if(strips[i].tex!=lasttex) + { + glBindTexture(GL_TEXTURE_2D, strips[i].tex); + lasttex = strips[i].tex; + }; + glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num); + }; +}; + +void overbright(float amount) { if(hasoverbright) glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, amount ); }; + +void addstrip(int tex, int start, int n) +{ + strip &s = strips.add(); + s.tex = tex; + s.start = start; + s.num = n; +}; + +VARF(gamma, 30, 100, 300, +{ + float f = gamma/100.0f; + if(SDL_SetGamma(f,f,f)==-1) + { + conoutf("Could not set gamma (card/driver doesn't support it?)"); + conoutf("sdl: %s", (int)SDL_GetError()); + }; +}); + +void transplayer() +{ + glLoadIdentity(); + + if(player1->state==CS_DEAD) + { + float t = (lastmillis-player1->lastaction)/1000.0f; + if(t >= 1.6f) t = 1.6f; + + player1->pitch = (float) sin(t)*(-70.0f-player1->oldpitch)+player1->oldpitch; + glRotated(player1->pitch,-1.0,0.0,0.0); + glRotated(player1->yaw,0.0,1.0,0.0); + + glTranslated(-player1->o.x, player1->eyeheight-sin(t)*7.0f-player1->o.z, -player1->o.y); + } + else + { + glRotated(player1->roll,0.0,0.0,1.0); + glRotated(player1->pitch,-1.0,0.0,0.0); + glRotated(player1->yaw,0.0,1.0,0.0); + + glTranslated(-player1->o.x, (player1->state==CS_DEAD ? player1->eyeheight-0.2f : 0)-player1->o.z, -player1->o.y); + }; +}; + +VAR(fov, 10, 105, 120); + +int xtraverts; + +VAR(fog, 64, 180, 1024); +VAR(fogcolour, 0, 0x8099B3, 0xFFFFFF); + +VAR(hudgun,0,1,1); + +char *hudgunnames[] = { "knife", "pistol", "shotgun", "subgun", "sniper", "assault", "grenade" }; + +void drawhudmodel(int start, int end, float speed, int base) +{ + sprintf_sd(mdl)("hudguns/%s", hudgunnames[player1->gunselect]); + rendermodel(mdl, start, end, 0, 1.0f, player1->o.x, player1->o.z, player1->o.y, player1->yaw+90, player1->pitch, false, 1.0f, speed, 0, base); +}; + +void drawhudgun(int w, int h, float aspect, int farplane) +{ + if(!hudgun || player1->state==CS_DEAD) return; + + glEnable(GL_CULL_FACE); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective((float)100.0f*h/w, aspect, 0.3f, farplane); // fov fixed at 100° + glMatrixMode(GL_MODELVIEW); + + rendermd3gun(); + rendermenumdl(); // lazy bastard + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective((float)fov*h/w, aspect, 0.15f, farplane); + glMatrixMode(GL_MODELVIEW); + + glDisable(GL_CULL_FACE); +}; + +void gl_drawframe(int w, int h, float changelod, float curfps) +{ + float hf = hdr.waterlevel-0.3f; + float fovy = (float)fov*h/w; + float aspect = w/(float)h; + bool 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(underwater) + { + fovy += (float)sin(lastmillis/1000.0)*2.0f; + aspect += (float)sin(lastmillis/1000.0+PI)*0.1f; + glFogi(GL_FOG_START, 0); + glFogi(GL_FOG_END, (fog+96)/8); + }; + + glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + int farplane = fog*5/2; + gluPerspective(fovy, aspect, 0.15f, farplane); + glMatrixMode(GL_MODELVIEW); + + transplayer(); + + glEnable(GL_TEXTURE_2D); + + int xs, ys; + skyoglid = lookuptexture(DEFAULT_SKY, xs, ys); + + resetcubes(); + + curvert = 0; + strips.setsize(0); + + render_world(player1->o.x, player1->o.y, player1->o.z, changelod, + (int)player1->yaw, (int)player1->pitch, (float)fov, w, h); + finishstrips(); + + setupworld(); + + renderstripssky(); + + glLoadIdentity(); + glRotated(player1->pitch, -1.0, 0.0, 0.0); + glRotated(player1->yaw, 0.0, 1.0, 0.0); + glRotated(90.0, 1.0, 0.0, 0.0); + glColor3f(1.0f, 1.0f, 1.0f); + glDisable(GL_FOG); + glDepthFunc(GL_GREATER); + draw_envbox(14, fog*4/3); + glDepthFunc(GL_LESS); + glEnable(GL_FOG); + + transplayer(); + + overbright(2); + + renderstrips(); + + xtraverts = 0; + + renderclients(); + + renderentities(); + + renderspheres(curtime); + renderents(); + + renderphysents(); + + rendershotlines(); + + glDisable(GL_CULL_FACE); + + drawhudgun(w, h, aspect, farplane); + + overbright(1); + int nquads = renderwater(hf); + + overbright(2); + render_particles(curtime); + overbright(1); + + glDisable(GL_FOG); + + glDisable(GL_TEXTURE_2D); + + gl_drawhud(w, h, (int)curfps, nquads, curvert, underwater); + + glEnable(GL_CULL_FACE); + glEnable(GL_FOG); +}; + diff --git a/source/src/rendermd3.cpp b/source/src/rendermd3.cpp index 10d19bc2..02d517d2 100644 --- a/source/src/rendermd3.cpp +++ b/source/src/rendermd3.cpp @@ -4,8 +4,8 @@ #include "cube.h" const int FIRSTMD3 = 20; -enum { MDL_LOWER = 0, MDL_UPPER, MDL_HEAD }; - +enum { MDL_LOWER = 0, MDL_UPPER, MDL_HEAD }; + enum { MDL_GUN_IDLE = 0, MDL_GUN_ATTACK, MDL_GUN_RELOAD, MDL_GUN_ATTACK2}; // attack2 is for grenade only #define MD3_DEFAULT_SCALE 0.08f @@ -79,7 +79,7 @@ struct md3model md3tag *tags; int numframes, numtags; bool loaded; - vec scale; + vec scale; void setanim(int anim); bool link(md3model *link, char *tag); bool load(char *path); @@ -106,12 +106,12 @@ md3model::~md3model() if(links) free(links); if(tags) delete [] tags; }; - -void md3model::setanim(int anim) -{ - if(animstate->anim != anim) animstate->lastTime = lastmillis; - animstate->anim = anim; -}; + +void md3model::setanim(int anim) +{ + if(animstate->anim != anim) animstate->lastTime = lastmillis; + animstate->anim = anim; +}; bool md3model::link(md3model *link, char *tag) { @@ -182,58 +182,58 @@ bool md3model::load(char *path) loaded = true; return true; }; - -VAR(animdebug, 0, 0, 1); + +VAR(animdebug, 0, 0, 1); void md3model::render() { if(!loaded || meshes.length() <= 0) return; float t = 0.0f; int nextfrm; - md3state *a = animstate; + md3state *a = animstate; if(a && animations.length() > a->anim) { - md3animinfo *info = &animations[a->anim]; - - int time = lastmillis - a->lastTime; - float speed = 1000.0f / (float)info->fps; - a->frm = (int) (time / speed); - - //printf("no loop %i > %i\n", a->frm, info->start + info->num-1); - if(!info->loop && a->frm >= info->num -1) - { - if(animdebug) printf("no loop %i > %i\n", a->frm, info->start + info->num-1); - a->frm = info->start + info->num-1; - nextfrm = a->frm; - } - else + md3animinfo *info = &animations[a->anim]; + + int time = lastmillis - a->lastTime; + float speed = 1000.0f / (float)info->fps; + a->frm = (int) (time / speed); + + //printf("no loop %i > %i\n", a->frm, info->start + info->num-1); + if(!info->loop && a->frm >= info->num -1) { - t = (time-a->frm*speed)/speed; - a->frm = info->start+(a->frm % info->num); - //if(a->frm>=info->start+info->num-1) - nextfrm = a->frm + 1; - //if(animdebug) printf("%i >= %i + %i", nextfrm, a->frm, info->num); - if(nextfrm>=info->start+info->num) nextfrm=a->frm; - }; - + if(animdebug) printf("no loop %i > %i\n", a->frm, info->start + info->num-1); + a->frm = info->start + info->num-1; + nextfrm = a->frm; + } + else + { + t = (time-a->frm*speed)/speed; + a->frm = info->start+(a->frm % info->num); + //if(a->frm>=info->start+info->num-1) + nextfrm = a->frm + 1; + //if(animdebug) printf("%i >= %i + %i", nextfrm, a->frm, info->num); + if(nextfrm>=info->start+info->num) nextfrm=a->frm; + }; + //if(a->frm < info->start || a->frm > info->end) // we switched to a new animation, jump to the start // a->frm = info->start; - //t = (float) (lastmillis - a->lastTime) / (1000.0f / (float) info->fps ); // t has a value from 0.0f to 1.0f - used for interpolation - - if(animdebug) printf("lm:%i lt:%i fps:%i\t(%i %i)\n", lastmillis, a->lastTime, info->fps, info->start, info->num); - + //t = (float) (lastmillis - a->lastTime) / (1000.0f / (float) info->fps ); // t has a value from 0.0f to 1.0f - used for interpolation + + if(animdebug) printf("lm:%i lt:%i fps:%i\t(%i %i)\n", lastmillis, a->lastTime, info->fps, info->start, info->num); + /*if(t >= 1.0f) // jump to the next keyframe { a->frm++; a->lastTime = lastmillis; - //t = 0.0f; - t = t % 1; + //t = 0.0f; + t = t % 1; //printf("next keyframe\n"); }; if(a->frm >= info->end) - { + { //printf("end\n"); if(info->loop) a->frm = info->start; @@ -245,14 +245,14 @@ void md3model::render() };*/ } else - { - if(animdebug) printf("overflow: curframe %i > maxframe %i\n", a->anim, animations.length()); + { + if(animdebug) printf("overflow: curframe %i > maxframe %i\n", a->anim, animations.length()); if(!a) a = new md3state(); - a->frm = 0; + a->frm = 0; nextfrm = a->frm + 1; - } - + } + if(animdebug) printf("interp. from frame %i to %i, %f percent done\n", a->frm, nextfrm, t*100.0f); #define interpolate(p1,p2) ((p1) + t * ((p2) - (p1))) @@ -363,10 +363,10 @@ COMMAND(md3skin, ARG_2STR); void md3animation(char *first, char *nums, char *loopings, char *fps) /* configurable animations */ { md3animinfo &a = tmp_animations.add(); - a.start = atoi(first); - int n = atoi(nums); + a.start = atoi(first); + int n = atoi(nums); if(n<=0) n = 1; - //a.end = a.start + n - 1; + //a.end = a.start + n - 1; a.num = n; (atoi(loopings) > 0) ? a.loop = true : a.loop = false; a.fps = atoi(fps); @@ -383,11 +383,11 @@ void loadweapons() md3model *mdl = new md3model(); models.add(mdl); sprintf_sd(mdl_path)("%s/tris_high.md3", basedir); - sprintf_sd(cfg_path)("%s/default.skin", basedir); + sprintf_sd(cfg_path)("%s/default.skin", basedir); sprintf_sd(modelcfg_path) ("%s/animations.cfg", basedir); mdl->load(mdl_path); - exec(cfg_path); - tmp_animations.setsize(0); + exec(cfg_path); + tmp_animations.setsize(0); exec(modelcfg_path); loopj(tmp_animations.length()) mdl->animations.add(tmp_animations[j]); } @@ -397,7 +397,7 @@ void rotatevec(vec &point, vec &rot) { point.x=point.z*sin(rot.x)+point.x*cos(rot.x); point.y=point.y; - point.z=point.z*cos(rot.x)-point.x*sin(rot.x); + point.z=point.z*cos(rot.x)-point.x*sin(rot.x); point.x=point.x; point.y=point.y*cos(rot.y)-point.z*sin(rot.y); @@ -406,17 +406,17 @@ void rotatevec(vec &point, vec &rot) point.x=point.y*sin(rot.z)+point.x*cos(rot.z); point.y=point.y*cos(rot.z)-point.x*sin(rot.z); point.z=point.z; -}; - -VAR(swayspeeddiv, 1, 125, 1000); -VAR(swaymovediv, 1, 200, 1000); - -VAR(swayupspeeddiv, 1, 125, 1000); -VAR(swayupmovediv, 1, 200, 1000); - -VAR(x, 0, 0, 1000); -VAR(y, 0, 0, 1000); -VAR(z, 0, 0, 1000); +}; + +VAR(swayspeeddiv, 1, 125, 1000); +VAR(swaymovediv, 1, 200, 1000); + +VAR(swayupspeeddiv, 1, 125, 1000); +VAR(swayupmovediv, 1, 200, 1000); + +VAR(x, 0, 0, 1000); +VAR(y, 0, 0, 1000); +VAR(z, 0, 0, 1000); void rendermd3gun() { @@ -434,16 +434,16 @@ void rendermd3gun() light.x = s->r/ll+of; light.y = s->g/ll+of; light.z = s->b/ll+of; - }; + }; - md3model *weapon = models[firstweapon + player1->gunselect]; - if(!weapon->animstate) - { - weapon->animstate = new md3state(); - weapon->animstate->lastTime = lastmillis; - }; + md3model *weapon = models[firstweapon + player1->gunselect]; + if(!weapon->animstate) + { + weapon->animstate = new md3state(); + weapon->animstate->lastTime = lastmillis; + }; - int rtime = reloadtime(player1->gunselect); + int rtime = reloadtime(player1->gunselect); if(player1->reloading) { @@ -454,50 +454,50 @@ void rendermd3gun() } else { - float kick = 0.0f; - bool throwingnade = player1->gunselect==GUN_GRENADE && player1->thrownademillis; + float kick = 0.0f; + bool throwingnade = player1->gunselect==GUN_GRENADE && player1->thrownademillis; if(player1->gunselect==player1->lastattackgun) - { + { int timedifference = throwingnade ? (lastmillis-player1->thrownademillis) : (lastmillis-player1->lastaction); int percent_done = timedifference*100/attackdelay(player1->gunselect); if(percent_done > 100) percent_done = 100.0; // f(x) = -sin(x-1.5)^3 kick = -sin(pow((1.5f/100*percent_done)-1.5f,3)); - }; - - if(kick > 0.01f) throwingnade ? weapon->setanim(MDL_GUN_ATTACK2) : weapon->setanim(MDL_GUN_ATTACK); - else weapon->setanim(MDL_GUN_IDLE); + }; + + if(kick > 0.01f) throwingnade ? weapon->setanim(MDL_GUN_ATTACK2) : weapon->setanim(MDL_GUN_ATTACK); + else weapon->setanim(MDL_GUN_IDLE); vdist(dist, unitv, player1->o, worldpos); vdiv(unitv, dist); float k_rot = kick_rot(player1->gunselect)*kick; float k_back = kick_back(player1->gunselect)*kick/10; - vec sway(unitv); + vec sway(unitv); - int swayt = (lastmillis+1)/10 % 100; - float swayspeed = (float) (sin((float)lastmillis/swayspeeddiv))/(swaymovediv/10.0f); - float swayupspeed = (float) (sin((float)lastmillis/swayupspeeddiv-90))/(swayupmovediv/10.0f); - - - #define g0(x) ((x) < 0.0f ? -(x) : (x)) - float plspeed = min(1.0f, sqrt(g0(player1->vel.x*player1->vel.x) + g0(player1->vel.y*player1->vel.y))); - - swayspeed *= plspeed/2; + int swayt = (lastmillis+1)/10 % 100; + float swayspeed = (float) (sin((float)lastmillis/swayspeeddiv))/(swaymovediv/10.0f); + float swayupspeed = (float) (sin((float)lastmillis/swayupspeeddiv-90))/(swayupmovediv/10.0f); + + + #define g0(x) ((x) < 0.0f ? -(x) : (x)) + float plspeed = min(1.0f, sqrt(g0(player1->vel.x*player1->vel.x) + g0(player1->vel.y*player1->vel.y))); + + swayspeed *= plspeed/2; swayupspeed *= plspeed/2; - - // rotate 90° around z axis - float tmp = sway.x; - sway.x = sway.y; - sway.y = -tmp; - - if(swayupspeed<0.0f)swayupspeed = -swayupspeed; // sway a semicirle only - sway.z = 1.0f; - - sway.x *= swayspeed; - sway.y *= swayspeed; - sway.z *= swayupspeed; + + // rotate 90° around z axis + float tmp = sway.x; + sway.x = sway.y; + sway.y = -tmp; + + if(swayupspeed<0.0f)swayupspeed = -swayupspeed; // sway a semicirle only + sway.z = 1.0f; + + sway.x *= swayspeed; + sway.y *= swayspeed; + sway.z *= swayupspeed; weapon->draw( player1->o.x-unitv.x*k_back+sway.x, player1->o.z-unitv.z*k_back+sway.z, diff --git a/source/src/server.cpp b/source/src/server.cpp index a3406004..70661eaa 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -3,6 +3,8 @@ #include "cube.h" +#define m_ctf_s (mode==5) + enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; struct client // server side version of "dynent" type @@ -17,8 +19,16 @@ struct client // server side version of "dynent" type vector clients; +int maxclients = 8; string smapname; +int numclients() +{ + int num = 0; + loopv(clients) if(clients[i].type!=ST_EMPTY) i++; + return num; +}; + struct server_entity // server side version of "entity" type { bool spawned; @@ -44,7 +54,7 @@ bool mapreload = false; char *serverpassword = ""; -bool listenserv; +bool isdedicated; ENetHost * serverhost = NULL; int bsend = 0, brec = 0, laststatus = 0, lastsec = 0; @@ -87,6 +97,47 @@ void send2(bool rel, int cn, int a, int b) if(packet->referenceCount==0) enet_packet_destroy(packet); }; +struct ctfflag +{ + int state; + int thief_cn; + int pos[3]; + int lastupdate; +} ctfflags[2]; + +bool ctfbroadcast = false; + +void sendflaginfo(int flag, int action, int cn = -1) +{ + ctfflag &f = ctfflags[flag]; + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); + uchar *start = packet->data; + uchar *p = start+2; + putint(p, SV_FLAGINFO); + putint(p, flag); printf("senflaginfo flag: %i\n", flag); + putint(p, f.state); printf("senflaginfo state: %i\n", f.state); + putint(p, action); printf("senflaginfo action: %i\n", action); + if(f.state==CTFF_STOLEN || action==SV_FLAGRETURN) putint(p, f.thief_cn); + else if(f.state==CTFF_DROPPED) loopi(3) putint(p, f.pos[i]); + *(ushort *)start = ENET_HOST_TO_NET_16(p-start); + enet_packet_resize(packet,p-start); + if(cn<0) multicast(packet, -1); + else send(cn, packet); + if(packet->referenceCount==0) enet_packet_destroy(packet); +}; + +void ctfreset(bool send=true) +{ + if(!m_ctf_s) return; + loopi(2) + { + ctfflags[i].thief_cn = 0; + ctfflags[i].state = CTFF_INBASE; + ctfflags[i].lastupdate = -1; + if(send) sendflaginfo(i, -1); + }; +} + void sendservmsg(char *msg) { ENetPacket *packet = enet_packet_create(NULL, _MAXDEFSTR+10, ENET_PACKET_FLAG_RELIABLE); @@ -102,6 +153,7 @@ void sendservmsg(char *msg) void disconnect_client(int n, char *reason) { + if(n<0 || n>=clients.length()) return; printf("disconnecting client (%s) [%s]\n", clients[n].hostname, reason); enet_peer_disconnect(clients[n].peer); clients[n].type = ST_EMPTY; @@ -110,9 +162,9 @@ void disconnect_client(int n, char *reason) void resetitems() { sents.setsize(0); notgotitems = true; }; -void pickup(int i, int sec, int sender) // server side item pickup, acknowledge first client that gets it +void pickup(uint i, int sec, int sender) // server side item pickup, acknowledge first client that gets it { - if(i>=sents.length()) return; + if(i>=(uint)sents.length()) return; if(sents[i].spawned) { sents[i].spawned = false; @@ -121,6 +173,89 @@ void pickup(int i, int sec, int sender) // server side item pickup, ackn }; }; +// EDIT: AH +struct configset +{ + string mapname; + int mode; + int time; + bool vote; +}; + +vector configsets; +int curcfgset = -1; + +void readscfg(char *cfg) +{ + configsets.setsize(0); + + string s; + strcpy_s(s, cfg); + char *buf = loadfile(path(s), NULL); + if(!buf) return; + char *p, *l; + + p = buf; + while((p = strstr(p, "//")) != NULL) // remove comments + while(p[0] != '\n' && p[0] != '\0') p++[0] = ' '; + + l = buf; + bool lastline = false; + while((p = strstr(l, "\n")) != NULL || (l && (lastline=true))) // remove empty/invalid lines + { + int len = lastline ? strlen(l) : p-l; + string line; + strn0cpy(line, l, len+1); + char *d = line; + int n = 0; + while((p = strstr(d, ":")) != NULL && (d = p+1)) n++; + if(n!=3) memset(l, ' ', len+1); + if(lastline) break; + l += len+1; + }; + + configset c; + int argc = 0; + string argv[4]; + + p = strtok(buf, ":\n\0"); + while(p != NULL) + { + strcpy(argv[argc], p); + if(++argc==4) + { + int numspaces; + for(numspaces = 0; argv[0][numspaces]==' '; numspaces++){}; // ingore space crap + strcpy(c.mapname, argv[0]+numspaces); + c.mode = atoi(argv[1]); + c.time = atoi(argv[2]); + c.vote = (bool) atoi(argv[3]); + configsets.add(c); + argc = 0; + }; + p = strtok(NULL, ":\n\0"); + }; +}; + +void nextcfgset() // load next maprotation set +{ + curcfgset++; + if(curcfgset>=configsets.length() || curcfgset<0) curcfgset=0; + + printf("nextcfgset: %i\n", curcfgset); + + configset &c = configsets[curcfgset]; + mode = c.mode; + minremain = c.time; + strcpy_s(smapname, c.mapname); + + mapend = lastsec+minremain*60; + mapreload = false; + interm = 0; + laststatus = lastsec-61; + resetitems(); +}; + void resetvotes() { loopv(clients) clients[i].mapvote[0] = 0; @@ -128,6 +263,12 @@ void resetvotes() bool vote(char *map, int reqmode, int sender) { + if(configsets.length() && curcfgset < configsets.length() && !configsets[curcfgset].vote) + { + sprintf_sd(msg)("%s voted, but voting is currently disabled", clients[sender].name); + sendservmsg(msg); + return false; + }; strcpy_s(clients[sender].mapvote, map); clients[sender].modevote = reqmode; int yes = 0, no = 0; @@ -145,9 +286,26 @@ bool vote(char *map, int reqmode, int sender) return true; }; +// force map change, EDIT: AH +void changemap() +{ + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); + uchar *start = packet->data; + uchar *p = start+2; + putint(p, SV_MAPCHANGE); + sendstring(smapname, p); + putint(p, mode); + *(ushort *)start = ENET_HOST_TO_NET_16(p-start); + enet_packet_resize(packet,p-start); + multicast(packet, -1); + if(packet->referenceCount==0) enet_packet_destroy(packet); +}; + // server side processing of updates: does very little and most state is tracked client only // could be extended to move more gameplay to server (at expense of lag) +int tmp_pos[3]; + void process(ENetPacket * packet, int sender) // sender may be -1 { if(ENET_NET_TO_HOST_16(*(ushort *)packet->data)!=packet->dataLength) @@ -163,6 +321,14 @@ void process(ENetPacket * packet, int sender) // sender may be -1 while(p1) return; + ctfflag &f = ctfflags[flag]; + if(f.state!=CTFF_STOLEN) + { + f.state = CTFF_STOLEN; + f.thief_cn = sender; + sendflaginfo(flag, SV_FLAGPICKUP); + printf("srv_flagpickup\n"); + }; + break; + }; + + case SV_FLAGDROP: + { + int flag = getint(p); + if(flag<0|| flag>1) return; + ctfflag &f = ctfflags[flag]; + if(f.state==CTFF_STOLEN && (sender==-1 || f.thief_cn==sender)) + { + f.state = CTFF_DROPPED; + f.lastupdate = lastsec; + loopi(3) f.pos[i] = tmp_pos[i]; + sendflaginfo(flag, SV_FLAGDROP); + printf("srv_flagdrop\n"); + }; + break; + }; + + case SV_FLAGRETURN: + { + int flag = getint(p); + if(flag<0|| flag>1) return; + ctfflag &f = ctfflags[flag]; + if(f.state==CTFF_DROPPED) + { + f.state = CTFF_INBASE; + f.thief_cn = sender; + sendflaginfo(flag, SV_FLAGRETURN); + printf("srv_flagsreturn\n"); + }; + break; + }; + + case SV_FLAGSCORE: + { + int flag = getint(p); + if(flag<0|| flag>1) return; + ctfflag &f = ctfflags[flag]; + if(f.state==CTFF_STOLEN) + { + f.state = CTFF_INBASE; + f.thief_cn = sender; + sendflaginfo(flag, SV_FLAGSCORE); + printf("srv_flagscore\n"); + }; + break; + }; + +/* case SV_EXT: // allows for new features that require no server updates { for(int n = getint(p); n; n--) getint(p); break; }; +*/ default: { @@ -267,8 +500,10 @@ void send_welcome(int n) putint(p, SV_INITS2C); putint(p, n); putint(p, PROTOCOL_VERSION); + if(!smapname[0] && configsets.length()) nextcfgset(); // EDIT:AH putint(p, smapname[0]); sendstring(serverpassword, p); + putint(p, numclients()>maxclients); if(smapname[0]) { putint(p, SV_MAPCHANGE); @@ -281,6 +516,7 @@ void send_welcome(int n) *(ushort *)start = ENET_HOST_TO_NET_16(p-start); enet_packet_resize(packet, p-start); send(n, packet); + if(smapname[0] && m_ctf_s) loopi(2) sendflaginfo(i, -1, n); // EDIT: AH }; void multicast(ENetPacket *packet, int sender) @@ -328,9 +564,11 @@ void resetserverifempty() minremain = 10; mapend = lastsec+minremain*60; interm = 0; + ctfreset(false); }; int nonlocalclients = 0; +int lastconnect = 0; void serverslice(int seconds, unsigned int timeout) // main server update, called from cube main loop in sp, or dedicated server loop { @@ -344,12 +582,37 @@ void serverslice(int seconds, unsigned int timeout) // main server update, cal }; }; + if(m_ctf_s) + { + loopi(2) + { + ctfflag &f = ctfflags[i]; + if(f.state==CTFF_DROPPED && seconds-f.lastupdate>30) + { + f.state=CTFF_INBASE; + sendflaginfo(i, -1); + sprintf_sd(msg)("the server reset the %s flag", rb_team_string(i)); + sendservmsg(msg); + send2(true, -1, SV_SOUND, S_FLAGDROP); + }; + }; + }; + lastsec = seconds; if((mode>1 || (mode==0 && nonlocalclients)) && seconds>mapend-minremain*60) checkintermission(); if(interm && seconds>interm) { interm = 0; + + if(configsets.length()) // EDIT: AH + { + nextcfgset(); + mapreload = true; + changemap(); + ctfbroadcast = true; + } + else loopv(clients) if(clients[i].type!=ST_EMPTY) { send2(true, i, SV_MAPRELOAD, 0); // ask a client to trigger map reload @@ -358,13 +621,18 @@ void serverslice(int seconds, unsigned int timeout) // main server update, cal }; }; + if(ctfbroadcast) + { + ctfreset(); + ctfbroadcast = false; + }; + resetserverifempty(); - if(!listenserv) return; // below is network only + if(!isdedicated) return; // below is network only - int numplayers = 0; - loopv(clients) if(clients[i].type!=ST_EMPTY) ++numplayers; - serverms(mode, numplayers, minremain, smapname, seconds); + int numplayers = numclients(); + serverms(mode, numplayers, minremain, smapname, seconds, numclients()>=maxclients); if(seconds-laststatus>60) // display bandwidth stats, useful for server ops { @@ -377,34 +645,44 @@ void serverslice(int seconds, unsigned int timeout) // main server update, cal ENetEvent event; if(enet_host_service(serverhost, &event, timeout) > 0) - switch(event.type) { - case ENET_EVENT_TYPE_CONNECT: + switch(event.type) { - client &c = addclient(); - c.type = ST_TCPIP; - c.peer = event.peer; - c.peer->data = (void *)(&c-&clients[0]); - char hn[1024]; - strcpy_s(c.hostname, (enet_address_get_host(&c.peer->address, hn, sizeof(hn))==0) ? hn : "localhost"); - printf("client connected (%s)\n", c.hostname); - send_welcome(&c-&clients[0]); - break; - } - case ENET_EVENT_TYPE_RECEIVE: - brec += event.packet->dataLength; - process(event.packet, (int)event.peer->data); - if(event.packet->referenceCount==0) enet_packet_destroy(event.packet); - break; + case ENET_EVENT_TYPE_CONNECT: + { + client &c = addclient(); + c.type = ST_TCPIP; + c.peer = event.peer; + c.peer->data = (void *)(&c-&clients[0]); + char hn[1024]; + strcpy_s(c.hostname, (enet_address_get_host(&c.peer->address, hn, sizeof(hn))==0) ? hn : "localhost"); + printf("client connected (%s)\n", c.hostname); + send_welcome(lastconnect = &c-&clients[0]); + break; + } + case ENET_EVENT_TYPE_RECEIVE: + brec += event.packet->dataLength; + process(event.packet, (int)event.peer->data); + if(event.packet->referenceCount==0) enet_packet_destroy(event.packet); + break; - case ENET_EVENT_TYPE_DISCONNECT: - if((int)event.peer->data<0) break; - printf("disconnected client (%s)\n", clients[(int)event.peer->data].hostname); - clients[(int)event.peer->data].type = ST_EMPTY; - send2(true, -1, SV_CDIS, (int)event.peer->data); - event.peer->data = (void *)-1; - break; + case ENET_EVENT_TYPE_DISCONNECT: + if((int)event.peer->data<0) break; + printf("disconnected client (%s)\n", clients[(int)event.peer->data].hostname); + clients[(int)event.peer->data].type = ST_EMPTY; + send2(true, -1, SV_CDIS, (int)event.peer->data); + event.peer->data = (void *)-1; + break; + }; + + if(numplayers>maxclients) + { + disconnect_client(lastconnect, "maxclients reached"); + }; }; + #ifndef WIN32 + fflush(stdout); + #endif }; void cleanupserver() @@ -425,23 +703,25 @@ void localconnect() send_welcome(&c-&clients[0]); }; -void initserver(bool dedicated, bool l, int uprate, char *sdesc, char *ip, char *master, char *passwd) +void initserver(bool dedicated, int uprate, char *sdesc, char *ip, char *master, char *passwd, int maxcl, char *maprot) // EDIT: AH { serverpassword = passwd; - servermsinit(master ? master : "wouter.fov120.com/cube/masterserver/", sdesc, l); + maxclients = maxcl; + servermsinit(master ? master : "cubebot.bots-united.com/cube/masterserver.cgi/", sdesc, dedicated); - if(listenserv = l) + if(isdedicated = dedicated) { ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT }; if(*ip && enet_address_set_host(&address, ip)<0) printf("WARNING: server ip not resolved"); serverhost = enet_host_create(&address, MAXCLIENTS, 0, uprate); if(!serverhost) fatal("could not create server host\n"); loopi(MAXCLIENTS) serverhost->peers[i].data = (void *)-1; + if(maprot) readscfg(maprot); // EDIT: AH }; resetserverifempty(); - if(dedicated) // do not return, this becomes main loop + if(isdedicated) // do not return, this becomes main loop { #ifdef WIN32 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); diff --git a/source/src/serverms.cpp b/source/src/serverms.cpp index ebc408c8..f5dcc5fe 100644 --- a/source/src/serverms.cpp +++ b/source/src/serverms.cpp @@ -92,7 +92,7 @@ uchar *retrieveservers(uchar *buf, int buflen) ENetSocket pongsock = ENET_SOCKET_NULL; string serverdesc; -void serverms(int mode, int numplayers, int minremain, char *smapname, int seconds) +void serverms(int mode, int numplayers, int minremain, char *smapname, int seconds, bool isfull) { checkmasterreply(); updatemasterserver(seconds); @@ -114,7 +114,10 @@ void serverms(int mode, int numplayers, int minremain, char *smapname, int secon putint(p, mode); putint(p, numplayers); putint(p, minremain); - sendstring(smapname, p); + string mname; + strcpy_s(mname, isfull ? "[FULL] " : ""); + strcat_s(mname, smapname); + sendstring(mname, p); sendstring(serverdesc, p); buf.dataLength = p - pong; enet_socket_send(pongsock, &addr, &buf, 1); diff --git a/source/src/serverutil.cpp b/source/src/serverutil.cpp index 4e0b5d15..92caa68c 100644 --- a/source/src/serverutil.cpp +++ b/source/src/serverutil.cpp @@ -44,8 +44,9 @@ char msgsizesl[] = // size inclusive message token, 0 for variable SV_MAPCHANGE, 0, SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 3, SV_DENIED, 2, SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2, SV_GAMEMODE, 2, SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6, - SV_SENDMAP, 0, SV_RECVMAP, 1, SV_SERVMSG, 0, SV_ITEMLIST, 0, SV_WEAPCHANGE, 2, + SV_SENDMAP, 0, SV_RECVMAP, 1, SV_SERVMSG, 0, SV_ITEMLIST, 0, SV_WEAPCHANGE, 2, SV_MODELSKIN, 2, + SV_FLAGPICKUP, 2, SV_FLAGDROP, 2, SV_FLAGRETURN, 2, SV_FLAGSCORE, 2, SV_FLAGINFO, 0, SV_FLAGS, 2, // EDIT: AH SV_EXT, 0, -1 }; @@ -88,17 +89,16 @@ ENetPacket *recvmap(int n) return packet; } - #ifdef STANDALONE void localservertoclient(uchar *buf, int len) {}; void fatal(char *s, char *o) { cleanupserver(); printf("servererror: %s\n", s); exit(1); }; void *alloc(int s) { void *b = calloc(1,s); if(!b) fatal("no memory!"); return b; }; -int main(int argc, char* argv[]) +int main(int argc, char* argv[]) // EDIT: AH { - int uprate = 0; - char *sdesc = "", *ip = "", *master = NULL, *passwd = ""; + int uprate = 0, maxcl = 4; + char *sdesc = "", *ip = "", *master = NULL, *passwd = "", *maprot = ""; for(int i = 1; i=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(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); - +// 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); diff --git a/source/src/tools.cpp b/source/src/tools.cpp index 0f5219ae..4406ae14 100644 --- a/source/src/tools.cpp +++ b/source/src/tools.cpp @@ -12,7 +12,7 @@ pool::pool() for(int i = 0; iMAXREUSESIZE) { @@ -39,7 +39,7 @@ void *pool::alloc(int size) }; }; -void pool::dealloc(void *p, int size) +void pool::dealloc(void *p, size_t size) { if(size>MAXREUSESIZE) { @@ -56,7 +56,7 @@ void pool::dealloc(void *p, int size) }; }; -void *pool::realloc(void *p, int oldsize, int newsize) +void *pool::realloc(void *p, size_t oldsize, size_t newsize) { void *np = alloc(newsize); if(!oldsize) return np; @@ -74,7 +74,7 @@ void pool::dealloc_block(void *b) }; } -void pool::allocnext(int allocsize) +void pool::allocnext(size_t allocsize) { char *b = (char *)malloc(allocsize+PTRSIZE); *((char **)b) = blocks; @@ -83,7 +83,7 @@ void pool::allocnext(int allocsize) left = allocsize; }; -char *pool::string(char *s, int l) +char *pool::string(char *s, size_t l) { char *b = (char *)alloc(l+1); strncpy(b,s,l); @@ -116,7 +116,7 @@ char *loadfile(char *fn, int *size) char *buf = (char *)malloc(len+1); if(!buf) return NULL; buf[len] = 0; - int rlen = fread(buf, 1, len, f); + size_t rlen = fread(buf, 1, len, f); fclose(f); if(len!=rlen || len<=0) { diff --git a/source/src/tools.h b/source/src/tools.h index daaeb55d..5292305d 100644 --- a/source/src/tools.h +++ b/source/src/tools.h @@ -57,9 +57,15 @@ typedef unsigned int uint; #define _MAXDEFSTR 260 typedef char string[_MAXDEFSTR]; -inline void strn0cpy(char *d, const char *s, int m) { strncpy(d,s,m); d[(m)-1] = 0; }; +inline void strn0cpy(char *d, const char *s, size_t m) { strncpy(d,s,m); d[(m)-1] = 0; }; inline void strcpy_s(char *d, const char *s) { strn0cpy(d,s,_MAXDEFSTR); }; -inline void strcat_s(char *d, const char *s) { int n = strlen(d); strn0cpy(d+n,s,_MAXDEFSTR-n); }; +inline void strcat_s(char *d, const char *s) { size_t n = strlen(d); strn0cpy(d+n,s,_MAXDEFSTR-n); }; + +inline void formatstring(char *d, const char *fmt, va_list v) +{ + _vsnprintf(d, _MAXDEFSTR, fmt, v); + d[_MAXDEFSTR-1] = 0; +}; struct sprintf_s_f { @@ -77,7 +83,8 @@ struct sprintf_s_f #define sprintf_s(d) sprintf_s_f((char *)d) #define sprintf_sd(d) string d; sprintf_s(d) - +#define sprintf_sdlv(d,last,fmt) string d; { va_list ap; va_start(ap, last); formatstring(d, fmt, ap); va_end(ap); } +#define sprintf_sdv(d,fmt) sprintf_sdlv(d,fmt,fmt) // fast pentium f2i @@ -112,28 +119,28 @@ struct pool enum { PTRSIZE = sizeof(char *) }; enum { MAXBUCKETS = 65 }; // meaning up to size 256 on 32bit pointer systems enum { MAXREUSESIZE = MAXBUCKETS*PTRSIZE-PTRSIZE }; - inline int bucket(int s) { return (s+PTRSIZE-1)>>PTRBITS; }; + inline size_t bucket(size_t s) { return (s+PTRSIZE-1)>>PTRBITS; }; enum { PTRBITS = PTRSIZE==2 ? 1 : PTRSIZE==4 ? 2 : 3 }; char *p; - int left; + size_t left; char *blocks; void *reuse[MAXBUCKETS]; pool(); ~pool() { dealloc_block(blocks); }; - void *alloc(int size); - void dealloc(void *p, int size); - void *realloc(void *p, int oldsize, int newsize); + void *alloc(size_t size); + void dealloc(void *p, size_t size); + void *realloc(void *p, size_t oldsize, size_t newsize); - char *string(char *s, int l); + char *string(char *s, size_t l); char *string(char *s) { return string(s, strlen(s)); }; void deallocstr(char *s) { dealloc(s, strlen(s)+1); }; char *stringbuf(char *s) { return string(s, _MAXDEFSTR-1); }; void dealloc_block(void *b); - void allocnext(int allocsize); + void allocnext(size_t allocsize); }; template struct vector @@ -261,7 +268,7 @@ template struct hashtable pool *gp(); inline char *newstring(char *s) { return gp()->string(s); }; -inline char *newstring(char *s, int l) { return gp()->string(s, l); }; +inline char *newstring(char *s, size_t l) { return gp()->string(s, l); }; inline char *newstringbuf(char *s) { return gp()->stringbuf(s); }; #endif diff --git a/source/src/weapon.cpp b/source/src/weapon.cpp index c1bc1fac..b95f9509 100644 --- a/source/src/weapon.cpp +++ b/source/src/weapon.cpp @@ -1,597 +1,591 @@ -// weapon.cpp: all shooting and effects code - -#include "cube.h" - -struct guninfo { short sound, reload, reloadtime, attackdelay, damage, projspeed, part, spread, recoil, magsize, mdl_kick_rot, mdl_kick_back; char *name; }; - -const int SGRAYS = 20; //down from 20 (defualt) -const float SGSPREAD = 2; -vec sg[SGRAYS]; - -//change the particales -guninfo guns[NUMGUNS] = -{ - { S_KNIFE, S_NULL, 0, 500, 50, 0, 0, 1, 1, 1, 0, 0, "knife" }, - - { S_PISTOL, S_RPISTOL, 1400, 170, 20, 0, 0, 80, 10, 8, 6, 5, "pistol" }, // *SGRAYS - - { S_SHOTGUN, S_RSHOTGUN, 2400, 1000, 6, 0, 0, 1, 35, 7, 9, 9, "shotgun" }, //reload time is for 1 shell from 7 too powerful to 6 - - { S_SUBGUN, S_RSUBGUN, 1650, 80, 17, 0, 0, 70, 15, 30, 1, 2, "subgun" }, - - { S_SNIPER, S_RSNIPER, 1950, 1500, 72, 0, 0, 60, 50, 5, 4, 4, "sniper" }, - - { S_ASSULT, S_RASSULT, 2000, 130, 20, 0, 0, 20, 40, 20, 0, 2, "assult" }, //recoil was 44 - - { S_GRENADE, S_NULL, 1000, 2000, 150, 20, 6, 1, 1, 1, 3, 1, "grenade" }, -}; - - -bool gun_changed = false; - -//weapon selection -void wup() -{ - if(player1->gunselect==GUN_KNIFE) - player1->gunselect = GUN_PISTOL; - else if(player1->gunselect==GUN_PISTOL && player1->primary!=GUN_PISTOL) - player1->gunselect = player1->primary; - else if(player1->gunselect==player1->primary) - player1->gunselect=GUN_KNIFE; - - conoutf("%s selected", (int)guns[player1->gunselect].name); - - gun_changed = true; -}; - -void wdw() -{ - if(player1->gunselect==GUN_KNIFE) - player1->gunselect = player1->primary; - else if(player1->gunselect==player1->primary && player1->primary!=GUN_PISTOL) - player1->gunselect = GUN_PISTOL; - else if(player1->gunselect==GUN_PISTOL) - player1->gunselect=GUN_KNIFE; - - conoutf("%s selected", (int)guns[player1->gunselect].name); - - gun_changed = true; -}; - -void primary() -{ - player1->gunselect=player1->primary; - gun_changed = true; -}; - -void secondary() -{ - player1->gunselect=GUN_PISTOL; - gun_changed = true; -}; - -void melee() -{ - player1->gunselect=GUN_KNIFE; - gun_changed = true; -}; - -COMMAND(primary,ARG_NONE); - -COMMAND(secondary,ARG_NONE); - -COMMAND(melee,ARG_NONE); -COMMAND(wup,ARG_NONE); -COMMAND(wdw,ARG_NONE); - -void reload() -{ - if(player1->gunselect==GUN_KNIFE || player1->gunselect==GUN_GRENADE) return; - if(player1->gunselect==GUN_PISTOL && player1->akimbo!=0 && player1->mag[player1->gunselect]>=(guns[player1->gunselect].magsize * 2)) return; - else if(player1->mag[player1->gunselect]>=guns[player1->gunselect].magsize && player1->akimbo==0) return; - if(player1->ammo[player1->gunselect]<=0) return; - if(player1->reloading) return; - - player1->reloading = true; - player1->lastaction = lastmillis; - - player1->gunwait = guns[player1->gunselect].reloadtime; - - //temp hack in drawhudgun() so no guns drawn while reloading, needs fixed - int a = guns[player1->gunselect].magsize - player1->mag[player1->gunselect]; - if(player1->gunselect==GUN_PISTOL && player1->akimbo!=0) - a = guns[player1->gunselect].magsize * 2 - player1->mag[player1->gunselect]; - - if (a >= player1->ammo[player1->gunselect]) - { - player1->mag[player1->gunselect] += player1->ammo[player1->gunselect]; - player1->ammo[player1->gunselect] = 0; - } - else - { - player1->mag[player1->gunselect] += a; - player1->ammo[player1->gunselect] -= a; - } - - playsoundc(guns[player1->gunselect].reload); -}; - -COMMAND(reload,ARG_NONE); - -int reloadtime(int gun) { return guns[gun].reloadtime; }; -int attackdelay(int gun) { return guns[gun].attackdelay; }; -int magsize(int gun) { return guns[gun].magsize; }; -int kick_rot(int gun) { return guns[gun].mdl_kick_rot; }; -int kick_back(int gun) { return guns[gun].mdl_kick_back; }; - - -void createrays(vec &from, vec &to) // create random spread of rays for the shotgun -{ - vdist(dist, dvec, from, to); - float f = dist*SGSPREAD/1000; - loopi(SGRAYS) - { - #define RNDD (rnd(101)-50)*f - vec r = { RNDD, RNDD, RNDD }; - sg[i] = to; - vadd(sg[i], r); - }; -}; - -bool intersect(dynent *d, vec &from, vec &to) // if lineseg hits entity bounding box -{ - vec v = to, w = d->o, *p; - vsub(v, from); - vsub(w, from); - float c1 = dotprod(w, v); - - if(c1<=0) p = &from; - else - { - float c2 = dotprod(v, v); - if(c2<=c1) p = &to; - else - { - float f = c1/c2; - vmul(v, f); - vadd(v, from); - p = &v; - }; - }; - - return p->x <= d->o.x+d->radius - && p->x >= d->o.x-d->radius - && p->y <= d->o.y+d->radius - && p->y >= d->o.y-d->radius - && p->z <= d->o.z+d->aboveeye - && p->z >= d->o.z-d->eyeheight; -}; - -dynent *playerincrosshair() -{ - if(demoplayback) return NULL; - loopv(players) - { - dynent *o = players[i]; - if(!o) continue; - if(intersect(o, player1->o, worldpos)) return o; - }; - return NULL; -}; - -const int MAXPROJ = 100; -struct projectile { vec o, to; float speed; dynent *owner; int gun; bool inuse, local; }; -projectile projs[MAXPROJ]; - -void projreset() { loopi(MAXPROJ) projs[i].inuse = false; }; - -void newprojectile(vec &from, vec &to, float speed, bool local, dynent *owner, int gun) -{ - loopi(MAXPROJ) - { - projectile *p = &projs[i]; - if(p->inuse) continue; - p->inuse = true; - p->o = from; - p->to = to; - p->speed = speed; - p->local = local; - p->owner = owner; - p->gun = gun; - return; - }; -}; - -void hit(int target, int damage, dynent *d, dynent *at) -{ - if(d==player1) selfdamage(damage, at==player1 ? -1 : -2, at); - else { addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence); playsound(S_PAIN1+rnd(5), &d->o); }; - particle_splash(3, damage, 1000, d->o); - demodamage(damage, d->o); -}; - -const float RL_RADIUS = 7; -const float RL_DAMRAD = 10; // hack - -void radialeffect(dynent *o, vec &v, int cn, int qdam, dynent *at) -{ - if(o->state!=CS_ALIVE) return; - vdist(dist, temp, v, o->o); - dist -= 2; // account for eye distance imprecision - if(distvel, temp); - }; -}; - -void splash(projectile *p, vec &v, vec &vold, int notthisplayer, int notthismonster, int qdam) -{ - particle_splash(0, 50, 300, v); - p->inuse = false; - if(p->gun!=GUN_GRENADE) - { - playsound(S_FEXPLODE, &v); - // no push? - } - else - { - //playsound(S_RLHIT, &v); - newsphere(v, RL_RADIUS, 0); - dodynlight(vold, v, 0, 0, p->owner); - if(!p->local) return; - radialeffect(player1, v, -1, qdam, p->owner); - loopv(players) - { - if(i==notthisplayer) continue; - dynent *o = players[i]; - if(!o) continue; - radialeffect(o, v, i, qdam, p->owner); - }; - }; -}; - -inline void projdamage(dynent *o, projectile *p, vec &v, int i, int im, int qdam) -{ - if(o->state!=CS_ALIVE) return; - if(intersect(o, p->o, v)) - { - splash(p, v, p->o, i, im, qdam); - hit(i, qdam, o, p->owner); - }; -}; - -void moveprojectiles(float time) -{ - loopi(MAXPROJ) - { - projectile *p = &projs[i]; - if(!p->inuse) continue; - //int qdam = guns[p->gun].damage*(p->owner->quadmillis ? 4 : 1); - int qdam = guns[p->gun].damage; - vdist(dist, v, p->o, p->to); - float dtime = dist*1000/p->speed; - if(time>dtime) dtime = time; - vmul(v, time/dtime); - vadd(v, p->o) - if(p->local) - { - loopv(players) - { - dynent *o = players[i]; - if(!o) continue; - projdamage(o, p, v, i, -1, qdam); - }; - if(p->owner!=player1) projdamage(player1, p, v, -1, -1, qdam); - }; - if(p->inuse) - { - if(time==dtime) splash(p, v, p->o, -1, -1, qdam); - else - { - if(p->gun==GUN_GRENADE) { dodynlight(p->o, v, 0, 255, p->owner); particle_splash(5, 2, 200, v); } - else { particle_splash(1, 1, 200, v); particle_splash(guns[p->gun].part, 1, 1, v); }; - }; - }; - p->o = v; - }; -}; - -extern float rad(float x); - -physent *curnade = NULL; - -void throw_nade(dynent *d, vec &to, physent *p) -{ - if(!p || !d) return; - printf("thrownade\n"); - - p->isphysent = true; - p->gravity = 20; - p->state = NADE_THROWED; - - p->timeinair = 0; - p->onfloor = false; - - p->o = d->o; - p->vel.z = sin(rad(d->pitch)); - float speed = cos(rad(d->pitch)); - p->vel.x = sin(rad(d->yaw))*speed; - p->vel.y = -cos(rad(d->yaw))*speed; - - vmul(p->vel, 1.7f); - - vec throwdir = p->vel; - vmul(throwdir, d->radius); - vadd(p->o, throwdir); - vadd(p->o, throwdir); - - vec &from = d->o; - - if(d==player1) - { - d->thrownademillis = lastmillis; - int percent_done = (lastmillis-p->millis)*100/2000; - if(percent_done < 0 || percent_done > 100) percent_done = 100; - addmsg(1, 9, SV_SHOT, d->gunselect, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), percent_done); -// printf("grenade: %i\n", (lastmillis-curnade->millis)/100); - if(curnade) curnade = NULL; - }; -}; - -physent *new_physent() -{ - physent *p = (physent *)gp()->alloc(sizeof(physent)); - - p->yaw = 270; - p->pitch = 0; - p->roll = 0; - p->isphysent = true; - - p->maxspeed = 40; - p->outsidemap = false; - p->inwater = false; - p->radius = 0.2f; - p->eyeheight = 0.3f; - p->aboveeye = 0.0f; - p->frags = 0; - p->plag = 0; - p->ping = 0; - p->lastupdate = lastmillis; - p->enemy = NULL; - p->monsterstate = 0; - p->name[0] = p->team[0] = 0; - p->blocked = false; - p->lifesequence = 0; - p->dynent::state = CS_ALIVE; - p->state = PHYSENT_NONE; - p->shots = 0; - p->reloading = false; - p->nextprimary = 1; - p->hasarmour = false; - - p->k_left = false; - p->k_right = false; - p->k_up = false; - p->k_down = false; - p->jumpnext = false; - p->onladder = false; - p->strafe = 0; - p->move = 0; - - physents.add(p); - return p; -} - -physent *new_nade(dynent *d, int millis = 0) -{ - printf("newnade\n"); - physent *p = new_physent(); - p->owner = d; - p->millis = lastmillis; - p->timetolife = 2000-millis; - p->state = NADE_ACTIVATED; - if(d==player1) - { - curnade = p; - d->thrownademillis = 0; - }; - return p; -}; - -void explode_nade(physent *i) -{ - if(!i) return; - printf("explode\n"); - - if(i->state != NADE_THROWED) - { - vec o = i->owner->o; - vec dist = { 0.1f, 0.1f, 0.1f }; - vadd(o, dist); - throw_nade(i->owner, o, i); - }; - newprojectile(i->o, i->o, 1, true, i->owner, GUN_GRENADE); -}; - -void shootv(int gun, vec &from, vec &to, dynent *d, bool local, int nademillis) // create visual effect from a shot -{ - playsound(guns[gun].sound, d==player1 ? NULL : &d->o); - int pspeed = 25; - switch(gun) - { - case GUN_KNIFE: - break; - - case GUN_SHOTGUN: - { - loopi(SGRAYS) particle_splash(0, 5, 200, sg[i]); - break; - }; - - case GUN_PISTOL: - case GUN_SUBGUN: - case GUN_ASSULT: - addshotline(d, from, to); - particle_splash(9, 5, 250, to); - //particle_trail(1, 10, from, to); - break; - - case GUN_GRENADE: - //pspeed = guns[gun].projspeed; - //newprojectile(from, to, (float)pspeed, local, d, gun); - - if(d!=player1) - { - physent *p = new_nade(d, nademillis); - throw_nade(d, to, p); - } - - break; - - case GUN_SNIPER: - addshotline(d, from, to); - particle_splash(0, 50, 200, to); - particle_trail(1, 500, from, to); - break; - }; -}; - -void hitpush(int target, int damage, dynent *d, dynent *at, vec &from, vec &to) -{ - hit(target, damage, d, at); - vdist(dist, v, from, to); - vmul(v, damage/dist/50); - vadd(d->vel, v); -}; - -void raydamage(dynent *o, vec &from, vec &to, dynent *d, int i) -{ - if(o->state!=CS_ALIVE) return; - int qdam = guns[d->gunselect].damage; - //if(d->quadmillis) qdam *= 4; - if(d->gunselect==GUN_SHOTGUN) - { - int damage = 0; - loop(r, SGRAYS) if(intersect(o, from, sg[r])) damage += qdam; - if(damage) hitpush(i, damage, o, d, from, to); - } - else if(intersect(o, from, to)) hitpush(i, qdam, o, d, from, to); -}; -void spreadandrecoil(vec & from, vec & to, dynent * d) -{ - //nothing special for a knife - if (d->gunselect==GUN_KNIFE || d->gunselect==GUN_GRENADE) return; - - //spread - vdist(dist, unitv, from, to); - float f = dist/1000; - int spd = guns[d->gunselect].spread; - - //recoil - int rcl = guns[d->gunselect].recoil*-0.01f; - - if (d->gunselect==GUN_ASSULT) - { - if(d->shots > 3) - spd = 70; - rcl += (rnd(8)*-0.01f); - }; - - if ((d->gunselect==GUN_SNIPER) && (d->vel.x<.25f && d->vel.y<.25f)) - { - spd = 1; - rcl = rcl / 3; - }; - - if (d->gunselect!=GUN_SHOTGUN) //no spread on shotgun - { - #define RNDD (rnd(spd)-spd/2)*f - vec r = { RNDD, RNDD, RNDD }; - vadd(to, r); - }; - - //increase pitch for recoil - vdiv(unitv, dist); - vec recoil = unitv; - vmul(recoil, rcl); - vadd(d->vel, recoil); - - if(d->pitch<80.0f) d->pitch += guns[d->gunselect].recoil*0.05f; -}; - -void shoot(dynent *d, vec &targ) -{ - int attacktime = lastmillis-d->lastaction; - - if(!d->attacking && d->gunselect==GUN_GRENADE && curnade) // throw - { - throw_nade(d, targ, curnade); - return; - } - - if(attacktimegunwait) return; - d->gunwait = 0; - d->reloading = false; - - if(!d->attacking) - { - d->shots = 0; - return; - }; - - if(d->gunselect!=GUN_SUBGUN && d->gunselect!=GUN_ASSULT && d->gunselect!=GUN_GRENADE) d->attacking = false; //makes sub/assult autos - else d->shots++; - - if (d->gunselect==GUN_PISTOL && d->akimbo!=0) d->attacking = true; //make akimbo auto - - d->lastaction = lastmillis; - d->lastattackgun = d->gunselect; - if(!d->mag[d->gunselect]) { playsoundc(S_NOAMMO); d->gunwait = 250; d->lastattackgun = -1; return; }; - - if(d->gunselect) d->mag[d->gunselect]--; - - vec from = d->o; - vec to = targ; - from.z -= 0.2f; // below eye - - spreadandrecoil(from,to,d); - - vdist(dist, unitv, from, to); - vdiv(unitv, dist); - - if(d->gunselect==GUN_KNIFE) - { - vmul(unitv, 3); // punch range - to = from; - vadd(to, unitv); - }; - if(d->gunselect==GUN_SHOTGUN) createrays(from, to); - - //if(d->quadmillis && attacktime>200) playsoundc(S_ITEMPUP); - - if(d->gunselect==GUN_PISTOL && d->akimbo) d->gunwait = guns[d->gunselect].attackdelay / 2; //make akimbo pistols shoot twice as fast as normal pistol - else d->gunwait = guns[d->gunselect].attackdelay; - - if(d->gunselect==GUN_GRENADE) // activate - { - if(!curnade) new_nade(player1); - return; - } - - shootv(d->gunselect, from, to, d, 0); - addmsg(1, 9, SV_SHOT, d->gunselect, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), 0 ); - - if(guns[d->gunselect].projspeed) return; - - loopv(players) - { - dynent *o = players[i]; - if(!o) continue; - raydamage(o, from, to, d, i); - }; - -}; +// weapon.cpp: all shooting and effects code + +#include "cube.h" + +struct guninfo { short sound, reload, reloadtime, attackdelay, damage, projspeed, part, spread, recoil, magsize, mdl_kick_rot, mdl_kick_back; char *name; }; + +const int SGRAYS = 20; //down from 20 (defualt) +const float SGSPREAD = 2; +vec sg[SGRAYS]; + +//change the particales +guninfo guns[NUMGUNS] = +{ + { S_KNIFE, S_NULL, 0, 500, 50, 0, 0, 1, 1, 1, 0, 0, "knife" }, + + { S_PISTOL, S_RPISTOL, 1400, 170, 20, 0, 0, 80, 10, 8, 6, 5, "pistol" }, // *SGRAYS + + { S_SHOTGUN, S_RSHOTGUN, 2400, 1000, 6, 0, 0, 1, 35, 7, 9, 9, "shotgun" }, //reload time is for 1 shell from 7 too powerful to 6 + + { S_SUBGUN, S_RSUBGUN, 1650, 80, 17, 0, 0, 70, 15, 30, 1, 2, "subgun" }, + + { S_SNIPER, S_RSNIPER, 1950, 1500, 72, 0, 0, 60, 50, 5, 4, 4, "sniper" }, + + { S_ASSULT, S_RASSULT, 2000, 130, 20, 0, 0, 20, 40, 20, 0, 2, "assult" }, //recoil was 44 + + { S_GRENADE, S_NULL, 1000, 2000, 150, 20, 6, 1, 1, 1, 3, 1, "grenade" }, +}; + + +bool gun_changed = false; + +//weapon selection +void wup() +{ + if(player1->gunselect==GUN_KNIFE) + player1->gunselect = GUN_PISTOL; + else if(player1->gunselect==GUN_PISTOL && player1->primary!=GUN_PISTOL) + player1->gunselect = player1->primary; + else if(player1->gunselect==player1->primary) + player1->gunselect=GUN_KNIFE; + + conoutf("%s selected", (int)guns[player1->gunselect].name); + + gun_changed = true; +}; + +void wdw() +{ + if(player1->gunselect==GUN_KNIFE) + player1->gunselect = player1->primary; + else if(player1->gunselect==player1->primary && player1->primary!=GUN_PISTOL) + player1->gunselect = GUN_PISTOL; + else if(player1->gunselect==GUN_PISTOL) + player1->gunselect=GUN_KNIFE; + + conoutf("%s selected", (int)guns[player1->gunselect].name); + + gun_changed = true; +}; + +void primary() +{ + player1->gunselect=player1->primary; + gun_changed = true; +}; + +void secondary() +{ + player1->gunselect=GUN_PISTOL; + gun_changed = true; +}; + +void melee() +{ + player1->gunselect=GUN_KNIFE; + gun_changed = true; +}; + +COMMAND(primary,ARG_NONE); + +COMMAND(secondary,ARG_NONE); + +COMMAND(melee,ARG_NONE); +COMMAND(wup,ARG_NONE); +COMMAND(wdw,ARG_NONE); + +void reload() +{ + if(player1->gunselect==GUN_KNIFE || player1->gunselect==GUN_GRENADE) return; + if(player1->mag[player1->gunselect]>=guns[player1->gunselect].magsize) return; + if(player1->ammo[player1->gunselect]<=0) return; + if(player1->reloading) return; + + player1->reloading = true; + player1->lastaction = lastmillis; + + player1->gunwait = guns[player1->gunselect].reloadtime; + + //temp hack in drawhudgun() so no guns drawn while reloading, needs fixed + + int a = guns[player1->gunselect].magsize - player1->mag[player1->gunselect]; + if (a >= player1->ammo[player1->gunselect]) + { + player1->mag[player1->gunselect] += player1->ammo[player1->gunselect]; + player1->ammo[player1->gunselect] = 0; + } + else + { + player1->mag[player1->gunselect] += a; + player1->ammo[player1->gunselect] -= a; + } + + playsoundc(guns[player1->gunselect].reload); +}; + +COMMAND(reload,ARG_NONE); + +int reloadtime(int gun) { return guns[gun].reloadtime; }; +int attackdelay(int gun) { return guns[gun].attackdelay; }; +int magsize(int gun) { return guns[gun].magsize; }; +int kick_rot(int gun) { return guns[gun].mdl_kick_rot; }; +int kick_back(int gun) { return guns[gun].mdl_kick_back; }; + + +void createrays(vec &from, vec &to) // create random spread of rays for the shotgun +{ + vdist(dist, dvec, from, to); + float f = dist*SGSPREAD/1000; + loopi(SGRAYS) + { + #define RNDD (rnd(101)-50)*f + vec r = { RNDD, RNDD, RNDD }; + sg[i] = to; + vadd(sg[i], r); + }; +}; + +bool intersect(dynent *d, vec &from, vec &to) // if lineseg hits entity bounding box +{ + vec v = to, w = d->o, *p; + vsub(v, from); + vsub(w, from); + float c1 = dotprod(w, v); + + if(c1<=0) p = &from; + else + { + float c2 = dotprod(v, v); + if(c2<=c1) p = &to; + else + { + float f = c1/c2; + vmul(v, f); + vadd(v, from); + p = &v; + }; + }; + + return p->x <= d->o.x+d->radius + && p->x >= d->o.x-d->radius + && p->y <= d->o.y+d->radius + && p->y >= d->o.y-d->radius + && p->z <= d->o.z+d->aboveeye + && p->z >= d->o.z-d->eyeheight; +}; + +dynent *playerincrosshair() +{ + if(demoplayback) return NULL; + loopv(players) + { + dynent *o = players[i]; + if(!o) continue; + if(intersect(o, player1->o, worldpos)) return o; + }; + return NULL; +}; + +const int MAXPROJ = 100; +struct projectile { vec o, to; float speed; dynent *owner; int gun; bool inuse, local; }; +projectile projs[MAXPROJ]; + +void projreset() { loopi(MAXPROJ) projs[i].inuse = false; }; + +void newprojectile(vec &from, vec &to, float speed, bool local, dynent *owner, int gun) +{ + loopi(MAXPROJ) + { + projectile *p = &projs[i]; + if(p->inuse) continue; + p->inuse = true; + p->o = from; + p->to = to; + p->speed = speed; + p->local = local; + p->owner = owner; + p->gun = gun; + return; + }; +}; + +void hit(int target, int damage, dynent *d, dynent *at) +{ + if(d==player1) selfdamage(damage, at==player1 ? -1 : -2, at); + else { addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence); playsound(S_PAIN1+rnd(5), &d->o); }; + particle_splash(3, damage, 1000, d->o); + demodamage(damage, d->o); +}; + +const float RL_RADIUS = 7; +const float RL_DAMRAD = 10; // hack + +void radialeffect(dynent *o, vec &v, int cn, int qdam, dynent *at) +{ + if(o->state!=CS_ALIVE) return; + vdist(dist, temp, v, o->o); + dist -= 2; // account for eye distance imprecision + if(distvel, temp); + }; +}; + +void splash(projectile *p, vec &v, vec &vold, int notthisplayer, int notthismonster, int qdam) +{ + particle_splash(0, 50, 300, v); + p->inuse = false; + if(p->gun!=GUN_GRENADE) + { + playsound(S_FEXPLODE, &v); + // no push? + } + else + { + //playsound(S_RLHIT, &v); + newsphere(v, RL_RADIUS, 0); + dodynlight(vold, v, 0, 0, p->owner); + if(!p->local) return; + radialeffect(player1, v, -1, qdam, p->owner); + loopv(players) + { + if(i==notthisplayer) continue; + dynent *o = players[i]; + if(!o) continue; + radialeffect(o, v, i, qdam, p->owner); + }; + }; +}; + +inline void projdamage(dynent *o, projectile *p, vec &v, int i, int im, int qdam) +{ + if(o->state!=CS_ALIVE) return; + if(intersect(o, p->o, v)) + { + splash(p, v, p->o, i, im, qdam); + hit(i, qdam, o, p->owner); + }; +}; + +void moveprojectiles(float time) +{ + loopi(MAXPROJ) + { + projectile *p = &projs[i]; + if(!p->inuse) continue; + //int qdam = guns[p->gun].damage*(p->owner->quadmillis ? 4 : 1); + int qdam = guns[p->gun].damage; + vdist(dist, v, p->o, p->to); + float dtime = dist*1000/p->speed; + if(time>dtime) dtime = time; + vmul(v, time/dtime); + vadd(v, p->o) + if(p->local) + { + loopv(players) + { + dynent *o = players[i]; + if(!o) continue; + projdamage(o, p, v, i, -1, qdam); + }; + if(p->owner!=player1) projdamage(player1, p, v, -1, -1, qdam); + }; + if(p->inuse) + { + if(time==dtime) splash(p, v, p->o, -1, -1, qdam); + else + { + if(p->gun==GUN_GRENADE) { dodynlight(p->o, v, 0, 255, p->owner); particle_splash(5, 2, 200, v); } + else { particle_splash(1, 1, 200, v); particle_splash(guns[p->gun].part, 1, 1, v); }; + }; + }; + p->o = v; + }; +}; + +extern float rad(float x); + +physent *curnade = NULL; + +void throw_nade(dynent *d, vec &to, physent *p) +{ + if(!p || !d) return; + printf("thrownade\n"); + + p->isphysent = true; + p->gravity = 20; + p->state = NADE_THROWED; + + p->timeinair = 0; + p->onfloor = false; + + p->o = d->o; + p->vel.z = sin(rad(d->pitch)); + float speed = cos(rad(d->pitch)); + p->vel.x = sin(rad(d->yaw))*speed; + p->vel.y = -cos(rad(d->yaw))*speed; + + vmul(p->vel, 1.7f); + + vec throwdir = p->vel; + vmul(throwdir, d->radius); + vadd(p->o, throwdir); + vadd(p->o, throwdir); + + vec &from = d->o; + + if(d==player1) + { + d->thrownademillis = lastmillis; + int percent_done = (lastmillis-p->millis)*100/2000; + if(percent_done < 0 || percent_done > 100) percent_done = 100; + addmsg(1, 9, SV_SHOT, d->gunselect, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), percent_done); +// printf("grenade: %i\n", (lastmillis-curnade->millis)/100); + if(curnade) curnade = NULL; + }; +}; + +physent *new_physent() +{ + physent *p = (physent *)gp()->alloc(sizeof(physent)); + + p->yaw = 270; + p->pitch = 0; + p->roll = 0; + p->isphysent = true; + + p->maxspeed = 40; + p->outsidemap = false; + p->inwater = false; + p->radius = 0.2f; + p->eyeheight = 0.3f; + p->aboveeye = 0.0f; + p->frags = 0; + p->plag = 0; + p->ping = 0; + p->lastupdate = lastmillis; + p->enemy = NULL; + p->monsterstate = 0; + p->name[0] = p->team[0] = 0; + p->blocked = false; + p->lifesequence = 0; + p->dynent::state = CS_ALIVE; + p->state = PHYSENT_NONE; + p->shots = 0; + p->reloading = false; + p->nextprimary = 1; + p->hasarmour = false; + + p->k_left = false; + p->k_right = false; + p->k_up = false; + p->k_down = false; + p->jumpnext = false; + p->onladder = false; + p->strafe = 0; + p->move = 0; + + physents.add(p); + return p; +} + +physent *new_nade(dynent *d, int millis = 0) +{ + printf("newnade\n"); + physent *p = new_physent(); + p->owner = d; + p->millis = lastmillis; + p->timetolife = 2000-millis; + p->state = NADE_ACTIVATED; + if(d==player1) + { + curnade = p; + d->thrownademillis = 0; + }; + return p; +}; + +void explode_nade(physent *i) +{ + if(!i) return; + printf("explode\n"); + + if(i->state != NADE_THROWED) + { + vec o = i->owner->o; + vec dist = { 0.1f, 0.1f, 0.1f }; + vadd(o, dist); + throw_nade(i->owner, o, i); + }; + newprojectile(i->o, i->o, 1, true, i->owner, GUN_GRENADE); +}; + +void shootv(int gun, vec &from, vec &to, dynent *d, bool local, int nademillis) // create visual effect from a shot +{ + playsound(guns[gun].sound, d==player1 ? NULL : &d->o); + int pspeed = 25; + switch(gun) + { + case GUN_KNIFE: + break; + + case GUN_SHOTGUN: + { + loopi(SGRAYS) particle_splash(0, 5, 200, sg[i]); + break; + }; + + case GUN_PISTOL: + case GUN_SUBGUN: + case GUN_ASSULT: + addshotline(d, from, to); + particle_splash(9, 5, 250, to); + //particle_trail(1, 10, from, to); + break; + + case GUN_GRENADE: + //pspeed = guns[gun].projspeed; + //newprojectile(from, to, (float)pspeed, local, d, gun); + + if(d!=player1) + { + physent *p = new_nade(d, nademillis); + throw_nade(d, to, p); + } + + break; + + case GUN_SNIPER: + addshotline(d, from, to); + particle_splash(0, 50, 200, to); + particle_trail(1, 500, from, to); + break; + }; +}; + +void hitpush(int target, int damage, dynent *d, dynent *at, vec &from, vec &to) +{ + hit(target, damage, d, at); + vdist(dist, v, from, to); + vmul(v, damage/dist/50); + vadd(d->vel, v); +}; + +void raydamage(dynent *o, vec &from, vec &to, dynent *d, int i) +{ + if(o->state!=CS_ALIVE) return; + int qdam = guns[d->gunselect].damage; + //if(d->quadmillis) qdam *= 4; + if(d->gunselect==GUN_SHOTGUN) + { + int damage = 0; + loop(r, SGRAYS) if(intersect(o, from, sg[r])) damage += qdam; + if(damage) hitpush(i, damage, o, d, from, to); + } + else if(intersect(o, from, to)) hitpush(i, qdam, o, d, from, to); +}; +void spreadandrecoil(vec & from, vec & to, dynent * d) +{ + //nothing special for a knife + if (d->gunselect==GUN_KNIFE || d->gunselect==GUN_GRENADE) return; + + //spread + vdist(dist, unitv, from, to); + float f = dist/1000; + int spd = guns[d->gunselect].spread; + + //recoil + int rcl = guns[d->gunselect].recoil*-0.01f; + + if (d->gunselect==GUN_ASSULT) + { + if(d->shots > 3) + spd = 70; + rcl += (rnd(8)*-0.01f); + }; + + if ((d->gunselect==GUN_SNIPER) && (d->vel.x<.25f && d->vel.y<.25f)) + { + spd = 1; + rcl = rcl / 3; + }; + + if (d->gunselect!=GUN_SHOTGUN) //no spread on shotgun + { + #define RNDD (rnd(spd)-spd/2)*f + vec r = { RNDD, RNDD, RNDD }; + vadd(to, r); + }; + + //increase pitch for recoil + vdiv(unitv, dist); + vec recoil = unitv; + vmul(recoil, rcl); + vadd(d->vel, recoil); + + if(d->pitch<80.0f) d->pitch += guns[d->gunselect].recoil*0.05f; +}; + +void shoot(dynent *d, vec &targ) +{ + int attacktime = lastmillis-d->lastaction; + + if(!d->attacking && d->gunselect==GUN_GRENADE && curnade) // throw + { + throw_nade(d, targ, curnade); + return; + } + + if(attacktimegunwait) return; + d->gunwait = 0; + d->reloading = false; + + if(!d->attacking) + { + d->shots = 0; + return; + }; + + if(d->gunselect!=GUN_SUBGUN && d->gunselect!=GUN_ASSULT && d->gunselect!=GUN_GRENADE) d->attacking = false; //makes sub/assult autos + else d->shots++; + + d->lastaction = lastmillis; + d->lastattackgun = d->gunselect; + if(!d->mag[d->gunselect]) { playsoundc(S_NOAMMO); d->gunwait = 250; d->lastattackgun = -1; return; }; + + if(d->gunselect) d->mag[d->gunselect]--; + + vec from = d->o; + vec to = targ; + from.z -= 0.2f; // below eye + + spreadandrecoil(from,to,d); + + vdist(dist, unitv, from, to); + vdiv(unitv, dist); + + if(d->gunselect==GUN_KNIFE) + { + vmul(unitv, 3); // punch range + to = from; + vadd(to, unitv); + }; + if(d->gunselect==GUN_SHOTGUN) createrays(from, to); + + //if(d->quadmillis && attacktime>200) playsoundc(S_ITEMPUP); + + d->gunwait = guns[d->gunselect].attackdelay; + + if(d->gunselect==GUN_GRENADE) // activate + { + if(!curnade) new_nade(player1); + return; + } + + shootv(d->gunselect, from, to, d, 0); + addmsg(1, 9, SV_SHOT, d->gunselect, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), 0 ); + + if(guns[d->gunselect].projspeed) return; + + loopv(players) + { + dynent *o = players[i]; + if(!o) continue; + raydamage(o, from, to, d, i); + }; + +}; diff --git a/source/src/world.cpp b/source/src/world.cpp index 7ec6d33a..92ca8276 100644 --- a/source/src/world.cpp +++ b/source/src/world.cpp @@ -130,7 +130,9 @@ void remip(block &b, int level) { if(o[0]->vdelta-o[1]->vdelta != o[1]->vdelta-SWS(w,x+2,y,ws)->vdelta || o[0]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+2,y+2,ws)->vdelta - || o[0]->vdelta-o[3]->vdelta != o[3]->vdelta-SWS(w,x,y+2,ws)->vdelta) goto c; + || o[0]->vdelta-o[3]->vdelta != o[3]->vdelta-SWS(w,x,y+2,ws)->vdelta + || o[3]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+2,y+1,ws)->vdelta + || o[1]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+1,y+2,ws)->vdelta) goto c; }; }; { loopi(4) if(o[i]->defer) goto c; }; // if any of the constituents is not perfect, then this one isn't either @@ -145,6 +147,16 @@ void remip(block &b, int level) remip(s, level+1); }; +void remipmore(block &b, int level) +{ + block bb = b; + if(bb.x>1) bb.x--; + if(bb.y>1) bb.y--; + if(bb.xsyaw; break; - }; - printf("attrs: %i %i %i %i\n", e.type, e.attr1, e.attr2, e.attr3, e.attr4); + }; addmsg(1, 10, SV_EDITENT, ents.length(), type, e.x, e.y, e.z, e.attr1, e.attr2, e.attr3, e.attr4); ents.add(*((entity *)&e)); // unsafe! if(type==LIGHT) calclight(); return &ents.last(); }; - -void mdlcheck() -{ - loopv(ents) - { - entity &e = ents[i]; - printf("%i %i %i %i %i\n", e.type, e.attr1, e.attr2, e.attr3, e.attr4); - } -}; COMMAND(mdlcheck, ARG_NONE); + +void mdlcheck() +{ + loopv(ents) + { + entity &e = ents[i]; + printf("%i %i %i %i %i\n", e.type, e.attr1, e.attr2, e.attr3, e.attr4); + } +}; COMMAND(mdlcheck, ARG_NONE); void clearents(char *name) { @@ -281,6 +294,14 @@ int findentity(int type, int index) return -1; }; +int findteamplayerstart(int team, int index) +{ + for(int i = index; i