Run clang-format on the entire codebase
FossilOrigin-Name: 0a0d4f91558171cf85e108964a5fed9e1c390a8343e9d2eda086c9c20fde7d1a
This commit is contained in:
parent
b8e3744913
commit
f84e7f7c72
37 changed files with 9648 additions and 7254 deletions
|
@ -1,6 +1,7 @@
|
|||
*.a
|
||||
*.dep
|
||||
*.o
|
||||
*/.deps
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
buildsys.mk
|
||||
|
|
6
src/.clang-format
Normal file
6
src/.clang-format
Normal file
|
@ -0,0 +1,6 @@
|
|||
IndentWidth: 8
|
||||
TabWidth: 8
|
||||
UseTab: ForIndentation
|
||||
BreakBeforeBraces: Linux
|
||||
AlwaysBreakAfterReturnType: AllDefinitions
|
||||
AlignAfterOpenBracket: DontAlign
|
283
src/client.cxx
283
src/client.cxx
|
@ -9,23 +9,33 @@ 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()
|
||||
int
|
||||
getclientnum()
|
||||
{
|
||||
// check not correct on listen server?
|
||||
if(clienthost) conoutf("operation not available in multiplayer");
|
||||
return clienthost!=NULL;
|
||||
return clientnum;
|
||||
};
|
||||
|
||||
bool allowedittoggle()
|
||||
bool
|
||||
multiplayer()
|
||||
{
|
||||
bool allow = !clienthost || gamemode==1;
|
||||
if(!allow) conoutf("editing in multiplayer requires coopedit mode (1)");
|
||||
// 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));
|
||||
VARF(rate, 0, 0, 25000,
|
||||
if (clienthost && (!rate || rate > 1000))
|
||||
enet_host_bandwidth_limit(clienthost, rate, rate));
|
||||
|
||||
void throttle();
|
||||
|
||||
|
@ -33,72 +43,83 @@ VARF(throttle_interval, 0, 5, 30, throttle());
|
|||
VARF(throttle_accel, 0, 2, 32, throttle());
|
||||
VARF(throttle_decel, 0, 2, 32, throttle());
|
||||
|
||||
void 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);
|
||||
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); };
|
||||
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 writeclientinfo(FILE *f)
|
||||
void
|
||||
writeclientinfo(FILE *f)
|
||||
{
|
||||
fprintf(f, "name \"%s\"\nteam \"%s\"\n", player1->name, player1->team);
|
||||
};
|
||||
|
||||
void connects(char *servername)
|
||||
void
|
||||
connects(char *servername)
|
||||
{
|
||||
disconnect(1); // reset state
|
||||
addserver(servername);
|
||||
|
||||
conoutf("attempting to connect to %s", servername);
|
||||
ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT };
|
||||
if(enet_address_set_host(&address, servername) < 0)
|
||||
{
|
||||
ENetAddress address = {ENET_HOST_ANY, CUBE_SERVER_PORT};
|
||||
if (enet_address_set_host(&address, servername) < 0) {
|
||||
conoutf("could not resolve server %s", servername);
|
||||
return;
|
||||
};
|
||||
|
||||
clienthost = enet_host_create(NULL, 1, rate, rate);
|
||||
|
||||
if(clienthost)
|
||||
{
|
||||
if (clienthost) {
|
||||
enet_host_connect(clienthost, &address, 1);
|
||||
enet_host_flush(clienthost);
|
||||
connecting = lastmillis;
|
||||
connattempts = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
conoutf("could not connect to server");
|
||||
disconnect();
|
||||
};
|
||||
};
|
||||
|
||||
void disconnect(int onlyclean, int async)
|
||||
void
|
||||
disconnect(int onlyclean, int async)
|
||||
{
|
||||
if(clienthost)
|
||||
{
|
||||
if(!connecting && !disconnecting)
|
||||
{
|
||||
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;
|
||||
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");
|
||||
if (clienthost && !connecting)
|
||||
conoutf("disconnected");
|
||||
clienthost = NULL;
|
||||
connecting = 0;
|
||||
connattempts = 0;
|
||||
|
@ -110,18 +131,20 @@ void disconnect(int onlyclean, int async)
|
|||
|
||||
localdisconnect();
|
||||
|
||||
if(!onlyclean) { stop(); localconnect(); };
|
||||
if (!onlyclean) {
|
||||
stop();
|
||||
localconnect();
|
||||
};
|
||||
};
|
||||
|
||||
void trydisconnect()
|
||||
void
|
||||
trydisconnect()
|
||||
{
|
||||
if(!clienthost)
|
||||
{
|
||||
if (!clienthost) {
|
||||
conoutf("not connected");
|
||||
return;
|
||||
};
|
||||
if(connecting)
|
||||
{
|
||||
if (connecting) {
|
||||
conoutf("aborting connection attempt");
|
||||
disconnect();
|
||||
return;
|
||||
|
@ -131,8 +154,17 @@ void trydisconnect()
|
|||
};
|
||||
|
||||
string ctext;
|
||||
void toserver(char *text) { conoutf("%s:\f %s", player1->name, text); strn0cpy(ctext, text, 80); };
|
||||
void echo(char *text) { conoutf("%s", text); };
|
||||
void
|
||||
toserver(char *text)
|
||||
{
|
||||
conoutf("%s:\f %s", player1->name, text);
|
||||
strn0cpy(ctext, text, 80);
|
||||
};
|
||||
void
|
||||
echo(char *text)
|
||||
{
|
||||
conoutf("%s", text);
|
||||
};
|
||||
|
||||
COMMAND(echo, ARG_VARI);
|
||||
COMMANDN(say, toserver, ARG_VARI);
|
||||
|
@ -143,22 +175,32 @@ COMMANDN(disconnect, trydisconnect, ARG_NONE);
|
|||
|
||||
vector<ivector> messages;
|
||||
|
||||
void addmsg(int rel, int num, int type, ...)
|
||||
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; };
|
||||
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));
|
||||
loopi(num - 1) msg.add(va_arg(marker, int));
|
||||
va_end(marker);
|
||||
};
|
||||
|
||||
void server_err()
|
||||
void
|
||||
server_err()
|
||||
{
|
||||
conoutf("server network error, disconnecting...");
|
||||
disconnect();
|
||||
|
@ -166,15 +208,26 @@ void server_err()
|
|||
|
||||
int lastupdate = 0, lastping = 0;
|
||||
string toservermap;
|
||||
bool senditemstoserver = false; // after a map change, since server doesn't have map data
|
||||
bool senditemstoserver =
|
||||
false; // after a map change, since server doesn't have map data
|
||||
|
||||
string clientpassword;
|
||||
void password(char *p) { strcpy_s(clientpassword, p); };
|
||||
void
|
||||
password(char *p)
|
||||
{
|
||||
strcpy_s(clientpassword, p);
|
||||
};
|
||||
COMMAND(password, ARG_1STR);
|
||||
|
||||
bool netmapstart() { senditemstoserver = true; return clienthost!=NULL; };
|
||||
bool
|
||||
netmapstart()
|
||||
{
|
||||
senditemstoserver = true;
|
||||
return clienthost != NULL;
|
||||
};
|
||||
|
||||
void initclientnet()
|
||||
void
|
||||
initclientnet()
|
||||
{
|
||||
ctext[0] = 0;
|
||||
toservermap[0] = 0;
|
||||
|
@ -183,61 +236,72 @@ void initclientnet()
|
|||
newteam("red");
|
||||
};
|
||||
|
||||
void sendpackettoserv(void *packet)
|
||||
void
|
||||
sendpackettoserv(void *packet)
|
||||
{
|
||||
if(clienthost) { enet_host_broadcast(clienthost, 0, (ENetPacket *)packet); enet_host_flush(clienthost); }
|
||||
else localclienttoserver((ENetPacket *)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
|
||||
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);
|
||||
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;
|
||||
uchar *p = start + 2;
|
||||
bool serveriteminitdone = false;
|
||||
if(toservermap[0]) // suggest server to change map
|
||||
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
|
||||
{
|
||||
} 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));
|
||||
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) );
|
||||
putint(p, (d->strafe & 3) | ((d->move & 3) << 2) |
|
||||
(((int)d->onfloor) << 4) |
|
||||
((editmode ? CS_EDITING : d->state) << 5));
|
||||
|
||||
if(senditemstoserver)
|
||||
{
|
||||
if (senditemstoserver) {
|
||||
packet->flags = ENET_PACKET_FLAG_RELIABLE;
|
||||
putint(p, SV_ITEMLIST);
|
||||
if(!m_noitems) putitems(p);
|
||||
if (!m_noitems)
|
||||
putitems(p);
|
||||
putint(p, -1);
|
||||
senditemstoserver = false;
|
||||
serveriteminitdone = true;
|
||||
};
|
||||
if(ctext[0]) // player chat, not flood protected for now
|
||||
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
|
||||
if (!c2sinit) // tell other clients who I am
|
||||
{
|
||||
packet->flags = ENET_PACKET_FLAG_RELIABLE;
|
||||
c2sinit = true;
|
||||
|
@ -246,48 +310,53 @@ void c2sinfo(dynent *d) // send update to the server
|
|||
sendstring(player1->team, p);
|
||||
putint(p, player1->lifesequence);
|
||||
};
|
||||
loopv(messages) // send messages collected during the previous frames
|
||||
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]);
|
||||
if (msg[1])
|
||||
packet->flags = ENET_PACKET_FLAG_RELIABLE;
|
||||
loopi(msg[0]) putint(p, msg[i + 2]);
|
||||
};
|
||||
messages.setsize(0);
|
||||
if(lastmillis-lastping>250)
|
||||
{
|
||||
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);
|
||||
*(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
|
||||
if (serveriteminitdone)
|
||||
loadgamerest(); // hack
|
||||
};
|
||||
|
||||
void gets2c() // get updates from the server
|
||||
void
|
||||
gets2c() // get updates from the server
|
||||
{
|
||||
ENetEvent event;
|
||||
if(!clienthost) return;
|
||||
if(connecting && lastmillis/3000 > connecting/3000)
|
||||
{
|
||||
if (!clienthost)
|
||||
return;
|
||||
if (connecting && lastmillis / 3000 > connecting / 3000) {
|
||||
conoutf("attempting to connect...");
|
||||
connecting = lastmillis;
|
||||
++connattempts;
|
||||
if(connattempts > 3)
|
||||
{
|
||||
if (connattempts > 3) {
|
||||
conoutf("could not connect to server");
|
||||
disconnect();
|
||||
return;
|
||||
};
|
||||
};
|
||||
while(clienthost!=NULL && enet_host_service(clienthost, &event, 0)>0)
|
||||
switch(event.type)
|
||||
{
|
||||
while (
|
||||
clienthost != NULL && enet_host_service(clienthost, &event, 0) > 0)
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
conoutf("connected to server");
|
||||
connecting = 0;
|
||||
|
@ -295,15 +364,19 @@ void gets2c() // get updates from the server
|
|||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
if(disconnecting) conoutf("attempting to disconnect...");
|
||||
else localservertoclient(event.packet->data, event.packet->dataLength);
|
||||
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();
|
||||
if (disconnecting)
|
||||
disconnect();
|
||||
else
|
||||
server_err();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -3,69 +3,111 @@
|
|||
#include "cube.h"
|
||||
|
||||
// render players & monsters
|
||||
// very messy ad-hoc handling of animation frames, should be made more configurable
|
||||
// very messy ad-hoc handling of animation frames, should be made more
|
||||
// configurable
|
||||
|
||||
// D D D D' D D D D' A A' P P' I I' R, R' E L J J'
|
||||
int frame[] = { 178, 184, 190, 137, 183, 189, 197, 164, 46, 51, 54, 32, 0, 0, 40, 1, 162, 162, 67, 168 };
|
||||
int range[] = { 6, 6, 8, 28, 1, 1, 1, 1, 8, 19, 4, 18, 40, 1, 6, 15, 1, 1, 1, 1 };
|
||||
// D D D D' D D D D' A A' P P' I I'
|
||||
// R, R' E L J J'
|
||||
int frame[] = {178, 184, 190, 137, 183, 189, 197, 164, 46, 51, 54, 32, 0, 0, 40,
|
||||
1, 162, 162, 67, 168};
|
||||
int range[] = {6, 6, 8, 28, 1, 1, 1, 1, 8, 19, 4, 18, 40, 1, 6, 15, 1, 1, 1, 1};
|
||||
|
||||
void renderclient(dynent *d, bool team, char *mdlname, bool hellpig, float scale)
|
||||
void
|
||||
renderclient(dynent *d, bool team, char *mdlname, bool hellpig, float scale)
|
||||
{
|
||||
int n = 3;
|
||||
float speed = 100.0f;
|
||||
float mz = d->o.z-d->eyeheight+1.55f*scale;
|
||||
int basetime = -((int)d&0xFFF);
|
||||
if(d->state==CS_DEAD)
|
||||
{
|
||||
float mz = d->o.z - d->eyeheight + 1.55f * scale;
|
||||
int basetime = -((int)d & 0xFFF);
|
||||
if (d->state == CS_DEAD) {
|
||||
int r;
|
||||
if(hellpig) { n = 2; r = range[3]; } else { n = (int)d%3; r = range[n]; };
|
||||
if (hellpig) {
|
||||
n = 2;
|
||||
r = range[3];
|
||||
} else {
|
||||
n = (int)d % 3;
|
||||
r = range[n];
|
||||
};
|
||||
basetime = d->lastaction;
|
||||
int t = lastmillis-d->lastaction;
|
||||
if(t<0 || t>20000) return;
|
||||
if(t>(r-1)*100) { n += 4; if(t>(r+10)*100) { t -= (r+10)*100; mz -= t*t/10000000000.0f*t; }; };
|
||||
if(mz<-1000) return;
|
||||
//mdl = (((int)d>>6)&1)+1;
|
||||
//mz = d->o.z-d->eyeheight+0.2f;
|
||||
//scale = 1.2f;
|
||||
}
|
||||
else if(d->state==CS_EDITING) { n = 16; }
|
||||
else if(d->state==CS_LAGGED) { n = 17; }
|
||||
else if(d->monsterstate==M_ATTACKING) { n = 8; }
|
||||
else if(d->monsterstate==M_PAIN) { n = 10; }
|
||||
else if((!d->move && !d->strafe) || !d->moving) { n = 12; }
|
||||
else if(!d->onfloor && d->timeinair>100) { n = 18; }
|
||||
else { n = 14; speed = 1200/d->maxspeed*scale; if(hellpig) speed = 300/d->maxspeed; };
|
||||
if(hellpig) { n++; scale *= 32; mz -= 1.9f; };
|
||||
rendermodel(mdlname, frame[n], range[n], 0, 1.5f, d->o.x, mz, d->o.y, d->yaw+90, d->pitch/2, team, scale, speed, 0, basetime);
|
||||
int t = lastmillis - d->lastaction;
|
||||
if (t < 0 || t > 20000)
|
||||
return;
|
||||
if (t > (r - 1) * 100) {
|
||||
n += 4;
|
||||
if (t > (r + 10) * 100) {
|
||||
t -= (r + 10) * 100;
|
||||
mz -= t * t / 10000000000.0f * t;
|
||||
};
|
||||
};
|
||||
if (mz < -1000)
|
||||
return;
|
||||
// mdl = (((int)d>>6)&1)+1;
|
||||
// mz = d->o.z-d->eyeheight+0.2f;
|
||||
// scale = 1.2f;
|
||||
} else if (d->state == CS_EDITING) {
|
||||
n = 16;
|
||||
} else if (d->state == CS_LAGGED) {
|
||||
n = 17;
|
||||
} else if (d->monsterstate == M_ATTACKING) {
|
||||
n = 8;
|
||||
} else if (d->monsterstate == M_PAIN) {
|
||||
n = 10;
|
||||
} else if ((!d->move && !d->strafe) || !d->moving) {
|
||||
n = 12;
|
||||
} else if (!d->onfloor && d->timeinair > 100) {
|
||||
n = 18;
|
||||
} else {
|
||||
n = 14;
|
||||
speed = 1200 / d->maxspeed * scale;
|
||||
if (hellpig)
|
||||
speed = 300 / d->maxspeed;
|
||||
};
|
||||
if (hellpig) {
|
||||
n++;
|
||||
scale *= 32;
|
||||
mz -= 1.9f;
|
||||
};
|
||||
rendermodel(mdlname, frame[n], range[n], 0, 1.5f, d->o.x, mz, d->o.y,
|
||||
d->yaw + 90, d->pitch / 2, team, scale, speed, 0, basetime);
|
||||
};
|
||||
|
||||
extern int democlientnum;
|
||||
|
||||
void renderclients()
|
||||
void
|
||||
renderclients()
|
||||
{
|
||||
dynent *d;
|
||||
loopv(players) if((d = players[i]) && (!demoplayback || i!=democlientnum)) renderclient(d, isteam(player1->team, d->team), "monster/ogro", false, 1.0f);
|
||||
loopv(players) if ((d = players[i]) &&
|
||||
(!demoplayback || i != democlientnum))
|
||||
renderclient(
|
||||
d, isteam(player1->team, d->team), "monster/ogro", false, 1.0f);
|
||||
};
|
||||
|
||||
// creation of scoreboard pseudo-menu
|
||||
|
||||
bool scoreson = false;
|
||||
|
||||
void showscores(bool on)
|
||||
void
|
||||
showscores(bool on)
|
||||
{
|
||||
scoreson = on;
|
||||
menuset(((int)on)-1);
|
||||
menuset(((int)on) - 1);
|
||||
};
|
||||
|
||||
struct sline { string s; };
|
||||
struct sline {
|
||||
string s;
|
||||
};
|
||||
vector<sline> scorelines;
|
||||
|
||||
void renderscore(dynent *d)
|
||||
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, d->team, d->state==CS_DEAD ? name : d->name);
|
||||
menumanual(0, scorelines.length()-1, scorelines.last().s);
|
||||
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, d->team,
|
||||
d->state == CS_DEAD ? name : d->name);
|
||||
menumanual(0, scorelines.length() - 1, scorelines.last().s);
|
||||
};
|
||||
|
||||
const int maxteams = 4;
|
||||
|
@ -74,27 +116,37 @@ int teamscore[maxteams], teamsused;
|
|||
string teamscores;
|
||||
int timeremain = 0;
|
||||
|
||||
void addteamscore(dynent *d)
|
||||
void
|
||||
addteamscore(dynent *d)
|
||||
{
|
||||
if(!d) return;
|
||||
loopi(teamsused) if(strcmp(teamname[i], d->team)==0) { teamscore[i] += d->frags; return; };
|
||||
if(teamsused==maxteams) return;
|
||||
if (!d)
|
||||
return;
|
||||
loopi(teamsused) if (strcmp(teamname[i], d->team) == 0)
|
||||
{
|
||||
teamscore[i] += d->frags;
|
||||
return;
|
||||
};
|
||||
if (teamsused == maxteams)
|
||||
return;
|
||||
teamname[teamsused] = d->team;
|
||||
teamscore[teamsused++] = d->frags;
|
||||
};
|
||||
|
||||
void renderscores()
|
||||
void
|
||||
renderscores()
|
||||
{
|
||||
if(!scoreson) return;
|
||||
if (!scoreson)
|
||||
return;
|
||||
scorelines.setsize(0);
|
||||
if(!demoplayback) renderscore(player1);
|
||||
loopv(players) if(players[i]) renderscore(players[i]);
|
||||
if (!demoplayback)
|
||||
renderscore(player1);
|
||||
loopv(players) if (players[i]) renderscore(players[i]);
|
||||
sortmenu(0, scorelines.length());
|
||||
if(m_teammode)
|
||||
{
|
||||
if (m_teammode) {
|
||||
teamsused = 0;
|
||||
loopv(players) addteamscore(players[i]);
|
||||
if(!demoplayback) addteamscore(player1);
|
||||
if (!demoplayback)
|
||||
addteamscore(player1);
|
||||
teamscores[0] = 0;
|
||||
loopj(teamsused)
|
||||
{
|
||||
|
@ -102,28 +154,31 @@ void renderscores()
|
|||
strcat_s(teamscores, sc);
|
||||
};
|
||||
menumanual(0, scorelines.length(), "");
|
||||
menumanual(0, scorelines.length()+1, teamscores);
|
||||
menumanual(0, scorelines.length() + 1, teamscores);
|
||||
};
|
||||
};
|
||||
|
||||
// sendmap/getmap commands, should be replaced by more intuitive map downloading
|
||||
|
||||
void sendmap(char *mapname)
|
||||
void
|
||||
sendmap(char *mapname)
|
||||
{
|
||||
if(*mapname) save_world(mapname);
|
||||
if (*mapname)
|
||||
save_world(mapname);
|
||||
changemap(mapname);
|
||||
mapname = getclientmap();
|
||||
int mapsize;
|
||||
uchar *mapdata = readmap(mapname, &mapsize);
|
||||
if(!mapdata) return;
|
||||
ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + mapsize, ENET_PACKET_FLAG_RELIABLE);
|
||||
if (!mapdata)
|
||||
return;
|
||||
ENetPacket *packet = enet_packet_create(
|
||||
NULL, MAXTRANS + mapsize, ENET_PACKET_FLAG_RELIABLE);
|
||||
uchar *start = packet->data;
|
||||
uchar *p = start+2;
|
||||
uchar *p = start + 2;
|
||||
putint(p, SV_SENDMAP);
|
||||
sendstring(mapname, p);
|
||||
putint(p, mapsize);
|
||||
if(65535 - (p - start) < mapsize)
|
||||
{
|
||||
if (65535 - (p - start) < mapsize) {
|
||||
conoutf("map %s is too large to send", mapname);
|
||||
free(mapdata);
|
||||
enet_packet_destroy(packet);
|
||||
|
@ -132,26 +187,28 @@ void sendmap(char *mapname)
|
|||
memcpy(p, mapdata, mapsize);
|
||||
p += mapsize;
|
||||
free(mapdata);
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p-start);
|
||||
enet_packet_resize(packet, p-start);
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
|
||||
enet_packet_resize(packet, p - start);
|
||||
sendpackettoserv(packet);
|
||||
conoutf("sending map %s to server...", mapname);
|
||||
sprintf_sd(msg)("[map %s uploaded to server, \"getmap\" to receive it]", mapname);
|
||||
sprintf_sd(msg)(
|
||||
"[map %s uploaded to server, \"getmap\" to receive it]", mapname);
|
||||
toserver(msg);
|
||||
}
|
||||
|
||||
void getmap()
|
||||
void
|
||||
getmap()
|
||||
{
|
||||
ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
|
||||
ENetPacket *packet =
|
||||
enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
|
||||
uchar *start = packet->data;
|
||||
uchar *p = start+2;
|
||||
uchar *p = start + 2;
|
||||
putint(p, SV_RECVMAP);
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p-start);
|
||||
enet_packet_resize(packet, p-start);
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
|
||||
enet_packet_resize(packet, p - start);
|
||||
sendpackettoserv(packet);
|
||||
conoutf("requesting map from server...");
|
||||
}
|
||||
|
||||
COMMAND(sendmap, ARG_1STR);
|
||||
COMMAND(getmap, ARG_NONE);
|
||||
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
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); };
|
||||
void
|
||||
mode(int n)
|
||||
{
|
||||
addmsg(1, 2, SV_GAMEMODE, nextmode = n);
|
||||
};
|
||||
COMMAND(mode, ARG_1INT);
|
||||
|
||||
bool intermission = false;
|
||||
|
@ -23,9 +27,14 @@ string clientmap;
|
|||
|
||||
extern int framesinmap;
|
||||
|
||||
char *getclientmap() { return clientmap; };
|
||||
char *
|
||||
getclientmap()
|
||||
{
|
||||
return clientmap;
|
||||
};
|
||||
|
||||
void resetmovement(dynent *d)
|
||||
void
|
||||
resetmovement(dynent *d)
|
||||
{
|
||||
d->k_left = false;
|
||||
d->k_right = false;
|
||||
|
@ -36,7 +45,8 @@ void resetmovement(dynent *d)
|
|||
d->move = 0;
|
||||
};
|
||||
|
||||
void spawnstate(dynent *d) // reset player state not persistent accross spawns
|
||||
void
|
||||
spawnstate(dynent *d) // reset player state not persistent accross spawns
|
||||
{
|
||||
resetmovement(d);
|
||||
d->vel.x = d->vel.y = d->vel.z = 0;
|
||||
|
@ -52,48 +62,45 @@ void spawnstate(dynent *d) // reset player state not persistent acc
|
|||
d->lastaction = 0;
|
||||
loopi(NUMGUNS) d->ammo[i] = 0;
|
||||
d->ammo[GUN_FIST] = 1;
|
||||
if(m_noitems)
|
||||
{
|
||||
if (m_noitems) {
|
||||
d->gunselect = GUN_RIFLE;
|
||||
d->armour = 0;
|
||||
if(m_noitemsrail)
|
||||
{
|
||||
if (m_noitemsrail) {
|
||||
d->health = 1;
|
||||
d->ammo[GUN_RIFLE] = 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(gamemode==12) { d->gunselect = GUN_FIST; return; }; // eihrul's secret "instafist" mode
|
||||
} else {
|
||||
if (gamemode == 12) {
|
||||
d->gunselect = GUN_FIST;
|
||||
return;
|
||||
}; // eihrul's secret "instafist" mode
|
||||
d->health = 256;
|
||||
if(m_tarena)
|
||||
{
|
||||
int gun1 = rnd(4)+1;
|
||||
if (m_tarena) {
|
||||
int gun1 = rnd(4) + 1;
|
||||
baseammo(d->gunselect = gun1);
|
||||
for(;;)
|
||||
{
|
||||
int gun2 = rnd(4)+1;
|
||||
if(gun1!=gun2) { baseammo(gun2); break; };
|
||||
for (;;) {
|
||||
int gun2 = rnd(4) + 1;
|
||||
if (gun1 != gun2) {
|
||||
baseammo(gun2);
|
||||
break;
|
||||
};
|
||||
}
|
||||
else if(m_arena) // insta arena
|
||||
};
|
||||
} else if (m_arena) // insta arena
|
||||
{
|
||||
d->ammo[GUN_RIFLE] = 100;
|
||||
}
|
||||
else // efficiency
|
||||
} else // efficiency
|
||||
{
|
||||
loopi(4) baseammo(i+1);
|
||||
loopi(4) baseammo(i + 1);
|
||||
d->gunselect = GUN_CG;
|
||||
};
|
||||
d->ammo[GUN_CG] /= 2;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
d->ammo[GUN_SG] = 5;
|
||||
};
|
||||
};
|
||||
|
||||
dynent *newdynent() // create a new blank player or monster
|
||||
dynent *
|
||||
newdynent() // create a new blank player or monster
|
||||
{
|
||||
dynent *d = (dynent *)gp()->alloc(sizeof(dynent));
|
||||
d->o.x = 0;
|
||||
|
@ -122,22 +129,22 @@ dynent *newdynent() // create a new blank player or monster
|
|||
return d;
|
||||
};
|
||||
|
||||
void respawnself()
|
||||
void
|
||||
respawnself()
|
||||
{
|
||||
spawnplayer(player1);
|
||||
showscores(false);
|
||||
};
|
||||
|
||||
void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam)
|
||||
void
|
||||
arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam)
|
||||
{
|
||||
if(d->state!=CS_DEAD)
|
||||
{
|
||||
if (d->state != CS_DEAD) {
|
||||
alive++;
|
||||
if(lastteam && strcmp(lastteam, d->team)) oneteam = false;
|
||||
if (lastteam && strcmp(lastteam, d->team))
|
||||
oneteam = false;
|
||||
lastteam = d->team;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
dead++;
|
||||
};
|
||||
};
|
||||
|
@ -145,150 +152,175 @@ void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam
|
|||
int arenarespawnwait = 0;
|
||||
int arenadetectwait = 0;
|
||||
|
||||
void arenarespawn()
|
||||
void
|
||||
arenarespawn()
|
||||
{
|
||||
if(arenarespawnwait)
|
||||
{
|
||||
if(arenarespawnwait<lastmillis)
|
||||
{
|
||||
if (arenarespawnwait) {
|
||||
if (arenarespawnwait < lastmillis) {
|
||||
arenarespawnwait = 0;
|
||||
conoutf("new round starting... fight!");
|
||||
respawnself();
|
||||
};
|
||||
}
|
||||
else if(arenadetectwait==0 || arenadetectwait<lastmillis)
|
||||
{
|
||||
} else if (arenadetectwait == 0 || arenadetectwait < lastmillis) {
|
||||
arenadetectwait = 0;
|
||||
int alive = 0, dead = 0;
|
||||
char *lastteam = NULL;
|
||||
bool oneteam = true;
|
||||
loopv(players) if(players[i]) arenacount(players[i], alive, dead, lastteam, oneteam);
|
||||
loopv(players) if (players[i])
|
||||
arenacount(players[i], alive, dead, lastteam, oneteam);
|
||||
arenacount(player1, alive, dead, lastteam, oneteam);
|
||||
if(dead>0 && (alive<=1 || (m_teammode && oneteam)))
|
||||
{
|
||||
conoutf("arena round is over! next round in 5 seconds...");
|
||||
if(alive) conoutf("team %s is last man standing", lastteam);
|
||||
else conoutf("everyone died!");
|
||||
arenarespawnwait = lastmillis+5000;
|
||||
arenadetectwait = lastmillis+10000;
|
||||
if (dead > 0 && (alive <= 1 || (m_teammode && oneteam))) {
|
||||
conoutf(
|
||||
"arena round is over! next round in 5 seconds...");
|
||||
if (alive)
|
||||
conoutf(
|
||||
"team %s is last man standing", lastteam);
|
||||
else
|
||||
conoutf("everyone died!");
|
||||
arenarespawnwait = lastmillis + 5000;
|
||||
arenadetectwait = lastmillis + 10000;
|
||||
player1->roll = 0;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
void zapdynent(dynent *&d)
|
||||
void
|
||||
zapdynent(dynent *&d)
|
||||
{
|
||||
if(d) gp()->dealloc(d, sizeof(dynent));
|
||||
if (d)
|
||||
gp()->dealloc(d, sizeof(dynent));
|
||||
d = NULL;
|
||||
};
|
||||
|
||||
extern int democlientnum;
|
||||
|
||||
void otherplayers()
|
||||
void
|
||||
otherplayers()
|
||||
{
|
||||
loopv(players) if(players[i])
|
||||
{
|
||||
const int lagtime = lastmillis-players[i]->lastupdate;
|
||||
if(lagtime>1000 && players[i]->state==CS_ALIVE)
|
||||
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
|
||||
if (lagtime && players[i]->state != CS_DEAD &&
|
||||
(!demoplayback || i != democlientnum))
|
||||
moveplayer(
|
||||
players[i], 2, false); // use physics to extrapolate
|
||||
// player position
|
||||
};
|
||||
};
|
||||
|
||||
void respawn()
|
||||
void
|
||||
respawn()
|
||||
{
|
||||
if(player1->state==CS_DEAD)
|
||||
{
|
||||
if (player1->state == CS_DEAD) {
|
||||
player1->attacking = false;
|
||||
if(m_arena) { conoutf("waiting for new round to start..."); return; };
|
||||
if(m_sp) { nextmode = gamemode; changemap(clientmap); return; }; // if we die in SP we try the same map again
|
||||
if (m_arena) {
|
||||
conoutf("waiting for new round to start...");
|
||||
return;
|
||||
};
|
||||
if (m_sp) {
|
||||
nextmode = gamemode;
|
||||
changemap(clientmap);
|
||||
return;
|
||||
}; // if we die in SP we try the same map again
|
||||
respawnself();
|
||||
};
|
||||
};
|
||||
|
||||
int sleepwait = 0;
|
||||
string sleepcmd;
|
||||
void sleepf(char *msec, char *cmd) { sleepwait = atoi(msec)+lastmillis; strcpy_s(sleepcmd, cmd); };
|
||||
void
|
||||
sleepf(char *msec, char *cmd)
|
||||
{
|
||||
sleepwait = atoi(msec) + lastmillis;
|
||||
strcpy_s(sleepcmd, cmd);
|
||||
};
|
||||
COMMANDN(sleep, sleepf, ARG_2STR);
|
||||
|
||||
void updateworld(int millis) // main game update loop
|
||||
void
|
||||
updateworld(int millis) // main game update loop
|
||||
{
|
||||
if(lastmillis)
|
||||
{
|
||||
if (lastmillis) {
|
||||
curtime = millis - lastmillis;
|
||||
if(sleepwait && lastmillis>sleepwait) { sleepwait = 0; execute(sleepcmd); };
|
||||
if (sleepwait && lastmillis > sleepwait) {
|
||||
sleepwait = 0;
|
||||
execute(sleepcmd);
|
||||
};
|
||||
physicsframe();
|
||||
checkquad(curtime);
|
||||
if(m_arena) arenarespawn();
|
||||
if (m_arena)
|
||||
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
|
||||
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
|
||||
};
|
||||
otherplayers();
|
||||
if(!demoplayback)
|
||||
{
|
||||
if (!demoplayback) {
|
||||
monsterthink();
|
||||
if(player1->state==CS_DEAD)
|
||||
{
|
||||
if(lastmillis-player1->lastaction<2000)
|
||||
{
|
||||
if (player1->state == CS_DEAD) {
|
||||
if (lastmillis - player1->lastaction < 2000) {
|
||||
player1->move = player1->strafe = 0;
|
||||
moveplayer(player1, 10, false);
|
||||
}
|
||||
else if(!m_arena && !m_sp && lastmillis-player1->lastaction>10000) respawn();
|
||||
}
|
||||
else if(!intermission)
|
||||
{
|
||||
} else if (!m_arena && !m_sp &&
|
||||
lastmillis - player1->lastaction >
|
||||
10000)
|
||||
respawn();
|
||||
} else if (!intermission) {
|
||||
moveplayer(player1, 20, true);
|
||||
checkitems();
|
||||
};
|
||||
c2sinfo(player1); // do this last, to reduce the effective frame lag
|
||||
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
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
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!
|
||||
void
|
||||
spawnplayer(dynent *d) // place at random spawn. also used by monsters!
|
||||
{
|
||||
int r = fixspawn-->0 ? 4 : rnd(10)+1;
|
||||
loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1);
|
||||
if(spawncycle!=-1)
|
||||
{
|
||||
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;
|
||||
} else {
|
||||
d->o.x = d->o.y = (float)ssize / 2;
|
||||
d->o.z = 4;
|
||||
};
|
||||
entinmap(d);
|
||||
|
@ -298,21 +330,36 @@ void spawnplayer(dynent *d) // place at random spawn. also used by monsters!
|
|||
|
||||
// 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; };
|
||||
#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)
|
||||
void
|
||||
attack(bool on)
|
||||
{
|
||||
if(intermission) return;
|
||||
if(editmode) editdrag(on);
|
||||
else if(player1->attacking = on) respawn();
|
||||
if (intermission)
|
||||
return;
|
||||
if (editmode)
|
||||
editdrag(on);
|
||||
else if (player1->attacking = on)
|
||||
respawn();
|
||||
};
|
||||
|
||||
void jumpn(bool on) { if(!intermission && (player1->jumpnext = on)) respawn(); };
|
||||
void
|
||||
jumpn(bool on)
|
||||
{
|
||||
if (!intermission && (player1->jumpnext = on))
|
||||
respawn();
|
||||
};
|
||||
|
||||
COMMAND(backward, ARG_DOWN);
|
||||
COMMAND(forward, ARG_DOWN);
|
||||
|
@ -322,61 +369,74 @@ COMMANDN(jump, jumpn, ARG_DOWN);
|
|||
COMMAND(attack, ARG_DOWN);
|
||||
COMMAND(showscores, ARG_DOWN);
|
||||
|
||||
void fixplayer1range()
|
||||
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;
|
||||
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)
|
||||
void
|
||||
mousemove(int dx, int dy)
|
||||
{
|
||||
if(player1->state==CS_DEAD || intermission) return;
|
||||
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);
|
||||
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)
|
||||
void
|
||||
selfdamage(int damage, int actor, dynent *act)
|
||||
{
|
||||
if(player1->state!=CS_ALIVE || editmode || intermission) return;
|
||||
if (player1->state != CS_ALIVE || editmode || intermission)
|
||||
return;
|
||||
damageblend(damage);
|
||||
demoblend(damage);
|
||||
int ad = damage*(player1->armourtype+1)*20/100; // let armour absorb when possible
|
||||
if(ad>player1->armour) ad = player1->armour;
|
||||
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)
|
||||
{
|
||||
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!", act->name);
|
||||
}
|
||||
else if(actor==-1)
|
||||
{
|
||||
} else if (actor == -1) {
|
||||
actor = getclientnum();
|
||||
conoutf("you suicided!");
|
||||
addmsg(1, 2, SV_FRAGS, --player1->frags);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
dynent *a = getclient(actor);
|
||||
if(a)
|
||||
{
|
||||
if(isteam(a->team, player1->team))
|
||||
{
|
||||
conoutf("you got fragged by a teammate (%s)", a->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
conoutf("you got fragged by %s", a->name);
|
||||
if (a) {
|
||||
if (isteam(a->team, player1->team)) {
|
||||
conoutf("you got fragged by a teammate "
|
||||
"(%s)",
|
||||
a->name);
|
||||
} else {
|
||||
conoutf(
|
||||
"you got fragged by %s", a->name);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -387,62 +447,65 @@ void selfdamage(int damage, int actor, dynent *act)
|
|||
player1->state = CS_DEAD;
|
||||
player1->pitch = 0;
|
||||
player1->roll = 60;
|
||||
playsound(S_DIE1+rnd(2));
|
||||
playsound(S_DIE1 + rnd(2));
|
||||
spawnstate(player1);
|
||||
player1->lastaction = lastmillis;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
playsound(S_PAIN6);
|
||||
};
|
||||
};
|
||||
|
||||
void timeupdate(int timeremain)
|
||||
void
|
||||
timeupdate(int timeremain)
|
||||
{
|
||||
if(!timeremain)
|
||||
{
|
||||
if (!timeremain) {
|
||||
intermission = true;
|
||||
player1->attacking = false;
|
||||
conoutf("intermission:");
|
||||
conoutf("game has ended!");
|
||||
showscores(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
conoutf("time remaining: %d minutes", timeremain);
|
||||
};
|
||||
};
|
||||
|
||||
dynent *getclient(int cn) // ensure valid entity
|
||||
dynent *
|
||||
getclient(int cn) // ensure valid entity
|
||||
{
|
||||
if(cn<0 || cn>=MAXCLIENTS)
|
||||
{
|
||||
if (cn < 0 || cn >= MAXCLIENTS) {
|
||||
neterr("clientnum");
|
||||
return NULL;
|
||||
};
|
||||
while(cn>=players.length()) players.add(NULL);
|
||||
while (cn >= players.length())
|
||||
players.add(NULL);
|
||||
return players[cn] ? players[cn] : (players[cn] = newdynent());
|
||||
};
|
||||
|
||||
void initclient()
|
||||
void
|
||||
initclient()
|
||||
{
|
||||
clientmap[0] = 0;
|
||||
initclientnet();
|
||||
};
|
||||
|
||||
void startmap(char *name) // called just after a map load
|
||||
void
|
||||
startmap(char *name) // called just after a map load
|
||||
{
|
||||
if(netmapstart() && m_sp) { gamemode = 0; conoutf("coop sp not supported yet"); };
|
||||
if (netmapstart() && m_sp) {
|
||||
gamemode = 0;
|
||||
conoutf("coop sp not supported yet");
|
||||
};
|
||||
sleepwait = 0;
|
||||
monsterclear();
|
||||
projreset();
|
||||
spawncycle = -1;
|
||||
spawnplayer(player1);
|
||||
player1->frags = 0;
|
||||
loopv(players) if(players[i]) players[i]->frags = 0;
|
||||
loopv(players) if (players[i]) players[i]->frags = 0;
|
||||
resetspawns();
|
||||
strcpy_s(clientmap, name);
|
||||
if(editmode) toggleedit();
|
||||
if (editmode)
|
||||
toggleedit();
|
||||
setvar("gamespeed", 100);
|
||||
setvar("fog", 180);
|
||||
setvar("fogcolour", 0x8099B3);
|
||||
|
|
|
@ -7,19 +7,22 @@ extern bool c2sinit, senditemstoserver;
|
|||
extern string toservermap;
|
||||
extern string clientpassword;
|
||||
|
||||
void neterr(char *s)
|
||||
void
|
||||
neterr(char *s)
|
||||
{
|
||||
conoutf("illegal network message (%s)", s);
|
||||
disconnect();
|
||||
};
|
||||
|
||||
void changemapserv(char *name, int mode) // forced map change from the server
|
||||
void
|
||||
changemapserv(char *name, int mode) // forced map change from the server
|
||||
{
|
||||
gamemode = mode;
|
||||
load_world(name);
|
||||
};
|
||||
|
||||
void changemap(char *name) // request map change, server may ignore
|
||||
void
|
||||
changemap(char *name) // request map change, server may ignore
|
||||
{
|
||||
strcpy_s(toservermap, name);
|
||||
};
|
||||
|
@ -28,63 +31,71 @@ void changemap(char *name) // request map change, server ma
|
|||
// don't care if he's in the scenery or other players,
|
||||
// just don't overlap with our client
|
||||
|
||||
void updatepos(dynent *d)
|
||||
void
|
||||
updatepos(dynent *d)
|
||||
{
|
||||
const float r = player1->radius+d->radius;
|
||||
const float dx = player1->o.x-d->o.x;
|
||||
const float dy = player1->o.y-d->o.y;
|
||||
const float dz = player1->o.z-d->o.z;
|
||||
const float rz = player1->aboveeye+d->eyeheight;
|
||||
const float fx = (float)fabs(dx), fy = (float)fabs(dy), fz = (float)fabs(dz);
|
||||
if(fx<r && fy<r && fz<rz && d->state!=CS_DEAD)
|
||||
{
|
||||
if(fx<fy) d->o.y += dy<0 ? r-fy : -(r-fy); // push aside
|
||||
else d->o.x += dx<0 ? r-fx : -(r-fx);
|
||||
const float r = player1->radius + d->radius;
|
||||
const float dx = player1->o.x - d->o.x;
|
||||
const float dy = player1->o.y - d->o.y;
|
||||
const float dz = player1->o.z - d->o.z;
|
||||
const float rz = player1->aboveeye + d->eyeheight;
|
||||
const float fx = (float)fabs(dx), fy = (float)fabs(dy),
|
||||
fz = (float)fabs(dz);
|
||||
if (fx < r && fy < r && fz < rz && d->state != CS_DEAD) {
|
||||
if (fx < fy)
|
||||
d->o.y += dy < 0 ? r - fy : -(r - fy); // push aside
|
||||
else
|
||||
d->o.x += dx < 0 ? r - fx : -(r - fx);
|
||||
};
|
||||
int lagtime = lastmillis-d->lastupdate;
|
||||
if(lagtime)
|
||||
{
|
||||
d->plag = (d->plag*5+lagtime)/6;
|
||||
int lagtime = lastmillis - d->lastupdate;
|
||||
if (lagtime) {
|
||||
d->plag = (d->plag * 5 + lagtime) / 6;
|
||||
d->lastupdate = lastmillis;
|
||||
};
|
||||
};
|
||||
|
||||
void localservertoclient(uchar *buf, int len) // processes any updates from the server
|
||||
void
|
||||
localservertoclient(
|
||||
uchar *buf, int len) // processes any updates from the server
|
||||
{
|
||||
if(ENET_NET_TO_HOST_16(*(ushort *)buf)!=len) neterr("packet length");
|
||||
if (ENET_NET_TO_HOST_16(*(ushort *)buf) != len)
|
||||
neterr("packet length");
|
||||
incomingdemodata(buf, len);
|
||||
|
||||
uchar *end = buf+len;
|
||||
uchar *p = buf+2;
|
||||
uchar *end = buf + len;
|
||||
uchar *p = buf + 2;
|
||||
char text[MAXTRANS];
|
||||
int cn = -1, type;
|
||||
dynent *d = NULL;
|
||||
bool mapchanged = false;
|
||||
|
||||
while(p<end) switch(type = getint(p))
|
||||
{
|
||||
while (p < end)
|
||||
switch (type = getint(p)) {
|
||||
case SV_INITS2C: // welcome messsage from the server
|
||||
{
|
||||
cn = getint(p);
|
||||
int prot = getint(p);
|
||||
if(prot!=PROTOCOL_VERSION)
|
||||
{
|
||||
conoutf("you are using a different game protocol (you: %d, server: %d)", PROTOCOL_VERSION, prot);
|
||||
if (prot != PROTOCOL_VERSION) {
|
||||
conoutf("you are using a different game "
|
||||
"protocol (you: %d, server: %d)",
|
||||
PROTOCOL_VERSION, prot);
|
||||
disconnect();
|
||||
return;
|
||||
};
|
||||
toservermap[0] = 0;
|
||||
clientnum = cn; // we are now fully connected
|
||||
if(!getint(p)) strcpy_s(toservermap, getclientmap()); // we are the first client on this server, set map
|
||||
if (!getint(p))
|
||||
strcpy_s(toservermap,
|
||||
getclientmap()); // we are the first client
|
||||
// on this server, set map
|
||||
sgetstr();
|
||||
if(text[0] && strcmp(text, clientpassword))
|
||||
{
|
||||
conoutf("you need to set the correct password to join this server!");
|
||||
if (text[0] && strcmp(text, clientpassword)) {
|
||||
conoutf("you need to set the correct password "
|
||||
"to join this server!");
|
||||
disconnect();
|
||||
return;
|
||||
};
|
||||
if(getint(p)==1)
|
||||
{
|
||||
if (getint(p) == 1) {
|
||||
conoutf("server is FULL, disconnecting..");
|
||||
};
|
||||
break;
|
||||
|
@ -94,25 +105,28 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th
|
|||
{
|
||||
cn = getint(p);
|
||||
d = getclient(cn);
|
||||
if(!d) return;
|
||||
d->o.x = getint(p)/DMF;
|
||||
d->o.y = getint(p)/DMF;
|
||||
d->o.z = getint(p)/DMF;
|
||||
d->yaw = getint(p)/DAF;
|
||||
d->pitch = getint(p)/DAF;
|
||||
d->roll = getint(p)/DAF;
|
||||
d->vel.x = getint(p)/DVF;
|
||||
d->vel.y = getint(p)/DVF;
|
||||
d->vel.z = getint(p)/DVF;
|
||||
if (!d)
|
||||
return;
|
||||
d->o.x = getint(p) / DMF;
|
||||
d->o.y = getint(p) / DMF;
|
||||
d->o.z = getint(p) / DMF;
|
||||
d->yaw = getint(p) / DAF;
|
||||
d->pitch = getint(p) / DAF;
|
||||
d->roll = getint(p) / DAF;
|
||||
d->vel.x = getint(p) / DVF;
|
||||
d->vel.y = getint(p) / DVF;
|
||||
d->vel.z = getint(p) / DVF;
|
||||
int f = getint(p);
|
||||
d->strafe = (f&3)==3 ? -1 : f&3;
|
||||
d->strafe = (f & 3) == 3 ? -1 : f & 3;
|
||||
f >>= 2;
|
||||
d->move = (f&3)==3 ? -1 : f&3;
|
||||
d->onfloor = (f>>2)&1;
|
||||
int state = f>>3;
|
||||
if(state==CS_DEAD && d->state!=CS_DEAD) d->lastaction = lastmillis;
|
||||
d->move = (f & 3) == 3 ? -1 : f & 3;
|
||||
d->onfloor = (f >> 2) & 1;
|
||||
int state = f >> 3;
|
||||
if (state == CS_DEAD && d->state != CS_DEAD)
|
||||
d->lastaction = lastmillis;
|
||||
d->state = state;
|
||||
if(!demoplayback) updatepos(d);
|
||||
if (!demoplayback)
|
||||
updatepos(d);
|
||||
break;
|
||||
};
|
||||
|
||||
|
@ -131,11 +145,15 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th
|
|||
mapchanged = true;
|
||||
break;
|
||||
|
||||
case SV_ITEMLIST:
|
||||
{
|
||||
case SV_ITEMLIST: {
|
||||
int n;
|
||||
if(mapchanged) { senditemstoserver = false; resetspawns(); };
|
||||
while((n = getint(p))!=-1) if(mapchanged) setspawn(n, true);
|
||||
if (mapchanged) {
|
||||
senditemstoserver = false;
|
||||
resetspawns();
|
||||
};
|
||||
while ((n = getint(p)) != -1)
|
||||
if (mapchanged)
|
||||
setspawn(n, true);
|
||||
break;
|
||||
};
|
||||
|
||||
|
@ -143,22 +161,25 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th
|
|||
{
|
||||
getint(p);
|
||||
sprintf_sd(nextmapalias)("nextmap_%s", getclientmap());
|
||||
char *map = getalias(nextmapalias); // look up map in the cycle
|
||||
char *map =
|
||||
getalias(nextmapalias); // look up map in the cycle
|
||||
changemap(map ? map : getclientmap());
|
||||
break;
|
||||
};
|
||||
|
||||
case SV_INITC2S: // another client either connected or changed name/team
|
||||
case SV_INITC2S: // another client either connected or changed
|
||||
// name/team
|
||||
{
|
||||
sgetstr();
|
||||
if(d->name[0]) // already connected
|
||||
if (d->name[0]) // already connected
|
||||
{
|
||||
if(strcmp(d->name, text))
|
||||
conoutf("%s is now known as %s", d->name, text);
|
||||
}
|
||||
else // new client
|
||||
if (strcmp(d->name, text))
|
||||
conoutf("%s is now known as %s",
|
||||
d->name, text);
|
||||
} else // new client
|
||||
{
|
||||
c2sinit = false; // send new players my info again
|
||||
c2sinit =
|
||||
false; // send new players my info again
|
||||
conoutf("connected: %s", text);
|
||||
};
|
||||
strcpy_s(d->name, text);
|
||||
|
@ -170,74 +191,70 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th
|
|||
|
||||
case SV_CDIS:
|
||||
cn = getint(p);
|
||||
if(!(d = getclient(cn))) break;
|
||||
conoutf("player %s disconnected", d->name[0] ? d->name : "[incompatible client]");
|
||||
if (!(d = getclient(cn)))
|
||||
break;
|
||||
conoutf("player %s disconnected",
|
||||
d->name[0] ? d->name : "[incompatible client]");
|
||||
zapdynent(players[cn]);
|
||||
break;
|
||||
|
||||
case SV_SHOT:
|
||||
{
|
||||
case SV_SHOT: {
|
||||
int gun = getint(p);
|
||||
vec s, e;
|
||||
s.x = getint(p)/DMF;
|
||||
s.y = getint(p)/DMF;
|
||||
s.z = getint(p)/DMF;
|
||||
e.x = getint(p)/DMF;
|
||||
e.y = getint(p)/DMF;
|
||||
e.z = getint(p)/DMF;
|
||||
if(gun==GUN_SG) createrays(s, e);
|
||||
s.x = getint(p) / DMF;
|
||||
s.y = getint(p) / DMF;
|
||||
s.z = getint(p) / DMF;
|
||||
e.x = getint(p) / DMF;
|
||||
e.y = getint(p) / DMF;
|
||||
e.z = getint(p) / DMF;
|
||||
if (gun == GUN_SG)
|
||||
createrays(s, e);
|
||||
shootv(gun, s, e, d);
|
||||
break;
|
||||
};
|
||||
|
||||
case SV_DAMAGE:
|
||||
{
|
||||
case SV_DAMAGE: {
|
||||
int target = getint(p);
|
||||
int damage = getint(p);
|
||||
int ls = getint(p);
|
||||
if(target==clientnum) { if(ls==player1->lifesequence) selfdamage(damage, cn, d); }
|
||||
else playsound(S_PAIN1+rnd(5), &getclient(target)->o);
|
||||
if (target == clientnum) {
|
||||
if (ls == player1->lifesequence)
|
||||
selfdamage(damage, cn, d);
|
||||
} else
|
||||
playsound(
|
||||
S_PAIN1 + rnd(5), &getclient(target)->o);
|
||||
break;
|
||||
};
|
||||
|
||||
case SV_DIED:
|
||||
{
|
||||
case SV_DIED: {
|
||||
int actor = getint(p);
|
||||
if(actor==cn)
|
||||
{
|
||||
if (actor == cn) {
|
||||
conoutf("%s suicided", d->name);
|
||||
}
|
||||
else if(actor==clientnum)
|
||||
{
|
||||
} else if (actor == clientnum) {
|
||||
int frags;
|
||||
if(isteam(player1->team, d->team))
|
||||
{
|
||||
if (isteam(player1->team, d->team)) {
|
||||
frags = -1;
|
||||
conoutf("you fragged a teammate (%s)", d->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
conoutf("you fragged a teammate (%s)",
|
||||
d->name);
|
||||
} else {
|
||||
frags = 1;
|
||||
conoutf("you fragged %s", d->name);
|
||||
};
|
||||
addmsg(1, 2, SV_FRAGS, player1->frags += frags);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
dynent *a = getclient(actor);
|
||||
if(a)
|
||||
{
|
||||
if(isteam(a->team, d->name))
|
||||
{
|
||||
conoutf("%s fragged his teammate (%s)", a->name, d->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
conoutf("%s fragged %s", a->name, d->name);
|
||||
if (a) {
|
||||
if (isteam(a->team, d->name)) {
|
||||
conoutf("%s fragged his "
|
||||
"teammate (%s)",
|
||||
a->name, d->name);
|
||||
} else {
|
||||
conoutf("%s fragged %s",
|
||||
a->name, d->name);
|
||||
};
|
||||
};
|
||||
};
|
||||
playsound(S_DIE1+rnd(2), &d->o);
|
||||
playsound(S_DIE1 + rnd(2), &d->o);
|
||||
d->lifesequence++;
|
||||
break;
|
||||
};
|
||||
|
@ -251,39 +268,49 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th
|
|||
getint(p);
|
||||
break;
|
||||
|
||||
case SV_ITEMSPAWN:
|
||||
{
|
||||
case SV_ITEMSPAWN: {
|
||||
uint i = getint(p);
|
||||
setspawn(i, true);
|
||||
if(i>=(uint)ents.length()) break;
|
||||
vec v = { ents[i].x, ents[i].y, ents[i].z };
|
||||
if (i >= (uint)ents.length())
|
||||
break;
|
||||
vec v = {ents[i].x, ents[i].y, ents[i].z};
|
||||
playsound(S_ITEMSPAWN, &v);
|
||||
break;
|
||||
};
|
||||
|
||||
case SV_ITEMACC: // server acknowledges that I picked up this item
|
||||
case SV_ITEMACC: // server acknowledges that I picked up this
|
||||
// item
|
||||
realpickup(getint(p), player1);
|
||||
break;
|
||||
|
||||
case SV_EDITH: // coop editing messages, should be extended to include all possible editing ops
|
||||
case SV_EDITH: // coop editing messages, should be extended to
|
||||
// include all possible editing ops
|
||||
case SV_EDITT:
|
||||
case SV_EDITS:
|
||||
case SV_EDITD:
|
||||
case SV_EDITE:
|
||||
{
|
||||
case SV_EDITE: {
|
||||
int x = getint(p);
|
||||
int y = getint(p);
|
||||
int xs = getint(p);
|
||||
int ys = getint(p);
|
||||
int v = getint(p);
|
||||
block b = { x, y, xs, ys };
|
||||
switch(type)
|
||||
{
|
||||
case SV_EDITH: editheightxy(v!=0, getint(p), b); break;
|
||||
case SV_EDITT: edittexxy(v, getint(p), b); break;
|
||||
case SV_EDITS: edittypexy(v, b); break;
|
||||
case SV_EDITD: setvdeltaxy(v, b); break;
|
||||
case SV_EDITE: editequalisexy(v!=0, b); break;
|
||||
block b = {x, y, xs, ys};
|
||||
switch (type) {
|
||||
case SV_EDITH:
|
||||
editheightxy(v != 0, getint(p), b);
|
||||
break;
|
||||
case SV_EDITT:
|
||||
edittexxy(v, getint(p), b);
|
||||
break;
|
||||
case SV_EDITS:
|
||||
edittypexy(v, b);
|
||||
break;
|
||||
case SV_EDITD:
|
||||
setvdeltaxy(v, b);
|
||||
break;
|
||||
case SV_EDITE:
|
||||
editequalisexy(v != 0, b);
|
||||
break;
|
||||
};
|
||||
break;
|
||||
};
|
||||
|
@ -291,7 +318,8 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th
|
|||
case SV_EDITENT: // coop edit of ent
|
||||
{
|
||||
uint i = getint(p);
|
||||
while((uint)ents.length()<=i) ents.add().type = NOTUSED;
|
||||
while ((uint)ents.length() <= i)
|
||||
ents.add().type = NOTUSED;
|
||||
int to = ents[i].type;
|
||||
ents[i].type = getint(p);
|
||||
ents[i].x = getint(p);
|
||||
|
@ -302,7 +330,8 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th
|
|||
ents[i].attr3 = getint(p);
|
||||
ents[i].attr4 = getint(p);
|
||||
ents[i].spawned = false;
|
||||
if(ents[i].type==LIGHT || to==LIGHT) calclight();
|
||||
if (ents[i].type == LIGHT || to == LIGHT)
|
||||
calclight();
|
||||
break;
|
||||
};
|
||||
|
||||
|
@ -311,7 +340,10 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th
|
|||
break;
|
||||
|
||||
case SV_PONG:
|
||||
addmsg(0, 2, SV_CLIENTPING, player1->ping = (player1->ping*5+lastmillis-getint(p))/6);
|
||||
addmsg(0, 2, SV_CLIENTPING,
|
||||
player1->ping =
|
||||
(player1->ping * 5 + lastmillis - getint(p)) /
|
||||
6);
|
||||
break;
|
||||
|
||||
case SV_CLIENTPING:
|
||||
|
@ -326,10 +358,10 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th
|
|||
timeupdate(getint(p));
|
||||
break;
|
||||
|
||||
case SV_RECVMAP:
|
||||
{
|
||||
case SV_RECVMAP: {
|
||||
sgetstr();
|
||||
conoutf("received map \"%s\" from server, reloading..", text);
|
||||
conoutf("received map \"%s\" from server, reloading..",
|
||||
text);
|
||||
int mapsize = getint(p);
|
||||
writemap(text, mapsize, p);
|
||||
p += mapsize;
|
||||
|
@ -342,9 +374,11 @@ void localservertoclient(uchar *buf, int len) // processes any updates from th
|
|||
conoutf("%s", text);
|
||||
break;
|
||||
|
||||
case SV_EXT: // so we can messages without breaking previous clients/servers, if necessary
|
||||
case SV_EXT: // so we can messages without breaking previous
|
||||
// clients/servers, if necessary
|
||||
{
|
||||
for(int n = getint(p); n; n--) getint(p);
|
||||
for (int n = getint(p); n; n--)
|
||||
getint(p);
|
||||
break;
|
||||
};
|
||||
|
||||
|
|
608
src/command.cxx
608
src/command.cxx
|
@ -1,12 +1,11 @@
|
|||
// command.cpp: implements the parsing and execution of a tiny script language which
|
||||
// is largely backwards compatible with the quake console language.
|
||||
// command.cpp: implements the parsing and execution of a tiny script language
|
||||
// which is largely backwards compatible with the quake console language.
|
||||
|
||||
#include "cube.h"
|
||||
|
||||
enum { ID_VAR, ID_COMMAND, ID_ALIAS };
|
||||
|
||||
struct ident
|
||||
{
|
||||
struct ident {
|
||||
int type; // one of ID_* above
|
||||
char *name;
|
||||
int min, max; // ID_VAR
|
||||
|
@ -17,24 +16,35 @@ struct ident
|
|||
bool persist;
|
||||
};
|
||||
|
||||
void itoa(char *s, int i) { sprintf_s(s)("%d", i); };
|
||||
char *exchangestr(char *o, char *n) { gp()->deallocstr(o); return newstring(n); };
|
||||
void
|
||||
itoa(char *s, int i)
|
||||
{
|
||||
sprintf_s(s)("%d", i);
|
||||
};
|
||||
char *
|
||||
exchangestr(char *o, char *n)
|
||||
{
|
||||
gp()->deallocstr(o);
|
||||
return newstring(n);
|
||||
};
|
||||
|
||||
hashtable<ident> *idents = NULL; // contains ALL vars/commands/aliases
|
||||
|
||||
void alias(char *name, char *action)
|
||||
void
|
||||
alias(char *name, char *action)
|
||||
{
|
||||
ident *b = idents->access(name);
|
||||
if(!b)
|
||||
{
|
||||
if (!b) {
|
||||
name = newstring(name);
|
||||
ident b = { ID_ALIAS, name, 0, 0, 0, 0, 0, newstring(action), true };
|
||||
ident b = {
|
||||
ID_ALIAS, name, 0, 0, 0, 0, 0, newstring(action), true};
|
||||
idents->access(name, &b);
|
||||
}
|
||||
} else {
|
||||
if (b->type == ID_ALIAS)
|
||||
b->action = exchangestr(b->action, action);
|
||||
else
|
||||
{
|
||||
if(b->type==ID_ALIAS) b->action = exchangestr(b->action, action);
|
||||
else conoutf("cannot redefine builtin %s with an alias", name);
|
||||
conoutf(
|
||||
"cannot redefine builtin %s with an alias", name);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -42,185 +52,328 @@ COMMAND(alias, ARG_2STR);
|
|||
|
||||
// variable's and commands are registered through globals, see cube.h
|
||||
|
||||
int variable(char *name, int min, int cur, int max, int *storage, void (*fun)(), bool persist)
|
||||
int
|
||||
variable(char *name, int min, int cur, int max, int *storage, void (*fun)(),
|
||||
bool persist)
|
||||
{
|
||||
if(!idents) idents = new hashtable<ident>;
|
||||
ident v = { ID_VAR, name, min, max, storage, fun, 0, 0, persist };
|
||||
if (!idents)
|
||||
idents = new hashtable<ident>;
|
||||
ident v = {ID_VAR, name, min, max, storage, fun, 0, 0, persist};
|
||||
idents->access(name, &v);
|
||||
return cur;
|
||||
};
|
||||
|
||||
void setvar(char *name, int i) { *idents->access(name)->storage = i; };
|
||||
int getvar(char *name) { return *idents->access(name)->storage; };
|
||||
bool identexists(char *name) { return idents->access(name)!=NULL; };
|
||||
|
||||
char *getalias(char *name)
|
||||
void
|
||||
setvar(char *name, int i)
|
||||
{
|
||||
ident *i = idents->access(name);
|
||||
return i && i->type==ID_ALIAS ? i->action : NULL;
|
||||
*idents->access(name)->storage = i;
|
||||
};
|
||||
int
|
||||
getvar(char *name)
|
||||
{
|
||||
return *idents->access(name)->storage;
|
||||
};
|
||||
bool
|
||||
identexists(char *name)
|
||||
{
|
||||
return idents->access(name) != NULL;
|
||||
};
|
||||
|
||||
bool addcommand(char *name, void (*fun)(), int narg)
|
||||
char *
|
||||
getalias(char *name)
|
||||
{
|
||||
if(!idents) idents = new hashtable<ident>;
|
||||
ident c = { ID_COMMAND, name, 0, 0, 0, fun, narg, 0, false };
|
||||
ident *i = idents->access(name);
|
||||
return i && i->type == ID_ALIAS ? i->action : NULL;
|
||||
};
|
||||
|
||||
bool
|
||||
addcommand(char *name, void (*fun)(), int narg)
|
||||
{
|
||||
if (!idents)
|
||||
idents = new hashtable<ident>;
|
||||
ident c = {ID_COMMAND, name, 0, 0, 0, fun, narg, 0, false};
|
||||
idents->access(name, &c);
|
||||
return false;
|
||||
};
|
||||
|
||||
char *parseexp(char *&p, int right) // parse any nested set of () or []
|
||||
char *
|
||||
parseexp(char *&p, int right) // parse any nested set of () or []
|
||||
{
|
||||
int left = *p++;
|
||||
char *word = p;
|
||||
for(int brak = 1; brak; )
|
||||
{
|
||||
for (int brak = 1; brak;) {
|
||||
int c = *p++;
|
||||
if(c=='\r') *(p-1) = ' '; // hack
|
||||
if(c==left) brak++;
|
||||
else if(c==right) brak--;
|
||||
else if(!c) { p--; conoutf("missing \"%c\"", right); return NULL; };
|
||||
if (c == '\r')
|
||||
*(p - 1) = ' '; // hack
|
||||
if (c == left)
|
||||
brak++;
|
||||
else if (c == right)
|
||||
brak--;
|
||||
else if (!c) {
|
||||
p--;
|
||||
conoutf("missing \"%c\"", right);
|
||||
return NULL;
|
||||
};
|
||||
char *s = newstring(word, p-word-1);
|
||||
if(left=='(')
|
||||
{
|
||||
};
|
||||
char *s = newstring(word, p - word - 1);
|
||||
if (left == '(') {
|
||||
string t;
|
||||
itoa(t, execute(s)); // evaluate () exps directly, and substitute result
|
||||
itoa(t,
|
||||
execute(
|
||||
s)); // evaluate () exps directly, and substitute result
|
||||
s = exchangestr(s, t);
|
||||
};
|
||||
return s;
|
||||
};
|
||||
|
||||
char *parseword(char *&p) // parse single argument, including expressions
|
||||
char *
|
||||
parseword(char *&p) // parse single argument, including expressions
|
||||
{
|
||||
p += strspn(p, " \t\r");
|
||||
if(p[0]=='/' && p[1]=='/') p += strcspn(p, "\n\0");
|
||||
if(*p=='\"')
|
||||
{
|
||||
if (p[0] == '/' && p[1] == '/')
|
||||
p += strcspn(p, "\n\0");
|
||||
if (*p == '\"') {
|
||||
p++;
|
||||
char *word = p;
|
||||
p += strcspn(p, "\"\r\n\0");
|
||||
char *s = newstring(word, p-word);
|
||||
if(*p=='\"') p++;
|
||||
char *s = newstring(word, p - word);
|
||||
if (*p == '\"')
|
||||
p++;
|
||||
return s;
|
||||
};
|
||||
if(*p=='(') return parseexp(p, ')');
|
||||
if(*p=='[') return parseexp(p, ']');
|
||||
if (*p == '(')
|
||||
return parseexp(p, ')');
|
||||
if (*p == '[')
|
||||
return parseexp(p, ']');
|
||||
char *word = p;
|
||||
p += strcspn(p, "; \t\r\n\0");
|
||||
if(p-word==0) return NULL;
|
||||
return newstring(word, p-word);
|
||||
if (p - word == 0)
|
||||
return NULL;
|
||||
return newstring(word, p - word);
|
||||
};
|
||||
|
||||
char *lookup(char *n) // find value of ident referenced with $ in exp
|
||||
char *
|
||||
lookup(char *n) // find value of ident referenced with $ in exp
|
||||
{
|
||||
ident *id = idents->access(n+1);
|
||||
if(id) switch(id->type)
|
||||
{
|
||||
case ID_VAR: string t; itoa(t, *(id->storage)); return exchangestr(n, t);
|
||||
case ID_ALIAS: return exchangestr(n, id->action);
|
||||
ident *id = idents->access(n + 1);
|
||||
if (id)
|
||||
switch (id->type) {
|
||||
case ID_VAR:
|
||||
string t;
|
||||
itoa(t, *(id->storage));
|
||||
return exchangestr(n, t);
|
||||
case ID_ALIAS:
|
||||
return exchangestr(n, id->action);
|
||||
};
|
||||
conoutf("unknown alias lookup: %s", n+1);
|
||||
conoutf("unknown alias lookup: %s", n + 1);
|
||||
return n;
|
||||
};
|
||||
|
||||
int execute(char *p, bool isdown) // all evaluation happens here, recursively
|
||||
int
|
||||
execute(char *p, bool isdown) // all evaluation happens here, recursively
|
||||
{
|
||||
const int MAXWORDS = 25; // limit, remove
|
||||
char *w[MAXWORDS];
|
||||
int val = 0;
|
||||
for(bool cont = true; cont;) // for each ; seperated statement
|
||||
for (bool cont = true; cont;) // for each ; seperated statement
|
||||
{
|
||||
int numargs = MAXWORDS;
|
||||
loopi(MAXWORDS) // collect all argument values
|
||||
{
|
||||
w[i] = "";
|
||||
if(i>numargs) continue;
|
||||
if (i > numargs)
|
||||
continue;
|
||||
char *s = parseword(p); // parse and evaluate exps
|
||||
if(!s) { numargs = i; s = ""; };
|
||||
if(*s=='$') s = lookup(s); // substitute variables
|
||||
if (!s) {
|
||||
numargs = i;
|
||||
s = "";
|
||||
};
|
||||
if (*s == '$')
|
||||
s = lookup(s); // substitute variables
|
||||
w[i] = s;
|
||||
};
|
||||
|
||||
p += strcspn(p, ";\n\0");
|
||||
cont = *p++!=0; // more statements if this isn't the end of the string
|
||||
cont = *p++ !=
|
||||
0; // more statements if this isn't the end of the string
|
||||
char *c = w[0];
|
||||
if(*c=='/') c++; // strip irc-style command prefix
|
||||
if(!*c) continue; // empty statement
|
||||
if (*c == '/')
|
||||
c++; // strip irc-style command prefix
|
||||
if (!*c)
|
||||
continue; // empty statement
|
||||
|
||||
ident *id = idents->access(c);
|
||||
if(!id)
|
||||
{
|
||||
if (!id) {
|
||||
val = ATOI(c);
|
||||
if(!val && *c!='0') conoutf("unknown command: %s", c);
|
||||
}
|
||||
else switch(id->type)
|
||||
{
|
||||
if (!val && *c != '0')
|
||||
conoutf("unknown command: %s", c);
|
||||
} else
|
||||
switch (id->type) {
|
||||
case ID_COMMAND: // game defined commands
|
||||
switch(id->narg) // use very ad-hoc function signature, and just call it
|
||||
{
|
||||
case ARG_1INT: if(isdown) ((void (__cdecl *)(int))id->fun)(ATOI(w[1])); break;
|
||||
case ARG_2INT: if(isdown) ((void (__cdecl *)(int, int))id->fun)(ATOI(w[1]), ATOI(w[2])); break;
|
||||
case ARG_3INT: if(isdown) ((void (__cdecl *)(int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3])); break;
|
||||
case ARG_4INT: if(isdown) ((void (__cdecl *)(int, int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3]), ATOI(w[4])); break;
|
||||
case ARG_NONE: if(isdown) ((void (__cdecl *)())id->fun)(); break;
|
||||
case ARG_1STR: if(isdown) ((void (__cdecl *)(char *))id->fun)(w[1]); break;
|
||||
case ARG_2STR: if(isdown) ((void (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break;
|
||||
case ARG_3STR: if(isdown) ((void (__cdecl *)(char *, char *, char*))id->fun)(w[1], w[2], w[3]); break;
|
||||
case ARG_5STR: if(isdown) ((void (__cdecl *)(char *, char *, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5]); break;
|
||||
case ARG_DOWN: ((void (__cdecl *)(bool))id->fun)(isdown); break;
|
||||
case ARG_DWN1: ((void (__cdecl *)(bool, char *))id->fun)(isdown, w[1]); break;
|
||||
case ARG_1EXP: if(isdown) val = ((int (__cdecl *)(int))id->fun)(execute(w[1])); break;
|
||||
case ARG_2EXP: if(isdown) val = ((int (__cdecl *)(int, int))id->fun)(execute(w[1]), execute(w[2])); break;
|
||||
case ARG_1EST: if(isdown) val = ((int (__cdecl *)(char *))id->fun)(w[1]); break;
|
||||
case ARG_2EST: if(isdown) val = ((int (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break;
|
||||
case ARG_VARI: if(isdown)
|
||||
switch (id->narg) // use very ad-hoc function
|
||||
// signature, and just call it
|
||||
{
|
||||
case ARG_1INT:
|
||||
if (isdown)
|
||||
((void(__cdecl *)(int))id->fun)(
|
||||
ATOI(w[1]));
|
||||
break;
|
||||
case ARG_2INT:
|
||||
if (isdown)
|
||||
((void(__cdecl *)(
|
||||
int, int))id->fun)(
|
||||
ATOI(w[1]), ATOI(w[2]));
|
||||
break;
|
||||
case ARG_3INT:
|
||||
if (isdown)
|
||||
((void(__cdecl *)(int, int,
|
||||
int))id->fun)(ATOI(w[1]),
|
||||
ATOI(w[2]), ATOI(w[3]));
|
||||
break;
|
||||
case ARG_4INT:
|
||||
if (isdown)
|
||||
((void(__cdecl *)(int, int, int,
|
||||
int))id->fun)(ATOI(w[1]),
|
||||
ATOI(w[2]), ATOI(w[3]),
|
||||
ATOI(w[4]));
|
||||
break;
|
||||
case ARG_NONE:
|
||||
if (isdown)
|
||||
((void(__cdecl *)())id->fun)();
|
||||
break;
|
||||
case ARG_1STR:
|
||||
if (isdown)
|
||||
((void(__cdecl *)(
|
||||
char *))id->fun)(w[1]);
|
||||
break;
|
||||
case ARG_2STR:
|
||||
if (isdown)
|
||||
((void(__cdecl *)(
|
||||
char *, char *))id->fun)(
|
||||
w[1], w[2]);
|
||||
break;
|
||||
case ARG_3STR:
|
||||
if (isdown)
|
||||
((void(__cdecl *)(char *,
|
||||
char *, char *))id->fun)(
|
||||
w[1], w[2], w[3]);
|
||||
break;
|
||||
case ARG_5STR:
|
||||
if (isdown)
|
||||
((void(__cdecl *)(char *,
|
||||
char *, char *, char *,
|
||||
char *))id->fun)(w[1], w[2],
|
||||
w[3], w[4], w[5]);
|
||||
break;
|
||||
case ARG_DOWN:
|
||||
((void(__cdecl *)(bool))id->fun)(
|
||||
isdown);
|
||||
break;
|
||||
case ARG_DWN1:
|
||||
((void(__cdecl *)(bool,
|
||||
char *))id->fun)(isdown, w[1]);
|
||||
break;
|
||||
case ARG_1EXP:
|
||||
if (isdown)
|
||||
val = ((int(__cdecl *)(
|
||||
int))id->fun)(
|
||||
execute(w[1]));
|
||||
break;
|
||||
case ARG_2EXP:
|
||||
if (isdown)
|
||||
val = ((int(__cdecl *)(int,
|
||||
int))id->fun)(execute(w[1]),
|
||||
execute(w[2]));
|
||||
break;
|
||||
case ARG_1EST:
|
||||
if (isdown)
|
||||
val = ((int(__cdecl *)(
|
||||
char *))id->fun)(w[1]);
|
||||
break;
|
||||
case ARG_2EST:
|
||||
if (isdown)
|
||||
val = ((int(__cdecl *)(
|
||||
char *, char *))id->fun)(
|
||||
w[1], w[2]);
|
||||
break;
|
||||
case ARG_VARI:
|
||||
if (isdown) {
|
||||
string r; // limit, remove
|
||||
r[0] = 0;
|
||||
for(int i = 1; i<numargs; i++)
|
||||
{
|
||||
strcat_s(r, w[i]); // make string-list out of all arguments
|
||||
if(i==numargs-1) break;
|
||||
for (int i = 1; i < numargs;
|
||||
i++) {
|
||||
strcat_s(r,
|
||||
w[i]); // make
|
||||
// string-list
|
||||
// out of all
|
||||
// arguments
|
||||
if (i == numargs - 1)
|
||||
break;
|
||||
strcat_s(r, " ");
|
||||
};
|
||||
((void (__cdecl *)(char *))id->fun)(r);
|
||||
((void(__cdecl *)(
|
||||
char *))id->fun)(r);
|
||||
break;
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
case ID_VAR: // game defined variabled
|
||||
if(isdown)
|
||||
{
|
||||
if(!w[1][0]) conoutf("%s = %d", c, *id->storage); // var with no value just prints its current value
|
||||
else
|
||||
{
|
||||
if(id->min>id->max)
|
||||
{
|
||||
conoutf("variable is read-only");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isdown) {
|
||||
if (!w[1][0])
|
||||
conoutf("%s = %d", c,
|
||||
*id->storage); // var with
|
||||
// no value
|
||||
// just
|
||||
// prints its
|
||||
// current
|
||||
// value
|
||||
else {
|
||||
if (id->min > id->max) {
|
||||
conoutf("variable is "
|
||||
"read-only");
|
||||
} else {
|
||||
int i1 = ATOI(w[1]);
|
||||
if(i1<id->min || i1>id->max)
|
||||
{
|
||||
i1 = i1<id->min ? id->min : id->max; // clamp to valid range
|
||||
conoutf("valid range for %s is %d..%d", c, id->min, id->max);
|
||||
if (i1 < id->min ||
|
||||
i1 > id->max) {
|
||||
i1 =
|
||||
i1 < id->min
|
||||
? id->min
|
||||
: id->max; // clamp to valid range
|
||||
conoutf(
|
||||
"valid "
|
||||
"range for "
|
||||
"%s is "
|
||||
"%d..%d",
|
||||
c, id->min,
|
||||
id->max);
|
||||
}
|
||||
*id->storage = i1;
|
||||
};
|
||||
if(id->fun) ((void (__cdecl *)())id->fun)(); // call trigger function if available
|
||||
if (id->fun)
|
||||
((void(__cdecl *)())id
|
||||
->fun)(); // call
|
||||
// trigger
|
||||
// function
|
||||
// if
|
||||
// available
|
||||
};
|
||||
};
|
||||
break;
|
||||
|
||||
case ID_ALIAS: // alias, also used as functions and (global) variables
|
||||
for(int i = 1; i<numargs; i++)
|
||||
{
|
||||
sprintf_sd(t)("arg%d", i); // set any arguments as (global) arg values so functions can access them
|
||||
case ID_ALIAS: // alias, also used as functions and
|
||||
// (global) variables
|
||||
for (int i = 1; i < numargs; i++) {
|
||||
sprintf_sd(t)("arg%d",
|
||||
i); // set any arguments as (global)
|
||||
// arg values so functions can
|
||||
// access them
|
||||
alias(t, w[i]);
|
||||
};
|
||||
char *action = newstring(id->action); // create new string here because alias could rebind itself
|
||||
char *action = newstring(
|
||||
id->action); // create new string here
|
||||
// because alias could rebind
|
||||
// itself
|
||||
val = execute(action, isdown);
|
||||
gp()->deallocstr(action);
|
||||
break;
|
||||
|
@ -234,101 +387,158 @@ int execute(char *p, bool isdown) // all evaluation happens here,
|
|||
|
||||
int completesize = 0, completeidx = 0;
|
||||
|
||||
void resetcomplete() { completesize = 0; };
|
||||
|
||||
void complete(char *s)
|
||||
void
|
||||
resetcomplete()
|
||||
{
|
||||
if(*s!='/')
|
||||
{
|
||||
completesize = 0;
|
||||
};
|
||||
|
||||
void
|
||||
complete(char *s)
|
||||
{
|
||||
if (*s != '/') {
|
||||
string t;
|
||||
strcpy_s(t, s);
|
||||
strcpy_s(s, "/");
|
||||
strcat_s(s, t);
|
||||
};
|
||||
if(!s[1]) return;
|
||||
if(!completesize) { completesize = (int)strlen(s)-1; completeidx = 0; };
|
||||
if (!s[1])
|
||||
return;
|
||||
if (!completesize) {
|
||||
completesize = (int)strlen(s) - 1;
|
||||
completeidx = 0;
|
||||
};
|
||||
int idx = 0;
|
||||
enumerate(idents, ident *, id,
|
||||
if(strncmp(id->name, s+1, completesize)==0 && idx++==completeidx)
|
||||
{
|
||||
enumerate(
|
||||
idents, ident *, id,
|
||||
if (strncmp(id->name, s + 1, completesize) == 0 &&
|
||||
idx++ == completeidx) {
|
||||
strcpy_s(s, "/");
|
||||
strcat_s(s, id->name);
|
||||
};
|
||||
);
|
||||
};);
|
||||
completeidx++;
|
||||
if(completeidx>=idx) completeidx = 0;
|
||||
if (completeidx >= idx)
|
||||
completeidx = 0;
|
||||
};
|
||||
|
||||
bool execfile(char *cfgfile)
|
||||
bool
|
||||
execfile(char *cfgfile)
|
||||
{
|
||||
string s;
|
||||
strcpy_s(s, cfgfile);
|
||||
char *buf = loadfile(path(s), NULL);
|
||||
if(!buf) return false;
|
||||
if (!buf)
|
||||
return false;
|
||||
execute(buf);
|
||||
free(buf);
|
||||
return true;
|
||||
};
|
||||
|
||||
void exec(char *cfgfile)
|
||||
void
|
||||
exec(char *cfgfile)
|
||||
{
|
||||
if(!execfile(cfgfile)) conoutf("could not read \"%s\"", cfgfile);
|
||||
if (!execfile(cfgfile))
|
||||
conoutf("could not read \"%s\"", cfgfile);
|
||||
};
|
||||
|
||||
void writecfg()
|
||||
void
|
||||
writecfg()
|
||||
{
|
||||
FILE *f = fopen("config.cfg", "w");
|
||||
if(!f) return;
|
||||
fprintf(f, "// automatically written on exit, do not modify\n// delete this file to have defaults.cfg overwrite these settings\n// modify settings in game, or put settings in autoexec.cfg to override anything\n\n");
|
||||
if (!f)
|
||||
return;
|
||||
fprintf(f, "// automatically written on exit, do not modify\n// delete "
|
||||
"this file to have defaults.cfg overwrite these "
|
||||
"settings\n// modify settings in game, or put settings in "
|
||||
"autoexec.cfg to override anything\n\n");
|
||||
writeclientinfo(f);
|
||||
fprintf(f, "\n");
|
||||
enumerate(idents, ident *, id,
|
||||
if(id->type==ID_VAR && id->persist)
|
||||
{
|
||||
enumerate(
|
||||
idents, ident *, id, if (id->type == ID_VAR && id->persist) {
|
||||
fprintf(f, "%s %d\n", id->name, *id->storage);
|
||||
};
|
||||
);
|
||||
};);
|
||||
fprintf(f, "\n");
|
||||
writebinds(f);
|
||||
fprintf(f, "\n");
|
||||
enumerate(idents, ident *, id,
|
||||
if(id->type==ID_ALIAS && !strstr(id->name, "nextmap_"))
|
||||
{
|
||||
enumerate(
|
||||
idents, ident *, id,
|
||||
if (id->type == ID_ALIAS && !strstr(id->name, "nextmap_")) {
|
||||
fprintf(f, "alias \"%s\" [%s]\n", id->name, id->action);
|
||||
};
|
||||
);
|
||||
};);
|
||||
fclose(f);
|
||||
};
|
||||
|
||||
COMMAND(writecfg, ARG_NONE);
|
||||
|
||||
// below the commands that implement a small imperative language. thanks to the semantics of
|
||||
// below the commands that implement a small imperative language. thanks to the
|
||||
// semantics of
|
||||
// () and [] expressions, any control construct can be defined trivially.
|
||||
|
||||
void intset(char *name, int v) { string b; itoa(b, v); alias(name, b); };
|
||||
|
||||
void ifthen(char *cond, char *thenp, char *elsep) { execute(cond[0]!='0' ? thenp : elsep); };
|
||||
void loopa(char *times, char *body) { int t = atoi(times); loopi(t) { intset("i", i); execute(body); }; };
|
||||
void whilea(char *cond, char *body) { while(execute(cond)) execute(body); }; // can't get any simpler than this :)
|
||||
void onrelease(bool on, char *body) { if(!on) execute(body); };
|
||||
|
||||
void concat(char *s) { alias("s", s); };
|
||||
|
||||
void concatword(char *s)
|
||||
void
|
||||
intset(char *name, int v)
|
||||
{
|
||||
for(char *a = s, *b = s; *a = *b; b++) if(*a!=' ') a++;
|
||||
string b;
|
||||
itoa(b, v);
|
||||
alias(name, b);
|
||||
};
|
||||
|
||||
void
|
||||
ifthen(char *cond, char *thenp, char *elsep)
|
||||
{
|
||||
execute(cond[0] != '0' ? thenp : elsep);
|
||||
};
|
||||
void
|
||||
loopa(char *times, char *body)
|
||||
{
|
||||
int t = atoi(times);
|
||||
loopi(t)
|
||||
{
|
||||
intset("i", i);
|
||||
execute(body);
|
||||
};
|
||||
};
|
||||
void
|
||||
whilea(char *cond, char *body)
|
||||
{
|
||||
while (execute(cond))
|
||||
execute(body);
|
||||
}; // can't get any simpler than this :)
|
||||
void
|
||||
onrelease(bool on, char *body)
|
||||
{
|
||||
if (!on)
|
||||
execute(body);
|
||||
};
|
||||
|
||||
void
|
||||
concat(char *s)
|
||||
{
|
||||
alias("s", s);
|
||||
};
|
||||
|
||||
void
|
||||
concatword(char *s)
|
||||
{
|
||||
for (char *a = s, *b = s; *a = *b; b++)
|
||||
if (*a != ' ')
|
||||
a++;
|
||||
concat(s);
|
||||
};
|
||||
|
||||
int listlen(char *a)
|
||||
int
|
||||
listlen(char *a)
|
||||
{
|
||||
if(!*a) return 0;
|
||||
if (!*a)
|
||||
return 0;
|
||||
int n = 0;
|
||||
while(*a) if(*a++==' ') n++;
|
||||
return n+1;
|
||||
while (*a)
|
||||
if (*a++ == ' ')
|
||||
n++;
|
||||
return n + 1;
|
||||
};
|
||||
|
||||
void at(char *s, char *pos)
|
||||
void
|
||||
at(char *s, char *pos)
|
||||
{
|
||||
int n = atoi(pos);
|
||||
loopi(n) s += strspn(s += strcspn(s, " \0"), " ");
|
||||
|
@ -346,18 +556,72 @@ COMMAND(concatword, ARG_VARI);
|
|||
COMMAND(at, ARG_2STR);
|
||||
COMMAND(listlen, ARG_1EST);
|
||||
|
||||
int add(int a, int b) { return a+b; }; COMMANDN(+, add, ARG_2EXP);
|
||||
int mul(int a, int b) { return a*b; }; COMMANDN(*, mul, ARG_2EXP);
|
||||
int sub(int a, int b) { return a-b; }; COMMANDN(-, sub, ARG_2EXP);
|
||||
int divi(int a, int b) { return b ? a/b : 0; }; COMMANDN(div, divi, ARG_2EXP);
|
||||
int mod(int a, int b) { return b ? a%b : 0; }; COMMAND(mod, ARG_2EXP);
|
||||
int equal(int a, int b) { return (int)(a==b); }; COMMANDN(=, equal, ARG_2EXP);
|
||||
int lt(int a, int b) { return (int)(a<b); }; COMMANDN(<, lt, ARG_2EXP);
|
||||
int gt(int a, int b) { return (int)(a>b); }; COMMANDN(>, gt, ARG_2EXP);
|
||||
int
|
||||
add(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
};
|
||||
COMMANDN(+, add, ARG_2EXP);
|
||||
int
|
||||
mul(int a, int b)
|
||||
{
|
||||
return a * b;
|
||||
};
|
||||
COMMANDN(*, mul, ARG_2EXP);
|
||||
int
|
||||
sub(int a, int b)
|
||||
{
|
||||
return a - b;
|
||||
};
|
||||
COMMANDN(-, sub, ARG_2EXP);
|
||||
int
|
||||
divi(int a, int b)
|
||||
{
|
||||
return b ? a / b : 0;
|
||||
};
|
||||
COMMANDN(div, divi, ARG_2EXP);
|
||||
int
|
||||
mod(int a, int b)
|
||||
{
|
||||
return b ? a % b : 0;
|
||||
};
|
||||
COMMAND(mod, ARG_2EXP);
|
||||
int
|
||||
equal(int a, int b)
|
||||
{
|
||||
return (int)(a == b);
|
||||
};
|
||||
COMMANDN(=, equal, ARG_2EXP);
|
||||
int
|
||||
lt(int a, int b)
|
||||
{
|
||||
return (int)(a < b);
|
||||
};
|
||||
COMMANDN(<, lt, ARG_2EXP);
|
||||
int
|
||||
gt(int a, int b)
|
||||
{
|
||||
return (int)(a > b);
|
||||
};
|
||||
COMMANDN(>, gt, ARG_2EXP);
|
||||
|
||||
int strcmpa(char *a, char *b) { return strcmp(a,b)==0; }; COMMANDN(strcmp, strcmpa, ARG_2EST);
|
||||
int
|
||||
strcmpa(char *a, char *b)
|
||||
{
|
||||
return strcmp(a, b) == 0;
|
||||
};
|
||||
COMMANDN(strcmp, strcmpa, ARG_2EST);
|
||||
|
||||
int rndn(int a) { return a>0 ? rnd(a) : 0; }; COMMANDN(rnd, rndn, ARG_1EXP);
|
||||
|
||||
int explastmillis() { return lastmillis; }; COMMANDN(millis, explastmillis, ARG_1EXP);
|
||||
int
|
||||
rndn(int a)
|
||||
{
|
||||
return a > 0 ? rnd(a) : 0;
|
||||
};
|
||||
COMMANDN(rnd, rndn, ARG_1EXP);
|
||||
|
||||
int
|
||||
explastmillis()
|
||||
{
|
||||
return lastmillis;
|
||||
};
|
||||
COMMANDN(millis, explastmillis, ARG_1EXP);
|
||||
|
|
215
src/console.cxx
215
src/console.cxx
|
@ -3,7 +3,10 @@
|
|||
#include "cube.h"
|
||||
#include <ctype.h>
|
||||
|
||||
struct cline { char *cref; int outtime; };
|
||||
struct cline {
|
||||
char *cref;
|
||||
int outtime;
|
||||
};
|
||||
vector<cline> conlines;
|
||||
|
||||
const int ndraw = 5;
|
||||
|
@ -13,72 +16,86 @@ int conskip = 0;
|
|||
bool saycommandon = false;
|
||||
string commandbuf;
|
||||
|
||||
void setconskip(int n)
|
||||
void
|
||||
setconskip(int n)
|
||||
{
|
||||
conskip += n;
|
||||
if(conskip<0) conskip = 0;
|
||||
if (conskip < 0)
|
||||
conskip = 0;
|
||||
};
|
||||
|
||||
COMMANDN(conskip, setconskip, ARG_1INT);
|
||||
|
||||
void conline(const char *sf, bool highlight) // add a line to the console buffer
|
||||
void
|
||||
conline(const char *sf, bool highlight) // add a line to the console buffer
|
||||
{
|
||||
cline cl;
|
||||
cl.cref = conlines.length()>100 ? conlines.pop().cref : newstringbuf(""); // constrain the buffer size
|
||||
cl.cref = conlines.length() > 100
|
||||
? conlines.pop().cref
|
||||
: newstringbuf(""); // constrain the buffer size
|
||||
cl.outtime = lastmillis; // for how long to keep line on screen
|
||||
conlines.insert(0,cl);
|
||||
if(highlight) // show line in a different colour, for chat etc.
|
||||
conlines.insert(0, cl);
|
||||
if (highlight) // show line in a different colour, for chat etc.
|
||||
{
|
||||
cl.cref[0] = '\f';
|
||||
cl.cref[1] = 0;
|
||||
strcat_s(cl.cref, sf);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
strcpy_s(cl.cref, sf);
|
||||
};
|
||||
puts(cl.cref);
|
||||
#ifndef _WIN32
|
||||
#ifndef _WIN32
|
||||
fflush(stdout);
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
void conoutf(const char *s, ...)
|
||||
void
|
||||
conoutf(const char *s, ...)
|
||||
{
|
||||
sprintf_sdv(sf, s);
|
||||
s = sf;
|
||||
int n = 0;
|
||||
while(strlen(s)>WORDWRAP) // cut strings to fit on screen
|
||||
while (strlen(s) > WORDWRAP) // cut strings to fit on screen
|
||||
{
|
||||
string t;
|
||||
strn0cpy(t, s, WORDWRAP+1);
|
||||
conline(t, n++!=0);
|
||||
strn0cpy(t, s, WORDWRAP + 1);
|
||||
conline(t, n++ != 0);
|
||||
s += WORDWRAP;
|
||||
};
|
||||
conline(s, n!=0);
|
||||
conline(s, n != 0);
|
||||
};
|
||||
|
||||
void renderconsole() // render buffer taking into account time & scrolling
|
||||
void
|
||||
renderconsole() // render buffer taking into account time & scrolling
|
||||
{
|
||||
int nd = 0;
|
||||
char *refs[ndraw];
|
||||
loopv(conlines) if(conskip ? i>=conskip-1 || i>=conlines.length()-ndraw : lastmillis-conlines[i].outtime<20000)
|
||||
loopv(conlines) if (conskip ? i >= conskip - 1 ||
|
||||
i >= conlines.length() - ndraw
|
||||
: lastmillis - conlines[i].outtime < 20000)
|
||||
{
|
||||
refs[nd++] = conlines[i].cref;
|
||||
if(nd==ndraw) break;
|
||||
if (nd == ndraw)
|
||||
break;
|
||||
};
|
||||
loopj(nd)
|
||||
{
|
||||
draw_text(refs[j], FONTH/3, (FONTH/4*5)*(nd-j-1)+FONTH/3, 2);
|
||||
draw_text(refs[j], FONTH / 3,
|
||||
(FONTH / 4 * 5) * (nd - j - 1) + FONTH / 3, 2);
|
||||
};
|
||||
};
|
||||
|
||||
// keymap is defined externally in keymap.cfg
|
||||
|
||||
struct keym { int code; char *name; char *action; } keyms[256];
|
||||
struct keym {
|
||||
int code;
|
||||
char *name;
|
||||
char *action;
|
||||
} keyms[256];
|
||||
int numkm = 0;
|
||||
|
||||
void keymap(char *code, char *key, char *action)
|
||||
void
|
||||
keymap(char *code, char *key, char *action)
|
||||
{
|
||||
keyms[numkm].code = atoi(code);
|
||||
keyms[numkm].name = newstring(key);
|
||||
|
@ -87,10 +104,12 @@ void keymap(char *code, char *key, char *action)
|
|||
|
||||
COMMAND(keymap, ARG_3STR);
|
||||
|
||||
void bindkey(char *key, char *action)
|
||||
void
|
||||
bindkey(char *key, char *action)
|
||||
{
|
||||
for(char *x = key; *x; x++) *x = toupper(*x);
|
||||
loopi(numkm) if(strcmp(keyms[i].name, key)==0)
|
||||
for (char *x = key; *x; x++)
|
||||
*x = toupper(*x);
|
||||
loopi(numkm) if (strcmp(keyms[i].name, key) == 0)
|
||||
{
|
||||
strcpy_s(keyms[i].action, action);
|
||||
return;
|
||||
|
@ -100,98 +119,118 @@ void bindkey(char *key, char *action)
|
|||
|
||||
COMMANDN(bind, bindkey, ARG_2STR);
|
||||
|
||||
void saycommand(char *init) // turns input to the command line on or off
|
||||
void
|
||||
saycommand(char *init) // turns input to the command line on or off
|
||||
{
|
||||
SDL_EnableUNICODE(saycommandon = (init!=NULL));
|
||||
if(!editmode) keyrepeat(saycommandon);
|
||||
if(!init) init = "";
|
||||
SDL_EnableUNICODE(saycommandon = (init != NULL));
|
||||
if (!editmode)
|
||||
keyrepeat(saycommandon);
|
||||
if (!init)
|
||||
init = "";
|
||||
strcpy_s(commandbuf, init);
|
||||
};
|
||||
|
||||
void mapmsg(char *s) { strn0cpy(hdr.maptitle, s, 128); };
|
||||
void
|
||||
mapmsg(char *s)
|
||||
{
|
||||
strn0cpy(hdr.maptitle, s, 128);
|
||||
};
|
||||
|
||||
COMMAND(saycommand, ARG_VARI);
|
||||
COMMAND(mapmsg, ARG_1STR);
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <X11/Xlib.h>
|
||||
#include <SDL_syswm.h>
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
void pasteconsole()
|
||||
void
|
||||
pasteconsole()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(!IsClipboardFormatAvailable(CF_TEXT)) return;
|
||||
if(!OpenClipboard(NULL)) return;
|
||||
#ifdef _WIN32
|
||||
if (!IsClipboardFormatAvailable(CF_TEXT))
|
||||
return;
|
||||
if (!OpenClipboard(NULL))
|
||||
return;
|
||||
char *cb = (char *)GlobalLock(GetClipboardData(CF_TEXT));
|
||||
strcat_s(commandbuf, cb);
|
||||
GlobalUnlock(cb);
|
||||
CloseClipboard();
|
||||
#else
|
||||
#else
|
||||
SDL_SysWMinfo wminfo;
|
||||
SDL_VERSION(&wminfo.version);
|
||||
wminfo.subsystem = SDL_SYSWM_X11;
|
||||
if(!SDL_GetWMInfo(&wminfo)) return;
|
||||
if (!SDL_GetWMInfo(&wminfo))
|
||||
return;
|
||||
int cbsize;
|
||||
char *cb = XFetchBytes(wminfo.info.x11.display, &cbsize);
|
||||
if(!cb || !cbsize) return;
|
||||
if (!cb || !cbsize)
|
||||
return;
|
||||
int commandlen = strlen(commandbuf);
|
||||
for(char *cbline = cb, *cbend; commandlen + 1 < _MAXDEFSTR && cbline < &cb[cbsize]; cbline = cbend + 1)
|
||||
{
|
||||
for (char *cbline = cb, *cbend;
|
||||
commandlen + 1 < _MAXDEFSTR && cbline < &cb[cbsize];
|
||||
cbline = cbend + 1) {
|
||||
cbend = (char *)memchr(cbline, '\0', &cb[cbsize] - cbline);
|
||||
if(!cbend) cbend = &cb[cbsize];
|
||||
if(commandlen + cbend - cbline + 1 > _MAXDEFSTR) cbend = cbline + _MAXDEFSTR - commandlen - 1;
|
||||
if (!cbend)
|
||||
cbend = &cb[cbsize];
|
||||
if (commandlen + cbend - cbline + 1 > _MAXDEFSTR)
|
||||
cbend = cbline + _MAXDEFSTR - commandlen - 1;
|
||||
memcpy(&commandbuf[commandlen], cbline, cbend - cbline);
|
||||
commandlen += cbend - cbline;
|
||||
commandbuf[commandlen] = '\n';
|
||||
if(commandlen + 1 < _MAXDEFSTR && cbend < &cb[cbsize]) ++commandlen;
|
||||
if (commandlen + 1 < _MAXDEFSTR && cbend < &cb[cbsize])
|
||||
++commandlen;
|
||||
commandbuf[commandlen] = '\0';
|
||||
};
|
||||
XFree(cb);
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
cvector vhistory;
|
||||
int histpos = 0;
|
||||
|
||||
void history(int n)
|
||||
void
|
||||
history(int n)
|
||||
{
|
||||
static bool rec = false;
|
||||
if(!rec && n>=0 && n<vhistory.length())
|
||||
{
|
||||
if (!rec && n >= 0 && n < vhistory.length()) {
|
||||
rec = true;
|
||||
execute(vhistory[vhistory.length()-n-1]);
|
||||
execute(vhistory[vhistory.length() - n - 1]);
|
||||
rec = false;
|
||||
};
|
||||
};
|
||||
|
||||
COMMAND(history, ARG_1INT);
|
||||
|
||||
void keypress(int code, bool isdown, int cooked)
|
||||
void
|
||||
keypress(int code, bool isdown, int cooked)
|
||||
{
|
||||
if(saycommandon) // keystrokes go to commandline
|
||||
{
|
||||
if(isdown)
|
||||
{
|
||||
switch(code)
|
||||
if (saycommandon) // keystrokes go to commandline
|
||||
{
|
||||
if (isdown) {
|
||||
switch (code) {
|
||||
case SDLK_RETURN:
|
||||
break;
|
||||
|
||||
case SDLK_BACKSPACE:
|
||||
case SDLK_LEFT:
|
||||
{
|
||||
for(int i = 0; commandbuf[i]; i++) if(!commandbuf[i+1]) commandbuf[i] = 0;
|
||||
case SDLK_LEFT: {
|
||||
for (int i = 0; commandbuf[i]; i++)
|
||||
if (!commandbuf[i + 1])
|
||||
commandbuf[i] = 0;
|
||||
resetcomplete();
|
||||
break;
|
||||
};
|
||||
|
||||
case SDLK_UP:
|
||||
if(histpos) strcpy_s(commandbuf, vhistory[--histpos]);
|
||||
if (histpos)
|
||||
strcpy_s(
|
||||
commandbuf, vhistory[--histpos]);
|
||||
break;
|
||||
|
||||
case SDLK_DOWN:
|
||||
if(histpos<vhistory.length()) strcpy_s(commandbuf, vhistory[histpos++]);
|
||||
if (histpos < vhistory.length())
|
||||
strcpy_s(
|
||||
commandbuf, vhistory[histpos++]);
|
||||
break;
|
||||
|
||||
case SDLK_TAB:
|
||||
|
@ -199,38 +238,44 @@ void keypress(int code, bool isdown, int cooked)
|
|||
break;
|
||||
|
||||
case SDLK_v:
|
||||
if(SDL_GetModState()&(KMOD_LCTRL|KMOD_RCTRL)) { pasteconsole(); return; };
|
||||
if (SDL_GetModState() &
|
||||
(KMOD_LCTRL | KMOD_RCTRL)) {
|
||||
pasteconsole();
|
||||
return;
|
||||
};
|
||||
|
||||
default:
|
||||
resetcomplete();
|
||||
if(cooked) { char add[] = { cooked, 0 }; strcat_s(commandbuf, add); };
|
||||
if (cooked) {
|
||||
char add[] = {cooked, 0};
|
||||
strcat_s(commandbuf, add);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
if(code==SDLK_RETURN)
|
||||
{
|
||||
if(commandbuf[0])
|
||||
{
|
||||
if(vhistory.empty() || strcmp(vhistory.last(), commandbuf))
|
||||
{
|
||||
vhistory.add(newstring(commandbuf)); // cap this?
|
||||
};
|
||||
} else {
|
||||
if (code == SDLK_RETURN) {
|
||||
if (commandbuf[0]) {
|
||||
if (vhistory.empty() ||
|
||||
strcmp(
|
||||
vhistory.last(), commandbuf)) {
|
||||
vhistory.add(newstring(
|
||||
commandbuf)); // cap this?
|
||||
};
|
||||
histpos = vhistory.length();
|
||||
if(commandbuf[0]=='/') execute(commandbuf, true);
|
||||
else toserver(commandbuf);
|
||||
if (commandbuf[0] == '/')
|
||||
execute(commandbuf, true);
|
||||
else
|
||||
toserver(commandbuf);
|
||||
};
|
||||
saycommand(NULL);
|
||||
}
|
||||
else if(code==SDLK_ESCAPE)
|
||||
{
|
||||
} else if (code == SDLK_ESCAPE) {
|
||||
saycommand(NULL);
|
||||
};
|
||||
};
|
||||
}
|
||||
else if(!menukey(code, isdown)) // keystrokes go to menu
|
||||
} else if (!menukey(code, isdown)) // keystrokes go to menu
|
||||
{
|
||||
loopi(numkm) if(keyms[i].code==code) // keystrokes go to game, lookup in keymap and execute
|
||||
loopi(numkm) if (keyms[i].code ==
|
||||
code) // keystrokes go to game, lookup in
|
||||
// keymap and execute
|
||||
{
|
||||
string temp;
|
||||
strcpy_s(temp, keyms[i].action);
|
||||
|
@ -240,15 +285,19 @@ void keypress(int code, bool isdown, int cooked)
|
|||
};
|
||||
};
|
||||
|
||||
char *getcurcommand()
|
||||
char *
|
||||
getcurcommand()
|
||||
{
|
||||
return saycommandon ? commandbuf : NULL;
|
||||
};
|
||||
|
||||
void writebinds(FILE *f)
|
||||
void
|
||||
writebinds(FILE *f)
|
||||
{
|
||||
loopi(numkm)
|
||||
{
|
||||
if(*keyms[i].action) fprintf(f, "bind \"%s\" [%s]\n", keyms[i].name, keyms[i].action);
|
||||
if (*keyms[i].action)
|
||||
fprintf(f, "bind \"%s\" [%s]\n", keyms[i].name,
|
||||
keyms[i].action);
|
||||
};
|
||||
};
|
||||
|
|
327
src/cube.h
327
src/cube.h
|
@ -13,14 +13,14 @@ enum // block types, order matters!
|
|||
MAXTYPE
|
||||
};
|
||||
|
||||
struct sqr
|
||||
{
|
||||
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 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
|
||||
|
@ -40,9 +40,14 @@ enum // static entity types
|
|||
NOTUSED = 0, // entity slot not in use in map
|
||||
LIGHT, // lightsource, attr1 = radius, attr2 = intensity
|
||||
PLAYERSTART, // attr1 = angle
|
||||
I_SHELLS, I_BULLETS, I_ROCKETS, I_ROUNDS,
|
||||
I_HEALTH, I_BOOST,
|
||||
I_GREENARMOUR, I_YELLOWARMOUR,
|
||||
I_SHELLS,
|
||||
I_BULLETS,
|
||||
I_ROCKETS,
|
||||
I_ROUNDS,
|
||||
I_HEALTH,
|
||||
I_BOOST,
|
||||
I_GREENARMOUR,
|
||||
I_YELLOWARMOUR,
|
||||
I_QUAD,
|
||||
TELEPORT, // attr1 = idx
|
||||
TELEDEST, // attr1 = angle, attr2 = idx
|
||||
|
@ -61,8 +66,7 @@ struct persistent_entity // map entity
|
|||
uchar attr2, attr3, attr4;
|
||||
};
|
||||
|
||||
struct entity : public persistent_entity
|
||||
{
|
||||
struct entity : public persistent_entity {
|
||||
bool spawned; // the only dynamic state of a map entity
|
||||
};
|
||||
|
||||
|
@ -81,21 +85,41 @@ struct header // map file format header
|
|||
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 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 SOLID(x) ((x)->type == SOLID)
|
||||
#define MINBORD 2 // 2 cubes from the edge of the world are always solid
|
||||
#define OUTBORD(x,y) ((x)<MINBORD || (y)<MINBORD || (x)>=ssize-MINBORD || (y)>=ssize-MINBORD)
|
||||
#define OUTBORD(x, y) \
|
||||
((x) < MINBORD || (y) < MINBORD || (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; };
|
||||
struct vec {
|
||||
float x, y, z;
|
||||
};
|
||||
struct block {
|
||||
int x, y, xs, ys;
|
||||
};
|
||||
struct mapmodelinfo {
|
||||
int rad, h, zoff, snap;
|
||||
char *name;
|
||||
};
|
||||
|
||||
enum { GUN_FIST = 0, GUN_SG, GUN_CG, GUN_RL, GUN_RIFLE, GUN_FIREBALL, GUN_ICEBALL, GUN_SLIMEBALL, GUN_BITE, NUMGUNS };
|
||||
enum {
|
||||
GUN_FIST = 0,
|
||||
GUN_SG,
|
||||
GUN_CG,
|
||||
GUN_RL,
|
||||
GUN_RIFLE,
|
||||
GUN_FIREBALL,
|
||||
GUN_ICEBALL,
|
||||
GUN_SLIMEBALL,
|
||||
GUN_BITE,
|
||||
NUMGUNS
|
||||
};
|
||||
|
||||
struct dynent // players & monsters
|
||||
{
|
||||
|
@ -123,16 +147,26 @@ struct dynent // players & monsters
|
|||
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
|
||||
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;
|
||||
};
|
||||
|
||||
#define SAVEGAMEVERSION 4 // bump if dynent/netprotocol changes or any other savegame/demo data
|
||||
#define SAVEGAMEVERSION \
|
||||
4 // bump if dynent/netprotocol changes or any other savegame/demo data
|
||||
|
||||
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 }; // monster states
|
||||
enum {
|
||||
M_NONE = 0,
|
||||
M_SEARCH,
|
||||
M_HOME,
|
||||
M_ATTACKING,
|
||||
M_PAIN,
|
||||
M_SLEEP,
|
||||
M_AIMING
|
||||
}; // 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
|
||||
|
@ -141,46 +175,104 @@ enum { M_NONE = 0, M_SEARCH, M_HOME, M_ATTACKING, M_PAIN, M_SLEEP, M_AIMING };
|
|||
#define PROTOCOL_VERSION 122 // bump when protocol changes
|
||||
|
||||
// network messages codes, c2s, c2c, s2c
|
||||
enum
|
||||
{
|
||||
SV_INITS2C, 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,
|
||||
enum {
|
||||
SV_INITS2C,
|
||||
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_EXT,
|
||||
};
|
||||
|
||||
enum { CS_ALIVE = 0, CS_DEAD, CS_LAGGED, CS_EDITING };
|
||||
|
||||
// hardcoded sounds, defined in sounds.cfg
|
||||
enum
|
||||
{
|
||||
S_JUMP = 0, S_LAND, S_RIFLE, S_PUNCH1, S_SG, S_CG,
|
||||
S_RLFIRE, S_RLHIT, S_WEAPLOAD, S_ITEMAMMO, S_ITEMHEALTH,
|
||||
S_ITEMARMOUR, S_ITEMPUP, S_ITEMSPAWN, S_TELEPORT, S_NOAMMO, S_PUPOUT,
|
||||
S_PAIN1, S_PAIN2, S_PAIN3, S_PAIN4, S_PAIN5, S_PAIN6,
|
||||
S_DIE1, S_DIE2,
|
||||
S_FLAUNCH, S_FEXPLODE,
|
||||
S_SPLASH1, S_SPLASH2,
|
||||
S_GRUNT1, S_GRUNT2, S_RUMBLE,
|
||||
enum {
|
||||
S_JUMP = 0,
|
||||
S_LAND,
|
||||
S_RIFLE,
|
||||
S_PUNCH1,
|
||||
S_SG,
|
||||
S_CG,
|
||||
S_RLFIRE,
|
||||
S_RLHIT,
|
||||
S_WEAPLOAD,
|
||||
S_ITEMAMMO,
|
||||
S_ITEMHEALTH,
|
||||
S_ITEMARMOUR,
|
||||
S_ITEMPUP,
|
||||
S_ITEMSPAWN,
|
||||
S_TELEPORT,
|
||||
S_NOAMMO,
|
||||
S_PUPOUT,
|
||||
S_PAIN1,
|
||||
S_PAIN2,
|
||||
S_PAIN3,
|
||||
S_PAIN4,
|
||||
S_PAIN5,
|
||||
S_PAIN6,
|
||||
S_DIE1,
|
||||
S_DIE2,
|
||||
S_FLAUNCH,
|
||||
S_FEXPLODE,
|
||||
S_SPLASH1,
|
||||
S_SPLASH2,
|
||||
S_GRUNT1,
|
||||
S_GRUNT2,
|
||||
S_RUMBLE,
|
||||
S_PAINO,
|
||||
S_PAINR, S_DEATHR,
|
||||
S_PAINE, S_DEATHE,
|
||||
S_PAINS, S_DEATHS,
|
||||
S_PAINB, S_DEATHB,
|
||||
S_PAINP, S_PIGGR2,
|
||||
S_PAINH, S_DEATHH,
|
||||
S_PAIND, S_DEATHD,
|
||||
S_PIGR1, S_ICEBALL, S_SLIMEBALL,
|
||||
S_PAINR,
|
||||
S_DEATHR,
|
||||
S_PAINE,
|
||||
S_DEATHE,
|
||||
S_PAINS,
|
||||
S_DEATHS,
|
||||
S_PAINB,
|
||||
S_DEATHB,
|
||||
S_PAINP,
|
||||
S_PIGGR2,
|
||||
S_PAINH,
|
||||
S_DEATHH,
|
||||
S_PAIND,
|
||||
S_DEATHD,
|
||||
S_PIGR1,
|
||||
S_ICEBALL,
|
||||
S_SLIMEBALL,
|
||||
S_JUMPPAD,
|
||||
};
|
||||
|
||||
// vertex array format
|
||||
|
||||
struct vertex { float u, v, x, y, z; uchar r, g, b, a; };
|
||||
struct vertex {
|
||||
float u, v, x, y, z;
|
||||
uchar r, g, b, a;
|
||||
};
|
||||
|
||||
typedef vector<dynent *> dvector;
|
||||
typedef vector<char *> cvector;
|
||||
|
@ -188,11 +280,13 @@ typedef vector<int> ivector;
|
|||
|
||||
// globals ooh naughty
|
||||
|
||||
extern sqr *world, *wmip[]; // map data, the mips are sequential 2D arrays in memory
|
||||
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 dynent
|
||||
*player1; // special client ent that receives input and acts as camera
|
||||
extern dvector players; // all the other clients (in multiplayer)
|
||||
extern bool editmode;
|
||||
extern vector<entity> ents; // map entities
|
||||
|
@ -203,7 +297,6 @@ extern int gamemode, nextmode;
|
|||
extern int xtraverts;
|
||||
extern bool demoplayback;
|
||||
|
||||
|
||||
#define DMF 16.0f
|
||||
#define DAF 1.0f
|
||||
#define DVF 100.0f
|
||||
|
@ -211,68 +304,125 @@ extern bool demoplayback;
|
|||
#define VIRTW 2400 // virtual screen size for text & HUD
|
||||
#define VIRTH 1800
|
||||
#define FONTH 64
|
||||
#define PIXELTAB (VIRTW/12)
|
||||
#define PIXELTAB (VIRTW / 12)
|
||||
|
||||
#define PI (3.1415927f)
|
||||
#define PI2 (2*PI)
|
||||
#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 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
|
||||
#define sgetstr() \
|
||||
{ \
|
||||
char *t = text; \
|
||||
do { \
|
||||
*t = getint(p); \
|
||||
} while (*t++); \
|
||||
} // used by networking
|
||||
|
||||
#define m_noitems (gamemode>=4)
|
||||
#define m_noitemsrail (gamemode<=5)
|
||||
#define m_arena (gamemode>=8)
|
||||
#define m_tarena (gamemode>=10)
|
||||
#define m_teammode (gamemode&1 && gamemode>2)
|
||||
#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)
|
||||
#define m_noitems (gamemode >= 4)
|
||||
#define m_noitemsrail (gamemode <= 5)
|
||||
#define m_arena (gamemode >= 8)
|
||||
#define m_tarena (gamemode >= 10)
|
||||
#define m_teammode (gamemode & 1 && gamemode > 2)
|
||||
#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_1INT,
|
||||
ARG_2INT,
|
||||
ARG_3INT,
|
||||
ARG_4INT,
|
||||
ARG_NONE,
|
||||
ARG_1STR, ARG_2STR, ARG_3STR, ARG_5STR,
|
||||
ARG_DOWN, ARG_DWN1,
|
||||
ARG_1EXP, ARG_2EXP,
|
||||
ARG_1EST, ARG_2EST,
|
||||
ARG_1STR,
|
||||
ARG_2STR,
|
||||
ARG_3STR,
|
||||
ARG_5STR,
|
||||
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)
|
||||
// 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 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 WIN32_LEAN_AND_MEAN
|
||||
#include "windows.h"
|
||||
#define _WINDOWS
|
||||
#define ZLIB_DLL
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#include <GL/glext.h>
|
||||
#include <GL/glu.h>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_image.h>
|
||||
|
@ -282,4 +432,3 @@ enum // function signatures for script functions, see command.cpp
|
|||
#include <zlib.h>
|
||||
|
||||
#include "protos.h" // external function decls
|
||||
|
||||
|
|
478
src/editing.cxx
478
src/editing.cxx
|
@ -1,14 +1,15 @@
|
|||
// editing.cpp: most map editing commands go here, entity editing commands are in world.cpp
|
||||
// editing.cpp: most map editing commands go here, entity editing commands are
|
||||
// in world.cpp
|
||||
|
||||
#include "cube.h"
|
||||
|
||||
bool editmode = false;
|
||||
|
||||
// the current selection, used by almost all editing commands
|
||||
// invariant: all code assumes that these are kept inside MINBORD distance of the edge of the map
|
||||
// invariant: all code assumes that these are kept inside MINBORD distance of
|
||||
// the edge of the map
|
||||
|
||||
block sel =
|
||||
{
|
||||
block sel = {
|
||||
variable("selx", 0, 0, 4096, &sel.x, NULL, false),
|
||||
variable("sely", 0, 0, 4096, &sel.y, NULL, false),
|
||||
variable("selxs", 0, 0, 4096, &sel.xs, NULL, false),
|
||||
|
@ -18,11 +19,20 @@ block sel =
|
|||
int selh = 0;
|
||||
bool selset = false;
|
||||
|
||||
#define loopselxy(b) { makeundo(); loop(x,sel.xs) loop(y,sel.ys) { sqr *s = S(sel.x+x, sel.y+y); b; }; remip(sel); }
|
||||
#define loopselxy(b) \
|
||||
{ \
|
||||
makeundo(); \
|
||||
loop(x, sel.xs) loop(y, sel.ys) \
|
||||
{ \
|
||||
sqr *s = S(sel.x + x, sel.y + y); \
|
||||
b; \
|
||||
}; \
|
||||
remip(sel); \
|
||||
}
|
||||
|
||||
int cx, cy, ch;
|
||||
|
||||
int curedittex[] = { -1, -1, -1 };
|
||||
int curedittex[] = {-1, -1, -1};
|
||||
|
||||
bool dragging = false;
|
||||
int lastx, lasty, lasth;
|
||||
|
@ -30,22 +40,26 @@ int lastx, lasty, lasth;
|
|||
int lasttype = 0, lasttex = 0;
|
||||
sqr rtex;
|
||||
|
||||
VAR(editing,0,0,1);
|
||||
VAR(editing, 0, 0, 1);
|
||||
|
||||
void toggleedit()
|
||||
void
|
||||
toggleedit()
|
||||
{
|
||||
if(player1->state==CS_DEAD) return; // do not allow dead players to edit to avoid state confusion
|
||||
if(!editmode && !allowedittoggle()) return; // not in most multiplayer modes
|
||||
if(!(editmode = !editmode))
|
||||
{
|
||||
if (player1->state == CS_DEAD)
|
||||
return; // do not allow dead players to edit to avoid state
|
||||
// confusion
|
||||
if (!editmode && !allowedittoggle())
|
||||
return; // not in most multiplayer modes
|
||||
if (!(editmode = !editmode)) {
|
||||
settagareas(); // reset triggers to allow quick playtesting
|
||||
entinmap(player1); // find spawn closest to current floating pos
|
||||
}
|
||||
else
|
||||
{
|
||||
resettagareas(); // clear trigger areas to allow them to be edited
|
||||
} else {
|
||||
resettagareas(); // clear trigger areas to allow them to be
|
||||
// edited
|
||||
player1->health = 100;
|
||||
if(m_classicsp) monsterclear(); // all monsters back at their spawns for editing
|
||||
if (m_classicsp)
|
||||
monsterclear(); // all monsters back at their spawns for
|
||||
// editing
|
||||
projreset();
|
||||
};
|
||||
keyrepeat(editmode);
|
||||
|
@ -55,84 +69,111 @@ void toggleedit()
|
|||
|
||||
COMMANDN(edittoggle, toggleedit, ARG_NONE);
|
||||
|
||||
void correctsel() // ensures above invariant
|
||||
void
|
||||
correctsel() // ensures above invariant
|
||||
{
|
||||
selset = !OUTBORD(sel.x, sel.y);
|
||||
int bsize = ssize-MINBORD;
|
||||
if(sel.xs+sel.x>bsize) sel.xs = bsize-sel.x;
|
||||
if(sel.ys+sel.y>bsize) sel.ys = bsize-sel.y;
|
||||
if(sel.xs<=0 || sel.ys<=0) selset = false;
|
||||
int bsize = ssize - MINBORD;
|
||||
if (sel.xs + sel.x > bsize)
|
||||
sel.xs = bsize - sel.x;
|
||||
if (sel.ys + sel.y > bsize)
|
||||
sel.ys = bsize - sel.y;
|
||||
if (sel.xs <= 0 || sel.ys <= 0)
|
||||
selset = false;
|
||||
};
|
||||
|
||||
bool noteditmode()
|
||||
bool
|
||||
noteditmode()
|
||||
{
|
||||
correctsel();
|
||||
if(!editmode) conoutf("this function is only allowed in edit mode");
|
||||
if (!editmode)
|
||||
conoutf("this function is only allowed in edit mode");
|
||||
return !editmode;
|
||||
};
|
||||
|
||||
bool noselection()
|
||||
bool
|
||||
noselection()
|
||||
{
|
||||
if(!selset) conoutf("no selection");
|
||||
if (!selset)
|
||||
conoutf("no selection");
|
||||
return !selset;
|
||||
};
|
||||
|
||||
#define EDITSEL if(noteditmode() || noselection()) return;
|
||||
#define EDITSELMP if(noteditmode() || noselection() || multiplayer()) return;
|
||||
#define EDITMP if(noteditmode() || multiplayer()) return;
|
||||
#define EDITSEL \
|
||||
if (noteditmode() || noselection()) \
|
||||
return;
|
||||
#define EDITSELMP \
|
||||
if (noteditmode() || noselection() || multiplayer()) \
|
||||
return;
|
||||
#define EDITMP \
|
||||
if (noteditmode() || multiplayer()) \
|
||||
return;
|
||||
|
||||
void selectpos(int x, int y, int xs, int ys)
|
||||
void
|
||||
selectpos(int x, int y, int xs, int ys)
|
||||
{
|
||||
block s = { x, y, xs, ys };
|
||||
block s = {x, y, xs, ys};
|
||||
sel = s;
|
||||
selh = 0;
|
||||
correctsel();
|
||||
};
|
||||
|
||||
void makesel()
|
||||
void
|
||||
makesel()
|
||||
{
|
||||
block s = { min(lastx,cx), min(lasty,cy), abs(lastx-cx)+1, abs(lasty-cy)+1 };
|
||||
block s = {min(lastx, cx), min(lasty, cy), abs(lastx - cx) + 1,
|
||||
abs(lasty - cy) + 1};
|
||||
sel = s;
|
||||
selh = max(lasth,ch);
|
||||
selh = max(lasth, ch);
|
||||
correctsel();
|
||||
if(selset) rtex = *S(sel.x, sel.y);
|
||||
if (selset)
|
||||
rtex = *S(sel.x, sel.y);
|
||||
};
|
||||
|
||||
VAR(flrceil,0,0,2);
|
||||
VAR(flrceil, 0, 0, 2);
|
||||
|
||||
float sheight(sqr *s, sqr *t, float z) // finds out z height when cursor points at wall
|
||||
float
|
||||
sheight(
|
||||
sqr *s, sqr *t, float z) // finds out z height when cursor points at wall
|
||||
{
|
||||
return !flrceil //z-s->floor<s->ceil-z
|
||||
? (s->type==FHF ? s->floor-t->vdelta/4.0f : (float)s->floor)
|
||||
: (s->type==CHF ? s->ceil+t->vdelta/4.0f : (float)s->ceil);
|
||||
return !flrceil // z-s->floor<s->ceil-z
|
||||
? (s->type == FHF ? s->floor - t->vdelta / 4.0f
|
||||
: (float)s->floor)
|
||||
: (s->type == CHF ? s->ceil + t->vdelta / 4.0f
|
||||
: (float)s->ceil);
|
||||
};
|
||||
|
||||
void cursorupdate() // called every frame from hud
|
||||
void
|
||||
cursorupdate() // called every frame from hud
|
||||
{
|
||||
flrceil = ((int)(player1->pitch>=0))*2;
|
||||
flrceil = ((int)(player1->pitch >= 0)) * 2;
|
||||
|
||||
volatile float x = worldpos.x; // volatile needed to prevent msvc7 optimizer bug?
|
||||
volatile float x =
|
||||
worldpos.x; // volatile needed to prevent msvc7 optimizer bug?
|
||||
volatile float y = worldpos.y;
|
||||
volatile float z = worldpos.z;
|
||||
|
||||
cx = (int)x;
|
||||
cy = (int)y;
|
||||
|
||||
if(OUTBORD(cx, cy)) return;
|
||||
sqr *s = S(cx,cy);
|
||||
if (OUTBORD(cx, cy))
|
||||
return;
|
||||
sqr *s = S(cx, cy);
|
||||
|
||||
if(fabs(sheight(s,s,z)-z)>1) // selected wall
|
||||
if (fabs(sheight(s, s, z) - z) > 1) // selected wall
|
||||
{
|
||||
x += x>player1->o.x ? 0.5f : -0.5f; // find right wall cube
|
||||
y += y>player1->o.y ? 0.5f : -0.5f;
|
||||
x += x > player1->o.x ? 0.5f : -0.5f; // find right wall cube
|
||||
y += y > player1->o.y ? 0.5f : -0.5f;
|
||||
|
||||
cx = (int)x;
|
||||
cy = (int)y;
|
||||
|
||||
if(OUTBORD(cx, cy)) return;
|
||||
if (OUTBORD(cx, cy))
|
||||
return;
|
||||
};
|
||||
|
||||
if(dragging) makesel();
|
||||
if (dragging)
|
||||
makesel();
|
||||
|
||||
const int GRIDSIZE = 5;
|
||||
const float GRIDW = 0.5f;
|
||||
|
@ -142,40 +183,49 @@ void cursorupdate() // called every frame fr
|
|||
|
||||
// render editing grid
|
||||
|
||||
for(int ix = cx-GRIDSIZE; ix<=cx+GRIDSIZE; ix++) for(int iy = cy-GRIDSIZE; iy<=cy+GRIDSIZE; iy++)
|
||||
{
|
||||
if(OUTBORD(ix, iy)) continue;
|
||||
sqr *s = S(ix,iy);
|
||||
if(SOLID(s)) continue;
|
||||
for (int ix = cx - GRIDSIZE; ix <= cx + GRIDSIZE; ix++)
|
||||
for (int iy = cy - GRIDSIZE; iy <= cy + GRIDSIZE; iy++) {
|
||||
if (OUTBORD(ix, iy))
|
||||
continue;
|
||||
sqr *s = S(ix, iy);
|
||||
if (SOLID(s))
|
||||
continue;
|
||||
float h1 = sheight(s, s, z);
|
||||
float h2 = sheight(s, SWS(s,1,0,ssize), z);
|
||||
float h3 = sheight(s, SWS(s,1,1,ssize), z);
|
||||
float h4 = sheight(s, SWS(s,0,1,ssize), z);
|
||||
if(s->tag) linestyle(GRIDW, 0xFF, 0x40, 0x40);
|
||||
else if(s->type==FHF || s->type==CHF) linestyle(GRIDW, 0x80, 0xFF, 0x80);
|
||||
else linestyle(GRIDW, 0x80, 0x80, 0x80);
|
||||
block b = { ix, iy, 1, 1 };
|
||||
float h2 = sheight(s, SWS(s, 1, 0, ssize), z);
|
||||
float h3 = sheight(s, SWS(s, 1, 1, ssize), z);
|
||||
float h4 = sheight(s, SWS(s, 0, 1, ssize), z);
|
||||
if (s->tag)
|
||||
linestyle(GRIDW, 0xFF, 0x40, 0x40);
|
||||
else if (s->type == FHF || s->type == CHF)
|
||||
linestyle(GRIDW, 0x80, 0xFF, 0x80);
|
||||
else
|
||||
linestyle(GRIDW, 0x80, 0x80, 0x80);
|
||||
block b = {ix, iy, 1, 1};
|
||||
box(b, h1, h2, h3, h4);
|
||||
linestyle(GRID8, 0x40, 0x40, 0xFF);
|
||||
if(!(ix&GRIDM)) line(ix, iy, h1, ix, iy+1, h4);
|
||||
if(!(ix+1&GRIDM)) line(ix+1, iy, h2, ix+1, iy+1, h3);
|
||||
if(!(iy&GRIDM)) line(ix, iy, h1, ix+1, iy, h2);
|
||||
if(!(iy+1&GRIDM)) line(ix, iy+1, h4, ix+1, iy+1, h3);
|
||||
if (!(ix & GRIDM))
|
||||
line(ix, iy, h1, ix, iy + 1, h4);
|
||||
if (!(ix + 1 & GRIDM))
|
||||
line(ix + 1, iy, h2, ix + 1, iy + 1, h3);
|
||||
if (!(iy & GRIDM))
|
||||
line(ix, iy, h1, ix + 1, iy, h2);
|
||||
if (!(iy + 1 & GRIDM))
|
||||
line(ix, iy + 1, h4, ix + 1, iy + 1, h3);
|
||||
};
|
||||
|
||||
if(!SOLID(s))
|
||||
{
|
||||
if (!SOLID(s)) {
|
||||
float ih = sheight(s, s, z);
|
||||
linestyle(GRIDS, 0xFF, 0xFF, 0xFF);
|
||||
block b = { cx, cy, 1, 1 };
|
||||
box(b, ih, sheight(s, SWS(s,1,0,ssize), z), sheight(s, SWS(s,1,1,ssize), z), sheight(s, SWS(s,0,1,ssize), z));
|
||||
block b = {cx, cy, 1, 1};
|
||||
box(b, ih, sheight(s, SWS(s, 1, 0, ssize), z),
|
||||
sheight(s, SWS(s, 1, 1, ssize), z),
|
||||
sheight(s, SWS(s, 0, 1, ssize), z));
|
||||
linestyle(GRIDS, 0xFF, 0x00, 0x00);
|
||||
dot(cx, cy, ih);
|
||||
ch = (int)ih;
|
||||
};
|
||||
|
||||
if(selset)
|
||||
{
|
||||
if (selset) {
|
||||
linestyle(GRIDS, 0xFF, 0x40, 0x40);
|
||||
box(sel, (float)selh, (float)selh, (float)selh, (float)selh);
|
||||
};
|
||||
|
@ -184,26 +234,33 @@ void cursorupdate() // called every frame fr
|
|||
vector<block *> undos; // unlimited undo
|
||||
VARP(undomegs, 0, 1, 10); // bounded by n megs
|
||||
|
||||
void pruneundos(int maxremain) // bound memory
|
||||
void
|
||||
pruneundos(int maxremain) // bound memory
|
||||
{
|
||||
int t = 0;
|
||||
loopvrev(undos)
|
||||
{
|
||||
t += undos[i]->xs*undos[i]->ys*sizeof(sqr);
|
||||
if(t>maxremain) free(undos.remove(i));
|
||||
t += undos[i]->xs * undos[i]->ys * sizeof(sqr);
|
||||
if (t > maxremain)
|
||||
free(undos.remove(i));
|
||||
};
|
||||
};
|
||||
|
||||
void makeundo()
|
||||
void
|
||||
makeundo()
|
||||
{
|
||||
undos.add(blockcopy(sel));
|
||||
pruneundos(undomegs<<20);
|
||||
pruneundos(undomegs << 20);
|
||||
};
|
||||
|
||||
void editundo()
|
||||
void
|
||||
editundo()
|
||||
{
|
||||
EDITMP;
|
||||
if(undos.empty()) { conoutf("nothing more to undo"); return; };
|
||||
if (undos.empty()) {
|
||||
conoutf("nothing more to undo");
|
||||
return;
|
||||
};
|
||||
block *p = undos.pop();
|
||||
blockpaste(*p);
|
||||
free(p);
|
||||
|
@ -211,47 +268,58 @@ void editundo()
|
|||
|
||||
block *copybuf = NULL;
|
||||
|
||||
void copy()
|
||||
void
|
||||
copy()
|
||||
{
|
||||
EDITSELMP;
|
||||
if(copybuf) free(copybuf);
|
||||
if (copybuf)
|
||||
free(copybuf);
|
||||
copybuf = blockcopy(sel);
|
||||
};
|
||||
|
||||
void paste()
|
||||
void
|
||||
paste()
|
||||
{
|
||||
EDITMP;
|
||||
if(!copybuf) { conoutf("nothing to paste"); return; };
|
||||
if (!copybuf) {
|
||||
conoutf("nothing to paste");
|
||||
return;
|
||||
};
|
||||
sel.xs = copybuf->xs;
|
||||
sel.ys = copybuf->ys;
|
||||
correctsel();
|
||||
if(!selset || sel.xs!=copybuf->xs || sel.ys!=copybuf->ys) { conoutf("incorrect selection"); return; };
|
||||
if (!selset || sel.xs != copybuf->xs || sel.ys != copybuf->ys) {
|
||||
conoutf("incorrect selection");
|
||||
return;
|
||||
};
|
||||
makeundo();
|
||||
copybuf->x = sel.x;
|
||||
copybuf->y = sel.y;
|
||||
blockpaste(*copybuf);
|
||||
};
|
||||
|
||||
void tofronttex() // maintain most recently used of the texture lists when applying texture
|
||||
void
|
||||
tofronttex() // maintain most recently used of the texture lists when applying
|
||||
// texture
|
||||
{
|
||||
loopi(3)
|
||||
{
|
||||
int c = curedittex[i];
|
||||
if(c>=0)
|
||||
{
|
||||
if (c >= 0) {
|
||||
uchar *p = hdr.texlists[i];
|
||||
int t = p[c];
|
||||
for(int a = c-1; a>=0; a--) p[a+1] = p[a];
|
||||
for (int a = c - 1; a >= 0; a--)
|
||||
p[a + 1] = p[a];
|
||||
p[0] = t;
|
||||
curedittex[i] = -1;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
void editdrag(bool isdown)
|
||||
void
|
||||
editdrag(bool isdown)
|
||||
{
|
||||
if(dragging = isdown)
|
||||
{
|
||||
if (dragging = isdown) {
|
||||
lastx = cx;
|
||||
lasty = cy;
|
||||
lasth = ch;
|
||||
|
@ -262,117 +330,167 @@ void editdrag(bool isdown)
|
|||
};
|
||||
|
||||
// the core editing function. all the *xy functions perform the core operations
|
||||
// and are also called directly from the network, the function below it is strictly
|
||||
// triggered locally. They all have very similar structure.
|
||||
// and are also called directly from the network, the function below it is
|
||||
// strictly triggered locally. They all have very similar structure.
|
||||
|
||||
void editheightxy(bool isfloor, int amount, block &sel)
|
||||
void
|
||||
editheightxy(bool isfloor, int amount, block &sel)
|
||||
{
|
||||
loopselxy(if(isfloor)
|
||||
{
|
||||
loopselxy(
|
||||
if (isfloor) {
|
||||
s->floor += amount;
|
||||
if(s->floor>=s->ceil) s->floor = s->ceil-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s->floor >= s->ceil)
|
||||
s->floor = s->ceil - 1;
|
||||
} else {
|
||||
s->ceil += amount;
|
||||
if(s->ceil<=s->floor) s->ceil = s->floor+1;
|
||||
if (s->ceil <= s->floor)
|
||||
s->ceil = s->floor + 1;
|
||||
});
|
||||
};
|
||||
|
||||
void editheight(int flr, int amount)
|
||||
void
|
||||
editheight(int flr, int amount)
|
||||
{
|
||||
EDITSEL;
|
||||
bool isfloor = flr==0;
|
||||
bool isfloor = flr == 0;
|
||||
editheightxy(isfloor, amount, sel);
|
||||
addmsg(1, 7, SV_EDITH, sel.x, sel.y, sel.xs, sel.ys, isfloor, amount);
|
||||
};
|
||||
|
||||
COMMAND(editheight, ARG_2INT);
|
||||
|
||||
void edittexxy(int type, int t, block &sel)
|
||||
void
|
||||
edittexxy(int type, int t, block &sel)
|
||||
{
|
||||
loopselxy(switch(type)
|
||||
{
|
||||
case 0: s->ftex = t; break;
|
||||
case 1: s->wtex = t; break;
|
||||
case 2: s->ctex = t; break;
|
||||
case 3: s->utex = t; break;
|
||||
loopselxy(switch (type) {
|
||||
case 0:
|
||||
s->ftex = t;
|
||||
break;
|
||||
case 1:
|
||||
s->wtex = t;
|
||||
break;
|
||||
case 2:
|
||||
s->ctex = t;
|
||||
break;
|
||||
case 3:
|
||||
s->utex = t;
|
||||
break;
|
||||
});
|
||||
};
|
||||
|
||||
void edittex(int type, int dir)
|
||||
void
|
||||
edittex(int type, int dir)
|
||||
{
|
||||
EDITSEL;
|
||||
if(type<0 || type>3) return;
|
||||
if(type!=lasttype) { tofronttex(); lasttype = type; };
|
||||
int atype = type==3 ? 1 : type;
|
||||
if (type < 0 || type > 3)
|
||||
return;
|
||||
if (type != lasttype) {
|
||||
tofronttex();
|
||||
lasttype = type;
|
||||
};
|
||||
int atype = type == 3 ? 1 : type;
|
||||
int i = curedittex[atype];
|
||||
i = i<0 ? 0 : i+dir;
|
||||
i = i < 0 ? 0 : i + dir;
|
||||
curedittex[atype] = i = min(max(i, 0), 255);
|
||||
int t = lasttex = hdr.texlists[atype][i];
|
||||
edittexxy(type, t, sel);
|
||||
addmsg(1, 7, SV_EDITT, sel.x, sel.y, sel.xs, sel.ys, type, t);
|
||||
};
|
||||
|
||||
void replace()
|
||||
void
|
||||
replace()
|
||||
{
|
||||
EDITSELMP;
|
||||
loop(x,ssize) loop(y,ssize)
|
||||
loop(x, ssize) loop(y, ssize)
|
||||
{
|
||||
sqr *s = S(x, y);
|
||||
switch(lasttype)
|
||||
{
|
||||
case 0: if(s->ftex == rtex.ftex) s->ftex = lasttex; break;
|
||||
case 1: if(s->wtex == rtex.wtex) s->wtex = lasttex; break;
|
||||
case 2: if(s->ctex == rtex.ctex) s->ctex = lasttex; break;
|
||||
case 3: if(s->utex == rtex.utex) s->utex = lasttex; break;
|
||||
switch (lasttype) {
|
||||
case 0:
|
||||
if (s->ftex == rtex.ftex)
|
||||
s->ftex = lasttex;
|
||||
break;
|
||||
case 1:
|
||||
if (s->wtex == rtex.wtex)
|
||||
s->wtex = lasttex;
|
||||
break;
|
||||
case 2:
|
||||
if (s->ctex == rtex.ctex)
|
||||
s->ctex = lasttex;
|
||||
break;
|
||||
case 3:
|
||||
if (s->utex == rtex.utex)
|
||||
s->utex = lasttex;
|
||||
break;
|
||||
};
|
||||
};
|
||||
block b = { 0, 0, ssize, ssize };
|
||||
block b = {0, 0, ssize, ssize};
|
||||
remip(b);
|
||||
};
|
||||
|
||||
void edittypexy(int type, block &sel)
|
||||
void
|
||||
edittypexy(int type, block &sel)
|
||||
{
|
||||
loopselxy(s->type = type);
|
||||
};
|
||||
|
||||
void edittype(int type)
|
||||
void
|
||||
edittype(int type)
|
||||
{
|
||||
EDITSEL;
|
||||
if(type==CORNER && (sel.xs!=sel.ys || sel.xs==3 || sel.xs>4 && sel.xs!=8
|
||||
|| sel.x&~-sel.xs || sel.y&~-sel.ys))
|
||||
{ conoutf("corner selection must be power of 2 aligned"); return; };
|
||||
if (type == CORNER &&
|
||||
(sel.xs != sel.ys || sel.xs == 3 || sel.xs > 4 && sel.xs != 8 ||
|
||||
sel.x & ~-sel.xs || sel.y & ~-sel.ys)) {
|
||||
conoutf("corner selection must be power of 2 aligned");
|
||||
return;
|
||||
};
|
||||
edittypexy(type, sel);
|
||||
addmsg(1, 6, SV_EDITS, sel.x, sel.y, sel.xs, sel.ys, type);
|
||||
};
|
||||
|
||||
void heightfield(int t) { edittype(t==0 ? FHF : CHF); };
|
||||
void solid(int t) { edittype(t==0 ? SPACE : SOLID); };
|
||||
void corner() { edittype(CORNER); };
|
||||
void
|
||||
heightfield(int t)
|
||||
{
|
||||
edittype(t == 0 ? FHF : CHF);
|
||||
};
|
||||
void
|
||||
solid(int t)
|
||||
{
|
||||
edittype(t == 0 ? SPACE : SOLID);
|
||||
};
|
||||
void
|
||||
corner()
|
||||
{
|
||||
edittype(CORNER);
|
||||
};
|
||||
|
||||
COMMAND(heightfield, ARG_1INT);
|
||||
COMMAND(solid, ARG_1INT);
|
||||
COMMAND(corner, ARG_NONE);
|
||||
|
||||
void editequalisexy(bool isfloor, block &sel)
|
||||
void
|
||||
editequalisexy(bool isfloor, block &sel)
|
||||
{
|
||||
int low = 127, hi = -128;
|
||||
loopselxy(
|
||||
{
|
||||
if(s->floor<low) low = s->floor;
|
||||
if(s->ceil>hi) hi = s->ceil;
|
||||
loopselxy({
|
||||
if (s->floor < low)
|
||||
low = s->floor;
|
||||
if (s->ceil > hi)
|
||||
hi = s->ceil;
|
||||
});
|
||||
loopselxy(
|
||||
{
|
||||
if(isfloor) s->floor = low; else s->ceil = hi;
|
||||
if(s->floor>=s->ceil) s->floor = s->ceil-1;
|
||||
loopselxy({
|
||||
if (isfloor)
|
||||
s->floor = low;
|
||||
else
|
||||
s->ceil = hi;
|
||||
if (s->floor >= s->ceil)
|
||||
s->floor = s->ceil - 1;
|
||||
});
|
||||
};
|
||||
|
||||
void equalize(int flr)
|
||||
void
|
||||
equalize(int flr)
|
||||
{
|
||||
bool isfloor = flr==0;
|
||||
bool isfloor = flr == 0;
|
||||
EDITSEL;
|
||||
editequalisexy(isfloor, sel);
|
||||
addmsg(1, 6, SV_EDITE, sel.x, sel.y, sel.xs, sel.ys, isfloor);
|
||||
|
@ -380,13 +498,15 @@ void equalize(int flr)
|
|||
|
||||
COMMAND(equalize, ARG_1INT);
|
||||
|
||||
void setvdeltaxy(int delta, block &sel)
|
||||
void
|
||||
setvdeltaxy(int delta, block &sel)
|
||||
{
|
||||
loopselxy(s->vdelta = max(s->vdelta+delta, 0));
|
||||
loopselxy(s->vdelta = max(s->vdelta + delta, 0));
|
||||
remipmore(sel);
|
||||
};
|
||||
|
||||
void setvdelta(int delta)
|
||||
void
|
||||
setvdelta(int delta)
|
||||
{
|
||||
EDITSEL;
|
||||
setvdeltaxy(delta, sel);
|
||||
|
@ -397,44 +517,54 @@ const int MAXARCHVERT = 50;
|
|||
int archverts[MAXARCHVERT][MAXARCHVERT];
|
||||
bool archvinit = false;
|
||||
|
||||
void archvertex(int span, int vert, int delta)
|
||||
void
|
||||
archvertex(int span, int vert, int delta)
|
||||
{
|
||||
if(!archvinit)
|
||||
{
|
||||
if (!archvinit) {
|
||||
archvinit = true;
|
||||
loop(s,MAXARCHVERT) loop(v,MAXARCHVERT) archverts[s][v] = 0;
|
||||
loop(s, MAXARCHVERT) loop(v, MAXARCHVERT) archverts[s][v] = 0;
|
||||
};
|
||||
if(span>=MAXARCHVERT || vert>=MAXARCHVERT || span<0 || vert<0) return;
|
||||
if (span >= MAXARCHVERT || vert >= MAXARCHVERT || span < 0 || vert < 0)
|
||||
return;
|
||||
archverts[span][vert] = delta;
|
||||
};
|
||||
|
||||
void arch(int sidedelta, int _a)
|
||||
void
|
||||
arch(int sidedelta, int _a)
|
||||
{
|
||||
EDITSELMP;
|
||||
sel.xs++;
|
||||
sel.ys++;
|
||||
if(sel.xs>MAXARCHVERT) sel.xs = MAXARCHVERT;
|
||||
if(sel.ys>MAXARCHVERT) sel.ys = MAXARCHVERT;
|
||||
loopselxy(s->vdelta =
|
||||
sel.xs>sel.ys
|
||||
? (archverts[sel.xs-1][x] + (y==0 || y==sel.ys-1 ? sidedelta : 0))
|
||||
: (archverts[sel.ys-1][y] + (x==0 || x==sel.xs-1 ? sidedelta : 0)));
|
||||
if (sel.xs > MAXARCHVERT)
|
||||
sel.xs = MAXARCHVERT;
|
||||
if (sel.ys > MAXARCHVERT)
|
||||
sel.ys = MAXARCHVERT;
|
||||
loopselxy(
|
||||
s->vdelta = sel.xs > sel.ys
|
||||
? (archverts[sel.xs - 1][x] +
|
||||
(y == 0 || y == sel.ys - 1 ? sidedelta : 0))
|
||||
: (archverts[sel.ys - 1][y] +
|
||||
(x == 0 || x == sel.xs - 1 ? sidedelta : 0)));
|
||||
remipmore(sel);
|
||||
};
|
||||
|
||||
void slope(int xd, int yd)
|
||||
void
|
||||
slope(int xd, int yd)
|
||||
{
|
||||
EDITSELMP;
|
||||
int off = 0;
|
||||
if(xd<0) off -= xd*sel.xs;
|
||||
if(yd<0) off -= yd*sel.ys;
|
||||
if (xd < 0)
|
||||
off -= xd * sel.xs;
|
||||
if (yd < 0)
|
||||
off -= yd * sel.ys;
|
||||
sel.xs++;
|
||||
sel.ys++;
|
||||
loopselxy(s->vdelta = xd*x+yd*y+off);
|
||||
loopselxy(s->vdelta = xd * x + yd * y + off);
|
||||
remipmore(sel);
|
||||
};
|
||||
|
||||
void perlin(int scale, int seed, int psize)
|
||||
void
|
||||
perlin(int scale, int seed, int psize)
|
||||
{
|
||||
EDITSELMP;
|
||||
sel.xs++;
|
||||
|
@ -450,24 +580,26 @@ void perlin(int scale, int seed, int psize)
|
|||
sel.ys--;
|
||||
};
|
||||
|
||||
VARF(fullbright, 0, 0, 1,
|
||||
if(fullbright)
|
||||
{
|
||||
if(noteditmode()) return;
|
||||
VARF(
|
||||
fullbright, 0, 0, 1, if (fullbright) {
|
||||
if (noteditmode())
|
||||
return;
|
||||
loopi(mipsize) world[i].r = world[i].g = world[i].b = 176;
|
||||
};
|
||||
);
|
||||
};);
|
||||
|
||||
void edittag(int tag)
|
||||
void
|
||||
edittag(int tag)
|
||||
{
|
||||
EDITSELMP;
|
||||
loopselxy(s->tag = tag);
|
||||
};
|
||||
|
||||
void newent(char *what, char *a1, char *a2, char *a3, char *a4)
|
||||
void
|
||||
newent(char *what, char *a1, char *a2, char *a3, char *a4)
|
||||
{
|
||||
EDITSEL;
|
||||
newentity(sel.x, sel.y, (int)player1->o.z, what, ATOI(a1), ATOI(a2), ATOI(a3), ATOI(a4));
|
||||
newentity(sel.x, sel.y, (int)player1->o.z, what, ATOI(a1), ATOI(a2),
|
||||
ATOI(a3), ATOI(a4));
|
||||
};
|
||||
|
||||
COMMANDN(select, selectpos, ARG_4INT);
|
||||
|
@ -483,5 +615,3 @@ COMMAND(paste, ARG_NONE);
|
|||
COMMAND(edittex, ARG_2INT);
|
||||
COMMAND(newent, ARG_5STR);
|
||||
COMMAND(perlin, ARG_3INT);
|
||||
|
||||
|
||||
|
|
312
src/entities.cxx
312
src/entities.cxx
|
@ -4,96 +4,172 @@
|
|||
|
||||
vector<entity> ents;
|
||||
|
||||
char *entmdlnames[] =
|
||||
{
|
||||
"shells", "bullets", "rockets", "rrounds", "health", "boost",
|
||||
"g_armour", "y_armour", "quad", "teleporter",
|
||||
char *entmdlnames[] = {
|
||||
"shells",
|
||||
"bullets",
|
||||
"rockets",
|
||||
"rrounds",
|
||||
"health",
|
||||
"boost",
|
||||
"g_armour",
|
||||
"y_armour",
|
||||
"quad",
|
||||
"teleporter",
|
||||
};
|
||||
|
||||
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)
|
||||
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);
|
||||
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()
|
||||
void
|
||||
renderentities()
|
||||
{
|
||||
if(lastmillis>triggertime+1000) triggertime = 0;
|
||||
if (lastmillis > triggertime + 1000)
|
||||
triggertime = 0;
|
||||
loopv(ents)
|
||||
{
|
||||
entity &e = ents[i];
|
||||
if(e.type==MAPMODEL)
|
||||
{
|
||||
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 && e.type!=TELEPORT) continue;
|
||||
if(e.type<I_SHELLS || e.type>TELEPORT) continue;
|
||||
renderent(e, entmdlnames[e.type-I_SHELLS], (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/10.0f);
|
||||
}
|
||||
else switch(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 && e.type != TELEPORT)
|
||||
continue;
|
||||
if (e.type < I_SHELLS || e.type > TELEPORT)
|
||||
continue;
|
||||
renderent(e, entmdlnames[e.type - I_SHELLS],
|
||||
(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));
|
||||
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;
|
||||
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, max, sound; } itemstats[] =
|
||||
struct itemstat {
|
||||
int add, max, sound;
|
||||
} itemstats[] = {
|
||||
10,
|
||||
50,
|
||||
S_ITEMAMMO,
|
||||
20,
|
||||
100,
|
||||
S_ITEMAMMO,
|
||||
5,
|
||||
25,
|
||||
S_ITEMAMMO,
|
||||
5,
|
||||
25,
|
||||
S_ITEMAMMO,
|
||||
25,
|
||||
100,
|
||||
S_ITEMHEALTH,
|
||||
50,
|
||||
200,
|
||||
S_ITEMHEALTH,
|
||||
100,
|
||||
100,
|
||||
S_ITEMARMOUR,
|
||||
150,
|
||||
150,
|
||||
S_ITEMARMOUR,
|
||||
20000,
|
||||
30000,
|
||||
S_ITEMPUP,
|
||||
};
|
||||
|
||||
void
|
||||
baseammo(int gun)
|
||||
{
|
||||
10, 50, S_ITEMAMMO,
|
||||
20, 100, S_ITEMAMMO,
|
||||
5, 25, S_ITEMAMMO,
|
||||
5, 25, S_ITEMAMMO,
|
||||
25, 100, S_ITEMHEALTH,
|
||||
50, 200, S_ITEMHEALTH,
|
||||
100, 100, S_ITEMARMOUR,
|
||||
150, 150, S_ITEMARMOUR,
|
||||
20000, 30000, S_ITEMPUP,
|
||||
player1->ammo[gun] = itemstats[gun - 1].add * 2;
|
||||
};
|
||||
|
||||
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)
|
||||
void
|
||||
radditem(int i, int &v)
|
||||
{
|
||||
itemstat &is = itemstats[ents[i].type-I_SHELLS];
|
||||
itemstat &is = itemstats[ents[i].type - I_SHELLS];
|
||||
ents[i].spawned = false;
|
||||
v += is.add;
|
||||
if(v>is.max) v = is.max;
|
||||
if (v > is.max)
|
||||
v = is.max;
|
||||
playsoundc(is.sound);
|
||||
};
|
||||
|
||||
void realpickup(int n, dynent *d)
|
||||
void
|
||||
realpickup(int n, dynent *d)
|
||||
{
|
||||
switch(ents[n].type)
|
||||
{
|
||||
case I_SHELLS: radditem(n, d->ammo[1]); break;
|
||||
case I_BULLETS: radditem(n, d->ammo[2]); break;
|
||||
case I_ROCKETS: radditem(n, d->ammo[3]); break;
|
||||
case I_ROUNDS: radditem(n, d->ammo[4]); break;
|
||||
case I_HEALTH: radditem(n, d->health); break;
|
||||
case I_BOOST: radditem(n, d->health); break;
|
||||
switch (ents[n].type) {
|
||||
case I_SHELLS:
|
||||
radditem(n, d->ammo[1]);
|
||||
break;
|
||||
case I_BULLETS:
|
||||
radditem(n, d->ammo[2]);
|
||||
break;
|
||||
case I_ROCKETS:
|
||||
radditem(n, d->ammo[3]);
|
||||
break;
|
||||
case I_ROUNDS:
|
||||
radditem(n, d->ammo[4]);
|
||||
break;
|
||||
case I_HEALTH:
|
||||
radditem(n, d->health);
|
||||
break;
|
||||
case I_BOOST:
|
||||
radditem(n, d->health);
|
||||
break;
|
||||
|
||||
case I_GREENARMOUR:
|
||||
radditem(n, d->armour);
|
||||
|
@ -114,25 +190,32 @@ void realpickup(int n, dynent *d)
|
|||
|
||||
// these functions are called when the client touches the item
|
||||
|
||||
void additem(int i, int &v, int spawnsec)
|
||||
void
|
||||
additem(int i, int &v, int spawnsec)
|
||||
{
|
||||
if(v<itemstats[ents[i].type-I_SHELLS].max) // don't pick up if not needed
|
||||
if (v < itemstats[ents[i].type - I_SHELLS]
|
||||
.max) // don't pick up if not needed
|
||||
{
|
||||
addmsg(1, 3, SV_ITEMPICKUP, i, m_classicsp ? 100000 : spawnsec); // first ask the server for an ack
|
||||
addmsg(1, 3, SV_ITEMPICKUP, i,
|
||||
m_classicsp ? 100000
|
||||
: spawnsec); // first ask the server for an ack
|
||||
ents[i].spawned = false; // even if someone else gets it first
|
||||
};
|
||||
};
|
||||
|
||||
void teleport(int n, dynent *d) // also used by monsters
|
||||
void
|
||||
teleport(int n, dynent *d) // also used by monsters
|
||||
{
|
||||
int e = -1, tag = ents[n].attr1, beenhere = -1;
|
||||
for(;;)
|
||||
{
|
||||
e = findentity(TELEDEST, e+1);
|
||||
if(e==beenhere || e<0) { conoutf("no teleport destination for tag %d", tag); return; };
|
||||
if(beenhere<0) beenhere = e;
|
||||
if(ents[e].attr2==tag)
|
||||
{
|
||||
for (;;) {
|
||||
e = findentity(TELEDEST, e + 1);
|
||||
if (e == beenhere || e < 0) {
|
||||
conoutf("no teleport destination for tag %d", tag);
|
||||
return;
|
||||
};
|
||||
if (beenhere < 0)
|
||||
beenhere = e;
|
||||
if (ents[e].attr2 == tag) {
|
||||
d->o.x = ents[e].x;
|
||||
d->o.y = ents[e].y;
|
||||
d->o.z = ents[e].z;
|
||||
|
@ -146,24 +229,38 @@ void teleport(int n, dynent *d) // also used by monsters
|
|||
};
|
||||
};
|
||||
|
||||
void pickup(int n, dynent *d)
|
||||
void
|
||||
pickup(int n, dynent *d)
|
||||
{
|
||||
int np = 1;
|
||||
loopv(players) if(players[i]) np++;
|
||||
np = np<3 ? 4 : (np>4 ? 2 : 3); // spawn times are dependent on number of players
|
||||
int ammo = np*2;
|
||||
switch(ents[n].type)
|
||||
{
|
||||
case I_SHELLS: additem(n, d->ammo[1], ammo); break;
|
||||
case I_BULLETS: additem(n, d->ammo[2], ammo); break;
|
||||
case I_ROCKETS: additem(n, d->ammo[3], ammo); break;
|
||||
case I_ROUNDS: additem(n, d->ammo[4], ammo); break;
|
||||
case I_HEALTH: additem(n, d->health, np*5); break;
|
||||
case I_BOOST: additem(n, d->health, 60); break;
|
||||
loopv(players) if (players[i]) np++;
|
||||
np = np < 3 ? 4 : (np > 4 ? 2 : 3); // spawn times are dependent on
|
||||
// number of players
|
||||
int ammo = np * 2;
|
||||
switch (ents[n].type) {
|
||||
case I_SHELLS:
|
||||
additem(n, d->ammo[1], ammo);
|
||||
break;
|
||||
case I_BULLETS:
|
||||
additem(n, d->ammo[2], ammo);
|
||||
break;
|
||||
case I_ROCKETS:
|
||||
additem(n, d->ammo[3], ammo);
|
||||
break;
|
||||
case I_ROUNDS:
|
||||
additem(n, d->ammo[4], ammo);
|
||||
break;
|
||||
case I_HEALTH:
|
||||
additem(n, d->health, np * 5);
|
||||
break;
|
||||
case I_BOOST:
|
||||
additem(n, d->health, 60);
|
||||
break;
|
||||
|
||||
case I_GREENARMOUR:
|
||||
// (100h/100g only absorbs 166 damage)
|
||||
if(d->armourtype==A_YELLOW && d->armour>66) break;
|
||||
if (d->armourtype == A_YELLOW && d->armour > 66)
|
||||
break;
|
||||
additem(n, d->armour, 20);
|
||||
break;
|
||||
|
||||
|
@ -178,24 +275,26 @@ void pickup(int n, dynent *d)
|
|||
case CARROT:
|
||||
ents[n].spawned = false;
|
||||
triggertime = lastmillis;
|
||||
trigger(ents[n].attr1, ents[n].attr2, false); // needs to go over server for multiplayer
|
||||
trigger(ents[n].attr1, ents[n].attr2,
|
||||
false); // needs to go over server for multiplayer
|
||||
break;
|
||||
|
||||
case TELEPORT:
|
||||
{
|
||||
case TELEPORT: {
|
||||
static int lastteleport = 0;
|
||||
if(lastmillis-lastteleport<500) break;
|
||||
if (lastmillis - lastteleport < 500)
|
||||
break;
|
||||
lastteleport = lastmillis;
|
||||
teleport(n, d);
|
||||
break;
|
||||
};
|
||||
|
||||
case JUMPPAD:
|
||||
{
|
||||
case JUMPPAD: {
|
||||
static int lastjumppad = 0;
|
||||
if(lastmillis-lastjumppad<300) break;
|
||||
if (lastmillis - lastjumppad < 300)
|
||||
break;
|
||||
lastjumppad = lastmillis;
|
||||
vec v = { (int)(char)ents[n].attr3/10.0f, (int)(char)ents[n].attr2/10.0f, ents[n].attr1/10.0f };
|
||||
vec v = {(int)(char)ents[n].attr3 / 10.0f,
|
||||
(int)(char)ents[n].attr2 / 10.0f, ents[n].attr1 / 10.0f};
|
||||
player1->vel.z = 0;
|
||||
vadd(player1->vel, v);
|
||||
playsoundc(S_JUMPPAD);
|
||||
|
@ -204,39 +303,56 @@ void pickup(int n, dynent *d)
|
|||
};
|
||||
};
|
||||
|
||||
void checkitems()
|
||||
void
|
||||
checkitems()
|
||||
{
|
||||
if(editmode) return;
|
||||
if (editmode)
|
||||
return;
|
||||
loopv(ents)
|
||||
{
|
||||
entity &e = ents[i];
|
||||
if(e.type==NOTUSED) continue;
|
||||
if(!ents[i].spawned && e.type!=TELEPORT && e.type!=JUMPPAD) continue;
|
||||
if(OUTBORD(e.x, e.y)) continue;
|
||||
vec v = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight };
|
||||
if (e.type == NOTUSED)
|
||||
continue;
|
||||
if (!ents[i].spawned && e.type != TELEPORT && e.type != JUMPPAD)
|
||||
continue;
|
||||
if (OUTBORD(e.x, e.y))
|
||||
continue;
|
||||
vec v = {e.x, e.y, S(e.x, e.y)->floor + player1->eyeheight};
|
||||
vdist(dist, t, player1->o, v);
|
||||
if(dist<(e.type==TELEPORT ? 4 : 2.5)) pickup(i, player1);
|
||||
if (dist < (e.type == TELEPORT ? 4 : 2.5))
|
||||
pickup(i, player1);
|
||||
};
|
||||
};
|
||||
|
||||
void checkquad(int time)
|
||||
void
|
||||
checkquad(int time)
|
||||
{
|
||||
if(player1->quadmillis && (player1->quadmillis -= time)<0)
|
||||
{
|
||||
if (player1->quadmillis && (player1->quadmillis -= time) < 0) {
|
||||
player1->quadmillis = 0;
|
||||
playsoundc(S_PUPOUT);
|
||||
conoutf("quad damage is over");
|
||||
};
|
||||
};
|
||||
|
||||
void putitems(uchar *&p) // puts items in network stream and also spawns them locally
|
||||
void
|
||||
putitems(uchar *&p) // puts items in network stream and also spawns them locally
|
||||
{
|
||||
loopv(ents) if((ents[i].type>=I_SHELLS && ents[i].type<=I_QUAD) || ents[i].type==CARROT)
|
||||
loopv(ents) if ((ents[i].type >= I_SHELLS && 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(uint i, bool on) { if(i<(uint)ents.length()) ents[i].spawned = on; };
|
||||
void
|
||||
resetspawns()
|
||||
{
|
||||
loopv(ents) ents[i].spawned = false;
|
||||
};
|
||||
void
|
||||
setspawn(uint i, bool on)
|
||||
{
|
||||
if (i < (uint)ents.length())
|
||||
ents[i].spawned = on;
|
||||
};
|
||||
|
|
206
src/main.cxx
206
src/main.cxx
|
@ -2,7 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
void cleanup(char *msg) // single program exit point;
|
||||
void
|
||||
cleanup(char *msg) // single program exit point;
|
||||
{
|
||||
stop();
|
||||
disconnect(true);
|
||||
|
@ -11,57 +12,67 @@ void cleanup(char *msg) // single program exit point;
|
|||
cleansound();
|
||||
cleanupserver();
|
||||
SDL_ShowCursor(1);
|
||||
if(msg)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
MessageBox(NULL, msg, "cube fatal error", MB_OK|MB_SYSTEMMODAL);
|
||||
#else
|
||||
if (msg) {
|
||||
#ifdef _WIN32
|
||||
MessageBox(
|
||||
NULL, msg, "cube fatal error", MB_OK | MB_SYSTEMMODAL);
|
||||
#else
|
||||
printf(msg);
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
SDL_Quit();
|
||||
exit(1);
|
||||
};
|
||||
|
||||
void quit() // normal exit
|
||||
void
|
||||
quit() // normal exit
|
||||
{
|
||||
writeservercfg();
|
||||
cleanup(NULL);
|
||||
};
|
||||
|
||||
void fatal(char *s, char *o) // failure exit
|
||||
void
|
||||
fatal(char *s, char *o) // failure exit
|
||||
{
|
||||
sprintf_sd(msg)("%s%s (%s)\n", s, o, SDL_GetError());
|
||||
cleanup(msg);
|
||||
};
|
||||
|
||||
void *alloc(int s) // for some big chunks... most other allocs use the memory pool
|
||||
void *
|
||||
alloc(int s) // for some big chunks... most other allocs use the memory pool
|
||||
{
|
||||
void *b = calloc(1,s);
|
||||
if(!b) fatal("out of memory!");
|
||||
void *b = calloc(1, s);
|
||||
if (!b)
|
||||
fatal("out of memory!");
|
||||
return b;
|
||||
};
|
||||
|
||||
int scr_w = 640;
|
||||
int scr_h = 480;
|
||||
|
||||
void screenshot()
|
||||
void
|
||||
screenshot()
|
||||
{
|
||||
SDL_Surface *image;
|
||||
SDL_Surface *temp;
|
||||
int idx;
|
||||
if(image = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0))
|
||||
{
|
||||
if(temp = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0))
|
||||
{
|
||||
glReadPixels(0, 0, scr_w, scr_h, GL_RGB, GL_UNSIGNED_BYTE, image->pixels);
|
||||
for (idx = 0; idx<scr_h; idx++)
|
||||
{
|
||||
char *dest = (char *)temp->pixels+3*scr_w*idx;
|
||||
memcpy(dest, (char *)image->pixels+3*scr_w*(scr_h-1-idx), 3*scr_w);
|
||||
if (image = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24,
|
||||
0x0000FF, 0x00FF00, 0xFF0000, 0)) {
|
||||
if (temp = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24,
|
||||
0x0000FF, 0x00FF00, 0xFF0000, 0)) {
|
||||
glReadPixels(0, 0, scr_w, scr_h, GL_RGB,
|
||||
GL_UNSIGNED_BYTE, image->pixels);
|
||||
for (idx = 0; idx < scr_h; idx++) {
|
||||
char *dest =
|
||||
(char *)temp->pixels + 3 * scr_w * idx;
|
||||
memcpy(dest,
|
||||
(char *)image->pixels +
|
||||
3 * scr_w * (scr_h - 1 - idx),
|
||||
3 * scr_w);
|
||||
endianswap(dest, 3, scr_w);
|
||||
};
|
||||
sprintf_sd(buf)("screenshots/screenshot_%d.bmp", lastmillis);
|
||||
sprintf_sd(buf)(
|
||||
"screenshots/screenshot_%d.bmp", lastmillis);
|
||||
SDL_SaveBMP(temp, path(buf));
|
||||
SDL_FreeSurface(temp);
|
||||
};
|
||||
|
@ -72,70 +83,98 @@ void screenshot()
|
|||
COMMAND(screenshot, ARG_NONE);
|
||||
COMMAND(quit, ARG_NONE);
|
||||
|
||||
void keyrepeat(bool on)
|
||||
void
|
||||
keyrepeat(bool on)
|
||||
{
|
||||
SDL_EnableKeyRepeat(on ? SDL_DEFAULT_REPEAT_DELAY : 0,
|
||||
SDL_DEFAULT_REPEAT_INTERVAL);
|
||||
SDL_EnableKeyRepeat(
|
||||
on ? SDL_DEFAULT_REPEAT_DELAY : 0, SDL_DEFAULT_REPEAT_INTERVAL);
|
||||
};
|
||||
|
||||
VARF(gamespeed, 10, 100, 1000, if(multiplayer()) gamespeed = 100);
|
||||
VARF(gamespeed, 10, 100, 1000, if (multiplayer()) gamespeed = 100);
|
||||
VARP(minmillis, 0, 5, 1000);
|
||||
|
||||
int islittleendian = 1;
|
||||
int framesinmap = 0;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
bool dedicated = false;
|
||||
int fs = SDL_FULLSCREEN, par = 0, uprate = 0, maxcl = 4;
|
||||
char *sdesc = "", *ip = "", *master = NULL, *passwd = "";
|
||||
islittleendian = *((char *)&islittleendian);
|
||||
|
||||
#define log(s) conoutf("init: %s", s)
|
||||
#define log(s) conoutf("init: %s", s)
|
||||
log("sdl");
|
||||
|
||||
for(int i = 1; i<argc; i++)
|
||||
{
|
||||
for (int i = 1; i < argc; i++) {
|
||||
char *a = &argv[i][2];
|
||||
if(argv[i][0]=='-') switch(argv[i][1])
|
||||
{
|
||||
case 'd': dedicated = true; break;
|
||||
case 't': fs = 0; break;
|
||||
case 'w': scr_w = atoi(a); break;
|
||||
case 'h': scr_h = atoi(a); break;
|
||||
case 'u': uprate = atoi(a); break;
|
||||
case 'n': sdesc = a; break;
|
||||
case 'i': ip = a; break;
|
||||
case 'm': master = a; break;
|
||||
case 'p': passwd = a; break;
|
||||
case 'c': maxcl = atoi(a); break;
|
||||
default: conoutf("unknown commandline option");
|
||||
if (argv[i][0] == '-')
|
||||
switch (argv[i][1]) {
|
||||
case 'd':
|
||||
dedicated = true;
|
||||
break;
|
||||
case 't':
|
||||
fs = 0;
|
||||
break;
|
||||
case 'w':
|
||||
scr_w = atoi(a);
|
||||
break;
|
||||
case 'h':
|
||||
scr_h = atoi(a);
|
||||
break;
|
||||
case 'u':
|
||||
uprate = atoi(a);
|
||||
break;
|
||||
case 'n':
|
||||
sdesc = a;
|
||||
break;
|
||||
case 'i':
|
||||
ip = a;
|
||||
break;
|
||||
case 'm':
|
||||
master = a;
|
||||
break;
|
||||
case 'p':
|
||||
passwd = a;
|
||||
break;
|
||||
case 'c':
|
||||
maxcl = atoi(a);
|
||||
break;
|
||||
default:
|
||||
conoutf("unknown commandline option");
|
||||
}
|
||||
else conoutf("unknown commandline argument");
|
||||
else
|
||||
conoutf("unknown commandline argument");
|
||||
};
|
||||
|
||||
#ifdef _DEBUG
|
||||
#ifdef _DEBUG
|
||||
par = SDL_INIT_NOPARACHUTE;
|
||||
fs = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if(SDL_Init(SDL_INIT_TIMER|SDL_INIT_VIDEO|par)<0) fatal("Unable to initialize SDL");
|
||||
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | par) < 0)
|
||||
fatal("Unable to initialize SDL");
|
||||
|
||||
log("net");
|
||||
if(enet_initialize()<0) fatal("Unable to initialise network module");
|
||||
if (enet_initialize() < 0)
|
||||
fatal("Unable to initialise network module");
|
||||
|
||||
initclient();
|
||||
initserver(dedicated, uprate, sdesc, ip, master, passwd, maxcl); // never returns if dedicated
|
||||
initserver(dedicated, uprate, sdesc, ip, master, passwd,
|
||||
maxcl); // never returns if dedicated
|
||||
|
||||
log("world");
|
||||
empty_world(7, true);
|
||||
|
||||
log("video: sdl");
|
||||
if(SDL_InitSubSystem(SDL_INIT_VIDEO)<0) fatal("Unable to initialize SDL Video");
|
||||
if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
|
||||
fatal("Unable to initialize SDL Video");
|
||||
|
||||
log("video: mode");
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
if(SDL_SetVideoMode(scr_w, scr_h, 0, SDL_OPENGL|fs)==NULL) fatal("Unable to create OpenGL screen");
|
||||
if (SDL_SetVideoMode(scr_w, scr_h, 0, SDL_OPENGL | fs) == NULL)
|
||||
fatal("Unable to create OpenGL screen");
|
||||
|
||||
log("video: misc");
|
||||
SDL_WM_SetCaption("cube engine", NULL);
|
||||
|
@ -148,7 +187,7 @@ int main(int argc, char **argv)
|
|||
|
||||
log("basetex");
|
||||
int xs, ys;
|
||||
if(!installtex(2, path(newstring("data/newchars.png")), xs, ys) ||
|
||||
if (!installtex(2, path(newstring("data/newchars.png")), xs, ys) ||
|
||||
!installtex(3, path(newstring("data/martin/base.png")), xs, ys) ||
|
||||
!installtex(6, path(newstring("data/martin/ball1.png")), xs, ys) ||
|
||||
!installtex(7, path(newstring("data/martin/smoke.png")), xs, ys) ||
|
||||
|
@ -156,7 +195,9 @@ int main(int argc, char **argv)
|
|||
!installtex(9, path(newstring("data/martin/ball3.png")), xs, ys) ||
|
||||
!installtex(4, path(newstring("data/explosion.jpg")), xs, ys) ||
|
||||
!installtex(5, path(newstring("data/items.png")), xs, ys) ||
|
||||
!installtex(1, path(newstring("data/crosshair.png")), xs, ys)) fatal("could not find core textures (hint: run cube from the parent of the bin directory)");
|
||||
!installtex(1, path(newstring("data/crosshair.png")), xs, ys))
|
||||
fatal("could not find core textures (hint: run cube from the "
|
||||
"parent of the bin directory)");
|
||||
|
||||
log("sound");
|
||||
initsound();
|
||||
|
@ -169,31 +210,39 @@ int main(int argc, char **argv)
|
|||
exec("data/prefabs.cfg");
|
||||
exec("data/sounds.cfg");
|
||||
exec("servers.cfg");
|
||||
if(!execfile("config.cfg")) execfile("data/defaults.cfg");
|
||||
if (!execfile("config.cfg"))
|
||||
execfile("data/defaults.cfg");
|
||||
exec("autoexec.cfg");
|
||||
|
||||
log("localconnect");
|
||||
localconnect();
|
||||
changemap("metl3"); // if this map is changed, also change depthcorrect()
|
||||
changemap(
|
||||
"metl3"); // if this map is changed, also change depthcorrect()
|
||||
|
||||
log("mainloop");
|
||||
int ignore = 5;
|
||||
for(;;)
|
||||
{
|
||||
int millis = SDL_GetTicks()*gamespeed/100;
|
||||
if(millis-lastmillis>200) lastmillis = millis-200;
|
||||
else if(millis-lastmillis<1) lastmillis = millis-1;
|
||||
if(millis-lastmillis<minmillis) SDL_Delay(minmillis-(millis-lastmillis));
|
||||
for (;;) {
|
||||
int millis = SDL_GetTicks() * gamespeed / 100;
|
||||
if (millis - lastmillis > 200)
|
||||
lastmillis = millis - 200;
|
||||
else if (millis - lastmillis < 1)
|
||||
lastmillis = millis - 1;
|
||||
if (millis - lastmillis < minmillis)
|
||||
SDL_Delay(minmillis - (millis - lastmillis));
|
||||
cleardlights();
|
||||
updateworld(millis);
|
||||
if(!demoplayback) serverslice((int)time(NULL), 0);
|
||||
if (!demoplayback)
|
||||
serverslice((int)time(NULL), 0);
|
||||
static float fps = 30.0f;
|
||||
fps = (1000.0f/curtime+fps*50)/51;
|
||||
fps = (1000.0f / curtime + fps * 50) / 51;
|
||||
computeraytable(player1->o.x, player1->o.y);
|
||||
readdepth(scr_w, scr_h);
|
||||
SDL_GL_SwapBuffers();
|
||||
extern void updatevol(); updatevol();
|
||||
if(framesinmap++<5) // cheap hack to get rid of initial sparklies, even when triple buffering etc.
|
||||
extern void updatevol();
|
||||
updatevol();
|
||||
if (framesinmap++ <
|
||||
5) // cheap hack to get rid of initial sparklies, even when
|
||||
// triple buffering etc.
|
||||
{
|
||||
player1->yaw += 5;
|
||||
gl_drawframe(scr_w, scr_h, fps);
|
||||
|
@ -202,28 +251,35 @@ int main(int argc, char **argv)
|
|||
gl_drawframe(scr_w, scr_h, fps);
|
||||
SDL_Event event;
|
||||
int lasttype = 0, lastbut = 0;
|
||||
while(SDL_PollEvent(&event))
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
quit();
|
||||
break;
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
keypress(event.key.keysym.sym, event.key.state==SDL_PRESSED, event.key.keysym.unicode);
|
||||
keypress(event.key.keysym.sym,
|
||||
event.key.state == SDL_PRESSED,
|
||||
event.key.keysym.unicode);
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
if(ignore) { ignore--; break; };
|
||||
if (ignore) {
|
||||
ignore--;
|
||||
break;
|
||||
};
|
||||
mousemove(event.motion.xrel, event.motion.yrel);
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if(lasttype==event.type && lastbut==event.button.button) break; // why?? get event twice without it
|
||||
keypress(-event.button.button, event.button.state!=0, 0);
|
||||
if (lasttype == event.type &&
|
||||
lastbut == event.button.button)
|
||||
break; // why?? get event twice without
|
||||
// it
|
||||
keypress(-event.button.button,
|
||||
event.button.state != 0, 0);
|
||||
lasttype = event.type;
|
||||
lastbut = event.button.button;
|
||||
break;
|
||||
|
@ -233,5 +289,3 @@ int main(int argc, char **argv)
|
|||
quit();
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
|
|
128
src/menus.cxx
128
src/menus.cxx
|
@ -2,10 +2,11 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
struct mitem { char *text, *action; };
|
||||
struct mitem {
|
||||
char *text, *action;
|
||||
};
|
||||
|
||||
struct gmenu
|
||||
{
|
||||
struct gmenu {
|
||||
char *name;
|
||||
vector<mitem> items;
|
||||
int mwidth;
|
||||
|
@ -18,63 +19,80 @@ int vmenu = -1;
|
|||
|
||||
ivector menustack;
|
||||
|
||||
void menuset(int menu)
|
||||
void
|
||||
menuset(int menu)
|
||||
{
|
||||
if((vmenu = menu)>=1) resetmovement(player1);
|
||||
if(vmenu==1) menus[1].menusel = 0;
|
||||
if ((vmenu = menu) >= 1)
|
||||
resetmovement(player1);
|
||||
if (vmenu == 1)
|
||||
menus[1].menusel = 0;
|
||||
};
|
||||
|
||||
void showmenu(char *name)
|
||||
void
|
||||
showmenu(char *name)
|
||||
{
|
||||
loopv(menus) if(i>1 && strcmp(menus[i].name, name)==0)
|
||||
loopv(menus) if (i > 1 && strcmp(menus[i].name, name) == 0)
|
||||
{
|
||||
menuset(i);
|
||||
return;
|
||||
};
|
||||
};
|
||||
|
||||
int menucompare(mitem *a, mitem *b)
|
||||
int
|
||||
menucompare(mitem *a, mitem *b)
|
||||
{
|
||||
int x = atoi(a->text);
|
||||
int y = atoi(b->text);
|
||||
if(x>y) return -1;
|
||||
if(x<y) return 1;
|
||||
if (x > y)
|
||||
return -1;
|
||||
if (x < y)
|
||||
return 1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
void sortmenu(int start, int num)
|
||||
void
|
||||
sortmenu(int start, int num)
|
||||
{
|
||||
qsort(&menus[0].items[start], num, sizeof(mitem), (int (__cdecl *)(const void *,const void *))menucompare);
|
||||
qsort(&menus[0].items[start], num, sizeof(mitem),
|
||||
(int(__cdecl *)(const void *, const void *))menucompare);
|
||||
};
|
||||
|
||||
void refreshservers();
|
||||
|
||||
bool rendermenu()
|
||||
bool
|
||||
rendermenu()
|
||||
{
|
||||
if(vmenu<0) { menustack.setsize(0); return false; };
|
||||
if(vmenu==1) refreshservers();
|
||||
if (vmenu < 0) {
|
||||
menustack.setsize(0);
|
||||
return false;
|
||||
};
|
||||
if (vmenu == 1)
|
||||
refreshservers();
|
||||
gmenu &m = menus[vmenu];
|
||||
sprintf_sd(title)(vmenu>1 ? "[ %s menu ]" : "%s", m.name);
|
||||
sprintf_sd(title)(vmenu > 1 ? "[ %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;
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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)
|
||||
{
|
||||
|
@ -84,22 +102,26 @@ bool rendermenu()
|
|||
return true;
|
||||
};
|
||||
|
||||
void newmenu(char *name)
|
||||
void
|
||||
newmenu(char *name)
|
||||
{
|
||||
gmenu &menu = menus.add();
|
||||
menu.name = newstring(name);
|
||||
menu.menusel = 0;
|
||||
};
|
||||
|
||||
void menumanual(int m, int n, char *text)
|
||||
void
|
||||
menumanual(int m, int n, char *text)
|
||||
{
|
||||
if(!n) menus[m].items.setsize(0);
|
||||
if (!n)
|
||||
menus[m].items.setsize(0);
|
||||
mitem &mitem = menus[m].items.add();
|
||||
mitem.text = text;
|
||||
mitem.action = "";
|
||||
}
|
||||
|
||||
void menuitem(char *text, char *action)
|
||||
void
|
||||
menuitem(char *text, char *action)
|
||||
{
|
||||
gmenu &menu = menus.last();
|
||||
mitem &mi = menu.items.add();
|
||||
|
@ -111,31 +133,33 @@ COMMAND(menuitem, ARG_2STR);
|
|||
COMMAND(showmenu, ARG_1STR);
|
||||
COMMAND(newmenu, ARG_1STR);
|
||||
|
||||
bool menukey(int code, bool isdown)
|
||||
bool
|
||||
menukey(int code, bool isdown)
|
||||
{
|
||||
if(vmenu<=0) return false;
|
||||
if (vmenu <= 0)
|
||||
return false;
|
||||
int menusel = menus[vmenu].menusel;
|
||||
if(isdown)
|
||||
{
|
||||
if(code==SDLK_ESCAPE)
|
||||
{
|
||||
if (isdown) {
|
||||
if (code == SDLK_ESCAPE) {
|
||||
menuset(-1);
|
||||
if(!menustack.empty()) menuset(menustack.pop());
|
||||
if (!menustack.empty())
|
||||
menuset(menustack.pop());
|
||||
return true;
|
||||
}
|
||||
else if(code==SDLK_UP || code==-4) menusel--;
|
||||
else if(code==SDLK_DOWN || code==-5) menusel++;
|
||||
} 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;
|
||||
if (menusel < 0)
|
||||
menusel = n - 1;
|
||||
else if (menusel >= n)
|
||||
menusel = 0;
|
||||
menus[vmenu].menusel = menusel;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(code==SDLK_RETURN || code==-2)
|
||||
{
|
||||
} else {
|
||||
if (code == SDLK_RETURN || code == -2) {
|
||||
char *action = menus[vmenu].items[menusel].action;
|
||||
if(vmenu==1) connects(getservername(menusel));
|
||||
if (vmenu == 1)
|
||||
connects(getservername(menusel));
|
||||
menustack.add(vmenu);
|
||||
menuset(-1);
|
||||
execute(action, true);
|
||||
|
|
355
src/monster.cxx
355
src/monster.cxx
|
@ -7,35 +7,51 @@ int nextmonster, spawnremain, numkilled, monstertotal, mtimestart;
|
|||
|
||||
VARF(skill, 1, 3, 10, conoutf("skill is now %d", skill));
|
||||
|
||||
dvector &getmonsters() { return monsters; };
|
||||
void restoremonsterstate() { loopv(monsters) if(monsters[i]->state==CS_DEAD) numkilled++; }; // for savegames
|
||||
dvector &
|
||||
getmonsters()
|
||||
{
|
||||
return monsters;
|
||||
};
|
||||
void
|
||||
restoremonsterstate()
|
||||
{
|
||||
loopv(monsters) if (monsters[i]->state == CS_DEAD) numkilled++;
|
||||
}; // for savegames
|
||||
|
||||
#define TOTMFREQ 13
|
||||
#define NUMMONSTERTYPES 8
|
||||
|
||||
struct monstertype // see docs for how these values modify behaviour
|
||||
{
|
||||
short gun, speed, health, freq, lag, rate, pain, loyalty, mscale, bscale;
|
||||
short gun, speed, health, freq, lag, rate, pain, loyalty, mscale,
|
||||
bscale;
|
||||
short painsound, diesound;
|
||||
char *name, *mdlname;
|
||||
}
|
||||
|
||||
monstertypes[NUMMONSTERTYPES] =
|
||||
{
|
||||
{ GUN_FIREBALL, 15, 100, 3, 0, 100, 800, 1, 10, 10, S_PAINO, S_DIE1, "an ogre", "monster/ogro" },
|
||||
{ GUN_CG, 18, 70, 2, 70, 10, 400, 2, 8, 9, S_PAINR, S_DEATHR, "a rhino", "monster/rhino" },
|
||||
{ GUN_SG, 14, 120, 1, 100, 300, 400, 4, 14, 14, S_PAINE, S_DEATHE, "ratamahatta", "monster/rat" },
|
||||
{ GUN_RIFLE, 15, 200, 1, 80, 300, 300, 4, 18, 18, S_PAINS, S_DEATHS, "a slith", "monster/slith" },
|
||||
{ GUN_RL, 13, 500, 1, 0, 100, 200, 6, 24, 24, S_PAINB, S_DEATHB, "bauul", "monster/bauul" },
|
||||
{ GUN_BITE, 22, 50, 3, 0, 100, 400, 1, 12, 15, S_PAINP, S_PIGGR2, "a hellpig", "monster/hellpig" },
|
||||
{ GUN_ICEBALL, 12, 250, 1, 0, 10, 400, 6, 18, 18, S_PAINH, S_DEATHH, "a knight", "monster/knight" },
|
||||
{ GUN_SLIMEBALL, 15, 100, 1, 0, 200, 400, 2, 13, 10, S_PAIND, S_DEATHD, "a goblin", "monster/goblin" },
|
||||
monstertypes[NUMMONSTERTYPES] = {
|
||||
{GUN_FIREBALL, 15, 100, 3, 0, 100, 800, 1, 10, 10, S_PAINO, S_DIE1,
|
||||
"an ogre", "monster/ogro"},
|
||||
{GUN_CG, 18, 70, 2, 70, 10, 400, 2, 8, 9, S_PAINR, S_DEATHR, "a rhino",
|
||||
"monster/rhino"},
|
||||
{GUN_SG, 14, 120, 1, 100, 300, 400, 4, 14, 14, S_PAINE, S_DEATHE,
|
||||
"ratamahatta", "monster/rat"},
|
||||
{GUN_RIFLE, 15, 200, 1, 80, 300, 300, 4, 18, 18, S_PAINS, S_DEATHS,
|
||||
"a slith", "monster/slith"},
|
||||
{GUN_RL, 13, 500, 1, 0, 100, 200, 6, 24, 24, S_PAINB, S_DEATHB, "bauul",
|
||||
"monster/bauul"},
|
||||
{GUN_BITE, 22, 50, 3, 0, 100, 400, 1, 12, 15, S_PAINP, S_PIGGR2,
|
||||
"a hellpig", "monster/hellpig"},
|
||||
{GUN_ICEBALL, 12, 250, 1, 0, 10, 400, 6, 18, 18, S_PAINH, S_DEATHH,
|
||||
"a knight", "monster/knight"},
|
||||
{GUN_SLIMEBALL, 15, 100, 1, 0, 200, 400, 2, 13, 10, S_PAIND, S_DEATHD,
|
||||
"a goblin", "monster/goblin"},
|
||||
};
|
||||
|
||||
dynent *basicmonster(int type, int yaw, int state, int trigger, int move)
|
||||
dynent *
|
||||
basicmonster(int type, int yaw, int state, int trigger, int move)
|
||||
{
|
||||
if(type>=NUMMONSTERTYPES)
|
||||
{
|
||||
if (type >= NUMMONSTERTYPES) {
|
||||
conoutf("warning: unknown monster in spawn: %d", type);
|
||||
type = 0;
|
||||
};
|
||||
|
@ -43,12 +59,13 @@ dynent *basicmonster(int type, int yaw, int state, int trigger, int move)
|
|||
monstertype *t = &monstertypes[m->mtype = type];
|
||||
m->eyeheight = 2.0f;
|
||||
m->aboveeye = 1.9f;
|
||||
m->radius *= t->bscale/10.0f;
|
||||
m->eyeheight *= t->bscale/10.0f;
|
||||
m->aboveeye *= t->bscale/10.0f;
|
||||
m->radius *= t->bscale / 10.0f;
|
||||
m->eyeheight *= t->bscale / 10.0f;
|
||||
m->aboveeye *= t->bscale / 10.0f;
|
||||
m->monsterstate = state;
|
||||
if(state!=M_SLEEP) spawnplayer(m);
|
||||
m->trigger = lastmillis+trigger;
|
||||
if (state != M_SLEEP)
|
||||
spawnplayer(m);
|
||||
m->trigger = lastmillis + trigger;
|
||||
m->targetyaw = m->yaw = (float)yaw;
|
||||
m->move = move;
|
||||
m->enemy = player1;
|
||||
|
@ -66,31 +83,36 @@ dynent *basicmonster(int type, int yaw, int state, int trigger, int move)
|
|||
return m;
|
||||
};
|
||||
|
||||
void spawnmonster() // spawn a random monster according to freq distribution in DMSP
|
||||
void
|
||||
spawnmonster() // spawn a random monster according to freq distribution in DMSP
|
||||
{
|
||||
int n = rnd(TOTMFREQ), type;
|
||||
for(int i = 0; ; i++) if((n -= monstertypes[i].freq)<0) { type = i; break; };
|
||||
for (int i = 0;; i++)
|
||||
if ((n -= monstertypes[i].freq) < 0) {
|
||||
type = i;
|
||||
break;
|
||||
};
|
||||
basicmonster(type, rnd(360), M_SEARCH, 1000, 1);
|
||||
};
|
||||
|
||||
void monsterclear() // called after map start of when toggling edit mode to reset/spawn all monsters to initial state
|
||||
void
|
||||
monsterclear() // called after map start of when toggling edit mode to
|
||||
// reset/spawn all monsters to initial state
|
||||
{
|
||||
loopv(monsters) gp()->dealloc(monsters[i], sizeof(dynent));
|
||||
monsters.setsize(0);
|
||||
numkilled = 0;
|
||||
monstertotal = 0;
|
||||
spawnremain = 0;
|
||||
if(m_dmsp)
|
||||
{
|
||||
nextmonster = mtimestart = lastmillis+10000;
|
||||
monstertotal = spawnremain = gamemode<0 ? skill*10 : 0;
|
||||
}
|
||||
else if(m_classicsp)
|
||||
{
|
||||
if (m_dmsp) {
|
||||
nextmonster = mtimestart = lastmillis + 10000;
|
||||
monstertotal = spawnremain = gamemode < 0 ? skill * 10 : 0;
|
||||
} else if (m_classicsp) {
|
||||
mtimestart = lastmillis;
|
||||
loopv(ents) if(ents[i].type==MONSTER)
|
||||
loopv(ents) if (ents[i].type == MONSTER)
|
||||
{
|
||||
dynent *m = basicmonster(ents[i].attr2, ents[i].attr1, M_SLEEP, 100, 0);
|
||||
dynent *m = basicmonster(
|
||||
ents[i].attr2, ents[i].attr1, M_SLEEP, 100, 0);
|
||||
m->o.x = ents[i].x;
|
||||
m->o.y = ents[i].y;
|
||||
m->o.z = ents[i].z;
|
||||
|
@ -100,125 +122,154 @@ void monsterclear() // called after map start of when toggling edit mode to
|
|||
};
|
||||
};
|
||||
|
||||
bool los(float lx, float ly, float lz, float bx, float by, float bz, vec &v) // height-correct line of sight for monster shooting/seeing
|
||||
bool
|
||||
los(float lx, float ly, float lz, float bx, float by, float bz,
|
||||
vec &v) // height-correct line of sight for monster shooting/seeing
|
||||
{
|
||||
if(OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by)) return false;
|
||||
float dx = bx-lx;
|
||||
float dy = by-ly;
|
||||
int steps = (int)(sqrt(dx*dx+dy*dy)/0.9);
|
||||
if(!steps) return false;
|
||||
if (OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by))
|
||||
return false;
|
||||
float dx = bx - lx;
|
||||
float dy = by - ly;
|
||||
int steps = (int)(sqrt(dx * dx + dy * dy) / 0.9);
|
||||
if (!steps)
|
||||
return false;
|
||||
float x = lx;
|
||||
float y = ly;
|
||||
int i = 0;
|
||||
for(;;)
|
||||
{
|
||||
for (;;) {
|
||||
sqr *s = S(fast_f2nat(x), fast_f2nat(y));
|
||||
if(SOLID(s)) break;
|
||||
if (SOLID(s))
|
||||
break;
|
||||
float floor = s->floor;
|
||||
if(s->type==FHF) floor -= s->vdelta/4.0f;
|
||||
if (s->type == FHF)
|
||||
floor -= s->vdelta / 4.0f;
|
||||
float ceil = s->ceil;
|
||||
if(s->type==CHF) ceil += s->vdelta/4.0f;
|
||||
float rz = lz-((lz-bz)*(i/(float)steps));
|
||||
if(rz<floor || rz>ceil) break;
|
||||
if (s->type == CHF)
|
||||
ceil += s->vdelta / 4.0f;
|
||||
float rz = lz - ((lz - bz) * (i / (float)steps));
|
||||
if (rz < floor || rz > ceil)
|
||||
break;
|
||||
v.x = x;
|
||||
v.y = y;
|
||||
v.z = rz;
|
||||
x += dx/(float)steps;
|
||||
y += dy/(float)steps;
|
||||
x += dx / (float)steps;
|
||||
y += dy / (float)steps;
|
||||
i++;
|
||||
};
|
||||
return i>=steps;
|
||||
return i >= steps;
|
||||
};
|
||||
|
||||
bool enemylos(dynent *m, vec &v)
|
||||
bool
|
||||
enemylos(dynent *m, vec &v)
|
||||
{
|
||||
v = m->o;
|
||||
return los(m->o.x, m->o.y, m->o.z, m->enemy->o.x, m->enemy->o.y, m->enemy->o.z, v);
|
||||
return los(m->o.x, m->o.y, m->o.z, m->enemy->o.x, m->enemy->o.y,
|
||||
m->enemy->o.z, v);
|
||||
};
|
||||
|
||||
// monster AI is sequenced using transitions: they are in a particular state where
|
||||
// they execute a particular behaviour until the trigger time is hit, and then they
|
||||
// reevaluate their situation based on the current state, the environment etc., and
|
||||
// transition to the next state. Transition timeframes are parametrized by difficulty
|
||||
// level (skill), faster transitions means quicker decision making means tougher AI.
|
||||
// monster AI is sequenced using transitions: they are in a particular state
|
||||
// where they execute a particular behaviour until the trigger time is hit, and
|
||||
// then they reevaluate their situation based on the current state, the
|
||||
// environment etc., and transition to the next state. Transition timeframes are
|
||||
// parametrized by difficulty level (skill), faster transitions means quicker
|
||||
// decision making means tougher AI.
|
||||
|
||||
void transition(dynent *m, int state, int moving, int n, int r) // n = at skill 0, n/2 = at skill 10, r = added random factor
|
||||
void
|
||||
transition(dynent *m, int state, int moving, int n,
|
||||
int r) // n = at skill 0, n/2 = at skill 10, r = added random factor
|
||||
{
|
||||
m->monsterstate = state;
|
||||
m->move = moving;
|
||||
n = n*130/100;
|
||||
m->trigger = lastmillis+n-skill*(n/16)+rnd(r+1);
|
||||
n = n * 130 / 100;
|
||||
m->trigger = lastmillis + n - skill * (n / 16) + rnd(r + 1);
|
||||
};
|
||||
|
||||
void normalise(dynent *m, float angle)
|
||||
void
|
||||
normalise(dynent *m, float angle)
|
||||
{
|
||||
while(m->yaw<angle-180.0f) m->yaw += 360.0f;
|
||||
while(m->yaw>angle+180.0f) m->yaw -= 360.0f;
|
||||
while (m->yaw < angle - 180.0f)
|
||||
m->yaw += 360.0f;
|
||||
while (m->yaw > angle + 180.0f)
|
||||
m->yaw -= 360.0f;
|
||||
};
|
||||
|
||||
void monsteraction(dynent *m) // main AI thinking routine, called every frame for every monster
|
||||
void
|
||||
monsteraction(
|
||||
dynent *m) // main AI thinking routine, called every frame for every monster
|
||||
{
|
||||
if(m->enemy->state==CS_DEAD) { m->enemy = player1; m->anger = 0; };
|
||||
if (m->enemy->state == CS_DEAD) {
|
||||
m->enemy = player1;
|
||||
m->anger = 0;
|
||||
};
|
||||
normalise(m, m->targetyaw);
|
||||
if(m->targetyaw>m->yaw) // slowly turn monster towards his target
|
||||
if (m->targetyaw > m->yaw) // slowly turn monster towards his target
|
||||
{
|
||||
m->yaw += curtime*0.5f;
|
||||
if(m->targetyaw<m->yaw) m->yaw = m->targetyaw;
|
||||
}
|
||||
else
|
||||
{
|
||||
m->yaw -= curtime*0.5f;
|
||||
if(m->targetyaw>m->yaw) m->yaw = m->targetyaw;
|
||||
m->yaw += curtime * 0.5f;
|
||||
if (m->targetyaw < m->yaw)
|
||||
m->yaw = m->targetyaw;
|
||||
} else {
|
||||
m->yaw -= curtime * 0.5f;
|
||||
if (m->targetyaw > m->yaw)
|
||||
m->yaw = m->targetyaw;
|
||||
};
|
||||
|
||||
vdist(disttoenemy, vectoenemy, m->o, m->enemy->o);
|
||||
m->pitch = atan2(m->enemy->o.z-m->o.z, disttoenemy)*180/PI;
|
||||
m->pitch = atan2(m->enemy->o.z - m->o.z, disttoenemy) * 180 / PI;
|
||||
|
||||
if(m->blocked) // special case: if we run into scenery
|
||||
if (m->blocked) // special case: if we run into scenery
|
||||
{
|
||||
m->blocked = false;
|
||||
if(!rnd(20000/monstertypes[m->mtype].speed)) // try to jump over obstackle (rare)
|
||||
if (!rnd(20000 /
|
||||
monstertypes[m->mtype]
|
||||
.speed)) // try to jump over obstackle (rare)
|
||||
{
|
||||
m->jumpnext = true;
|
||||
}
|
||||
else if(m->trigger<lastmillis && (m->monsterstate!=M_HOME || !rnd(5))) // search for a way around (common)
|
||||
} else if (m->trigger < lastmillis &&
|
||||
(m->monsterstate != M_HOME ||
|
||||
!rnd(5))) // search for a way around (common)
|
||||
{
|
||||
m->targetyaw += 180+rnd(180); // patented "random walk" AI pathfinding (tm) ;)
|
||||
m->targetyaw +=
|
||||
180 + rnd(180); // patented "random walk" AI
|
||||
// pathfinding (tm) ;)
|
||||
transition(m, M_SEARCH, 1, 400, 1000);
|
||||
};
|
||||
};
|
||||
|
||||
float enemyyaw = -(float)atan2(m->enemy->o.x - m->o.x, m->enemy->o.y - m->o.y)/PI*180+180;
|
||||
float enemyyaw =
|
||||
-(float)atan2(m->enemy->o.x - m->o.x, m->enemy->o.y - m->o.y) / PI *
|
||||
180 +
|
||||
180;
|
||||
|
||||
switch(m->monsterstate)
|
||||
{
|
||||
switch (m->monsterstate) {
|
||||
case M_PAIN:
|
||||
case M_ATTACKING:
|
||||
case M_SEARCH:
|
||||
if(m->trigger<lastmillis) transition(m, M_HOME, 1, 100, 200);
|
||||
if (m->trigger < lastmillis)
|
||||
transition(m, M_HOME, 1, 100, 200);
|
||||
break;
|
||||
|
||||
case M_SLEEP: // state classic sp monster start in, wait for visual contact
|
||||
case M_SLEEP: // state classic sp monster start in, wait for visual
|
||||
// contact
|
||||
{
|
||||
vec target;
|
||||
if(editmode || !enemylos(m, target)) return; // skip running physics
|
||||
if (editmode || !enemylos(m, target))
|
||||
return; // skip running physics
|
||||
normalise(m, enemyyaw);
|
||||
float angle = (float)fabs(enemyyaw-m->yaw);
|
||||
if(disttoenemy<8 // the better the angle to the player, the further the monster can see/hear
|
||||
||(disttoenemy<16 && angle<135)
|
||||
||(disttoenemy<32 && angle<90)
|
||||
||(disttoenemy<64 && angle<45)
|
||||
|| angle<10)
|
||||
{
|
||||
float angle = (float)fabs(enemyyaw - m->yaw);
|
||||
if (disttoenemy < 8 // the better the angle to the player, the
|
||||
// further the monster can see/hear
|
||||
|| (disttoenemy < 16 && angle < 135) ||
|
||||
(disttoenemy < 32 && angle < 90) ||
|
||||
(disttoenemy < 64 && angle < 45) || angle < 10) {
|
||||
transition(m, M_HOME, 1, 500, 200);
|
||||
playsound(S_GRUNT1+rnd(2), &m->o);
|
||||
playsound(S_GRUNT1 + rnd(2), &m->o);
|
||||
};
|
||||
break;
|
||||
};
|
||||
|
||||
case M_AIMING: // this state is the delay between wanting to shoot and actually firing
|
||||
if(m->trigger<lastmillis)
|
||||
{
|
||||
case M_AIMING: // this state is the delay between wanting to shoot and
|
||||
// actually firing
|
||||
if (m->trigger < lastmillis) {
|
||||
m->lastaction = 0;
|
||||
m->attacking = true;
|
||||
shoot(m, m->attacktarget);
|
||||
|
@ -226,25 +277,31 @@ void monsteraction(dynent *m) // main AI thinking routine, called ever
|
|||
};
|
||||
break;
|
||||
|
||||
case M_HOME: // monster has visual contact, heads straight for player and may want to shoot at any time
|
||||
case M_HOME: // monster has visual contact, heads straight for player
|
||||
// and may want to shoot at any time
|
||||
m->targetyaw = enemyyaw;
|
||||
if(m->trigger<lastmillis)
|
||||
{
|
||||
if (m->trigger < lastmillis) {
|
||||
vec target;
|
||||
if(!enemylos(m, target)) // no visual contact anymore, let monster get as close as possible then search for player
|
||||
if (!enemylos(
|
||||
m, target)) // no visual contact anymore, let
|
||||
// monster get as close as possible
|
||||
// then search for player
|
||||
{
|
||||
transition(m, M_HOME, 1, 800, 500);
|
||||
}
|
||||
else // the closer the monster is the more likely he wants to shoot
|
||||
} else // the closer the monster is the more likely he
|
||||
// wants to shoot
|
||||
{
|
||||
if(!rnd((int)disttoenemy/3+1) && m->enemy->state==CS_ALIVE) // get ready to fire
|
||||
if (!rnd((int)disttoenemy / 3 + 1) &&
|
||||
m->enemy->state ==
|
||||
CS_ALIVE) // get ready to fire
|
||||
{
|
||||
m->attacktarget = target;
|
||||
transition(m, M_AIMING, 0, monstertypes[m->mtype].lag, 10);
|
||||
}
|
||||
else // track player some more
|
||||
transition(m, M_AIMING, 0,
|
||||
monstertypes[m->mtype].lag, 10);
|
||||
} else // track player some more
|
||||
{
|
||||
transition(m, M_HOME, 1, monstertypes[m->mtype].rate, 0);
|
||||
transition(m, M_HOME, 1,
|
||||
monstertypes[m->mtype].rate, 0);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -254,68 +311,77 @@ void monsteraction(dynent *m) // main AI thinking routine, called ever
|
|||
moveplayer(m, 1, false); // use physics to move monster
|
||||
};
|
||||
|
||||
void monsterpain(dynent *m, int damage, dynent *d)
|
||||
void
|
||||
monsterpain(dynent *m, int damage, dynent *d)
|
||||
{
|
||||
if(d->monsterstate) // a monster hit us
|
||||
if (d->monsterstate) // a monster hit us
|
||||
{
|
||||
if(m!=d) // guard for RL guys shooting themselves :)
|
||||
if (m != d) // guard for RL guys shooting themselves :)
|
||||
{
|
||||
m->anger++; // don't attack straight away, first get angry
|
||||
int anger = m->mtype==d->mtype ? m->anger/2 : m->anger;
|
||||
if(anger>=monstertypes[m->mtype].loyalty) m->enemy = d; // monster infight if very angry
|
||||
m->anger++; // don't attack straight away, first get
|
||||
// angry
|
||||
int anger =
|
||||
m->mtype == d->mtype ? m->anger / 2 : m->anger;
|
||||
if (anger >= monstertypes[m->mtype].loyalty)
|
||||
m->enemy = d; // monster infight if very angry
|
||||
};
|
||||
}
|
||||
else // player hit us
|
||||
} else // player hit us
|
||||
{
|
||||
m->anger = 0;
|
||||
m->enemy = d;
|
||||
};
|
||||
transition(m, M_PAIN, 0, monstertypes[m->mtype].pain,200); // in this state monster won't attack
|
||||
if((m->health -= damage)<=0)
|
||||
{
|
||||
transition(m, M_PAIN, 0, monstertypes[m->mtype].pain,
|
||||
200); // in this state monster won't attack
|
||||
if ((m->health -= damage) <= 0) {
|
||||
m->state = CS_DEAD;
|
||||
m->lastaction = lastmillis;
|
||||
numkilled++;
|
||||
player1->frags = numkilled;
|
||||
playsound(monstertypes[m->mtype].diesound, &m->o);
|
||||
int remain = monstertotal-numkilled;
|
||||
if(remain>0 && remain<=5) conoutf("only %d monster(s) remaining", remain);
|
||||
}
|
||||
else
|
||||
{
|
||||
int remain = monstertotal - numkilled;
|
||||
if (remain > 0 && remain <= 5)
|
||||
conoutf("only %d monster(s) remaining", remain);
|
||||
} else {
|
||||
playsound(monstertypes[m->mtype].painsound, &m->o);
|
||||
};
|
||||
};
|
||||
|
||||
void endsp(bool allkilled)
|
||||
void
|
||||
endsp(bool allkilled)
|
||||
{
|
||||
conoutf(allkilled ? "you have cleared the map!" : "you reached the exit!");
|
||||
conoutf("score: %d kills in %d seconds", numkilled, (lastmillis-mtimestart)/1000);
|
||||
conoutf(
|
||||
allkilled ? "you have cleared the map!" : "you reached the exit!");
|
||||
conoutf("score: %d kills in %d seconds", numkilled,
|
||||
(lastmillis - mtimestart) / 1000);
|
||||
monstertotal = 0;
|
||||
startintermission();
|
||||
};
|
||||
|
||||
void monsterthink()
|
||||
void
|
||||
monsterthink()
|
||||
{
|
||||
if(m_dmsp && spawnremain && lastmillis>nextmonster)
|
||||
{
|
||||
if(spawnremain--==monstertotal) conoutf("The invasion has begun!");
|
||||
nextmonster = lastmillis+1000;
|
||||
if (m_dmsp && spawnremain && lastmillis > nextmonster) {
|
||||
if (spawnremain-- == monstertotal)
|
||||
conoutf("The invasion has begun!");
|
||||
nextmonster = lastmillis + 1000;
|
||||
spawnmonster();
|
||||
};
|
||||
|
||||
if(monstertotal && !spawnremain && numkilled==monstertotal) endsp(true);
|
||||
if (monstertotal && !spawnremain && numkilled == monstertotal)
|
||||
endsp(true);
|
||||
|
||||
loopv(ents) // equivalent of player entity touch, but only teleports are used
|
||||
loopv(ents) // equivalent of player entity touch, but only teleports are
|
||||
// used
|
||||
{
|
||||
entity &e = ents[i];
|
||||
if(e.type!=TELEPORT) continue;
|
||||
if(OUTBORD(e.x, e.y)) continue;
|
||||
vec v = { e.x, e.y, S(e.x, e.y)->floor };
|
||||
loopv(monsters) if(monsters[i]->state==CS_DEAD)
|
||||
{
|
||||
if(lastmillis-monsters[i]->lastaction<2000)
|
||||
if (e.type != TELEPORT)
|
||||
continue;
|
||||
if (OUTBORD(e.x, e.y))
|
||||
continue;
|
||||
vec v = {e.x, e.y, S(e.x, e.y)->floor};
|
||||
loopv(monsters) if (monsters[i]->state == CS_DEAD)
|
||||
{
|
||||
if (lastmillis - monsters[i]->lastaction < 2000) {
|
||||
monsters[i]->move = 0;
|
||||
moveplayer(monsters[i], 1, false);
|
||||
};
|
||||
|
@ -325,14 +391,19 @@ void monsterthink()
|
|||
v.z += monsters[i]->eyeheight;
|
||||
vdist(dist, t, monsters[i]->o, v);
|
||||
v.z -= monsters[i]->eyeheight;
|
||||
if(dist<4) teleport((int)(&e-&ents[0]), monsters[i]);
|
||||
if (dist < 4)
|
||||
teleport((int)(&e - &ents[0]), monsters[i]);
|
||||
};
|
||||
};
|
||||
|
||||
loopv(monsters) if(monsters[i]->state==CS_ALIVE) monsteraction(monsters[i]);
|
||||
loopv(monsters) if (monsters[i]->state == CS_ALIVE)
|
||||
monsteraction(monsters[i]);
|
||||
};
|
||||
|
||||
void monsterrender()
|
||||
void
|
||||
monsterrender()
|
||||
{
|
||||
loopv(monsters) renderclient(monsters[i], false, monstertypes[monsters[i]->mtype].mdlname, monsters[i]->mtype==5, monstertypes[monsters[i]->mtype].mscale/10.0f);
|
||||
loopv(monsters) renderclient(monsters[i], false,
|
||||
monstertypes[monsters[i]->mtype].mdlname, monsters[i]->mtype == 5,
|
||||
monstertypes[monsters[i]->mtype].mscale / 10.0f);
|
||||
};
|
||||
|
|
419
src/physics.cxx
419
src/physics.cxx
|
@ -1,217 +1,269 @@
|
|||
// 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).
|
||||
// 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, float &hi, float &lo) // collide with player or monster
|
||||
bool
|
||||
plcollide(dynent *d, dynent *o, float &headspace, float &hi,
|
||||
float &lo) // 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)<r && fabs(o->o.y-d->o.y)<r)
|
||||
{
|
||||
if(d->o.z-d->eyeheight<o->o.z-o->eyeheight) { if(o->o.z-o->eyeheight<hi) hi = o->o.z-o->eyeheight-1; }
|
||||
else if(o->o.z+o->aboveeye>lo) lo = o->o.z+o->aboveeye+1;
|
||||
if (o->state != CS_ALIVE)
|
||||
return true;
|
||||
const float r = o->radius + d->radius;
|
||||
if (fabs(o->o.x - d->o.x) < r && fabs(o->o.y - d->o.y) < r) {
|
||||
if (d->o.z - d->eyeheight < o->o.z - o->eyeheight) {
|
||||
if (o->o.z - o->eyeheight < hi)
|
||||
hi = o->o.z - o->eyeheight - 1;
|
||||
} else if (o->o.z + o->aboveeye > lo)
|
||||
lo = o->o.z + o->aboveeye + 1;
|
||||
|
||||
if(fabs(o->o.z-d->o.z)<o->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;
|
||||
if (fabs(o->o.z - d->o.z) < o->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
|
||||
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));
|
||||
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<<mip;
|
||||
by = y<<mip;
|
||||
bs = 1<<mip;
|
||||
if (SWS(wmip[mip], x, y, ssize >> mip)->type == CORNER) {
|
||||
bx = x << mip;
|
||||
by = y << mip;
|
||||
bs = 1 << mip;
|
||||
return cornertest(mip, x, y, dx, dy, bx, by, bs);
|
||||
};
|
||||
return stest;
|
||||
};
|
||||
|
||||
void mmcollide(dynent *d, float &hi, float &lo) // collide with a mapmodel
|
||||
void
|
||||
mmcollide(dynent *d, float &hi, float &lo) // collide with a mapmodel
|
||||
{
|
||||
loopv(ents)
|
||||
{
|
||||
entity &e = ents[i];
|
||||
if(e.type!=MAPMODEL) continue;
|
||||
if (e.type != MAPMODEL)
|
||||
continue;
|
||||
mapmodelinfo &mmi = getmminfo(e.attr2);
|
||||
if(!&mmi || !mmi.h) continue;
|
||||
const float r = mmi.rad+d->radius;
|
||||
if(fabs(e.x-d->o.x)<r && fabs(e.y-d->o.y)<r)
|
||||
{
|
||||
float mmz = (float)(S(e.x, e.y)->floor+mmi.zoff+e.attr3);
|
||||
if(d->o.z-d->eyeheight<mmz) { if(mmz<hi) hi = mmz; }
|
||||
else if(mmz+mmi.h>lo) lo = mmz+mmi.h;
|
||||
if (!&mmi || !mmi.h)
|
||||
continue;
|
||||
const float r = mmi.rad + d->radius;
|
||||
if (fabs(e.x - d->o.x) < r && fabs(e.y - d->o.y) < r) {
|
||||
float mmz =
|
||||
(float)(S(e.x, e.y)->floor + mmi.zoff + e.attr3);
|
||||
if (d->o.z - d->eyeheight < mmz) {
|
||||
if (mmz < hi)
|
||||
hi = mmz;
|
||||
} else if (mmz + mmi.h > lo)
|
||||
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
|
||||
// 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)
|
||||
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 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 :)
|
||||
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
|
||||
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);
|
||||
if (OUTBORD(x, y))
|
||||
return false;
|
||||
sqr *s = S(x, y);
|
||||
float ceil = s->ceil;
|
||||
float floor = s->floor;
|
||||
switch(s->type)
|
||||
{
|
||||
switch (s->type) {
|
||||
case SOLID:
|
||||
return false;
|
||||
|
||||
case CORNER:
|
||||
{
|
||||
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)
|
||||
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;
|
||||
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;
|
||||
|
||||
ceil += (s->vdelta + S(x + 1, y)->vdelta +
|
||||
S(x, y + 1)->vdelta +
|
||||
S(x + 1, y + 1)->vdelta) /
|
||||
16.0f;
|
||||
};
|
||||
if(ceil<hi) hi = ceil;
|
||||
if(floor>lo) lo = floor;
|
||||
if(floor<minfloor) return false;
|
||||
if (ceil < hi)
|
||||
hi = ceil;
|
||||
if (floor > lo)
|
||||
lo = floor;
|
||||
if (floor < minfloor)
|
||||
return false;
|
||||
};
|
||||
|
||||
if(hi-lo < d->eyeheight+d->aboveeye) return false;
|
||||
if (hi - lo < d->eyeheight + 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, hi, lo)) return false;
|
||||
if (!o || o == d)
|
||||
continue;
|
||||
if (!plcollide(d, o, headspace, hi, lo))
|
||||
return false;
|
||||
};
|
||||
if(d!=player1) if(!plcollide(d, player1, headspace, hi, lo)) return false;
|
||||
if (d != player1)
|
||||
if (!plcollide(d, player1, headspace, hi, lo))
|
||||
return false;
|
||||
dvector &v = getmonsters();
|
||||
// this loop can be a performance bottleneck with many monster on a slow cpu,
|
||||
// should replace with a blockmap but seems mostly fast enough
|
||||
loopv(v) if(!vreject(d->o, v[i]->o, 7.0f) && d!=v[i] && !plcollide(d, v[i], headspace, hi, lo)) return false;
|
||||
// this loop can be a performance bottleneck with many monster on a slow
|
||||
// cpu, should replace with a blockmap but seems mostly fast enough
|
||||
loopv(v) if (!vreject(d->o, v[i]->o, 7.0f) && d != v[i] &&
|
||||
!plcollide(d, v[i], headspace, hi, lo)) 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)
|
||||
if (spawn) {
|
||||
d->o.z = lo + d->eyeheight; // just drop to floor (sideeffect)
|
||||
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->o.z += rise; // rise thru stair
|
||||
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->o.z += rise; // rise thru stair
|
||||
else return false;
|
||||
}
|
||||
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
|
||||
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.001f;
|
||||
d->onfloor = d->o.z - d->eyeheight - lo < 0.001f;
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
float rad(float x) { return x*3.14159f/180; };
|
||||
float
|
||||
rad(float x)
|
||||
{
|
||||
return x * 3.14159f / 180;
|
||||
};
|
||||
|
||||
VARP(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
|
||||
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
|
||||
{
|
||||
if (curtime >= MINFRAMETIME) {
|
||||
int faketime = curtime + physicsfraction;
|
||||
physicsrepeat = faketime / MINFRAMETIME;
|
||||
physicsfraction = faketime - physicsrepeat * MINFRAMETIME;
|
||||
} else {
|
||||
physicsrepeat = 1;
|
||||
};
|
||||
};
|
||||
|
||||
// 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
|
||||
// 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)
|
||||
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;
|
||||
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
|
||||
|
||||
d.x = (float)(pl->move*cos(rad(pl->yaw-90)));
|
||||
d.y = (float)(pl->move*sin(rad(pl->yaw-90)));
|
||||
d.x = (float)(pl->move * cos(rad(pl->yaw - 90)));
|
||||
d.y = (float)(pl->move * sin(rad(pl->yaw - 90)));
|
||||
d.z = 0;
|
||||
|
||||
if(floating || water)
|
||||
{
|
||||
if (floating || water) {
|
||||
d.x *= (float)cos(rad(pl->pitch));
|
||||
d.y *= (float)cos(rad(pl->pitch));
|
||||
d.z = (float)(pl->move*sin(rad(pl->pitch)));
|
||||
d.z = (float)(pl->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)));
|
||||
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 : 30.0f);
|
||||
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;
|
||||
const float fpsfric = friction / curtime * 20.0f;
|
||||
|
||||
vmul(pl->vel, fpsfric-1); // slowly apply friction and direction to velocity, gives a smooth movement
|
||||
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;
|
||||
|
@ -220,104 +272,133 @@ void moveplayer(dynent *pl, int moveres, bool local, int curtime)
|
|||
pl->blocked = false;
|
||||
pl->moving = true;
|
||||
|
||||
if(floating) // just apply velocity
|
||||
if (floating) // just apply velocity
|
||||
{
|
||||
vadd(pl->o, d);
|
||||
if(pl->jumpnext) { pl->jumpnext = false; pl->vel.z = 2; }
|
||||
if (pl->jumpnext) {
|
||||
pl->jumpnext = false;
|
||||
pl->vel.z = 2;
|
||||
}
|
||||
else // apply velocity with collision
|
||||
{
|
||||
if(pl->onfloor || water)
|
||||
{
|
||||
if(pl->jumpnext)
|
||||
} else // apply velocity with collision
|
||||
{
|
||||
if (pl->onfloor || water) {
|
||||
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 (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);
|
||||
if (local)
|
||||
playsoundc(S_LAND);
|
||||
else if (pl->monsterstate)
|
||||
playsound(S_LAND, &pl->o);
|
||||
};
|
||||
pl->timeinair = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
pl->timeinair += curtime;
|
||||
};
|
||||
|
||||
const float gravity = 20;
|
||||
const float f = 1.0f/moveres;
|
||||
float dropf = ((gravity-1)+pl->timeinair/15.0f); // incorrect, but works fine
|
||||
if(water) { dropf = 5; pl->timeinair = 0; }; // float slowly down in water
|
||||
const float drop = dropf*curtime/gravity/100/moveres; // at high fps, gravity kicks in too fast
|
||||
const float rise = speed/moveres/1.2f; // extra smoothness when lifting up stairs
|
||||
const float f = 1.0f / moveres;
|
||||
float dropf =
|
||||
((gravity - 1) +
|
||||
pl->timeinair / 15.0f); // incorrect, but works fine
|
||||
if (water) {
|
||||
dropf = 5;
|
||||
pl->timeinair = 0;
|
||||
}; // float slowly down in water
|
||||
const float drop =
|
||||
dropf * curtime / gravity / 100 /
|
||||
moveres; // at high fps, gravity kicks in too fast
|
||||
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;
|
||||
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; continue; };
|
||||
pl->o.x += f*d.x;
|
||||
pl->o.x -= f * d.x;
|
||||
if (collide(pl, false, drop, rise)) {
|
||||
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)) { d.y = 0; continue; };
|
||||
pl->o.y += f*d.y;
|
||||
pl->o.y -= f * d.y;
|
||||
if (collide(pl, false, drop, rise)) {
|
||||
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; continue; };
|
||||
pl->o.z -= f*d.z;
|
||||
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
|
||||
// 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)
|
||||
{
|
||||
if (pl->o.x < 0 || pl->o.x >= ssize || pl->o.y < 0 || pl->o.y > ssize) {
|
||||
pl->outsidemap = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
} 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);
|
||||
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;
|
||||
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);
|
||||
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)
|
||||
void
|
||||
moveplayer(dynent *pl, int moveres, bool local)
|
||||
{
|
||||
loopi(physicsrepeat) moveplayer(pl, moveres, local, i ? curtime/physicsrepeat : curtime-curtime/physicsrepeat*(physicsrepeat-1));
|
||||
loopi(physicsrepeat) moveplayer(pl, moveres, local,
|
||||
i ? curtime / physicsrepeat
|
||||
: curtime - curtime / physicsrepeat * (physicsrepeat - 1));
|
||||
};
|
||||
|
||||
|
|
51
src/protos.h
51
src/protos.h
|
@ -1,7 +1,8 @@
|
|||
// protos for ALL external functions in cube...
|
||||
|
||||
// command
|
||||
extern int variable(char *name, int min, int cur, int max, int *storage, void (*fun)(), bool persist);
|
||||
extern int variable(char *name, int min, int cur, int max, int *storage,
|
||||
void (*fun)(), bool persist);
|
||||
extern void setvar(char *name, int i);
|
||||
extern int getvar(char *name);
|
||||
extern bool identexists(char *name);
|
||||
|
@ -39,7 +40,8 @@ extern void writeservercfg();
|
|||
extern void gl_init(int w, int h);
|
||||
extern void cleangl();
|
||||
extern void gl_drawframe(int w, int h, float curfps);
|
||||
extern bool installtex(int tnum, char *texname, int &xs, int &ys, bool clamp = false);
|
||||
extern bool installtex(
|
||||
int tnum, char *texname, int &xs, int &ys, bool clamp = false);
|
||||
extern void mipstats(int a, int b, int c);
|
||||
extern void vertf(float v1, float v2, float v3, sqr *ls, float t1, float t2);
|
||||
extern void addstrip(int tex, int start, int n);
|
||||
|
@ -47,10 +49,16 @@ extern int lookuptexture(int tex, int &xs, int &ys);
|
|||
|
||||
// rendercubes
|
||||
extern void resetcubes();
|
||||
extern void render_flat(int tex, int x, int y, int size, int h, sqr *l1, sqr *l2, sqr *l3, sqr *l4, bool isceil);
|
||||
extern void render_flatdelta(int wtex, int x, int y, int size, float h1, float h2, float h3, float h4, sqr *l1, sqr *l2, sqr *l3, sqr *l4, bool isceil);
|
||||
extern void render_square(int wtex, float floor1, float floor2, float ceil1, float ceil2, int x1, int y1, int x2, int y2, int size, sqr *l1, sqr *l2, bool topleft);
|
||||
extern void render_tris(int x, int y, int size, bool topleft, sqr *h1, sqr *h2, sqr *s, sqr *t, sqr *u, sqr *v);
|
||||
extern void render_flat(int tex, int x, int y, int size, int h, sqr *l1,
|
||||
sqr *l2, sqr *l3, sqr *l4, bool isceil);
|
||||
extern void render_flatdelta(int wtex, int x, int y, int size, float h1,
|
||||
float h2, float h3, float h4, sqr *l1, sqr *l2, sqr *l3, sqr *l4,
|
||||
bool isceil);
|
||||
extern void render_square(int wtex, float floor1, float floor2, float ceil1,
|
||||
float ceil2, int x1, int y1, int x2, int y2, int size, sqr *l1, sqr *l2,
|
||||
bool topleft);
|
||||
extern void render_tris(int x, int y, int size, bool topleft, sqr *h1, sqr *h2,
|
||||
sqr *s, sqr *t, sqr *u, sqr *v);
|
||||
extern void addwaterquad(int x, int y, int size);
|
||||
extern int renderwater(float hf);
|
||||
extern void finishstrips();
|
||||
|
@ -93,7 +101,8 @@ extern void fixplayer1range();
|
|||
|
||||
// clientextras
|
||||
extern void renderclients();
|
||||
extern void renderclient(dynent *d, bool team, char *mdlname, bool hellpig, float scale);
|
||||
extern void renderclient(
|
||||
dynent *d, bool team, char *mdlname, bool hellpig, float scale);
|
||||
void showscores(bool on);
|
||||
extern void renderscores();
|
||||
|
||||
|
@ -107,17 +116,20 @@ extern int findentity(int type, int index = 0);
|
|||
extern void trigger(int tag, int type, bool savegame);
|
||||
extern void resettagareas();
|
||||
extern void settagareas();
|
||||
extern entity *newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v4);
|
||||
extern entity *newentity(
|
||||
int x, int y, int z, char *what, int v1, int v2, int v3, int v4);
|
||||
|
||||
// worldlight
|
||||
extern void calclight();
|
||||
extern void dodynlight(vec &vold, vec &v, int reach, int strength, dynent *owner);
|
||||
extern void dodynlight(
|
||||
vec &vold, vec &v, int reach, int strength, dynent *owner);
|
||||
extern void cleardlights();
|
||||
extern block *blockcopy(block &b);
|
||||
extern void blockpaste(block &b);
|
||||
|
||||
// worldrender
|
||||
extern void render_world(float vx, float vy, float vh, int yaw, int pitch, float widef, int w, int h);
|
||||
extern void render_world(float vx, float vy, float vh, int yaw, int pitch,
|
||||
float widef, int w, int h);
|
||||
|
||||
// worldocull
|
||||
extern void computeraytable(float vx, float vy);
|
||||
|
@ -153,7 +165,8 @@ extern void dot(int x, int y, float z);
|
|||
extern void linestyle(float width, int r, int g, int b);
|
||||
extern void newsphere(vec &o, float max, int type);
|
||||
extern void renderspheres(int time);
|
||||
extern void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater);
|
||||
extern void gl_drawhud(
|
||||
int w, int h, int curfps, int nquads, int curvert, bool underwater);
|
||||
extern void readdepth(int w, int h);
|
||||
extern void blendbox(int x1, int y1, int x2, int y2, bool border);
|
||||
extern void damageblend(int n);
|
||||
|
@ -191,11 +204,14 @@ extern void initsound();
|
|||
extern void cleansound();
|
||||
|
||||
// rendermd2
|
||||
extern void rendermodel(char *mdl, int frame, int range, int tex, float rad, float x, float y, float z, float yaw, float pitch, bool teammate, float scale, float speed, int snap = 0, int basetime = 0);
|
||||
extern void rendermodel(char *mdl, int frame, int range, int tex, float rad,
|
||||
float x, float y, float z, float yaw, float pitch, bool teammate,
|
||||
float scale, float speed, int snap = 0, int basetime = 0);
|
||||
extern mapmodelinfo &getmminfo(int i);
|
||||
|
||||
// server
|
||||
extern void initserver(bool dedicated, int uprate, char *sdesc, char *ip, char *master, char *passwd, int maxcl);
|
||||
extern void initserver(bool dedicated, int uprate, char *sdesc, char *ip,
|
||||
char *master, char *passwd, int maxcl);
|
||||
extern void cleanupserver();
|
||||
extern void localconnect();
|
||||
extern void localdisconnect();
|
||||
|
@ -208,15 +224,17 @@ extern void startintermission();
|
|||
extern void restoreserverstate(vector<entity> &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, bool isfull);
|
||||
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);
|
||||
|
||||
// weapon
|
||||
extern void selectgun(int a = -1, int b = -1, int c =-1);
|
||||
extern void selectgun(int a = -1, int b = -1, int c = -1);
|
||||
extern void shoot(dynent *d, vec &to);
|
||||
extern void shootv(int gun, vec &from, vec &to, dynent *d = 0, bool local = false);
|
||||
extern void shootv(
|
||||
int gun, vec &from, vec &to, dynent *d = 0, bool local = false);
|
||||
extern void createrays(vec &from, vec &to);
|
||||
extern void moveprojectiles(float time);
|
||||
extern void projreset();
|
||||
|
@ -246,4 +264,3 @@ extern void baseammo(int gun);
|
|||
|
||||
// rndmap
|
||||
extern void perlinarea(block &b, int scale, int seed, int psize);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills the vertex array for different cube surfaces.
|
||||
// rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills
|
||||
// the vertex array for different cube surfaces.
|
||||
|
||||
#include "cube.h"
|
||||
|
||||
|
@ -6,33 +7,52 @@ vertex *verts = NULL;
|
|||
int curvert;
|
||||
int curmaxverts = 10000;
|
||||
|
||||
void setarraypointers()
|
||||
void
|
||||
setarraypointers()
|
||||
{
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(vertex), &verts[0].x);
|
||||
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), &verts[0].r);
|
||||
glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), &verts[0].u);
|
||||
};
|
||||
|
||||
void reallocv()
|
||||
void
|
||||
reallocv()
|
||||
{
|
||||
verts = (vertex *)realloc(verts, (curmaxverts *= 2)*sizeof(vertex));
|
||||
verts = (vertex *)realloc(verts, (curmaxverts *= 2) * sizeof(vertex));
|
||||
curmaxverts -= 10;
|
||||
if(!verts) fatal("no vertex memory!");
|
||||
if (!verts)
|
||||
fatal("no vertex memory!");
|
||||
setarraypointers();
|
||||
};
|
||||
|
||||
// generating the actual vertices is done dynamically every frame and sits at the
|
||||
// leaves of all these functions, and are part of the cpu bottleneck on really slow
|
||||
// machines, hence the macros.
|
||||
// generating the actual vertices is done dynamically every frame and sits at
|
||||
// the leaves of all these functions, and are part of the cpu bottleneck on
|
||||
// really slow machines, hence the macros.
|
||||
|
||||
#define vertcheck() { if(curvert>=curmaxverts) reallocv(); }
|
||||
#define vertcheck() \
|
||||
{ \
|
||||
if (curvert >= curmaxverts) \
|
||||
reallocv(); \
|
||||
}
|
||||
|
||||
#define vertf(v1, v2, v3, ls, t1, t2) { vertex &v = verts[curvert++]; \
|
||||
v.u = t1; v.v = t2; \
|
||||
v.x = v1; v.y = v2; v.z = v3; \
|
||||
v.r = ls->r; v.g = ls->g; v.b = ls->b; v.a = 255; };
|
||||
#define vertf(v1, v2, v3, ls, t1, t2) \
|
||||
{ \
|
||||
vertex &v = verts[curvert++]; \
|
||||
v.u = t1; \
|
||||
v.v = t2; \
|
||||
v.x = v1; \
|
||||
v.y = v2; \
|
||||
v.z = v3; \
|
||||
v.r = ls->r; \
|
||||
v.g = ls->g; \
|
||||
v.b = ls->b; \
|
||||
v.a = 255; \
|
||||
};
|
||||
|
||||
#define vert(v1, v2, v3, ls, t1, t2) { vertf((float)(v1), (float)(v2), (float)(v3), ls, t1, t2); }
|
||||
#define vert(v1, v2, v3, ls, t1, t2) \
|
||||
{ \
|
||||
vertf((float)(v1), (float)(v2), (float)(v3), ls, t1, t2); \
|
||||
}
|
||||
|
||||
int nquads;
|
||||
const float TEXTURESCALE = 32.0f;
|
||||
|
@ -42,34 +62,59 @@ int ol3r, ol3g, ol3b, ol4r, ol4g, ol4b;
|
|||
int firstindex;
|
||||
bool showm = false;
|
||||
|
||||
void showmip() { showm = !showm; };
|
||||
void mipstats(int a, int b, int c) { if(showm) conoutf("1x1/2x2/4x4: %d / %d / %d", a, b, c); };
|
||||
void
|
||||
showmip()
|
||||
{
|
||||
showm = !showm;
|
||||
};
|
||||
void
|
||||
mipstats(int a, int b, int c)
|
||||
{
|
||||
if (showm)
|
||||
conoutf("1x1/2x2/4x4: %d / %d / %d", a, b, c);
|
||||
};
|
||||
|
||||
COMMAND(showmip, ARG_NONE);
|
||||
|
||||
#define stripend() { if(floorstrip || deltastrip) { addstrip(ogltex, firstindex, curvert-firstindex); floorstrip = deltastrip = false; }; };
|
||||
void finishstrips() { stripend(); };
|
||||
#define stripend() \
|
||||
{ \
|
||||
if (floorstrip || deltastrip) { \
|
||||
addstrip(ogltex, firstindex, curvert - firstindex); \
|
||||
floorstrip = deltastrip = false; \
|
||||
}; \
|
||||
};
|
||||
void
|
||||
finishstrips()
|
||||
{
|
||||
stripend();
|
||||
};
|
||||
|
||||
sqr sbright, sdark;
|
||||
VAR(lighterror,1,8,100);
|
||||
VAR(lighterror, 1, 8, 100);
|
||||
|
||||
void render_flat(int wtex, int x, int y, int size, int h, sqr *l1, sqr *l2, sqr *l3, sqr *l4, bool isceil) // floor/ceil quads
|
||||
void
|
||||
render_flat(int wtex, int x, int y, int size, int h, sqr *l1, sqr *l2, sqr *l3,
|
||||
sqr *l4, bool isceil) // floor/ceil quads
|
||||
{
|
||||
vertcheck();
|
||||
if(showm) { l3 = l1 = &sbright; l4 = l2 = &sdark; };
|
||||
if (showm) {
|
||||
l3 = l1 = &sbright;
|
||||
l4 = l2 = &sdark;
|
||||
};
|
||||
|
||||
int sx, sy;
|
||||
int gltex = lookuptexture(wtex, sx, sy);
|
||||
float xf = TEXTURESCALE/sx;
|
||||
float yf = TEXTURESCALE/sy;
|
||||
float xs = size*xf;
|
||||
float ys = size*yf;
|
||||
float xo = xf*x;
|
||||
float yo = yf*y;
|
||||
float xf = TEXTURESCALE / sx;
|
||||
float yf = TEXTURESCALE / sy;
|
||||
float xs = size * xf;
|
||||
float ys = size * yf;
|
||||
float xo = xf * x;
|
||||
float yo = yf * y;
|
||||
|
||||
bool first = !floorstrip || y!=oy+size || ogltex!=gltex || h!=oh || x!=ox;
|
||||
bool first = !floorstrip || y != oy + size || ogltex != gltex ||
|
||||
h != oh || x != ox;
|
||||
|
||||
if(first) // start strip here
|
||||
if (first) // start strip here
|
||||
{
|
||||
stripend();
|
||||
firstindex = curvert;
|
||||
|
@ -77,15 +122,12 @@ void render_flat(int wtex, int x, int y, int size, int h, sqr *l1, sqr *l2, sqr
|
|||
oh = h;
|
||||
ox = x;
|
||||
floorstrip = true;
|
||||
if(isceil)
|
||||
{
|
||||
vert(x+size, h, y, l2, xo+xs, yo);
|
||||
if (isceil) {
|
||||
vert(x + size, h, y, l2, xo + xs, yo);
|
||||
vert(x, h, y, l1, xo, yo);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
vert(x, h, y, l1, xo, yo);
|
||||
vert(x+size, h, y, l2, xo+xs, yo);
|
||||
vert(x + size, h, y, l2, xo + xs, yo);
|
||||
};
|
||||
ol3r = l1->r;
|
||||
ol3g = l1->g;
|
||||
|
@ -93,77 +135,78 @@ void render_flat(int wtex, int x, int y, int size, int h, sqr *l1, sqr *l2, sqr
|
|||
ol4r = l2->r;
|
||||
ol4g = l2->g;
|
||||
ol4b = l2->b;
|
||||
}
|
||||
else // continue strip
|
||||
{
|
||||
int lighterr = lighterror*2;
|
||||
if((abs(ol3r-l3->r)<lighterr && abs(ol4r-l4->r)<lighterr // skip vertices if light values are close enough
|
||||
&& abs(ol3g-l3->g)<lighterr && abs(ol4g-l4->g)<lighterr
|
||||
&& abs(ol3b-l3->b)<lighterr && abs(ol4b-l4->b)<lighterr) || !wtex)
|
||||
} else // continue strip
|
||||
{
|
||||
int lighterr = lighterror * 2;
|
||||
if ((abs(ol3r - l3->r) < lighterr &&
|
||||
abs(ol4r - l4->r) < lighterr // skip vertices if light
|
||||
// values are close enough
|
||||
&& abs(ol3g - l3->g) < lighterr &&
|
||||
abs(ol4g - l4->g) < lighterr &&
|
||||
abs(ol3b - l3->b) < lighterr &&
|
||||
abs(ol4b - l4->b) < lighterr) ||
|
||||
!wtex) {
|
||||
curvert -= 2;
|
||||
nquads--;
|
||||
}
|
||||
else
|
||||
{
|
||||
uchar *p3 = (uchar *)(&verts[curvert-1].r);
|
||||
} else {
|
||||
uchar *p3 = (uchar *)(&verts[curvert - 1].r);
|
||||
ol3r = p3[0];
|
||||
ol3g = p3[1];
|
||||
ol3b = p3[2];
|
||||
uchar *p4 = (uchar *)(&verts[curvert-2].r);
|
||||
uchar *p4 = (uchar *)(&verts[curvert - 2].r);
|
||||
ol4r = p4[0];
|
||||
ol4g = p4[1];
|
||||
ol4b = p4[2];
|
||||
};
|
||||
};
|
||||
|
||||
if(isceil)
|
||||
{
|
||||
vert(x+size, h, y+size, l3, xo+xs, yo+ys);
|
||||
vert(x, h, y+size, l4, xo, yo+ys);
|
||||
}
|
||||
else
|
||||
{
|
||||
vert(x, h, y+size, l4, xo, yo+ys);
|
||||
vert(x+size, h, y+size, l3, xo+xs, yo+ys);
|
||||
if (isceil) {
|
||||
vert(x + size, h, y + size, l3, xo + xs, yo + ys);
|
||||
vert(x, h, y + size, l4, xo, yo + ys);
|
||||
} else {
|
||||
vert(x, h, y + size, l4, xo, yo + ys);
|
||||
vert(x + size, h, y + size, l3, xo + xs, yo + ys);
|
||||
};
|
||||
|
||||
oy = y;
|
||||
nquads++;
|
||||
};
|
||||
|
||||
void render_flatdelta(int wtex, int x, int y, int size, float h1, float h2, float h3, float h4, sqr *l1, sqr *l2, sqr *l3, sqr *l4, bool isceil) // floor/ceil quads on a slope
|
||||
void
|
||||
render_flatdelta(int wtex, int x, int y, int size, float h1, float h2, float h3,
|
||||
float h4, sqr *l1, sqr *l2, sqr *l3, sqr *l4,
|
||||
bool isceil) // floor/ceil quads on a slope
|
||||
{
|
||||
vertcheck();
|
||||
if(showm) { l3 = l1 = &sbright; l4 = l2 = &sdark; };
|
||||
if (showm) {
|
||||
l3 = l1 = &sbright;
|
||||
l4 = l2 = &sdark;
|
||||
};
|
||||
|
||||
int sx, sy;
|
||||
int gltex = lookuptexture(wtex, sx, sy);
|
||||
float xf = TEXTURESCALE/sx;
|
||||
float yf = TEXTURESCALE/sy;
|
||||
float xs = size*xf;
|
||||
float ys = size*yf;
|
||||
float xo = xf*x;
|
||||
float yo = yf*y;
|
||||
float xf = TEXTURESCALE / sx;
|
||||
float yf = TEXTURESCALE / sy;
|
||||
float xs = size * xf;
|
||||
float ys = size * yf;
|
||||
float xo = xf * x;
|
||||
float yo = yf * y;
|
||||
|
||||
bool first = !deltastrip || y!=oy+size || ogltex!=gltex || x!=ox;
|
||||
bool first =
|
||||
!deltastrip || y != oy + size || ogltex != gltex || x != ox;
|
||||
|
||||
if(first)
|
||||
{
|
||||
if (first) {
|
||||
stripend();
|
||||
firstindex = curvert;
|
||||
ogltex = gltex;
|
||||
ox = x;
|
||||
deltastrip = true;
|
||||
if(isceil)
|
||||
{
|
||||
vertf((float)x+size, h2, (float)y, l2, xo+xs, yo);
|
||||
if (isceil) {
|
||||
vertf((float)x + size, h2, (float)y, l2, xo + xs, yo);
|
||||
vertf((float)x, h1, (float)y, l1, xo, yo);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
vertf((float)x, h1, (float)y, l1, xo, yo);
|
||||
vertf((float)x+size, h2, (float)y, l2, xo+xs, yo);
|
||||
vertf((float)x + size, h2, (float)y, l2, xo + xs, yo);
|
||||
};
|
||||
ol3r = l1->r;
|
||||
ol3g = l1->g;
|
||||
|
@ -173,113 +216,137 @@ void render_flatdelta(int wtex, int x, int y, int size, float h1, float h2, floa
|
|||
ol4b = l2->b;
|
||||
};
|
||||
|
||||
if(isceil)
|
||||
{
|
||||
vertf((float)x+size, h3, (float)y+size, l3, xo+xs, yo+ys);
|
||||
vertf((float)x, h4, (float)y+size, l4, xo, yo+ys);
|
||||
}
|
||||
else
|
||||
{
|
||||
vertf((float)x, h4, (float)y+size, l4, xo, yo+ys);
|
||||
vertf((float)x+size, h3, (float)y+size, l3, xo+xs, yo+ys);
|
||||
if (isceil) {
|
||||
vertf(
|
||||
(float)x + size, h3, (float)y + size, l3, xo + xs, yo + ys);
|
||||
vertf((float)x, h4, (float)y + size, l4, xo, yo + ys);
|
||||
} else {
|
||||
vertf((float)x, h4, (float)y + size, l4, xo, yo + ys);
|
||||
vertf(
|
||||
(float)x + size, h3, (float)y + size, l3, xo + xs, yo + ys);
|
||||
};
|
||||
|
||||
oy = y;
|
||||
nquads++;
|
||||
};
|
||||
|
||||
void render_2tris(sqr *h, sqr *s, int x1, int y1, int x2, int y2, int x3, int y3, sqr *l1, sqr *l2, sqr *l3) // floor/ceil tris on a corner cube
|
||||
void
|
||||
render_2tris(sqr *h, sqr *s, int x1, int y1, int x2, int y2, int x3, int y3,
|
||||
sqr *l1, sqr *l2, sqr *l3) // floor/ceil tris on a corner cube
|
||||
{
|
||||
stripend();
|
||||
vertcheck();
|
||||
|
||||
int sx, sy;
|
||||
int gltex = lookuptexture(h->ftex, sx, sy);
|
||||
float xf = TEXTURESCALE/sx;
|
||||
float yf = TEXTURESCALE/sy;
|
||||
float xf = TEXTURESCALE / sx;
|
||||
float yf = TEXTURESCALE / sy;
|
||||
|
||||
vertf((float)x1, h->floor, (float)y1, l1, xf*x1, yf*y1);
|
||||
vertf((float)x2, h->floor, (float)y2, l2, xf*x2, yf*y2);
|
||||
vertf((float)x3, h->floor, (float)y3, l3, xf*x3, yf*y3);
|
||||
addstrip(gltex, curvert-3, 3);
|
||||
vertf((float)x1, h->floor, (float)y1, l1, xf * x1, yf * y1);
|
||||
vertf((float)x2, h->floor, (float)y2, l2, xf * x2, yf * y2);
|
||||
vertf((float)x3, h->floor, (float)y3, l3, xf * x3, yf * y3);
|
||||
addstrip(gltex, curvert - 3, 3);
|
||||
|
||||
gltex = lookuptexture(h->ctex, sx, sy);
|
||||
xf = TEXTURESCALE/sx;
|
||||
yf = TEXTURESCALE/sy;
|
||||
xf = TEXTURESCALE / sx;
|
||||
yf = TEXTURESCALE / sy;
|
||||
|
||||
vertf((float)x3, h->ceil, (float)y3, l3, xf*x3, yf*y3);
|
||||
vertf((float)x2, h->ceil, (float)y2, l2, xf*x2, yf*y2);
|
||||
vertf((float)x1, h->ceil, (float)y1, l1, xf*x1, yf*y1);
|
||||
addstrip(gltex, curvert-3, 3);
|
||||
vertf((float)x3, h->ceil, (float)y3, l3, xf * x3, yf * y3);
|
||||
vertf((float)x2, h->ceil, (float)y2, l2, xf * x2, yf * y2);
|
||||
vertf((float)x1, h->ceil, (float)y1, l1, xf * x1, yf * y1);
|
||||
addstrip(gltex, curvert - 3, 3);
|
||||
nquads++;
|
||||
};
|
||||
|
||||
void render_tris(int x, int y, int size, bool topleft,
|
||||
sqr *h1, sqr *h2, sqr *s, sqr *t, sqr *u, sqr *v)
|
||||
void
|
||||
render_tris(int x, int y, int size, bool topleft, sqr *h1, sqr *h2, sqr *s,
|
||||
sqr *t, sqr *u, sqr *v)
|
||||
{
|
||||
if(topleft)
|
||||
{
|
||||
if(h1) render_2tris(h1, s, x+size, y+size, x, y+size, x, y, u, v, s);
|
||||
if(h2) render_2tris(h2, s, x, y, x+size, y, x+size, y+size, s, t, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(h1) render_2tris(h1, s, x, y, x+size, y, x, y+size, s, t, u);
|
||||
if(h2) render_2tris(h2, s, x+size, y, x+size, y+size, x, y+size, t, u, v);
|
||||
if (topleft) {
|
||||
if (h1)
|
||||
render_2tris(h1, s, x + size, y + size, x, y + size, x,
|
||||
y, u, v, s);
|
||||
if (h2)
|
||||
render_2tris(h2, s, x, y, x + size, y, x + size,
|
||||
y + size, s, t, v);
|
||||
} else {
|
||||
if (h1)
|
||||
render_2tris(
|
||||
h1, s, x, y, x + size, y, x, y + size, s, t, u);
|
||||
if (h2)
|
||||
render_2tris(h2, s, x + size, y, x + size, y + size, x,
|
||||
y + size, t, u, v);
|
||||
};
|
||||
};
|
||||
|
||||
void render_square(int wtex, float floor1, float floor2, float ceil1, float ceil2, int x1, int y1, int x2, int y2, int size, sqr *l1, sqr *l2, bool flip) // wall quads
|
||||
void
|
||||
render_square(int wtex, float floor1, float floor2, float ceil1, float ceil2,
|
||||
int x1, int y1, int x2, int y2, int size, sqr *l1, sqr *l2,
|
||||
bool flip) // wall quads
|
||||
{
|
||||
stripend();
|
||||
vertcheck();
|
||||
if(showm) { l1 = &sbright; l2 = &sdark; };
|
||||
if (showm) {
|
||||
l1 = &sbright;
|
||||
l2 = &sdark;
|
||||
};
|
||||
|
||||
int sx, sy;
|
||||
int gltex = lookuptexture(wtex, sx, sy);
|
||||
float xf = TEXTURESCALE/sx;
|
||||
float yf = TEXTURESCALE/sy;
|
||||
float xs = size*xf;
|
||||
float xo = xf*(x1==x2 ? min(y1,y2) : min(x1,x2));
|
||||
float xf = TEXTURESCALE / sx;
|
||||
float yf = TEXTURESCALE / sy;
|
||||
float xs = size * xf;
|
||||
float xo = xf * (x1 == x2 ? min(y1, y2) : min(x1, x2));
|
||||
|
||||
if(!flip)
|
||||
{
|
||||
vertf((float)x2, ceil2, (float)y2, l2, xo+xs, -yf*ceil2);
|
||||
vertf((float)x1, ceil1, (float)y1, l1, xo, -yf*ceil1);
|
||||
vertf((float)x2, floor2, (float)y2, l2, xo+xs, -floor2*yf);
|
||||
vertf((float)x1, floor1, (float)y1, l1, xo, -floor1*yf);
|
||||
}
|
||||
else
|
||||
{
|
||||
vertf((float)x1, ceil1, (float)y1, l1, xo, -yf*ceil1);
|
||||
vertf((float)x2, ceil2, (float)y2, l2, xo+xs, -yf*ceil2);
|
||||
vertf((float)x1, floor1, (float)y1, l1, xo, -floor1*yf);
|
||||
vertf((float)x2, floor2, (float)y2, l2, xo+xs, -floor2*yf);
|
||||
if (!flip) {
|
||||
vertf((float)x2, ceil2, (float)y2, l2, xo + xs, -yf * ceil2);
|
||||
vertf((float)x1, ceil1, (float)y1, l1, xo, -yf * ceil1);
|
||||
vertf((float)x2, floor2, (float)y2, l2, xo + xs, -floor2 * yf);
|
||||
vertf((float)x1, floor1, (float)y1, l1, xo, -floor1 * yf);
|
||||
} else {
|
||||
vertf((float)x1, ceil1, (float)y1, l1, xo, -yf * ceil1);
|
||||
vertf((float)x2, ceil2, (float)y2, l2, xo + xs, -yf * ceil2);
|
||||
vertf((float)x1, floor1, (float)y1, l1, xo, -floor1 * yf);
|
||||
vertf((float)x2, floor2, (float)y2, l2, xo + xs, -floor2 * yf);
|
||||
};
|
||||
|
||||
nquads++;
|
||||
addstrip(gltex, curvert-4, 4);
|
||||
addstrip(gltex, curvert - 4, 4);
|
||||
};
|
||||
|
||||
int wx1, wy1, wx2, wy2;
|
||||
|
||||
VAR(watersubdiv, 1, 4, 64);
|
||||
VARF(waterlevel, -128, -128, 127, if(!noteditmode()) hdr.waterlevel = waterlevel);
|
||||
VARF(waterlevel, -128, -128, 127,
|
||||
if (!noteditmode()) hdr.waterlevel = waterlevel);
|
||||
|
||||
inline void vertw(int v1, float v2, int v3, sqr *c, float t1, float t2, float t)
|
||||
inline void
|
||||
vertw(int v1, float v2, int v3, sqr *c, float t1, float t2, float t)
|
||||
{
|
||||
vertcheck();
|
||||
vertf((float)v1, v2-(float)sin(v1*v3*0.1+t)*0.2f, (float)v3, c, t1, t2);
|
||||
vertf((float)v1, v2 - (float)sin(v1 * v3 * 0.1 + t) * 0.2f, (float)v3,
|
||||
c, t1, t2);
|
||||
};
|
||||
|
||||
inline float dx(float x) { return x+(float)sin(x*2+lastmillis/1000.0f)*0.04f; };
|
||||
inline float dy(float x) { return x+(float)sin(x*2+lastmillis/900.0f+PI/5)*0.05f; };
|
||||
|
||||
// renders water for bounding rect area that contains water... simple but very inefficient
|
||||
|
||||
int renderwater(float hf)
|
||||
inline float
|
||||
dx(float x)
|
||||
{
|
||||
if(wx1<0) return nquads;
|
||||
return x + (float)sin(x * 2 + lastmillis / 1000.0f) * 0.04f;
|
||||
};
|
||||
inline float
|
||||
dy(float x)
|
||||
{
|
||||
return x + (float)sin(x * 2 + lastmillis / 900.0f + PI / 5) * 0.05f;
|
||||
};
|
||||
|
||||
// renders water for bounding rect area that contains water... simple but very
|
||||
// inefficient
|
||||
|
||||
int
|
||||
renderwater(float hf)
|
||||
{
|
||||
if (wx1 < 0)
|
||||
return nquads;
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_BLEND);
|
||||
|
@ -287,36 +354,36 @@ int renderwater(float hf)
|
|||
int sx, sy;
|
||||
glBindTexture(GL_TEXTURE_2D, lookuptexture(DEFAULT_LIQUID, sx, sy));
|
||||
|
||||
wx1 &= ~(watersubdiv-1);
|
||||
wy1 &= ~(watersubdiv-1);
|
||||
wx1 &= ~(watersubdiv - 1);
|
||||
wy1 &= ~(watersubdiv - 1);
|
||||
|
||||
float xf = TEXTURESCALE/sx;
|
||||
float yf = TEXTURESCALE/sy;
|
||||
float xs = watersubdiv*xf;
|
||||
float ys = watersubdiv*yf;
|
||||
float t1 = lastmillis/300.0f;
|
||||
float t2 = lastmillis/4000.0f;
|
||||
float xf = TEXTURESCALE / sx;
|
||||
float yf = TEXTURESCALE / sy;
|
||||
float xs = watersubdiv * xf;
|
||||
float ys = watersubdiv * yf;
|
||||
float t1 = lastmillis / 300.0f;
|
||||
float t2 = lastmillis / 4000.0f;
|
||||
|
||||
sqr dl;
|
||||
dl.r = dl.g = dl.b = 255;
|
||||
|
||||
for(int xx = wx1; xx<wx2; xx += watersubdiv)
|
||||
{
|
||||
for(int yy = wy1; yy<wy2; yy += watersubdiv)
|
||||
{
|
||||
float xo = xf*(xx+t2);
|
||||
float yo = yf*(yy+t2);
|
||||
if(yy==wy1)
|
||||
{
|
||||
for (int xx = wx1; xx < wx2; xx += watersubdiv) {
|
||||
for (int yy = wy1; yy < wy2; yy += watersubdiv) {
|
||||
float xo = xf * (xx + t2);
|
||||
float yo = yf * (yy + t2);
|
||||
if (yy == wy1) {
|
||||
vertw(xx, hf, yy, &dl, dx(xo), dy(yo), t1);
|
||||
vertw(xx+watersubdiv, hf, yy, &dl, dx(xo+xs), dy(yo), t1);
|
||||
vertw(xx + watersubdiv, hf, yy, &dl,
|
||||
dx(xo + xs), dy(yo), t1);
|
||||
};
|
||||
vertw(xx, hf, yy+watersubdiv, &dl, dx(xo), dy(yo+ys), t1);
|
||||
vertw(xx+watersubdiv, hf, yy+watersubdiv, &dl, dx(xo+xs), dy(yo+ys), t1);
|
||||
vertw(xx, hf, yy + watersubdiv, &dl, dx(xo),
|
||||
dy(yo + ys), t1);
|
||||
vertw(xx + watersubdiv, hf, yy + watersubdiv, &dl,
|
||||
dx(xo + xs), dy(yo + ys), t1);
|
||||
};
|
||||
int n = (wy2-wy1-1)/watersubdiv;
|
||||
int n = (wy2 - wy1 - 1) / watersubdiv;
|
||||
nquads += n;
|
||||
n = (n+2)*2;
|
||||
n = (n + 2) * 2;
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, curvert -= n, n);
|
||||
};
|
||||
|
||||
|
@ -326,34 +393,36 @@ int renderwater(float hf)
|
|||
return nquads;
|
||||
};
|
||||
|
||||
void addwaterquad(int x, int y, int size) // update bounding rect that contains water
|
||||
void
|
||||
addwaterquad(int x, int y, int size) // update bounding rect that contains water
|
||||
{
|
||||
int x2 = x+size;
|
||||
int y2 = y+size;
|
||||
if(wx1<0)
|
||||
{
|
||||
int x2 = x + size;
|
||||
int y2 = y + size;
|
||||
if (wx1 < 0) {
|
||||
wx1 = x;
|
||||
wy1 = y;
|
||||
wx2 = x2;
|
||||
wy2 = y2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(x<wx1) wx1 = x;
|
||||
if(y<wy1) wy1 = y;
|
||||
if(x2>wx2) wx2 = x2;
|
||||
if(y2>wy2) wy2 = y2;
|
||||
} else {
|
||||
if (x < wx1)
|
||||
wx1 = x;
|
||||
if (y < wy1)
|
||||
wy1 = y;
|
||||
if (x2 > wx2)
|
||||
wx2 = x2;
|
||||
if (y2 > wy2)
|
||||
wy2 = y2;
|
||||
};
|
||||
};
|
||||
|
||||
void resetcubes()
|
||||
void
|
||||
resetcubes()
|
||||
{
|
||||
if(!verts) reallocv();
|
||||
if (!verts)
|
||||
reallocv();
|
||||
floorstrip = deltastrip = false;
|
||||
wx1 = -1;
|
||||
nquads = 0;
|
||||
sbright.r = sbright.g = sbright.b = 255;
|
||||
sdark.r = sdark.g = sdark.b = 0;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -2,54 +2,61 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
void line(int x1, int y1, float z1, int x2, int y2, float z2)
|
||||
void
|
||||
line(int x1, int y1, float z1, int x2, int y2, float z2)
|
||||
{
|
||||
glBegin(GL_POLYGON);
|
||||
glVertex3f((float)x1, z1, (float)y1);
|
||||
glVertex3f((float)x1, z1, y1+0.01f);
|
||||
glVertex3f((float)x2, z2, y2+0.01f);
|
||||
glVertex3f((float)x1, z1, y1 + 0.01f);
|
||||
glVertex3f((float)x2, z2, y2 + 0.01f);
|
||||
glVertex3f((float)x2, z2, (float)y2);
|
||||
glEnd();
|
||||
xtraverts += 4;
|
||||
};
|
||||
|
||||
void linestyle(float width, int r, int g, int b)
|
||||
void
|
||||
linestyle(float width, int r, int g, int b)
|
||||
{
|
||||
glLineWidth(width);
|
||||
glColor3ub(r,g,b);
|
||||
glColor3ub(r, g, b);
|
||||
};
|
||||
|
||||
void box(block &b, float z1, float z2, float z3, float z4)
|
||||
void
|
||||
box(block &b, float z1, float z2, float z3, float z4)
|
||||
{
|
||||
glBegin(GL_POLYGON);
|
||||
glVertex3f((float)b.x, z1, (float)b.y);
|
||||
glVertex3f((float)b.x+b.xs, z2, (float)b.y);
|
||||
glVertex3f((float)b.x+b.xs, z3, (float)b.y+b.ys);
|
||||
glVertex3f((float)b.x, z4, (float)b.y+b.ys);
|
||||
glVertex3f((float)b.x + b.xs, z2, (float)b.y);
|
||||
glVertex3f((float)b.x + b.xs, z3, (float)b.y + b.ys);
|
||||
glVertex3f((float)b.x, z4, (float)b.y + b.ys);
|
||||
glEnd();
|
||||
xtraverts += 4;
|
||||
};
|
||||
|
||||
void dot(int x, int y, float z)
|
||||
void
|
||||
dot(int x, int y, float z)
|
||||
{
|
||||
const float DOF = 0.1f;
|
||||
glBegin(GL_POLYGON);
|
||||
glVertex3f(x-DOF, (float)z, y-DOF);
|
||||
glVertex3f(x+DOF, (float)z, y-DOF);
|
||||
glVertex3f(x+DOF, (float)z, y+DOF);
|
||||
glVertex3f(x-DOF, (float)z, y+DOF);
|
||||
glVertex3f(x - DOF, (float)z, y - DOF);
|
||||
glVertex3f(x + DOF, (float)z, y - DOF);
|
||||
glVertex3f(x + DOF, (float)z, y + DOF);
|
||||
glVertex3f(x - DOF, (float)z, y + DOF);
|
||||
glEnd();
|
||||
xtraverts += 4;
|
||||
};
|
||||
|
||||
void blendbox(int x1, int y1, int x2, int y2, bool border)
|
||||
void
|
||||
blendbox(int x1, int y1, int x2, int y2, bool border)
|
||||
{
|
||||
glDepthMask(GL_FALSE);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
|
||||
glBegin(GL_QUADS);
|
||||
if(border) glColor3d(0.5, 0.3, 0.4);
|
||||
else glColor3d(1.0, 1.0, 1.0);
|
||||
if (border)
|
||||
glColor3d(0.5, 0.3, 0.4);
|
||||
else
|
||||
glColor3d(1.0, 1.0, 1.0);
|
||||
glVertex2i(x1, y1);
|
||||
glVertex2i(x2, y1);
|
||||
glVertex2i(x2, y2);
|
||||
|
@ -72,14 +79,19 @@ void blendbox(int x1, int y1, int x2, int y2, bool border)
|
|||
};
|
||||
|
||||
const int MAXSPHERES = 50;
|
||||
struct sphere { vec o; float size, max; int type; sphere *next; };
|
||||
struct sphere {
|
||||
vec o;
|
||||
float size, max;
|
||||
int type;
|
||||
sphere *next;
|
||||
};
|
||||
sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL;
|
||||
bool sinit = false;
|
||||
|
||||
void newsphere(vec &o, float max, int type)
|
||||
void
|
||||
newsphere(vec &o, float max, int type)
|
||||
{
|
||||
if(!sinit)
|
||||
{
|
||||
if (!sinit) {
|
||||
loopi(MAXSPHERES)
|
||||
{
|
||||
spheres[i].next = sempty;
|
||||
|
@ -87,8 +99,7 @@ void newsphere(vec &o, float max, int type)
|
|||
};
|
||||
sinit = true;
|
||||
};
|
||||
if(sempty)
|
||||
{
|
||||
if (sempty) {
|
||||
sphere *p = sempty;
|
||||
sempty = p->next;
|
||||
p->o = o;
|
||||
|
@ -100,36 +111,33 @@ void newsphere(vec &o, float max, int type)
|
|||
};
|
||||
};
|
||||
|
||||
void renderspheres(int time)
|
||||
void
|
||||
renderspheres(int time)
|
||||
{
|
||||
glDepthMask(GL_FALSE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||
glBindTexture(GL_TEXTURE_2D, 4);
|
||||
|
||||
for(sphere *p, **pp = &slist; p = *pp;)
|
||||
{
|
||||
for (sphere *p, **pp = &slist; p = *pp;) {
|
||||
glPushMatrix();
|
||||
float size = p->size/p->max;
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f-size);
|
||||
float size = p->size / p->max;
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f - size);
|
||||
glTranslatef(p->o.x, p->o.z, p->o.y);
|
||||
glRotatef(lastmillis/5.0f, 1, 1, 1);
|
||||
glRotatef(lastmillis / 5.0f, 1, 1, 1);
|
||||
glScalef(p->size, p->size, p->size);
|
||||
glCallList(1);
|
||||
glScalef(0.8f, 0.8f, 0.8f);
|
||||
glCallList(1);
|
||||
glPopMatrix();
|
||||
xtraverts += 12*6*2;
|
||||
xtraverts += 12 * 6 * 2;
|
||||
|
||||
if(p->size>p->max)
|
||||
{
|
||||
if (p->size > p->max) {
|
||||
*pp = p->next;
|
||||
p->next = sempty;
|
||||
sempty = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
p->size += time/100.0f;
|
||||
} else {
|
||||
p->size += time / 100.0f;
|
||||
pp = &p->next;
|
||||
};
|
||||
};
|
||||
|
@ -139,46 +147,70 @@ void renderspheres(int time)
|
|||
};
|
||||
|
||||
string closeent;
|
||||
char *entnames[] =
|
||||
{
|
||||
"none?", "light", "playerstart",
|
||||
"shells", "bullets", "rockets", "riflerounds",
|
||||
"health", "healthboost", "greenarmour", "yellowarmour", "quaddamage",
|
||||
"teleport", "teledest",
|
||||
"mapmodel", "monster", "trigger", "jumppad",
|
||||
"?", "?", "?", "?", "?",
|
||||
char *entnames[] = {
|
||||
"none?",
|
||||
"light",
|
||||
"playerstart",
|
||||
"shells",
|
||||
"bullets",
|
||||
"rockets",
|
||||
"riflerounds",
|
||||
"health",
|
||||
"healthboost",
|
||||
"greenarmour",
|
||||
"yellowarmour",
|
||||
"quaddamage",
|
||||
"teleport",
|
||||
"teledest",
|
||||
"mapmodel",
|
||||
"monster",
|
||||
"trigger",
|
||||
"jumppad",
|
||||
"?",
|
||||
"?",
|
||||
"?",
|
||||
"?",
|
||||
"?",
|
||||
};
|
||||
|
||||
void renderents() // show sparkly thingies for map entities in edit mode
|
||||
void
|
||||
renderents() // show sparkly thingies for map entities in edit mode
|
||||
{
|
||||
closeent[0] = 0;
|
||||
if(!editmode) return;
|
||||
if (!editmode)
|
||||
return;
|
||||
loopv(ents)
|
||||
{
|
||||
entity &e = ents[i];
|
||||
if(e.type==NOTUSED) continue;
|
||||
vec v = { e.x, e.y, e.z };
|
||||
if (e.type == NOTUSED)
|
||||
continue;
|
||||
vec v = {e.x, e.y, e.z};
|
||||
particle_splash(2, 2, 40, v);
|
||||
};
|
||||
int e = closestent();
|
||||
if(e>=0)
|
||||
{
|
||||
if (e >= 0) {
|
||||
entity &c = ents[e];
|
||||
sprintf_s(closeent)("closest entity = %s (%d, %d, %d, %d), selection = (%d, %d)", entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4, getvar("selxs"), getvar("selys"));
|
||||
sprintf_s(closeent)("closest entity = %s (%d, %d, %d, %d), "
|
||||
"selection = (%d, %d)",
|
||||
entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4,
|
||||
getvar("selxs"), getvar("selys"));
|
||||
};
|
||||
};
|
||||
|
||||
void loadsky(char *basename)
|
||||
void
|
||||
loadsky(char *basename)
|
||||
{
|
||||
static string lastsky = "";
|
||||
if(strcmp(lastsky, basename)==0) return;
|
||||
char *side[] = { "ft", "bk", "lf", "rt", "dn", "up" };
|
||||
if (strcmp(lastsky, basename) == 0)
|
||||
return;
|
||||
char *side[] = {"ft", "bk", "lf", "rt", "dn", "up"};
|
||||
int texnum = 14;
|
||||
loopi(6)
|
||||
{
|
||||
sprintf_sd(name)("packages/%s_%s.jpg", basename, side[i]);
|
||||
int xs, ys;
|
||||
if(!installtex(texnum+i, path(name), xs, ys, true)) conoutf("could not load sky textures");
|
||||
if (!installtex(texnum + i, path(name), xs, ys, true))
|
||||
conoutf("could not load sky textures");
|
||||
};
|
||||
strcpy_s(lastsky, basename);
|
||||
};
|
||||
|
@ -190,66 +222,79 @@ GLint viewport[4];
|
|||
GLdouble mm[16], pm[16];
|
||||
vec worldpos;
|
||||
|
||||
void readmatrices()
|
||||
void
|
||||
readmatrices()
|
||||
{
|
||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
glGetDoublev(GL_MODELVIEW_MATRIX, mm);
|
||||
glGetDoublev(GL_PROJECTION_MATRIX, pm);
|
||||
};
|
||||
|
||||
// stupid function to cater for stupid ATI linux drivers that return incorrect depth values
|
||||
// stupid function to cater for stupid ATI linux drivers that return incorrect
|
||||
// depth values
|
||||
|
||||
float depthcorrect(float d)
|
||||
float
|
||||
depthcorrect(float d)
|
||||
{
|
||||
return (d<=1/256.0f) ? d*256 : d;
|
||||
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 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
|
||||
// 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
|
||||
// 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
|
||||
|
||||
void readdepth(int w, int h)
|
||||
void
|
||||
readdepth(int w, int h)
|
||||
{
|
||||
glReadPixels(w/2, h/2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cursordepth);
|
||||
glReadPixels(
|
||||
w / 2, h / 2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cursordepth);
|
||||
double worldx = 0, worldy = 0, worldz = 0;
|
||||
gluUnProject(w/2, h/2, depthcorrect(cursordepth), mm, pm, viewport, &worldx, &worldz, &worldy);
|
||||
gluUnProject(w / 2, h / 2, depthcorrect(cursordepth), mm, pm, viewport,
|
||||
&worldx, &worldz, &worldy);
|
||||
worldpos.x = (float)worldx;
|
||||
worldpos.y = (float)worldy;
|
||||
worldpos.z = (float)worldz;
|
||||
vec r = { (float)mm[0], (float)mm[4], (float)mm[8] };
|
||||
vec u = { (float)mm[1], (float)mm[5], (float)mm[9] };
|
||||
vec r = {(float)mm[0], (float)mm[4], (float)mm[8]};
|
||||
vec u = {(float)mm[1], (float)mm[5], (float)mm[9]};
|
||||
setorient(r, u);
|
||||
};
|
||||
|
||||
void drawicon(float tx, float ty, int x, int y)
|
||||
void
|
||||
drawicon(float tx, float ty, int x, int y)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, 5);
|
||||
glBegin(GL_QUADS);
|
||||
tx /= 192;
|
||||
ty /= 192;
|
||||
float o = 1/3.0f;
|
||||
float o = 1 / 3.0f;
|
||||
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);
|
||||
glTexCoord2f(tx, ty+o); glVertex2i(x, y+s);
|
||||
glTexCoord2f(tx, ty);
|
||||
glVertex2i(x, y);
|
||||
glTexCoord2f(tx + o, ty);
|
||||
glVertex2i(x + s, y);
|
||||
glTexCoord2f(tx + o, ty + o);
|
||||
glVertex2i(x + s, y + s);
|
||||
glTexCoord2f(tx, ty + o);
|
||||
glVertex2i(x, y + s);
|
||||
glEnd();
|
||||
xtraverts += 4;
|
||||
};
|
||||
|
||||
void invertperspective()
|
||||
void
|
||||
invertperspective()
|
||||
{
|
||||
// This only generates a valid inverse matrix for matrices generated by gluPerspective()
|
||||
// This only generates a valid inverse matrix for matrices generated by
|
||||
// gluPerspective()
|
||||
GLdouble inv[16];
|
||||
memset(inv, 0, sizeof(inv));
|
||||
|
||||
inv[0*4+0] = 1.0/pm[0*4+0];
|
||||
inv[1*4+1] = 1.0/pm[1*4+1];
|
||||
inv[2*4+3] = 1.0/pm[3*4+2];
|
||||
inv[3*4+2] = -1.0;
|
||||
inv[3*4+3] = pm[2*4+2]/pm[3*4+2];
|
||||
inv[0 * 4 + 0] = 1.0 / pm[0 * 4 + 0];
|
||||
inv[1 * 4 + 1] = 1.0 / pm[1 * 4 + 1];
|
||||
inv[2 * 4 + 3] = 1.0 / pm[3 * 4 + 2];
|
||||
inv[3 * 4 + 2] = -1.0;
|
||||
inv[3 * 4 + 3] = pm[2 * 4 + 2] / pm[3 * 4 + 2];
|
||||
|
||||
glLoadMatrixd(inv);
|
||||
};
|
||||
|
@ -257,17 +302,22 @@ void invertperspective()
|
|||
VARP(crosshairsize, 0, 15, 50);
|
||||
|
||||
int dblend = 0;
|
||||
void damageblend(int n) { dblend += n; };
|
||||
void
|
||||
damageblend(int n)
|
||||
{
|
||||
dblend += n;
|
||||
};
|
||||
|
||||
VAR(hidestats, 0, 0, 1);
|
||||
VARP(crosshairfx, 0, 1, 1);
|
||||
|
||||
void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater)
|
||||
void
|
||||
gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater)
|
||||
{
|
||||
readmatrices();
|
||||
if(editmode)
|
||||
{
|
||||
if(cursordepth==1.0f) worldpos = player1->o;
|
||||
if (editmode) {
|
||||
if (cursordepth == 1.0f)
|
||||
worldpos = player1->o;
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
cursorupdate();
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
|
@ -281,61 +331,70 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat
|
|||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
if(dblend || underwater)
|
||||
{
|
||||
if (dblend || underwater) {
|
||||
glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
|
||||
glBegin(GL_QUADS);
|
||||
if(dblend) glColor3d(0.0f, 0.9f, 0.9f);
|
||||
else glColor3d(0.9f, 0.5f, 0.0f);
|
||||
if (dblend)
|
||||
glColor3d(0.0f, 0.9f, 0.9f);
|
||||
else
|
||||
glColor3d(0.9f, 0.5f, 0.0f);
|
||||
glVertex2i(0, 0);
|
||||
glVertex2i(VIRTW, 0);
|
||||
glVertex2i(VIRTW, VIRTH);
|
||||
glVertex2i(0, VIRTH);
|
||||
glEnd();
|
||||
dblend -= curtime/3;
|
||||
if(dblend<0) dblend = 0;
|
||||
dblend -= curtime / 3;
|
||||
if (dblend < 0)
|
||||
dblend = 0;
|
||||
};
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
char *command = getcurcommand();
|
||||
char *player = playerincrosshair();
|
||||
if(command) draw_textf("> %s_", 20, 1570, 2, command);
|
||||
else if(closeent[0]) draw_text(closeent, 20, 1570, 2);
|
||||
else if(player) draw_text(player, 20, 1570, 2);
|
||||
if (command)
|
||||
draw_textf("> %s_", 20, 1570, 2, command);
|
||||
else if (closeent[0])
|
||||
draw_text(closeent, 20, 1570, 2);
|
||||
else if (player)
|
||||
draw_text(player, 20, 1570, 2);
|
||||
|
||||
renderscores();
|
||||
if(!rendermenu())
|
||||
{
|
||||
if (!rendermenu()) {
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
|
||||
glBindTexture(GL_TEXTURE_2D, 1);
|
||||
glBegin(GL_QUADS);
|
||||
glColor3ub(255,255,255);
|
||||
if(crosshairfx)
|
||||
{
|
||||
if(player1->gunwait) glColor3ub(128,128,128);
|
||||
else if(player1->health<=25) glColor3ub(255,0,0);
|
||||
else if(player1->health<=50) glColor3ub(255,128,0);
|
||||
glColor3ub(255, 255, 255);
|
||||
if (crosshairfx) {
|
||||
if (player1->gunwait)
|
||||
glColor3ub(128, 128, 128);
|
||||
else if (player1->health <= 25)
|
||||
glColor3ub(255, 0, 0);
|
||||
else if (player1->health <= 50)
|
||||
glColor3ub(255, 128, 0);
|
||||
};
|
||||
float chsize = (float)crosshairsize;
|
||||
glTexCoord2d(0.0, 0.0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 - chsize);
|
||||
glTexCoord2d(1.0, 0.0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 - chsize);
|
||||
glTexCoord2d(1.0, 1.0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 + chsize);
|
||||
glTexCoord2d(0.0, 1.0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 + chsize);
|
||||
glTexCoord2d(0.0, 0.0);
|
||||
glVertex2f(VIRTW / 2 - chsize, VIRTH / 2 - chsize);
|
||||
glTexCoord2d(1.0, 0.0);
|
||||
glVertex2f(VIRTW / 2 + chsize, VIRTH / 2 - chsize);
|
||||
glTexCoord2d(1.0, 1.0);
|
||||
glVertex2f(VIRTW / 2 + chsize, VIRTH / 2 + chsize);
|
||||
glTexCoord2d(0.0, 1.0);
|
||||
glVertex2f(VIRTW / 2 - chsize, VIRTH / 2 + chsize);
|
||||
glEnd();
|
||||
};
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glPushMatrix();
|
||||
glOrtho(0, VIRTW*4/3, VIRTH*4/3, 0, -1, 1);
|
||||
glOrtho(0, VIRTW * 4 / 3, VIRTH * 4 / 3, 0, -1, 1);
|
||||
renderconsole();
|
||||
|
||||
if(!hidestats)
|
||||
{
|
||||
if (!hidestats) {
|
||||
glPopMatrix();
|
||||
glPushMatrix();
|
||||
glOrtho(0, VIRTW*3/2, VIRTH*3/2, 0, -1, 1);
|
||||
glOrtho(0, VIRTW * 3 / 2, VIRTH * 3 / 2, 0, -1, 1);
|
||||
draw_textf("fps %d", 3200, 2390, 2, curfps);
|
||||
draw_textf("wqd %d", 3200, 2460, 2, nquads);
|
||||
draw_textf("wvt %d", 3200, 2530, 2, curvert);
|
||||
|
@ -344,23 +403,29 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat
|
|||
|
||||
glPopMatrix();
|
||||
|
||||
if(player1->state==CS_ALIVE)
|
||||
{
|
||||
if (player1->state == CS_ALIVE) {
|
||||
glPushMatrix();
|
||||
glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1);
|
||||
glOrtho(0, VIRTW / 2, VIRTH / 2, 0, -1, 1);
|
||||
draw_textf("%d", 90, 827, 2, player1->health);
|
||||
if(player1->armour) draw_textf("%d", 390, 827, 2, player1->armour);
|
||||
draw_textf("%d", 690, 827, 2, player1->ammo[player1->gunselect]);
|
||||
if (player1->armour)
|
||||
draw_textf("%d", 390, 827, 2, player1->armour);
|
||||
draw_textf(
|
||||
"%d", 690, 827, 2, player1->ammo[player1->gunselect]);
|
||||
glPopMatrix();
|
||||
glPushMatrix();
|
||||
glOrtho(0, VIRTW, VIRTH, 0, -1, 1);
|
||||
glDisable(GL_BLEND);
|
||||
drawicon(128, 128, 20, 1650);
|
||||
if(player1->armour) drawicon((float)(player1->armourtype*64), 0, 620, 1650);
|
||||
if (player1->armour)
|
||||
drawicon(
|
||||
(float)(player1->armourtype * 64), 0, 620, 1650);
|
||||
int g = player1->gunselect;
|
||||
int r = 64;
|
||||
if(g>2) { g -= 3; r = 128; };
|
||||
drawicon((float)(g*64), (float)r, 1220, 1650);
|
||||
if (g > 2) {
|
||||
g -= 3;
|
||||
r = 128;
|
||||
};
|
||||
drawicon((float)(g * 64), (float)r, 1220, 1650);
|
||||
glPopMatrix();
|
||||
};
|
||||
|
||||
|
@ -369,4 +434,3 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat
|
|||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
};
|
||||
|
||||
|
|
248
src/rendergl.cxx
248
src/rendergl.cxx
|
@ -19,9 +19,10 @@ void purgetextures();
|
|||
GLUquadricObj *qsphere = NULL;
|
||||
int glmaxtexsize = 256;
|
||||
|
||||
void gl_init(int w, int h)
|
||||
void
|
||||
gl_init(int w, int h)
|
||||
{
|
||||
//#define fogvalues 0.5f, 0.6f, 0.7f, 1.0f
|
||||
// #define fogvalues 0.5f, 0.6f, 0.7f, 1.0f
|
||||
|
||||
glViewport(0, 0, w, h);
|
||||
glClearDepth(1.0);
|
||||
|
@ -29,13 +30,11 @@ void gl_init(int w, int h)
|
|||
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);
|
||||
|
@ -46,14 +45,18 @@ void gl_init(int w, int h)
|
|||
|
||||
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!");
|
||||
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");
|
||||
if (!(qsphere = gluNewQuadric()))
|
||||
fatal("glu sphere");
|
||||
gluQuadricDrawStyle(qsphere, GLU_FILL);
|
||||
gluQuadricOrientation(qsphere, GLU_INSIDE);
|
||||
gluQuadricTexture(qsphere, GL_TRUE);
|
||||
|
@ -62,43 +65,62 @@ void gl_init(int w, int h)
|
|||
glEndList();
|
||||
};
|
||||
|
||||
void cleangl()
|
||||
void
|
||||
cleangl()
|
||||
{
|
||||
if(qsphere) gluDeleteQuadric(qsphere);
|
||||
if (qsphere)
|
||||
gluDeleteQuadric(qsphere);
|
||||
};
|
||||
|
||||
bool installtex(int tnum, char *texname, int &xs, int &ys, bool clamp)
|
||||
bool
|
||||
installtex(int tnum, char *texname, int &xs, int &ys, bool clamp)
|
||||
{
|
||||
SDL_Surface *s = IMG_Load(texname);
|
||||
if(!s) { conoutf("couldn't load texture %s", texname); return false; };
|
||||
if(s->format->BitsPerPixel!=24) { conoutf("texture must be 24bpp: %s", texname); return false; };
|
||||
if (!s) {
|
||||
conoutf("couldn't load texture %s", texname);
|
||||
return false;
|
||||
};
|
||||
if (s->format->BitsPerPixel != 24) {
|
||||
conoutf("texture must be 24bpp: %s", 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_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, GL_LINEAR_MIPMAP_LINEAR); //NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||
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", 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);
|
||||
while (xs > glmaxtexsize || ys > glmaxtexsize) {
|
||||
xs /= 2;
|
||||
ys /= 2;
|
||||
};
|
||||
if(gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, xs, ys, GL_RGB, GL_UNSIGNED_BYTE, scaledimg)) fatal("could not build mipmaps");
|
||||
if(xs!=s->w) free(scaledimg);
|
||||
void *scaledimg = s->pixels;
|
||||
if (xs != s->w) {
|
||||
conoutf("warning: quality loss: scaling %s",
|
||||
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);
|
||||
};
|
||||
if (gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, xs, ys, GL_RGB,
|
||||
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
|
||||
// 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 )
|
||||
|
@ -112,19 +134,26 @@ const int MAXFRAMES = 2; // increase to allow more complex sh
|
|||
int mapping[256][MAXFRAMES]; // ( cube texture, frame ) -> ( opengl id, name )
|
||||
string mapname[256][MAXFRAMES];
|
||||
|
||||
void purgetextures()
|
||||
void
|
||||
purgetextures()
|
||||
{
|
||||
loopi(256) loop(j,MAXFRAMES) mapping[i][j] = 0;
|
||||
loopi(256) loop(j, MAXFRAMES) mapping[i][j] = 0;
|
||||
};
|
||||
|
||||
int curtexnum = 0;
|
||||
|
||||
void texturereset() { curtexnum = 0; };
|
||||
void
|
||||
texturereset()
|
||||
{
|
||||
curtexnum = 0;
|
||||
};
|
||||
|
||||
void texture(char *aframe, char *name)
|
||||
void
|
||||
texture(char *aframe, char *name)
|
||||
{
|
||||
int num = curtexnum++, frame = atoi(aframe);
|
||||
if(num<0 || num>=256 || frame<0 || frame>=MAXFRAMES) return;
|
||||
if (num < 0 || num >= 256 || frame < 0 || frame >= MAXFRAMES)
|
||||
return;
|
||||
mapping[num][frame] = 1;
|
||||
char *n = mapname[num][frame];
|
||||
strcpy_s(n, name);
|
||||
|
@ -134,87 +163,90 @@ void texture(char *aframe, char *name)
|
|||
COMMAND(texturereset, ARG_NONE);
|
||||
COMMAND(texture, ARG_2STR);
|
||||
|
||||
int lookuptexture(int tex, int &xs, int &ys)
|
||||
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];
|
||||
if (tid >= FIRSTTEX) {
|
||||
xs = texx[tid - FIRSTTEX];
|
||||
ys = texy[tid - FIRSTTEX];
|
||||
return tid;
|
||||
};
|
||||
|
||||
xs = ys = 16;
|
||||
if(!tid) return 1; // crosshair :)
|
||||
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;
|
||||
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");
|
||||
if (curtex == MAXTEX)
|
||||
fatal("loaded too many textures");
|
||||
|
||||
int tnum = curtex+FIRSTTEX;
|
||||
int tnum = curtex + FIRSTTEX;
|
||||
strcpy_s(texname[curtex], mapname[tex][frame]);
|
||||
|
||||
sprintf_sd(name)("packages%c%s", PATHDIV, texname[curtex]);
|
||||
|
||||
if(installtex(tnum, name, xs, ys))
|
||||
{
|
||||
if (installtex(tnum, name, xs, ys)) {
|
||||
mapping[tex][frame] = tnum;
|
||||
texx[curtex] = xs;
|
||||
texy[curtex] = ys;
|
||||
curtex++;
|
||||
return tnum;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return mapping[tex][frame] = FIRSTTEX; // temp fix
|
||||
};
|
||||
};
|
||||
|
||||
void setupworld()
|
||||
void
|
||||
setupworld()
|
||||
{
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
setarraypointers();
|
||||
|
||||
if(hasoverbright)
|
||||
{
|
||||
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);
|
||||
glTexEnvi(
|
||||
GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);
|
||||
};
|
||||
};
|
||||
|
||||
int skyoglid;
|
||||
|
||||
struct strip { int tex, start, num; };
|
||||
struct strip {
|
||||
int tex, start, num;
|
||||
};
|
||||
vector<strip> strips;
|
||||
|
||||
void renderstripssky()
|
||||
void
|
||||
renderstripssky()
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, skyoglid);
|
||||
loopv(strips) if(strips[i].tex==skyoglid) glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num);
|
||||
loopv(strips) if (strips[i].tex == skyoglid)
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num);
|
||||
};
|
||||
|
||||
void renderstrips()
|
||||
void
|
||||
renderstrips()
|
||||
{
|
||||
int lasttex = -1;
|
||||
loopv(strips) if(strips[i].tex!=skyoglid)
|
||||
{
|
||||
if(strips[i].tex!=lasttex)
|
||||
loopv(strips) if (strips[i].tex != skyoglid)
|
||||
{
|
||||
if (strips[i].tex != lasttex) {
|
||||
glBindTexture(GL_TEXTURE_2D, strips[i].tex);
|
||||
lasttex = strips[i].tex;
|
||||
};
|
||||
|
@ -222,9 +254,15 @@ void renderstrips()
|
|||
};
|
||||
};
|
||||
|
||||
void overbright(float amount) { if(hasoverbright) glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, amount ); };
|
||||
void
|
||||
overbright(float amount)
|
||||
{
|
||||
if (hasoverbright)
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, amount);
|
||||
};
|
||||
|
||||
void addstrip(int tex, int start, int n)
|
||||
void
|
||||
addstrip(int tex, int start, int n)
|
||||
{
|
||||
strip &s = strips.add();
|
||||
s.tex = tex;
|
||||
|
@ -232,25 +270,28 @@ void addstrip(int tex, int start, int n)
|
|||
s.num = n;
|
||||
};
|
||||
|
||||
VARFP(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?)");
|
||||
VARFP(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", SDL_GetError());
|
||||
};
|
||||
});
|
||||
|
||||
void transplayer()
|
||||
void
|
||||
transplayer()
|
||||
{
|
||||
glLoadIdentity();
|
||||
|
||||
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);
|
||||
glRotated(player1->roll, 0.0, 0.0, 1.0);
|
||||
glRotated(player1->pitch, -1.0, 0.0, 0.0);
|
||||
glRotated(player1->yaw, 0.0, 1.0, 0.0);
|
||||
|
||||
glTranslated(-player1->o.x, (player1->state==CS_DEAD ? player1->eyeheight-0.2f : 0)-player1->o.z, -player1->o.y);
|
||||
glTranslated(-player1->o.x,
|
||||
(player1->state == CS_DEAD ? player1->eyeheight - 0.2f : 0) -
|
||||
player1->o.z,
|
||||
-player1->o.y);
|
||||
};
|
||||
|
||||
VARP(fov, 10, 105, 120);
|
||||
|
@ -260,18 +301,24 @@ int xtraverts;
|
|||
VAR(fog, 64, 180, 1024);
|
||||
VAR(fogcolour, 0, 0x8099B3, 0xFFFFFF);
|
||||
|
||||
VARP(hudgun,0,1,1);
|
||||
VARP(hudgun, 0, 1, 1);
|
||||
|
||||
char *hudgunnames[] = { "hudguns/fist", "hudguns/shotg", "hudguns/chaing", "hudguns/rocket", "hudguns/rifle" };
|
||||
char *hudgunnames[] = {"hudguns/fist", "hudguns/shotg", "hudguns/chaing",
|
||||
"hudguns/rocket", "hudguns/rifle"};
|
||||
|
||||
void drawhudmodel(int start, int end, float speed, int base)
|
||||
void
|
||||
drawhudmodel(int start, int end, float speed, int base)
|
||||
{
|
||||
rendermodel(hudgunnames[player1->gunselect], 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);
|
||||
rendermodel(hudgunnames[player1->gunselect], 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(float fovy, float aspect, int farplane)
|
||||
void
|
||||
drawhudgun(float fovy, float aspect, int farplane)
|
||||
{
|
||||
if(!hudgun /*|| !player1->gunselect*/) return;
|
||||
if (!hudgun /*|| !player1->gunselect*/)
|
||||
return;
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
|
@ -280,14 +327,13 @@ void drawhudgun(float fovy, float aspect, int farplane)
|
|||
gluPerspective(fovy, aspect, 0.3f, farplane);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
//glClear(GL_DEPTH_BUFFER_BIT);
|
||||
// glClear(GL_DEPTH_BUFFER_BIT);
|
||||
int rtime = reloadtime(player1->gunselect);
|
||||
if(player1->lastaction && player1->lastattackgun==player1->gunselect && lastmillis-player1->lastaction<rtime)
|
||||
{
|
||||
drawhudmodel(7, 18, rtime/18.0f, player1->lastaction);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player1->lastaction &&
|
||||
player1->lastattackgun == player1->gunselect &&
|
||||
lastmillis - player1->lastaction < rtime) {
|
||||
drawhudmodel(7, 18, rtime / 18.0f, player1->lastaction);
|
||||
} else {
|
||||
drawhudmodel(6, 1, 100, 0);
|
||||
};
|
||||
|
||||
|
@ -299,32 +345,35 @@ void drawhudgun(float fovy, float aspect, int farplane)
|
|||
glDisable(GL_CULL_FACE);
|
||||
};
|
||||
|
||||
void gl_drawframe(int w, int h, float curfps)
|
||||
void
|
||||
gl_drawframe(int w, int h, float curfps)
|
||||
{
|
||||
float hf = hdr.waterlevel-0.3f;
|
||||
float fovy = (float)fov*h/w;
|
||||
float aspect = w/(float)h;
|
||||
bool underwater = player1->o.z<hf;
|
||||
float hf = hdr.waterlevel - 0.3f;
|
||||
float fovy = (float)fov * h / w;
|
||||
float aspect = w / (float)h;
|
||||
bool underwater = player1->o.z < hf;
|
||||
|
||||
glFogi(GL_FOG_START, (fog+64)/8);
|
||||
glFogi(GL_FOG_START, (fog + 64) / 8);
|
||||
glFogi(GL_FOG_END, fog);
|
||||
float fogc[4] = { (fogcolour>>16)/256.0f, ((fogcolour>>8)&255)/256.0f, (fogcolour&255)/256.0f, 1.0f };
|
||||
float fogc[4] = {(fogcolour >> 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;
|
||||
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);
|
||||
glFogi(GL_FOG_END, (fog + 96) / 8);
|
||||
};
|
||||
|
||||
glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT);
|
||||
glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) |
|
||||
GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
int farplane = fog*5/2;
|
||||
int farplane = fog * 5 / 2;
|
||||
gluPerspective(fovy, aspect, 0.15f, farplane);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
|
@ -355,7 +404,7 @@ void gl_drawframe(int w, int h, float curfps)
|
|||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
glDisable(GL_FOG);
|
||||
glDepthFunc(GL_GREATER);
|
||||
draw_envbox(14, fog*4/3);
|
||||
draw_envbox(14, fog * 4 / 3);
|
||||
glDepthFunc(GL_LESS);
|
||||
glEnable(GL_FOG);
|
||||
|
||||
|
@ -395,4 +444,3 @@ void gl_drawframe(int w, int h, float curfps)
|
|||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_FOG);
|
||||
};
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
struct md2_header
|
||||
{
|
||||
struct md2_header {
|
||||
int magic;
|
||||
int version;
|
||||
int skinWidth, skinHeight;
|
||||
|
@ -14,28 +13,25 @@ struct md2_header
|
|||
int offsetFrames, offsetGlCommands, offsetEnd;
|
||||
};
|
||||
|
||||
struct md2_vertex
|
||||
{
|
||||
struct md2_vertex {
|
||||
uchar vertex[3], lightNormalIndex;
|
||||
};
|
||||
|
||||
struct md2_frame
|
||||
{
|
||||
struct md2_frame {
|
||||
float scale[3];
|
||||
float translate[3];
|
||||
char name[16];
|
||||
md2_vertex vertices[1];
|
||||
};
|
||||
|
||||
struct md2
|
||||
{
|
||||
struct md2 {
|
||||
int numGlCommands;
|
||||
int* glCommands;
|
||||
int *glCommands;
|
||||
int numTriangles;
|
||||
int frameSize;
|
||||
int numFrames;
|
||||
int numVerts;
|
||||
char* frames;
|
||||
char *frames;
|
||||
vec **mverts;
|
||||
int displaylist;
|
||||
int displaylistverts;
|
||||
|
@ -45,134 +41,149 @@ struct md2
|
|||
int mdlnum;
|
||||
bool loaded;
|
||||
|
||||
bool load(char* filename);
|
||||
void render(vec &light, int numFrame, int range, float x, float y, float z, float yaw, float pitch, float scale, float speed, int snap, int basetime);
|
||||
bool load(char *filename);
|
||||
void render(vec &light, int numFrame, int range, float x, float y,
|
||||
float z, float yaw, float pitch, float scale, float speed, int snap,
|
||||
int basetime);
|
||||
void scale(int frame, float scale, int sn);
|
||||
|
||||
md2() : numGlCommands(0), frameSize(0), numFrames(0), displaylist(0), loaded(false) {};
|
||||
md2()
|
||||
: numGlCommands(0), frameSize(0), numFrames(0), displaylist(0),
|
||||
loaded(false) {};
|
||||
|
||||
~md2()
|
||||
{
|
||||
if(glCommands)
|
||||
delete [] glCommands;
|
||||
if(frames)
|
||||
delete [] frames;
|
||||
if (glCommands)
|
||||
delete[] glCommands;
|
||||
if (frames)
|
||||
delete[] frames;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
bool md2::load(char* filename)
|
||||
bool
|
||||
md2::load(char *filename)
|
||||
{
|
||||
FILE* file;
|
||||
FILE *file;
|
||||
md2_header header;
|
||||
|
||||
if((file= fopen(filename, "rb"))==NULL) return false;
|
||||
if ((file = fopen(filename, "rb")) == NULL)
|
||||
return false;
|
||||
|
||||
fread(&header, sizeof(md2_header), 1, file);
|
||||
endianswap(&header, sizeof(int), sizeof(md2_header)/sizeof(int));
|
||||
endianswap(&header, sizeof(int), sizeof(md2_header) / sizeof(int));
|
||||
|
||||
if(header.magic!= 844121161 || header.version!=8) return false;
|
||||
if (header.magic != 844121161 || header.version != 8)
|
||||
return false;
|
||||
|
||||
frames = new char[header.frameSize*header.numFrames];
|
||||
if(frames==NULL) return false;
|
||||
frames = new char[header.frameSize * header.numFrames];
|
||||
if (frames == NULL)
|
||||
return false;
|
||||
|
||||
fseek(file, header.offsetFrames, SEEK_SET);
|
||||
fread(frames, header.frameSize*header.numFrames, 1, file);
|
||||
fread(frames, header.frameSize * header.numFrames, 1, file);
|
||||
|
||||
for(int i = 0; i < header.numFrames; ++i)
|
||||
{
|
||||
for (int i = 0; i < header.numFrames; ++i) {
|
||||
endianswap(frames + i * header.frameSize, sizeof(float), 6);
|
||||
}
|
||||
|
||||
glCommands = new int[header.numGlCommands];
|
||||
if(glCommands==NULL) return false;
|
||||
if (glCommands == NULL)
|
||||
return false;
|
||||
|
||||
fseek(file, header.offsetGlCommands, SEEK_SET);
|
||||
fread(glCommands, header.numGlCommands*sizeof(int), 1, file);
|
||||
fread(glCommands, header.numGlCommands * sizeof(int), 1, file);
|
||||
|
||||
endianswap(glCommands, sizeof(int), header.numGlCommands);
|
||||
|
||||
numFrames = header.numFrames;
|
||||
numGlCommands= header.numGlCommands;
|
||||
numGlCommands = header.numGlCommands;
|
||||
frameSize = header.frameSize;
|
||||
numTriangles = header.numTriangles;
|
||||
numVerts = header.numVertices;
|
||||
|
||||
fclose(file);
|
||||
|
||||
mverts = new vec*[numFrames];
|
||||
mverts = new vec *[numFrames];
|
||||
loopj(numFrames) mverts[j] = NULL;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
float snap(int sn, float f) { return sn ? (float)(((int)(f+sn*0.5f))&(~(sn-1))) : f; };
|
||||
float
|
||||
snap(int sn, float f)
|
||||
{
|
||||
return sn ? (float)(((int)(f + sn * 0.5f)) & (~(sn - 1))) : f;
|
||||
};
|
||||
|
||||
void md2::scale(int frame, float scale, int sn)
|
||||
void
|
||||
md2::scale(int frame, float scale, int sn)
|
||||
{
|
||||
mverts[frame] = new vec[numVerts];
|
||||
md2_frame *cf = (md2_frame *) ((char*)frames+frameSize*frame);
|
||||
float sc = 16.0f/scale;
|
||||
md2_frame *cf = (md2_frame *)((char *)frames + frameSize * frame);
|
||||
float sc = 16.0f / scale;
|
||||
loop(vi, numVerts)
|
||||
{
|
||||
uchar *cv = (uchar *)&cf->vertices[vi].vertex;
|
||||
vec *v = &(mverts[frame])[vi];
|
||||
v->x = (snap(sn, cv[0]*cf->scale[0])+cf->translate[0])/sc;
|
||||
v->y = -(snap(sn, cv[1]*cf->scale[1])+cf->translate[1])/sc;
|
||||
v->z = (snap(sn, cv[2]*cf->scale[2])+cf->translate[2])/sc;
|
||||
v->x = (snap(sn, cv[0] * cf->scale[0]) + cf->translate[0]) / sc;
|
||||
v->y =
|
||||
-(snap(sn, cv[1] * cf->scale[1]) + cf->translate[1]) / sc;
|
||||
v->z = (snap(sn, cv[2] * cf->scale[2]) + cf->translate[2]) / sc;
|
||||
};
|
||||
};
|
||||
|
||||
void md2::render(vec &light, int frame, int range, float x, float y, float z, float yaw, float pitch, float sc, float speed, int snap, int basetime)
|
||||
void
|
||||
md2::render(vec &light, int frame, int range, float x, float y, float z,
|
||||
float yaw, float pitch, float sc, float speed, int snap, int basetime)
|
||||
{
|
||||
loopi(range) if(!mverts[frame+i]) scale(frame+i, sc, snap);
|
||||
loopi(range) if (!mverts[frame + i]) scale(frame + i, sc, snap);
|
||||
|
||||
glPushMatrix ();
|
||||
glPushMatrix();
|
||||
glTranslatef(x, y, z);
|
||||
glRotatef(yaw+180, 0, -1, 0);
|
||||
glRotatef(yaw + 180, 0, -1, 0);
|
||||
glRotatef(pitch, 0, 0, 1);
|
||||
|
||||
glColor3fv((float *)&light);
|
||||
|
||||
if(displaylist && frame==0 && range==1)
|
||||
{
|
||||
if (displaylist && frame == 0 && range == 1) {
|
||||
glCallList(displaylist);
|
||||
xtraverts += displaylistverts;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(frame==0 && range==1)
|
||||
{
|
||||
} else {
|
||||
if (frame == 0 && range == 1) {
|
||||
static int displaylistn = 10;
|
||||
glNewList(displaylist = displaylistn++, GL_COMPILE);
|
||||
displaylistverts = xtraverts;
|
||||
};
|
||||
|
||||
int time = lastmillis-basetime;
|
||||
int fr1 = (int)(time/speed);
|
||||
float frac1 = (time-fr1*speed)/speed;
|
||||
float frac2 = 1-frac1;
|
||||
fr1 = fr1%range+frame;
|
||||
int fr2 = fr1+1;
|
||||
if(fr2>=frame+range) fr2 = frame;
|
||||
int time = lastmillis - basetime;
|
||||
int fr1 = (int)(time / speed);
|
||||
float frac1 = (time - fr1 * speed) / speed;
|
||||
float frac2 = 1 - frac1;
|
||||
fr1 = fr1 % range + frame;
|
||||
int fr2 = fr1 + 1;
|
||||
if (fr2 >= frame + range)
|
||||
fr2 = frame;
|
||||
vec *verts1 = mverts[fr1];
|
||||
vec *verts2 = mverts[fr2];
|
||||
|
||||
for(int *command = glCommands; (*command)!=0;)
|
||||
{
|
||||
for (int *command = glCommands; (*command) != 0;) {
|
||||
int numVertex = *command++;
|
||||
if(numVertex>0) { glBegin(GL_TRIANGLE_STRIP); }
|
||||
else { glBegin(GL_TRIANGLE_FAN); numVertex = -numVertex; };
|
||||
if (numVertex > 0) {
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
} else {
|
||||
glBegin(GL_TRIANGLE_FAN);
|
||||
numVertex = -numVertex;
|
||||
};
|
||||
|
||||
loopi(numVertex)
|
||||
{
|
||||
float tu = *((float*)command++);
|
||||
float tv = *((float*)command++);
|
||||
float tu = *((float *)command++);
|
||||
float tv = *((float *)command++);
|
||||
glTexCoord2f(tu, tv);
|
||||
int vn = *command++;
|
||||
vec &v1 = verts1[vn];
|
||||
vec &v2 = verts2[vn];
|
||||
#define ip(c) v1.c*frac2+v2.c*frac1
|
||||
#define ip(c) v1.c *frac2 + v2.c *frac1
|
||||
glVertex3f(ip(x), ip(z), ip(y));
|
||||
};
|
||||
|
||||
|
@ -181,10 +192,9 @@ void md2::render(vec &light, int frame, int range, float x, float y, float z, fl
|
|||
glEnd();
|
||||
};
|
||||
|
||||
if(displaylist)
|
||||
{
|
||||
if (displaylist) {
|
||||
glEndList();
|
||||
displaylistverts = xtraverts-displaylistverts;
|
||||
displaylistverts = xtraverts - displaylistverts;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -195,81 +205,99 @@ hashtable<md2 *> *mdllookup = NULL;
|
|||
vector<md2 *> mapmodels;
|
||||
const int FIRSTMDL = 20;
|
||||
|
||||
void delayedload(md2 *m)
|
||||
void
|
||||
delayedload(md2 *m)
|
||||
{
|
||||
if(!m->loaded)
|
||||
{
|
||||
if (!m->loaded) {
|
||||
sprintf_sd(name1)("packages/models/%s/tris.md2", m->loadname);
|
||||
if(!m->load(path(name1))) fatal("loadmodel: ", name1);
|
||||
if (!m->load(path(name1)))
|
||||
fatal("loadmodel: ", name1);
|
||||
sprintf_sd(name2)("packages/models/%s/skin.jpg", m->loadname);
|
||||
int xs, ys;
|
||||
installtex(FIRSTMDL+m->mdlnum, path(name2), xs, ys);
|
||||
installtex(FIRSTMDL + m->mdlnum, path(name2), xs, ys);
|
||||
m->loaded = true;
|
||||
};
|
||||
};
|
||||
|
||||
int modelnum = 0;
|
||||
|
||||
md2 *loadmodel(char *name)
|
||||
md2 *
|
||||
loadmodel(char *name)
|
||||
{
|
||||
if(!mdllookup) mdllookup = new hashtable<md2 *>;
|
||||
if (!mdllookup)
|
||||
mdllookup = new hashtable<md2 *>;
|
||||
md2 **mm = mdllookup->access(name);
|
||||
if(mm) return *mm;
|
||||
if (mm)
|
||||
return *mm;
|
||||
md2 *m = new md2();
|
||||
m->mdlnum = modelnum++;
|
||||
mapmodelinfo mmi = { 2, 2, 0, 0, "" };
|
||||
mapmodelinfo mmi = {2, 2, 0, 0, ""};
|
||||
m->mmi = mmi;
|
||||
m->loadname = newstring(name);
|
||||
mdllookup->access(m->loadname, &m);
|
||||
return m;
|
||||
};
|
||||
|
||||
void mapmodel(char *rad, char *h, char *zoff, char *snap, char *name)
|
||||
void
|
||||
mapmodel(char *rad, char *h, char *zoff, char *snap, char *name)
|
||||
{
|
||||
md2 *m = loadmodel(name);
|
||||
mapmodelinfo mmi = { atoi(rad), atoi(h), atoi(zoff), atoi(snap), m->loadname };
|
||||
mapmodelinfo mmi = {
|
||||
atoi(rad), atoi(h), atoi(zoff), atoi(snap), m->loadname};
|
||||
m->mmi = mmi;
|
||||
mapmodels.add(m);
|
||||
};
|
||||
|
||||
void mapmodelreset() { mapmodels.setsize(0); };
|
||||
void
|
||||
mapmodelreset()
|
||||
{
|
||||
mapmodels.setsize(0);
|
||||
};
|
||||
|
||||
mapmodelinfo &getmminfo(int i) { return i<mapmodels.length() ? mapmodels[i]->mmi : *(mapmodelinfo *)0; };
|
||||
mapmodelinfo &
|
||||
getmminfo(int i)
|
||||
{
|
||||
return i < mapmodels.length() ? mapmodels[i]->mmi : *(mapmodelinfo *)0;
|
||||
};
|
||||
|
||||
COMMAND(mapmodel, ARG_5STR);
|
||||
COMMAND(mapmodelreset, ARG_NONE);
|
||||
|
||||
void rendermodel(char *mdl, int frame, int range, int tex, float rad, float x, float y, float z, float yaw, float pitch, bool teammate, float scale, float speed, int snap, int basetime)
|
||||
void
|
||||
rendermodel(char *mdl, int frame, int range, int tex, float rad, float x,
|
||||
float y, float z, float yaw, float pitch, bool teammate, float scale,
|
||||
float speed, int snap, int basetime)
|
||||
{
|
||||
md2 *m = loadmodel(mdl);
|
||||
|
||||
if(isoccluded(player1->o.x, player1->o.y, x-rad, z-rad, rad*2)) return;
|
||||
if (isoccluded(player1->o.x, player1->o.y, x - rad, z - rad, rad * 2))
|
||||
return;
|
||||
|
||||
delayedload(m);
|
||||
|
||||
int xs, ys;
|
||||
glBindTexture(GL_TEXTURE_2D, tex ? lookuptexture(tex, xs, ys) : FIRSTMDL+m->mdlnum);
|
||||
glBindTexture(GL_TEXTURE_2D,
|
||||
tex ? lookuptexture(tex, xs, ys) : FIRSTMDL + m->mdlnum);
|
||||
|
||||
int ix = (int)x;
|
||||
int iy = (int)z;
|
||||
vec light = { 1.0f, 1.0f, 1.0f };
|
||||
vec light = {1.0f, 1.0f, 1.0f};
|
||||
|
||||
if(!OUTBORD(ix, iy))
|
||||
{
|
||||
sqr *s = S(ix,iy);
|
||||
if (!OUTBORD(ix, iy)) {
|
||||
sqr *s = S(ix, iy);
|
||||
float ll = 256.0f; // 0.96f;
|
||||
float of = 0.0f; // 0.1f;
|
||||
light.x = s->r/ll+of;
|
||||
light.y = s->g/ll+of;
|
||||
light.z = s->b/ll+of;
|
||||
light.x = s->r / ll + of;
|
||||
light.y = s->g / ll + of;
|
||||
light.z = s->b / ll + of;
|
||||
};
|
||||
|
||||
if(teammate)
|
||||
{
|
||||
if (teammate) {
|
||||
light.x *= 0.6f;
|
||||
light.y *= 0.7f;
|
||||
light.z *= 1.2f;
|
||||
};
|
||||
|
||||
m->render(light, frame, range, x, y, z, yaw, pitch, scale, speed, snap, basetime);
|
||||
m->render(light, frame, range, x, y, z, yaw, pitch, scale, speed, snap,
|
||||
basetime);
|
||||
};
|
||||
|
|
|
@ -4,16 +4,21 @@
|
|||
|
||||
const int MAXPARTICLES = 10500;
|
||||
const int NUMPARTCUTOFF = 20;
|
||||
struct particle { vec o, d; int fade, type; int millis; particle *next; };
|
||||
struct particle {
|
||||
vec o, d;
|
||||
int fade, type;
|
||||
int millis;
|
||||
particle *next;
|
||||
};
|
||||
particle particles[MAXPARTICLES], *parlist = NULL, *parempty = NULL;
|
||||
bool parinit = false;
|
||||
|
||||
VARP(maxparticles, 100, 2000, MAXPARTICLES-500);
|
||||
VARP(maxparticles, 100, 2000, MAXPARTICLES - 500);
|
||||
|
||||
void newparticle(vec &o, vec &d, int fade, int type)
|
||||
void
|
||||
newparticle(vec &o, vec &d, int fade, int type)
|
||||
{
|
||||
if(!parinit)
|
||||
{
|
||||
if (!parinit) {
|
||||
loopi(MAXPARTICLES)
|
||||
{
|
||||
particles[i].next = parempty;
|
||||
|
@ -21,8 +26,7 @@ void newparticle(vec &o, vec &d, int fade, int type)
|
|||
};
|
||||
parinit = true;
|
||||
};
|
||||
if(parempty)
|
||||
{
|
||||
if (parempty) {
|
||||
particle *p = parempty;
|
||||
parempty = p->next;
|
||||
p->o = o;
|
||||
|
@ -40,13 +44,18 @@ VARP(particlesize, 20, 100, 500);
|
|||
|
||||
vec right, up;
|
||||
|
||||
void setorient(vec &r, vec &u) { right = r; up = u; };
|
||||
|
||||
void render_particles(int time)
|
||||
void
|
||||
setorient(vec &r, vec &u)
|
||||
{
|
||||
if(demoplayback && demotracking)
|
||||
{
|
||||
vec nom = { 0, 0, 0 };
|
||||
right = r;
|
||||
up = u;
|
||||
};
|
||||
|
||||
void
|
||||
render_particles(int time)
|
||||
{
|
||||
if (demoplayback && demotracking) {
|
||||
vec nom = {0, 0, 0};
|
||||
newparticle(player1->o, nom, 100000000, 8);
|
||||
};
|
||||
|
||||
|
@ -55,50 +64,63 @@ void render_particles(int time)
|
|||
glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
|
||||
glDisable(GL_FOG);
|
||||
|
||||
struct parttype { float r, g, b; int gr, tex; float sz; } parttypes[] =
|
||||
{
|
||||
{ 0.7f, 0.6f, 0.3f, 2, 3, 0.06f }, // yellow: sparks
|
||||
{ 0.5f, 0.5f, 0.5f, 20, 7, 0.15f }, // grey: small smoke
|
||||
{ 0.2f, 0.2f, 1.0f, 20, 3, 0.08f }, // blue: edit mode entities
|
||||
{ 1.0f, 0.1f, 0.1f, 1, 7, 0.06f }, // red: blood spats
|
||||
{ 1.0f, 0.8f, 0.8f, 20, 6, 1.2f }, // yellow: fireball1
|
||||
{ 0.5f, 0.5f, 0.5f, 20, 7, 0.6f }, // grey: big smoke
|
||||
{ 1.0f, 1.0f, 1.0f, 20, 8, 1.2f }, // blue: fireball2
|
||||
{ 1.0f, 1.0f, 1.0f, 20, 9, 1.2f }, // green: fireball3
|
||||
{ 1.0f, 0.1f, 0.1f, 0, 7, 0.2f }, // red: demotrack
|
||||
struct parttype {
|
||||
float r, g, b;
|
||||
int gr, tex;
|
||||
float sz;
|
||||
} parttypes[] = {
|
||||
{0.7f, 0.6f, 0.3f, 2, 3, 0.06f}, // yellow: sparks
|
||||
{0.5f, 0.5f, 0.5f, 20, 7, 0.15f}, // grey: small smoke
|
||||
{0.2f, 0.2f, 1.0f, 20, 3, 0.08f}, // blue: edit mode entities
|
||||
{1.0f, 0.1f, 0.1f, 1, 7, 0.06f}, // red: blood spats
|
||||
{1.0f, 0.8f, 0.8f, 20, 6, 1.2f}, // yellow: fireball1
|
||||
{0.5f, 0.5f, 0.5f, 20, 7, 0.6f}, // grey: big smoke
|
||||
{1.0f, 1.0f, 1.0f, 20, 8, 1.2f}, // blue: fireball2
|
||||
{1.0f, 1.0f, 1.0f, 20, 9, 1.2f}, // green: fireball3
|
||||
{1.0f, 0.1f, 0.1f, 0, 7, 0.2f}, // red: demotrack
|
||||
};
|
||||
|
||||
int numrender = 0;
|
||||
|
||||
for(particle *p, **pp = &parlist; p = *pp;)
|
||||
{
|
||||
for (particle *p, **pp = &parlist; p = *pp;) {
|
||||
parttype *pt = &parttypes[p->type];
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, pt->tex);
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
glColor3d(pt->r, pt->g, pt->b);
|
||||
float sz = pt->sz*particlesize/100.0f;
|
||||
float sz = pt->sz * particlesize / 100.0f;
|
||||
// perf varray?
|
||||
glTexCoord2f(0.0, 1.0); glVertex3d(p->o.x+(-right.x+up.x)*sz, p->o.z+(-right.y+up.y)*sz, p->o.y+(-right.z+up.z)*sz);
|
||||
glTexCoord2f(1.0, 1.0); glVertex3d(p->o.x+( right.x+up.x)*sz, p->o.z+( right.y+up.y)*sz, p->o.y+( right.z+up.z)*sz);
|
||||
glTexCoord2f(1.0, 0.0); glVertex3d(p->o.x+( right.x-up.x)*sz, p->o.z+( right.y-up.y)*sz, p->o.y+( right.z-up.z)*sz);
|
||||
glTexCoord2f(0.0, 0.0); glVertex3d(p->o.x+(-right.x-up.x)*sz, p->o.z+(-right.y-up.y)*sz, p->o.y+(-right.z-up.z)*sz);
|
||||
glTexCoord2f(0.0, 1.0);
|
||||
glVertex3d(p->o.x + (-right.x + up.x) * sz,
|
||||
p->o.z + (-right.y + up.y) * sz,
|
||||
p->o.y + (-right.z + up.z) * sz);
|
||||
glTexCoord2f(1.0, 1.0);
|
||||
glVertex3d(p->o.x + (right.x + up.x) * sz,
|
||||
p->o.z + (right.y + up.y) * sz,
|
||||
p->o.y + (right.z + up.z) * sz);
|
||||
glTexCoord2f(1.0, 0.0);
|
||||
glVertex3d(p->o.x + (right.x - up.x) * sz,
|
||||
p->o.z + (right.y - up.y) * sz,
|
||||
p->o.y + (right.z - up.z) * sz);
|
||||
glTexCoord2f(0.0, 0.0);
|
||||
glVertex3d(p->o.x + (-right.x - up.x) * sz,
|
||||
p->o.z + (-right.y - up.y) * sz,
|
||||
p->o.y + (-right.z - up.z) * sz);
|
||||
glEnd();
|
||||
xtraverts += 4;
|
||||
|
||||
if(numrender++>maxparticles || (p->fade -= time)<0)
|
||||
{
|
||||
if (numrender++ > maxparticles || (p->fade -= time) < 0) {
|
||||
*pp = p->next;
|
||||
p->next = parempty;
|
||||
parempty = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(pt->gr) p->o.z -= ((lastmillis-p->millis)/3.0f)*curtime/(pt->gr*10000);
|
||||
} else {
|
||||
if (pt->gr)
|
||||
p->o.z -= ((lastmillis - p->millis) / 3.0f) *
|
||||
curtime / (pt->gr * 10000);
|
||||
vec a = p->d;
|
||||
vmul(a,time);
|
||||
vdiv(a,20000.0f);
|
||||
vmul(a, time);
|
||||
vdiv(a, 20000.0f);
|
||||
vadd(p->o, a);
|
||||
pp = &p->next;
|
||||
};
|
||||
|
@ -109,34 +131,34 @@ void render_particles(int time)
|
|||
glDepthMask(GL_TRUE);
|
||||
};
|
||||
|
||||
void particle_splash(int type, int num, int fade, vec &p)
|
||||
void
|
||||
particle_splash(int type, int num, int fade, vec &p)
|
||||
{
|
||||
loopi(num)
|
||||
{
|
||||
const int radius = type==5 ? 50 : 150;
|
||||
const int radius = type == 5 ? 50 : 150;
|
||||
int x, y, z;
|
||||
do
|
||||
{
|
||||
x = rnd(radius*2)-radius;
|
||||
y = rnd(radius*2)-radius;
|
||||
z = rnd(radius*2)-radius;
|
||||
}
|
||||
while(x*x+y*y+z*z>radius*radius);
|
||||
vec d = { (float)x, (float)y, (float)z };
|
||||
newparticle(p, d, rnd(fade*3), type);
|
||||
do {
|
||||
x = rnd(radius * 2) - radius;
|
||||
y = rnd(radius * 2) - radius;
|
||||
z = rnd(radius * 2) - radius;
|
||||
} while (x * x + y * y + z * z > radius * radius);
|
||||
vec d = {(float)x, (float)y, (float)z};
|
||||
newparticle(p, d, rnd(fade * 3), type);
|
||||
};
|
||||
};
|
||||
|
||||
void particle_trail(int type, int fade, vec &s, vec &e)
|
||||
void
|
||||
particle_trail(int type, int fade, vec &s, vec &e)
|
||||
{
|
||||
vdist(d, v, s, e);
|
||||
vdiv(v, d*2+0.1f);
|
||||
vdiv(v, d * 2 + 0.1f);
|
||||
vec p = s;
|
||||
loopi((int)d*2)
|
||||
loopi((int)d * 2)
|
||||
{
|
||||
vadd(p, v);
|
||||
vec d = { float(rnd(11)-5), float(rnd(11)-5), float(rnd(11)-5) };
|
||||
newparticle(p, d, rnd(fade)+fade, type);
|
||||
vec d = {
|
||||
float(rnd(11) - 5), float(rnd(11) - 5), float(rnd(11) - 5)};
|
||||
newparticle(p, d, rnd(fade) + fade, type);
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -2,133 +2,141 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
short char_coords[96][4] =
|
||||
{
|
||||
{0,0,25,64}, //!
|
||||
{25,0,54,64}, //"
|
||||
{54,0,107,64}, //#
|
||||
{107,0,148,64}, //$
|
||||
{148,0,217,64}, //%
|
||||
{217,0,263,64}, //&
|
||||
{263,0,280,64}, //'
|
||||
{280,0,309,64}, //(
|
||||
{309,0,338,64}, //)
|
||||
{338,0,379,64}, //*
|
||||
{379,0,432,64}, //+
|
||||
{432,0,455,64}, //,
|
||||
{455,0,484,64}, //-
|
||||
{0,64,21,128}, //.
|
||||
{23,64,52,128}, ///
|
||||
{52,64,93,128}, //0
|
||||
{93,64,133,128}, //1
|
||||
{133,64,174,128}, //2
|
||||
{174,64,215,128}, //3
|
||||
{215,64,256,128}, //4
|
||||
{256,64,296,128}, //5
|
||||
{296,64,337,128}, //6
|
||||
{337,64,378,128}, //7
|
||||
{378,64,419,128}, //8
|
||||
{419,64,459,128}, //9
|
||||
{459,64,488,128}, //:
|
||||
{0,128,29,192}, //;
|
||||
{29,128,81,192}, //<
|
||||
{81,128,134,192}, //=
|
||||
{134,128,186,192}, //>
|
||||
{186,128,221,192}, //?
|
||||
{221,128,285,192}, //@
|
||||
{285,128,329,192}, //A
|
||||
{329,128,373,192}, //B
|
||||
{373,128,418,192}, //C
|
||||
{418,128,467,192}, //D
|
||||
{0,192,40,256}, //E
|
||||
{40,192,77,256}, //F
|
||||
{77,192,127,256}, //G
|
||||
{127,192,175,256}, //H
|
||||
{175,192,202,256}, //I
|
||||
{202,192,231,256}, //J
|
||||
{231,192,275,256}, //K
|
||||
{275,192,311,256}, //L
|
||||
{311,192,365,256}, //M
|
||||
{365,192,413,256}, //N
|
||||
{413,192,463,256}, //O
|
||||
{1,256,38,320}, //P
|
||||
{38,256,89,320}, //Q
|
||||
{89,256,133,320}, //R
|
||||
{133,256,176,320}, //S
|
||||
{177,256,216,320}, //T
|
||||
{217,256,263,320}, //U
|
||||
{263,256,307,320}, //V
|
||||
{307,256,370,320}, //W
|
||||
{370,256,414,320}, //X
|
||||
{414,256,453,320}, //Y
|
||||
{453,256,497,320}, //Z
|
||||
{0,320,29,384}, //[
|
||||
{29,320,58,384}, //"\"
|
||||
{59,320,87,384}, //]
|
||||
{87,320,139,384}, //^
|
||||
{139,320,180,384}, //_
|
||||
{180,320,221,384}, //`
|
||||
{221,320,259,384}, //a
|
||||
{259,320,299,384}, //b
|
||||
{299,320,332,384}, //c
|
||||
{332,320,372,384}, //d
|
||||
{372,320,411,384}, //e
|
||||
{411,320,433,384}, //f
|
||||
{435,320,473,384}, //g
|
||||
{0,384,40,448}, //h
|
||||
{40,384,56,448}, //i
|
||||
{58,384,80,448}, //j
|
||||
{80,384,118,448}, //k
|
||||
{118,384,135,448}, //l
|
||||
{135,384,197,448}, //m
|
||||
{197,384,238,448}, //n
|
||||
{238,384,277,448}, //o
|
||||
{277,384,317,448}, //p
|
||||
{317,384,356,448}, //q
|
||||
{357,384,384,448}, //r
|
||||
{385,384,417,448}, //s
|
||||
{417,384,442,448}, //t
|
||||
{443,384,483,448}, //u
|
||||
{0,448,38,512}, //v
|
||||
{38,448,90,512}, //w
|
||||
{90,448,128,512}, //x
|
||||
{128,448,166,512}, //y
|
||||
{166,448,200,512}, //z
|
||||
{200,448,241,512}, //{
|
||||
{241,448,270,512}, //|
|
||||
{270,448,310,512}, //}
|
||||
{310,448,363,512}, //~
|
||||
short char_coords[96][4] = {
|
||||
{0, 0, 25, 64}, //!
|
||||
{25, 0, 54, 64}, //"
|
||||
{54, 0, 107, 64}, // #
|
||||
{107, 0, 148, 64}, //$
|
||||
{148, 0, 217, 64}, //%
|
||||
{217, 0, 263, 64}, //&
|
||||
{263, 0, 280, 64}, //'
|
||||
{280, 0, 309, 64}, //(
|
||||
{309, 0, 338, 64}, //)
|
||||
{338, 0, 379, 64}, //*
|
||||
{379, 0, 432, 64}, //+
|
||||
{432, 0, 455, 64}, //,
|
||||
{455, 0, 484, 64}, //-
|
||||
{0, 64, 21, 128}, //.
|
||||
{23, 64, 52, 128}, ///
|
||||
{52, 64, 93, 128}, // 0
|
||||
{93, 64, 133, 128}, // 1
|
||||
{133, 64, 174, 128}, // 2
|
||||
{174, 64, 215, 128}, // 3
|
||||
{215, 64, 256, 128}, // 4
|
||||
{256, 64, 296, 128}, // 5
|
||||
{296, 64, 337, 128}, // 6
|
||||
{337, 64, 378, 128}, // 7
|
||||
{378, 64, 419, 128}, // 8
|
||||
{419, 64, 459, 128}, // 9
|
||||
{459, 64, 488, 128}, //:
|
||||
{0, 128, 29, 192}, //;
|
||||
{29, 128, 81, 192}, //<
|
||||
{81, 128, 134, 192}, //=
|
||||
{134, 128, 186, 192}, //>
|
||||
{186, 128, 221, 192}, //?
|
||||
{221, 128, 285, 192}, //@
|
||||
{285, 128, 329, 192}, // A
|
||||
{329, 128, 373, 192}, // B
|
||||
{373, 128, 418, 192}, // C
|
||||
{418, 128, 467, 192}, // D
|
||||
{0, 192, 40, 256}, // E
|
||||
{40, 192, 77, 256}, // F
|
||||
{77, 192, 127, 256}, // G
|
||||
{127, 192, 175, 256}, // H
|
||||
{175, 192, 202, 256}, // I
|
||||
{202, 192, 231, 256}, // J
|
||||
{231, 192, 275, 256}, // K
|
||||
{275, 192, 311, 256}, // L
|
||||
{311, 192, 365, 256}, // M
|
||||
{365, 192, 413, 256}, // N
|
||||
{413, 192, 463, 256}, // O
|
||||
{1, 256, 38, 320}, // P
|
||||
{38, 256, 89, 320}, // Q
|
||||
{89, 256, 133, 320}, // R
|
||||
{133, 256, 176, 320}, // S
|
||||
{177, 256, 216, 320}, // T
|
||||
{217, 256, 263, 320}, // U
|
||||
{263, 256, 307, 320}, // V
|
||||
{307, 256, 370, 320}, // W
|
||||
{370, 256, 414, 320}, // X
|
||||
{414, 256, 453, 320}, // Y
|
||||
{453, 256, 497, 320}, // Z
|
||||
{0, 320, 29, 384}, //[
|
||||
{29, 320, 58, 384}, //"\"
|
||||
{59, 320, 87, 384}, //]
|
||||
{87, 320, 139, 384}, //^
|
||||
{139, 320, 180, 384}, //_
|
||||
{180, 320, 221, 384}, //`
|
||||
{221, 320, 259, 384}, // a
|
||||
{259, 320, 299, 384}, // b
|
||||
{299, 320, 332, 384}, // c
|
||||
{332, 320, 372, 384}, // d
|
||||
{372, 320, 411, 384}, // e
|
||||
{411, 320, 433, 384}, // f
|
||||
{435, 320, 473, 384}, // g
|
||||
{0, 384, 40, 448}, // h
|
||||
{40, 384, 56, 448}, // i
|
||||
{58, 384, 80, 448}, // j
|
||||
{80, 384, 118, 448}, // k
|
||||
{118, 384, 135, 448}, // l
|
||||
{135, 384, 197, 448}, // m
|
||||
{197, 384, 238, 448}, // n
|
||||
{238, 384, 277, 448}, // o
|
||||
{277, 384, 317, 448}, // p
|
||||
{317, 384, 356, 448}, // q
|
||||
{357, 384, 384, 448}, // r
|
||||
{385, 384, 417, 448}, // s
|
||||
{417, 384, 442, 448}, // t
|
||||
{443, 384, 483, 448}, // u
|
||||
{0, 448, 38, 512}, // v
|
||||
{38, 448, 90, 512}, // w
|
||||
{90, 448, 128, 512}, // x
|
||||
{128, 448, 166, 512}, // y
|
||||
{166, 448, 200, 512}, // z
|
||||
{200, 448, 241, 512}, //{
|
||||
{241, 448, 270, 512}, //|
|
||||
{270, 448, 310, 512}, //}
|
||||
{310, 448, 363, 512}, //~
|
||||
};
|
||||
|
||||
int text_width(char *str)
|
||||
int
|
||||
text_width(char *str)
|
||||
{
|
||||
int x = 0;
|
||||
for (int i = 0; str[i] != 0; i++)
|
||||
{
|
||||
for (int i = 0; str[i] != 0; i++) {
|
||||
int c = str[i];
|
||||
if(c=='\t') { x = (x+PIXELTAB)/PIXELTAB*PIXELTAB; continue; };
|
||||
if(c=='\f') continue;
|
||||
if(c==' ') { x += FONTH/2; continue; };
|
||||
if (c == '\t') {
|
||||
x = (x + PIXELTAB) / PIXELTAB * PIXELTAB;
|
||||
continue;
|
||||
};
|
||||
if (c == '\f')
|
||||
continue;
|
||||
if (c == ' ') {
|
||||
x += FONTH / 2;
|
||||
continue;
|
||||
};
|
||||
c -= 33;
|
||||
if(c<0 || c>=95) continue;
|
||||
if (c < 0 || c >= 95)
|
||||
continue;
|
||||
int in_width = char_coords[c][2] - char_coords[c][0];
|
||||
x += in_width + 1;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
void draw_textf(char *fstr, int left, int top, int gl_num, ...)
|
||||
void
|
||||
draw_textf(char *fstr, int left, int top, int gl_num, ...)
|
||||
{
|
||||
sprintf_sdlv(str, gl_num, fstr);
|
||||
draw_text(str, left, top, gl_num);
|
||||
};
|
||||
|
||||
void draw_text(char *str, int left, int top, int gl_num)
|
||||
void
|
||||
draw_text(char *str, int left, int top, int gl_num)
|
||||
{
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
glBindTexture(GL_TEXTURE_2D, gl_num);
|
||||
glColor3ub(255,255,255);
|
||||
glColor3ub(255, 255, 255);
|
||||
|
||||
int x = left;
|
||||
int y = top;
|
||||
|
@ -137,28 +145,41 @@ void draw_text(char *str, int left, int top, int gl_num)
|
|||
float in_left, in_top, in_right, in_bottom;
|
||||
int in_width, in_height;
|
||||
|
||||
for (i = 0; str[i] != 0; i++)
|
||||
{
|
||||
for (i = 0; str[i] != 0; i++) {
|
||||
int c = str[i];
|
||||
if(c=='\t') { x = (x-left+PIXELTAB)/PIXELTAB*PIXELTAB+left; continue; };
|
||||
if(c=='\f') { glColor3ub(64,255,128); continue; };
|
||||
if(c==' ') { x += FONTH/2; continue; };
|
||||
if (c == '\t') {
|
||||
x = (x - left + PIXELTAB) / PIXELTAB * PIXELTAB + left;
|
||||
continue;
|
||||
};
|
||||
if (c == '\f') {
|
||||
glColor3ub(64, 255, 128);
|
||||
continue;
|
||||
};
|
||||
if (c == ' ') {
|
||||
x += FONTH / 2;
|
||||
continue;
|
||||
};
|
||||
c -= 33;
|
||||
if(c<0 || c>=95) continue;
|
||||
if (c < 0 || c >= 95)
|
||||
continue;
|
||||
|
||||
in_left = ((float) char_coords[c][0]) / 512.0f;
|
||||
in_top = ((float) char_coords[c][1]+2) / 512.0f;
|
||||
in_right = ((float) char_coords[c][2]) / 512.0f;
|
||||
in_bottom = ((float) char_coords[c][3]-2) / 512.0f;
|
||||
in_left = ((float)char_coords[c][0]) / 512.0f;
|
||||
in_top = ((float)char_coords[c][1] + 2) / 512.0f;
|
||||
in_right = ((float)char_coords[c][2]) / 512.0f;
|
||||
in_bottom = ((float)char_coords[c][3] - 2) / 512.0f;
|
||||
|
||||
in_width = char_coords[c][2] - char_coords[c][0];
|
||||
in_height = char_coords[c][3] - char_coords[c][1];
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(in_left, in_top ); glVertex2i(x, y);
|
||||
glTexCoord2f(in_right, in_top ); glVertex2i(x + in_width, y);
|
||||
glTexCoord2f(in_right, in_bottom); glVertex2i(x + in_width, y + in_height);
|
||||
glTexCoord2f(in_left, in_bottom); glVertex2i(x, y + in_height);
|
||||
glTexCoord2f(in_left, in_top);
|
||||
glVertex2i(x, y);
|
||||
glTexCoord2f(in_right, in_top);
|
||||
glVertex2i(x + in_width, y);
|
||||
glTexCoord2f(in_right, in_bottom);
|
||||
glVertex2i(x + in_width, y + in_height);
|
||||
glTexCoord2f(in_left, in_bottom);
|
||||
glVertex2i(x, y + in_height);
|
||||
glEnd();
|
||||
|
||||
xtraverts += 4;
|
||||
|
@ -168,55 +189,47 @@ void draw_text(char *str, int left, int top, int gl_num)
|
|||
|
||||
// also Don's code, so goes in here too :)
|
||||
|
||||
void draw_envbox_aux(float s0, float t0, int x0, int y0, int z0,
|
||||
float s1, float t1, int x1, int y1, int z1,
|
||||
float s2, float t2, int x2, int y2, int z2,
|
||||
float s3, float t3, int x3, int y3, int z3,
|
||||
int texture)
|
||||
void
|
||||
draw_envbox_aux(float s0, float t0, int x0, int y0, int z0, float s1, float t1,
|
||||
int x1, int y1, int z1, float s2, float t2, int x2, int y2, int z2,
|
||||
float s3, float t3, int x3, int y3, int z3, int texture)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(s3, t3); glVertex3d(x3, y3, z3);
|
||||
glTexCoord2f(s2, t2); glVertex3d(x2, y2, z2);
|
||||
glTexCoord2f(s1, t1); glVertex3d(x1, y1, z1);
|
||||
glTexCoord2f(s0, t0); glVertex3d(x0, y0, z0);
|
||||
glTexCoord2f(s3, t3);
|
||||
glVertex3d(x3, y3, z3);
|
||||
glTexCoord2f(s2, t2);
|
||||
glVertex3d(x2, y2, z2);
|
||||
glTexCoord2f(s1, t1);
|
||||
glVertex3d(x1, y1, z1);
|
||||
glTexCoord2f(s0, t0);
|
||||
glVertex3d(x0, y0, z0);
|
||||
glEnd();
|
||||
xtraverts += 4;
|
||||
}
|
||||
|
||||
void draw_envbox(int t, int w)
|
||||
void
|
||||
draw_envbox(int t, int w)
|
||||
{
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
draw_envbox_aux(1.0f, 1.0f, -w, -w, w,
|
||||
0.0f, 1.0f, w, -w, w,
|
||||
0.0f, 0.0f, w, -w, -w,
|
||||
1.0f, 0.0f, -w, -w, -w, t);
|
||||
draw_envbox_aux(1.0f, 1.0f, -w, -w, w, 0.0f, 1.0f, w, -w, w, 0.0f, 0.0f,
|
||||
w, -w, -w, 1.0f, 0.0f, -w, -w, -w, t);
|
||||
|
||||
draw_envbox_aux(1.0f, 1.0f, +w, w, w,
|
||||
0.0f, 1.0f, -w, w, w,
|
||||
0.0f, 0.0f, -w, w, -w,
|
||||
1.0f, 0.0f, +w, w, -w, t+1);
|
||||
draw_envbox_aux(1.0f, 1.0f, +w, w, w, 0.0f, 1.0f, -w, w, w, 0.0f, 0.0f,
|
||||
-w, w, -w, 1.0f, 0.0f, +w, w, -w, t + 1);
|
||||
|
||||
draw_envbox_aux(0.0f, 0.0f, -w, -w, -w,
|
||||
1.0f, 0.0f, -w, w, -w,
|
||||
1.0f, 1.0f, -w, w, w,
|
||||
0.0f, 1.0f, -w, -w, w, t+2);
|
||||
draw_envbox_aux(0.0f, 0.0f, -w, -w, -w, 1.0f, 0.0f, -w, w, -w, 1.0f,
|
||||
1.0f, -w, w, w, 0.0f, 1.0f, -w, -w, w, t + 2);
|
||||
|
||||
draw_envbox_aux(1.0f, 1.0f, +w, -w, w,
|
||||
0.0f, 1.0f, +w, w, w,
|
||||
0.0f, 0.0f, +w, w, -w,
|
||||
1.0f, 0.0f, +w, -w, -w, t+3);
|
||||
draw_envbox_aux(1.0f, 1.0f, +w, -w, w, 0.0f, 1.0f, +w, w, w, 0.0f, 0.0f,
|
||||
+w, w, -w, 1.0f, 0.0f, +w, -w, -w, t + 3);
|
||||
|
||||
draw_envbox_aux(0.0f, 1.0f, -w, w, w,
|
||||
0.0f, 0.0f, +w, w, w,
|
||||
1.0f, 0.0f, +w, -w, w,
|
||||
1.0f, 1.0f, -w, -w, w, t+4);
|
||||
draw_envbox_aux(0.0f, 1.0f, -w, w, w, 0.0f, 0.0f, +w, w, w, 1.0f, 0.0f,
|
||||
+w, -w, w, 1.0f, 1.0f, -w, -w, w, t + 4);
|
||||
|
||||
draw_envbox_aux(0.0f, 1.0f, +w, w, -w,
|
||||
0.0f, 0.0f, -w, w, -w,
|
||||
1.0f, 0.0f, -w, -w, -w,
|
||||
1.0f, 1.0f, +w, -w, -w, t+5);
|
||||
draw_envbox_aux(0.0f, 1.0f, +w, w, -w, 0.0f, 0.0f, -w, w, -w, 1.0f,
|
||||
0.0f, -w, -w, -w, 1.0f, 1.0f, +w, -w, -w, t + 5);
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
|
|
@ -1,70 +1,89 @@
|
|||
// rndmap.cpp: perlin noise landscape generation and some experimental random map stuff, currently not used
|
||||
// rndmap.cpp: perlin noise landscape generation and some experimental random
|
||||
// map stuff, currently not used
|
||||
|
||||
#include "cube.h"
|
||||
|
||||
float noise(int x, int y, int seed)
|
||||
float
|
||||
noise(int x, int y, int seed)
|
||||
{
|
||||
int n = x+y*57;
|
||||
n = (n<<13)^n;
|
||||
return 1.0f-((n*(n*n*15731+789221)+1376312589)&0x7fffffff)/1073741824.0f;
|
||||
int n = x + y * 57;
|
||||
n = (n << 13) ^ n;
|
||||
return 1.0f -
|
||||
((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) /
|
||||
1073741824.0f;
|
||||
}
|
||||
|
||||
float smoothednoise(int x, int y, int seed)
|
||||
float
|
||||
smoothednoise(int x, int y, int seed)
|
||||
{
|
||||
float corners = (noise(x-1, y-1, seed)+noise(x+1, y-1, seed)+noise(x-1, y+1, seed)+noise(x+1, y+1, seed))/16;
|
||||
float sides = (noise(x-1, y, seed)+noise(x+1, y, seed)+noise(x, y-1, seed)+noise(x, y+1, seed))/8;
|
||||
float center = noise(x, y, seed)/4;
|
||||
return corners+sides+center;
|
||||
float corners =
|
||||
(noise(x - 1, y - 1, seed) + noise(x + 1, y - 1, seed) +
|
||||
noise(x - 1, y + 1, seed) + noise(x + 1, y + 1, seed)) /
|
||||
16;
|
||||
float sides = (noise(x - 1, y, seed) + noise(x + 1, y, seed) +
|
||||
noise(x, y - 1, seed) + noise(x, y + 1, seed)) /
|
||||
8;
|
||||
float center = noise(x, y, seed) / 4;
|
||||
return corners + sides + center;
|
||||
}
|
||||
|
||||
float interpolate(float a, float b, float x)
|
||||
float
|
||||
interpolate(float a, float b, float x)
|
||||
{
|
||||
float ft = x*3.1415927f;
|
||||
float f = (1.0f-(float)cos(ft))*0.5f;
|
||||
return a*(1-f)+b*f;
|
||||
float ft = x * 3.1415927f;
|
||||
float f = (1.0f - (float)cos(ft)) * 0.5f;
|
||||
return a * (1 - f) + b * f;
|
||||
}
|
||||
|
||||
float interpolatednoise(float x, float y, int seed)
|
||||
float
|
||||
interpolatednoise(float x, float y, int seed)
|
||||
{
|
||||
int ix = (int)x;
|
||||
float fx = x-ix;
|
||||
float fx = x - ix;
|
||||
int iy = (int)y;
|
||||
float fy = y-iy;
|
||||
float fy = y - iy;
|
||||
float v1 = smoothednoise(ix, iy, seed);
|
||||
float v2 = smoothednoise(ix+1, iy, seed);
|
||||
float v3 = smoothednoise(ix, iy+1, seed);
|
||||
float v4 = smoothednoise(ix+1, iy+1, seed);
|
||||
float v2 = smoothednoise(ix + 1, iy, seed);
|
||||
float v3 = smoothednoise(ix, iy + 1, seed);
|
||||
float v4 = smoothednoise(ix + 1, iy + 1, seed);
|
||||
float i1 = interpolate(v1, v2, fx);
|
||||
float i2 = interpolate(v3, v4, fy);
|
||||
return interpolate(i1, i2, fy);
|
||||
}
|
||||
|
||||
float perlinnoise_2D(float x, float y, int seedstep, float pers)
|
||||
float
|
||||
perlinnoise_2D(float x, float y, int seedstep, float pers)
|
||||
{
|
||||
float total = 0;
|
||||
int seed = 0;
|
||||
for(int i = 0; i<7; i++)
|
||||
{
|
||||
float frequency = (float)(2^i);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
float frequency = (float)(2 ^ i);
|
||||
float amplitude = (float)pow(pers, i);
|
||||
total += interpolatednoise(x*frequency, y*frequency, seed)*amplitude;
|
||||
total += interpolatednoise(x * frequency, y * frequency, seed) *
|
||||
amplitude;
|
||||
seed += seedstep;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
void perlinarea(block &b, int scale, int seed, int psize)
|
||||
void
|
||||
perlinarea(block &b, int scale, int seed, int psize)
|
||||
{
|
||||
srand(seed);
|
||||
seed = rnd(10000);
|
||||
if(!scale) scale = 10;
|
||||
for(int x = b.x; x<=b.x+b.xs; x++) for(int y = b.y; y<=b.y+b.ys; y++)
|
||||
{
|
||||
sqr *s = S(x,y);
|
||||
if(!SOLID(s) && x!=b.x+b.xs && y!=b.y+b.ys) s->type = FHF;
|
||||
s->vdelta = (int)(perlinnoise_2D(x/((float)scale)+seed, y/((float)scale)+seed, 1000, 0.01f)*50+25);
|
||||
if(s->vdelta>128) s->vdelta = 0;
|
||||
if (!scale)
|
||||
scale = 10;
|
||||
for (int x = b.x; x <= b.x + b.xs; x++)
|
||||
for (int y = b.y; y <= b.y + b.ys; y++) {
|
||||
sqr *s = S(x, y);
|
||||
if (!SOLID(s) && x != b.x + b.xs && y != b.y + b.ys)
|
||||
s->type = FHF;
|
||||
s->vdelta =
|
||||
(int)(perlinnoise_2D(x / ((float)scale) + seed,
|
||||
y / ((float)scale) + seed, 1000, 0.01f) *
|
||||
50 +
|
||||
25);
|
||||
if (s->vdelta > 128)
|
||||
s->vdelta = 0;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// loading and saving of savegames & demos, dumps the spawn state of all mapents, the full state of all dynents (monsters + player)
|
||||
// loading and saving of savegames & demos, dumps the spawn state of all
|
||||
// mapents, the full state of all dynents (monsters + player)
|
||||
|
||||
#include "cube.h"
|
||||
|
||||
|
@ -13,20 +14,53 @@ int democlientnum = 0;
|
|||
|
||||
void startdemo();
|
||||
|
||||
void gzput(int i) { gzputc(f, i); };
|
||||
void gzputi(int i) { gzwrite(f, &i, sizeof(int)); };
|
||||
void gzputv(vec &v) { gzwrite(f, &v, sizeof(vec)); };
|
||||
|
||||
void gzcheck(int a, int b) { if(a!=b) fatal("savegame file corrupt (short)"); };
|
||||
int gzget() { char c = gzgetc(f); return c; };
|
||||
int gzgeti() { int i; gzcheck(gzread(f, &i, sizeof(int)), sizeof(int)); return i; };
|
||||
void gzgetv(vec &v) { gzcheck(gzread(f, &v, sizeof(vec)), sizeof(vec)); };
|
||||
|
||||
void stop()
|
||||
void
|
||||
gzput(int i)
|
||||
{
|
||||
if(f)
|
||||
{
|
||||
if(demorecording) gzputi(-1);
|
||||
gzputc(f, i);
|
||||
};
|
||||
void
|
||||
gzputi(int i)
|
||||
{
|
||||
gzwrite(f, &i, sizeof(int));
|
||||
};
|
||||
void
|
||||
gzputv(vec &v)
|
||||
{
|
||||
gzwrite(f, &v, sizeof(vec));
|
||||
};
|
||||
|
||||
void
|
||||
gzcheck(int a, int b)
|
||||
{
|
||||
if (a != b)
|
||||
fatal("savegame file corrupt (short)");
|
||||
};
|
||||
int
|
||||
gzget()
|
||||
{
|
||||
char c = gzgetc(f);
|
||||
return c;
|
||||
};
|
||||
int
|
||||
gzgeti()
|
||||
{
|
||||
int i;
|
||||
gzcheck(gzread(f, &i, sizeof(int)), sizeof(int));
|
||||
return i;
|
||||
};
|
||||
void
|
||||
gzgetv(vec &v)
|
||||
{
|
||||
gzcheck(gzread(f, &v, sizeof(vec)), sizeof(vec));
|
||||
};
|
||||
|
||||
void
|
||||
stop()
|
||||
{
|
||||
if (f) {
|
||||
if (demorecording)
|
||||
gzputi(-1);
|
||||
gzclose(f);
|
||||
};
|
||||
f = NULL;
|
||||
|
@ -37,13 +71,22 @@ void stop()
|
|||
playerhistory.setsize(0);
|
||||
};
|
||||
|
||||
void stopifrecording() { if(demorecording) stop(); };
|
||||
void
|
||||
stopifrecording()
|
||||
{
|
||||
if (demorecording)
|
||||
stop();
|
||||
};
|
||||
|
||||
void savestate(char *fn)
|
||||
void
|
||||
savestate(char *fn)
|
||||
{
|
||||
stop();
|
||||
f = gzopen(fn, "wb9");
|
||||
if(!f) { conoutf("could not write %s", fn); return; };
|
||||
if (!f) {
|
||||
conoutf("could not write %s", fn);
|
||||
return;
|
||||
};
|
||||
gzwrite(f, (void *)"CUBESAVE", 8);
|
||||
gzputc(f, islittleendian);
|
||||
gzputi(SAVEGAMEVERSION);
|
||||
|
@ -59,63 +102,85 @@ void savestate(char *fn)
|
|||
gzputi(players.length());
|
||||
loopv(players)
|
||||
{
|
||||
gzput(players[i]==NULL);
|
||||
gzput(players[i] == NULL);
|
||||
gzwrite(f, players[i], sizeof(dynent));
|
||||
};
|
||||
};
|
||||
|
||||
void savegame(char *name)
|
||||
void
|
||||
savegame(char *name)
|
||||
{
|
||||
if(!m_classicsp) { conoutf("can only save classic sp games"); return; };
|
||||
if (!m_classicsp) {
|
||||
conoutf("can only save classic sp games");
|
||||
return;
|
||||
};
|
||||
sprintf_sd(fn)("savegames/%s.csgz", name);
|
||||
savestate(fn);
|
||||
stop();
|
||||
conoutf("wrote %s", fn);
|
||||
};
|
||||
|
||||
void loadstate(char *fn)
|
||||
void
|
||||
loadstate(char *fn)
|
||||
{
|
||||
stop();
|
||||
if(multiplayer()) return;
|
||||
if (multiplayer())
|
||||
return;
|
||||
f = gzopen(fn, "rb9");
|
||||
if(!f) { conoutf("could not open %s", fn); return; };
|
||||
if (!f) {
|
||||
conoutf("could not open %s", fn);
|
||||
return;
|
||||
};
|
||||
|
||||
string buf;
|
||||
gzread(f, buf, 8);
|
||||
if(strncmp(buf, "CUBESAVE", 8)) goto out;
|
||||
if(gzgetc(f)!=islittleendian) goto out; // not supporting save->load accross incompatible architectures simpifies things a LOT
|
||||
if(gzgeti()!=SAVEGAMEVERSION || gzgeti()!=sizeof(dynent)) goto out;
|
||||
if (strncmp(buf, "CUBESAVE", 8))
|
||||
goto out;
|
||||
if (gzgetc(f) != islittleendian)
|
||||
goto out; // not supporting save->load accross incompatible
|
||||
// architectures simpifies things a LOT
|
||||
if (gzgeti() != SAVEGAMEVERSION || gzgeti() != sizeof(dynent))
|
||||
goto out;
|
||||
string mapname;
|
||||
gzread(f, mapname, _MAXDEFSTR);
|
||||
nextmode = gzgeti();
|
||||
changemap(mapname); // continue below once map has been loaded and client & server have updated
|
||||
changemap(mapname); // continue below once map has been loaded and
|
||||
// client & server have updated
|
||||
return;
|
||||
out:
|
||||
conoutf("aborting: savegame/demo from a different version of cube or cpu architecture");
|
||||
out:
|
||||
conoutf("aborting: savegame/demo from a different version of cube or "
|
||||
"cpu architecture");
|
||||
stop();
|
||||
};
|
||||
|
||||
void loadgame(char *name)
|
||||
void
|
||||
loadgame(char *name)
|
||||
{
|
||||
sprintf_sd(fn)("savegames/%s.csgz", name);
|
||||
loadstate(fn);
|
||||
};
|
||||
|
||||
void loadgameout()
|
||||
void
|
||||
loadgameout()
|
||||
{
|
||||
stop();
|
||||
conoutf("loadgame incomplete: savegame from a different version of this map");
|
||||
conoutf("loadgame incomplete: savegame from a different version of "
|
||||
"this map");
|
||||
};
|
||||
|
||||
void loadgamerest()
|
||||
void
|
||||
loadgamerest()
|
||||
{
|
||||
if(demoplayback || !f) return;
|
||||
if (demoplayback || !f)
|
||||
return;
|
||||
|
||||
if(gzgeti()!=ents.length()) return loadgameout();
|
||||
if (gzgeti() != ents.length())
|
||||
return loadgameout();
|
||||
loopv(ents)
|
||||
{
|
||||
ents[i].spawned = gzgetc(f)!=0;
|
||||
if(ents[i].type==CARROT && !ents[i].spawned) trigger(ents[i].attr1, ents[i].attr2, true);
|
||||
ents[i].spawned = gzgetc(f) != 0;
|
||||
if (ents[i].type == CARROT && !ents[i].spawned)
|
||||
trigger(ents[i].attr1, ents[i].attr2, true);
|
||||
};
|
||||
restoreserverstate(ents);
|
||||
|
||||
|
@ -124,18 +189,23 @@ void loadgamerest()
|
|||
|
||||
int nmonsters = gzgeti();
|
||||
dvector &monsters = getmonsters();
|
||||
if(nmonsters!=monsters.length()) return loadgameout();
|
||||
if (nmonsters != monsters.length())
|
||||
return loadgameout();
|
||||
loopv(monsters)
|
||||
{
|
||||
gzread(f, monsters[i], sizeof(dynent));
|
||||
monsters[i]->enemy = player1; // lazy, could save id of enemy instead
|
||||
monsters[i]->lastaction = monsters[i]->trigger = lastmillis+500; // also lazy, but no real noticable effect on game
|
||||
if(monsters[i]->state==CS_DEAD) monsters[i]->lastaction = 0;
|
||||
monsters[i]->enemy =
|
||||
player1; // lazy, could save id of enemy instead
|
||||
monsters[i]->lastaction = monsters[i]->trigger =
|
||||
lastmillis +
|
||||
500; // also lazy, but no real noticable effect on game
|
||||
if (monsters[i]->state == CS_DEAD)
|
||||
monsters[i]->lastaction = 0;
|
||||
};
|
||||
restoremonsterstate();
|
||||
|
||||
int nplayers = gzgeti();
|
||||
loopi(nplayers) if(!gzget())
|
||||
loopi(nplayers) if (!gzget())
|
||||
{
|
||||
dynent *d = getclient(i);
|
||||
assert(d);
|
||||
|
@ -143,7 +213,10 @@ void loadgamerest()
|
|||
};
|
||||
|
||||
conoutf("savegame restored");
|
||||
if(demoloading) startdemo(); else stop();
|
||||
if (demoloading)
|
||||
startdemo();
|
||||
else
|
||||
stop();
|
||||
};
|
||||
|
||||
// demo functions
|
||||
|
@ -153,11 +226,16 @@ int playbacktime = 0;
|
|||
int ddamage, bdamage;
|
||||
vec dorig;
|
||||
|
||||
void record(char *name)
|
||||
void
|
||||
record(char *name)
|
||||
{
|
||||
if(m_sp) { conoutf("cannot record singleplayer games"); return; };
|
||||
if (m_sp) {
|
||||
conoutf("cannot record singleplayer games");
|
||||
return;
|
||||
};
|
||||
int cn = getclientnum();
|
||||
if(cn<0) return;
|
||||
if (cn < 0)
|
||||
return;
|
||||
sprintf_sd(fn)("demos/%s.cdgz", name);
|
||||
savestate(fn);
|
||||
gzputi(cn);
|
||||
|
@ -167,21 +245,31 @@ void record(char *name)
|
|||
ddamage = bdamage = 0;
|
||||
};
|
||||
|
||||
void demodamage(int damage, vec &o) { ddamage = damage; dorig = o; };
|
||||
void demoblend(int damage) { bdamage = damage; };
|
||||
|
||||
void incomingdemodata(uchar *buf, int len, bool extras)
|
||||
void
|
||||
demodamage(int damage, vec &o)
|
||||
{
|
||||
if(!demorecording) return;
|
||||
gzputi(lastmillis-starttime);
|
||||
ddamage = damage;
|
||||
dorig = o;
|
||||
};
|
||||
void
|
||||
demoblend(int damage)
|
||||
{
|
||||
bdamage = damage;
|
||||
};
|
||||
|
||||
void
|
||||
incomingdemodata(uchar *buf, int len, bool extras)
|
||||
{
|
||||
if (!demorecording)
|
||||
return;
|
||||
gzputi(lastmillis - starttime);
|
||||
gzputi(len);
|
||||
gzwrite(f, buf, len);
|
||||
gzput(extras);
|
||||
if(extras)
|
||||
{
|
||||
if (extras) {
|
||||
gzput(player1->gunselect);
|
||||
gzput(player1->lastattackgun);
|
||||
gzputi(player1->lastaction-starttime);
|
||||
gzputi(player1->lastaction - starttime);
|
||||
gzputi(player1->gunwait);
|
||||
gzputi(player1->health);
|
||||
gzputi(player1->armour);
|
||||
|
@ -191,40 +279,51 @@ void incomingdemodata(uchar *buf, int len, bool extras)
|
|||
gzputi(bdamage);
|
||||
bdamage = 0;
|
||||
gzputi(ddamage);
|
||||
if(ddamage) { gzputv(dorig); ddamage = 0; };
|
||||
// FIXME: add all other client state which is not send through the network
|
||||
if (ddamage) {
|
||||
gzputv(dorig);
|
||||
ddamage = 0;
|
||||
};
|
||||
// FIXME: add all other client state which is not send through
|
||||
// the network
|
||||
};
|
||||
};
|
||||
|
||||
void demo(char *name)
|
||||
void
|
||||
demo(char *name)
|
||||
{
|
||||
sprintf_sd(fn)("demos/%s.cdgz", name);
|
||||
loadstate(fn);
|
||||
demoloading = true;
|
||||
};
|
||||
|
||||
void stopreset()
|
||||
void
|
||||
stopreset()
|
||||
{
|
||||
conoutf("demo stopped (%d msec elapsed)", lastmillis-starttime);
|
||||
conoutf("demo stopped (%d msec elapsed)", lastmillis - starttime);
|
||||
stop();
|
||||
loopv(players) zapdynent(players[i]);
|
||||
disconnect(0, 0);
|
||||
};
|
||||
|
||||
VAR(demoplaybackspeed, 10, 100, 1000);
|
||||
int scaletime(int t) { return (int)(t*(100.0f/demoplaybackspeed))+starttime; };
|
||||
|
||||
void readdemotime()
|
||||
int
|
||||
scaletime(int t)
|
||||
{
|
||||
if(gzeof(f) || (playbacktime = gzgeti())==-1)
|
||||
{
|
||||
return (int)(t * (100.0f / demoplaybackspeed)) + starttime;
|
||||
};
|
||||
|
||||
void
|
||||
readdemotime()
|
||||
{
|
||||
if (gzeof(f) || (playbacktime = gzgeti()) == -1) {
|
||||
stopreset();
|
||||
return;
|
||||
};
|
||||
playbacktime = scaletime(playbacktime);
|
||||
};
|
||||
|
||||
void startdemo()
|
||||
void
|
||||
startdemo()
|
||||
{
|
||||
democlientnum = gzgeti();
|
||||
demoplayback = true;
|
||||
|
@ -238,39 +337,48 @@ void startdemo()
|
|||
|
||||
VAR(demodelaymsec, 0, 120, 500);
|
||||
|
||||
void catmulrom(vec &z, vec &a, vec &b, vec &c, float s, vec &dest) // spline interpolation
|
||||
void
|
||||
catmulrom(
|
||||
vec &z, vec &a, vec &b, vec &c, float s, vec &dest) // spline interpolation
|
||||
{
|
||||
vec t1 = b, t2 = c;
|
||||
|
||||
vsub(t1, z); vmul(t1, 0.5f)
|
||||
vsub(t2, a); vmul(t2, 0.5f);
|
||||
vsub(t1, z);
|
||||
vmul(t1, 0.5f) vsub(t2, a);
|
||||
vmul(t2, 0.5f);
|
||||
|
||||
float s2 = s*s;
|
||||
float s3 = s*s2;
|
||||
float s2 = s * s;
|
||||
float s3 = s * s2;
|
||||
|
||||
dest = a;
|
||||
vec t = b;
|
||||
|
||||
vmul(dest, 2*s3 - 3*s2 + 1);
|
||||
vmul(t, -2*s3 + 3*s2); vadd(dest, t);
|
||||
vmul(t1, s3 - 2*s2 + s); vadd(dest, t1);
|
||||
vmul(t2, s3 - s2); vadd(dest, t2);
|
||||
vmul(dest, 2 * s3 - 3 * s2 + 1);
|
||||
vmul(t, -2 * s3 + 3 * s2);
|
||||
vadd(dest, t);
|
||||
vmul(t1, s3 - 2 * s2 + s);
|
||||
vadd(dest, t1);
|
||||
vmul(t2, s3 - s2);
|
||||
vadd(dest, t2);
|
||||
};
|
||||
|
||||
void fixwrap(dynent *a, dynent *b)
|
||||
void
|
||||
fixwrap(dynent *a, dynent *b)
|
||||
{
|
||||
while(b->yaw-a->yaw>180) a->yaw += 360;
|
||||
while(b->yaw-a->yaw<-180) a->yaw -= 360;
|
||||
while (b->yaw - a->yaw > 180)
|
||||
a->yaw += 360;
|
||||
while (b->yaw - a->yaw < -180)
|
||||
a->yaw -= 360;
|
||||
};
|
||||
|
||||
void demoplaybackstep()
|
||||
void
|
||||
demoplaybackstep()
|
||||
{
|
||||
while(demoplayback && lastmillis>=playbacktime)
|
||||
{
|
||||
while (demoplayback && lastmillis >= playbacktime) {
|
||||
int len = gzgeti();
|
||||
if(len<1 || len>MAXTRANS)
|
||||
{
|
||||
conoutf("error: huge packet during demo play (%d)", len);
|
||||
if (len < 1 || len > MAXTRANS) {
|
||||
conoutf(
|
||||
"error: huge packet during demo play (%d)", len);
|
||||
stopreset();
|
||||
return;
|
||||
};
|
||||
|
@ -282,7 +390,8 @@ void demoplaybackstep()
|
|||
assert(target);
|
||||
|
||||
int extras;
|
||||
if(extras = gzget()) // read additional client side state not present in normal network stream
|
||||
if (extras = gzget()) // read additional client side state not
|
||||
// present in normal network stream
|
||||
{
|
||||
target->gunselect = gzget();
|
||||
target->lastattackgun = gzget();
|
||||
|
@ -294,20 +403,24 @@ void demoplaybackstep()
|
|||
loopi(NUMGUNS) target->ammo[i] = gzget();
|
||||
target->state = gzget();
|
||||
target->lastmove = playbacktime;
|
||||
if(bdamage = gzgeti()) damageblend(bdamage);
|
||||
if(ddamage = gzgeti()) { gzgetv(dorig); particle_splash(3, ddamage, 1000, dorig); };
|
||||
if (bdamage = gzgeti())
|
||||
damageblend(bdamage);
|
||||
if (ddamage = gzgeti()) {
|
||||
gzgetv(dorig);
|
||||
particle_splash(3, ddamage, 1000, dorig);
|
||||
};
|
||||
// FIXME: set more client state here
|
||||
};
|
||||
|
||||
// insert latest copy of player into history
|
||||
if(extras && (playerhistory.empty() || playerhistory.last()->lastupdate!=playbacktime))
|
||||
{
|
||||
if (extras &&
|
||||
(playerhistory.empty() ||
|
||||
playerhistory.last()->lastupdate != playbacktime)) {
|
||||
dynent *d = newdynent();
|
||||
*d = *target;
|
||||
d->lastupdate = playbacktime;
|
||||
playerhistory.add(d);
|
||||
if(playerhistory.length()>20)
|
||||
{
|
||||
if (playerhistory.length() > 20) {
|
||||
zapdynent(playerhistory[0]);
|
||||
playerhistory.remove(0);
|
||||
};
|
||||
|
@ -316,41 +429,62 @@ void demoplaybackstep()
|
|||
readdemotime();
|
||||
};
|
||||
|
||||
if(demoplayback)
|
||||
{
|
||||
int itime = lastmillis-demodelaymsec;
|
||||
loopvrev(playerhistory) if(playerhistory[i]->lastupdate<itime) // find 2 positions in history that surround interpolation time point
|
||||
if (demoplayback) {
|
||||
int itime = lastmillis - demodelaymsec;
|
||||
loopvrev(playerhistory) if (playerhistory[i]->lastupdate <
|
||||
itime) // find 2 positions in
|
||||
// history that surround
|
||||
// interpolation time point
|
||||
{
|
||||
dynent *a = playerhistory[i];
|
||||
dynent *b = a;
|
||||
if(i+1<playerhistory.length()) b = playerhistory[i+1];
|
||||
if (i + 1 < playerhistory.length())
|
||||
b = playerhistory[i + 1];
|
||||
*player1 = *b;
|
||||
if(a!=b) // interpolate pos & angles
|
||||
if (a != b) // interpolate pos & angles
|
||||
{
|
||||
dynent *c = b;
|
||||
if(i+2<playerhistory.length()) c = playerhistory[i+2];
|
||||
if (i + 2 < playerhistory.length())
|
||||
c = playerhistory[i + 2];
|
||||
dynent *z = a;
|
||||
if(i-1>=0) z = playerhistory[i-1];
|
||||
//if(a==z || b==c) printf("* %d\n", lastmillis);
|
||||
float bf = (itime-a->lastupdate)/(float)(b->lastupdate-a->lastupdate);
|
||||
if (i - 1 >= 0)
|
||||
z = playerhistory[i - 1];
|
||||
// if(a==z || b==c) printf("* %d\n",
|
||||
// lastmillis);
|
||||
float bf =
|
||||
(itime - a->lastupdate) /
|
||||
(float)(b->lastupdate - a->lastupdate);
|
||||
fixwrap(a, player1);
|
||||
fixwrap(c, player1);
|
||||
fixwrap(z, player1);
|
||||
vdist(dist, v, z->o, c->o);
|
||||
if(dist<16) // if teleport or spawn, dont't interpolate
|
||||
if (dist < 16) // if teleport or spawn, dont't
|
||||
// interpolate
|
||||
{
|
||||
catmulrom(z->o, a->o, b->o, c->o, bf, player1->o);
|
||||
catmulrom(*(vec *)&z->yaw, *(vec *)&a->yaw, *(vec *)&b->yaw, *(vec *)&c->yaw, bf, *(vec *)&player1->yaw);
|
||||
catmulrom(z->o, a->o, b->o, c->o, bf,
|
||||
player1->o);
|
||||
catmulrom(*(vec *)&z->yaw,
|
||||
*(vec *)&a->yaw, *(vec *)&b->yaw,
|
||||
*(vec *)&c->yaw, bf,
|
||||
*(vec *)&player1->yaw);
|
||||
};
|
||||
fixplayer1range();
|
||||
};
|
||||
break;
|
||||
};
|
||||
//if(player1->state!=CS_DEAD) showscores(false);
|
||||
// if(player1->state!=CS_DEAD) showscores(false);
|
||||
};
|
||||
};
|
||||
|
||||
void stopn() { if(demoplayback) stopreset(); else stop(); conoutf("demo stopped"); };
|
||||
void
|
||||
stopn()
|
||||
{
|
||||
if (demoplayback)
|
||||
stopreset();
|
||||
else
|
||||
stop();
|
||||
conoutf("demo stopped");
|
||||
};
|
||||
|
||||
COMMAND(record, ARG_1STR);
|
||||
COMMAND(demo, ARG_1STR);
|
||||
|
|
376
src/server.cxx
376
src/server.cxx
|
@ -28,10 +28,13 @@ struct server_entity // server side version of "entity" type
|
|||
|
||||
vector<server_entity> sents;
|
||||
|
||||
bool notgotitems = true; // true when map has changed and waiting for clients to send item
|
||||
bool notgotitems =
|
||||
true; // true when map has changed and waiting for clients to send item
|
||||
int mode = 0;
|
||||
|
||||
void restoreserverstate(vector<entity> &ents) // hack: called from savegame code, only works in SP
|
||||
void
|
||||
restoreserverstate(
|
||||
vector<entity> &ents) // hack: called from savegame code, only works in SP
|
||||
{
|
||||
loopv(sents)
|
||||
{
|
||||
|
@ -46,7 +49,7 @@ bool mapreload = false;
|
|||
char *serverpassword = "";
|
||||
|
||||
bool isdedicated;
|
||||
ENetHost * serverhost = NULL;
|
||||
ENetHost *serverhost = NULL;
|
||||
int bsend = 0, brec = 0, laststatus = 0, lastsec = 0;
|
||||
|
||||
#define MAXOBUF 100000
|
||||
|
@ -55,13 +58,13 @@ void process(ENetPacket *packet, int sender);
|
|||
void multicast(ENetPacket *packet, int sender);
|
||||
void disconnect_client(int n, char *reason);
|
||||
|
||||
void send(int n, ENetPacket *packet)
|
||||
void
|
||||
send(int n, ENetPacket *packet)
|
||||
{
|
||||
if(!packet) return;
|
||||
switch(clients[n].type)
|
||||
{
|
||||
case ST_TCPIP:
|
||||
{
|
||||
if (!packet)
|
||||
return;
|
||||
switch (clients[n].type) {
|
||||
case ST_TCPIP: {
|
||||
enet_peer_send(clients[n].peer, 0, packet);
|
||||
bsend += packet->dataLength;
|
||||
break;
|
||||
|
@ -70,38 +73,46 @@ void send(int n, ENetPacket *packet)
|
|||
case ST_LOCAL:
|
||||
localservertoclient(packet->data, packet->dataLength);
|
||||
break;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
void send2(bool rel, int cn, int a, int b)
|
||||
void
|
||||
send2(bool rel, int cn, int a, int b)
|
||||
{
|
||||
ENetPacket *packet = enet_packet_create(NULL, 32, rel ? ENET_PACKET_FLAG_RELIABLE : 0);
|
||||
ENetPacket *packet =
|
||||
enet_packet_create(NULL, 32, rel ? ENET_PACKET_FLAG_RELIABLE : 0);
|
||||
uchar *start = packet->data;
|
||||
uchar *p = start+2;
|
||||
uchar *p = start + 2;
|
||||
putint(p, a);
|
||||
putint(p, b);
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p-start);
|
||||
enet_packet_resize(packet, p-start);
|
||||
if(cn<0) process(packet, -1);
|
||||
else send(cn, packet);
|
||||
if(packet->referenceCount==0) enet_packet_destroy(packet);
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
|
||||
enet_packet_resize(packet, p - start);
|
||||
if (cn < 0)
|
||||
process(packet, -1);
|
||||
else
|
||||
send(cn, packet);
|
||||
if (packet->referenceCount == 0)
|
||||
enet_packet_destroy(packet);
|
||||
};
|
||||
|
||||
void sendservmsg(char *msg)
|
||||
void
|
||||
sendservmsg(char *msg)
|
||||
{
|
||||
ENetPacket *packet = enet_packet_create(NULL, _MAXDEFSTR+10, ENET_PACKET_FLAG_RELIABLE);
|
||||
ENetPacket *packet = enet_packet_create(
|
||||
NULL, _MAXDEFSTR + 10, ENET_PACKET_FLAG_RELIABLE);
|
||||
uchar *start = packet->data;
|
||||
uchar *p = start+2;
|
||||
uchar *p = start + 2;
|
||||
putint(p, SV_SERVMSG);
|
||||
sendstring(msg, p);
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p-start);
|
||||
enet_packet_resize(packet, p-start);
|
||||
*(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);
|
||||
if (packet->referenceCount == 0)
|
||||
enet_packet_destroy(packet);
|
||||
};
|
||||
|
||||
void disconnect_client(int n, char *reason)
|
||||
void
|
||||
disconnect_client(int n, char *reason)
|
||||
{
|
||||
printf("disconnecting client (%s) [%s]\n", clients[n].hostname, reason);
|
||||
enet_peer_disconnect(clients[n].peer);
|
||||
|
@ -109,61 +120,81 @@ void disconnect_client(int n, char *reason)
|
|||
send2(true, -1, SV_CDIS, n);
|
||||
};
|
||||
|
||||
void resetitems() { sents.setsize(0); notgotitems = true; };
|
||||
|
||||
void pickup(uint i, int sec, int sender) // server side item pickup, acknowledge first client that gets it
|
||||
void
|
||||
resetitems()
|
||||
{
|
||||
if(i>=(uint)sents.length()) return;
|
||||
if(sents[i].spawned)
|
||||
{
|
||||
sents.setsize(0);
|
||||
notgotitems = true;
|
||||
};
|
||||
|
||||
void
|
||||
pickup(uint i, int sec, int sender) // server side item pickup, acknowledge
|
||||
// first client that gets it
|
||||
{
|
||||
if (i >= (uint)sents.length())
|
||||
return;
|
||||
if (sents[i].spawned) {
|
||||
sents[i].spawned = false;
|
||||
sents[i].spawnsecs = sec;
|
||||
send2(true, sender, SV_ITEMACC, i);
|
||||
};
|
||||
};
|
||||
|
||||
void resetvotes()
|
||||
void
|
||||
resetvotes()
|
||||
{
|
||||
loopv(clients) clients[i].mapvote[0] = 0;
|
||||
};
|
||||
|
||||
bool vote(char *map, int reqmode, int sender)
|
||||
bool
|
||||
vote(char *map, int reqmode, int sender)
|
||||
{
|
||||
strcpy_s(clients[sender].mapvote, map);
|
||||
clients[sender].modevote = reqmode;
|
||||
int yes = 0, no = 0;
|
||||
loopv(clients) if(clients[i].type!=ST_EMPTY)
|
||||
loopv(clients) if (clients[i].type != ST_EMPTY)
|
||||
{
|
||||
if(clients[i].mapvote[0]) { if(strcmp(clients[i].mapvote, map)==0 && clients[i].modevote==reqmode) yes++; else no++; }
|
||||
else no++;
|
||||
if (clients[i].mapvote[0]) {
|
||||
if (strcmp(clients[i].mapvote, map) == 0 &&
|
||||
clients[i].modevote == reqmode)
|
||||
yes++;
|
||||
else
|
||||
no++;
|
||||
} else
|
||||
no++;
|
||||
};
|
||||
if(yes==1 && no==0) return true; // single player
|
||||
sprintf_sd(msg)("%s suggests %s on map %s (set map to vote)", clients[sender].name, modestr(reqmode), map);
|
||||
if (yes == 1 && no == 0)
|
||||
return true; // single player
|
||||
sprintf_sd(msg)("%s suggests %s on map %s (set map to vote)",
|
||||
clients[sender].name, modestr(reqmode), map);
|
||||
sendservmsg(msg);
|
||||
if(yes/(float)(yes+no) <= 0.5f) return false;
|
||||
if (yes / (float)(yes + no) <= 0.5f)
|
||||
return false;
|
||||
sendservmsg("vote passed");
|
||||
resetvotes();
|
||||
return true;
|
||||
};
|
||||
|
||||
// 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)
|
||||
// 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)
|
||||
|
||||
void process(ENetPacket * packet, int sender) // sender may be -1
|
||||
void
|
||||
process(ENetPacket *packet, int sender) // sender may be -1
|
||||
{
|
||||
if(ENET_NET_TO_HOST_16(*(ushort *)packet->data)!=packet->dataLength)
|
||||
{
|
||||
if (ENET_NET_TO_HOST_16(*(ushort *)packet->data) !=
|
||||
packet->dataLength) {
|
||||
disconnect_client(sender, "packet length");
|
||||
return;
|
||||
};
|
||||
|
||||
uchar *end = packet->data+packet->dataLength;
|
||||
uchar *p = packet->data+2;
|
||||
uchar *end = packet->data + packet->dataLength;
|
||||
uchar *p = packet->data + 2;
|
||||
char text[MAXTRANS];
|
||||
int cn = -1, type;
|
||||
|
||||
while(p<end) switch(type = getint(p))
|
||||
{
|
||||
while (p < end)
|
||||
switch (type = getint(p)) {
|
||||
case SV_TEXT:
|
||||
sgetstr();
|
||||
break;
|
||||
|
@ -175,16 +206,18 @@ void process(ENetPacket * packet, int sender) // sender may be -1
|
|||
getint(p);
|
||||
break;
|
||||
|
||||
case SV_MAPCHANGE:
|
||||
{
|
||||
case SV_MAPCHANGE: {
|
||||
sgetstr();
|
||||
int reqmode = getint(p);
|
||||
if(reqmode<0) reqmode = 0;
|
||||
if(smapname[0] && !mapreload && !vote(text, reqmode, sender)) return;
|
||||
if (reqmode < 0)
|
||||
reqmode = 0;
|
||||
if (smapname[0] && !mapreload &&
|
||||
!vote(text, reqmode, sender))
|
||||
return;
|
||||
mapreload = false;
|
||||
mode = reqmode;
|
||||
minremain = mode&1 ? 15 : 10;
|
||||
mapend = lastsec+minremain*60;
|
||||
minremain = mode & 1 ? 15 : 10;
|
||||
mapend = lastsec + minremain * 60;
|
||||
interm = 0;
|
||||
strcpy_s(smapname, text);
|
||||
resetitems();
|
||||
|
@ -192,21 +225,20 @@ void process(ENetPacket * packet, int sender) // sender may be -1
|
|||
break;
|
||||
};
|
||||
|
||||
case SV_ITEMLIST:
|
||||
{
|
||||
case SV_ITEMLIST: {
|
||||
int n;
|
||||
while((n = getint(p))!=-1) if(notgotitems)
|
||||
{
|
||||
server_entity se = { false, 0 };
|
||||
while(sents.length()<=n) sents.add(se);
|
||||
while ((n = getint(p)) != -1)
|
||||
if (notgotitems) {
|
||||
server_entity se = {false, 0};
|
||||
while (sents.length() <= n)
|
||||
sents.add(se);
|
||||
sents[n].spawned = true;
|
||||
};
|
||||
notgotitems = false;
|
||||
break;
|
||||
};
|
||||
|
||||
case SV_ITEMPICKUP:
|
||||
{
|
||||
case SV_ITEMPICKUP: {
|
||||
int n = getint(p);
|
||||
pickup(n, getint(p), sender);
|
||||
break;
|
||||
|
@ -216,22 +248,20 @@ void process(ENetPacket * packet, int sender) // sender may be -1
|
|||
send2(false, cn, SV_PONG, getint(p));
|
||||
break;
|
||||
|
||||
case SV_POS:
|
||||
{
|
||||
case SV_POS: {
|
||||
cn = getint(p);
|
||||
if(cn<0 || cn>=clients.length() || clients[cn].type==ST_EMPTY)
|
||||
{
|
||||
if (cn < 0 || cn >= clients.length() ||
|
||||
clients[cn].type == ST_EMPTY) {
|
||||
disconnect_client(sender, "client num");
|
||||
return;
|
||||
};
|
||||
int size = msgsizelookup(type);
|
||||
assert(size!=-1);
|
||||
loopi(size-2) getint(p);
|
||||
assert(size != -1);
|
||||
loopi(size - 2) getint(p);
|
||||
break;
|
||||
};
|
||||
|
||||
case SV_SENDMAP:
|
||||
{
|
||||
case SV_SENDMAP: {
|
||||
sgetstr();
|
||||
int mapsize = getint(p);
|
||||
sendmaps(sender, text, mapsize, p);
|
||||
|
@ -242,85 +272,104 @@ void process(ENetPacket * packet, int sender) // sender may be -1
|
|||
send(sender, recvmap(sender));
|
||||
return;
|
||||
|
||||
case SV_EXT: // allows for new features that require no server updates
|
||||
case SV_EXT: // allows for new features that require no server
|
||||
// updates
|
||||
{
|
||||
for(int n = getint(p); n; n--) getint(p);
|
||||
for (int n = getint(p); n; n--)
|
||||
getint(p);
|
||||
break;
|
||||
};
|
||||
|
||||
default:
|
||||
{
|
||||
default: {
|
||||
int size = msgsizelookup(type);
|
||||
if(size==-1) { disconnect_client(sender, "tag type"); return; };
|
||||
loopi(size-1) getint(p);
|
||||
if (size == -1) {
|
||||
disconnect_client(sender, "tag type");
|
||||
return;
|
||||
};
|
||||
loopi(size - 1) getint(p);
|
||||
};
|
||||
};
|
||||
|
||||
if(p>end) { disconnect_client(sender, "end of packet"); return; };
|
||||
if (p > end) {
|
||||
disconnect_client(sender, "end of packet");
|
||||
return;
|
||||
};
|
||||
multicast(packet, sender);
|
||||
};
|
||||
|
||||
void send_welcome(int n)
|
||||
void
|
||||
send_welcome(int n)
|
||||
{
|
||||
ENetPacket * packet = enet_packet_create (NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
|
||||
ENetPacket *packet =
|
||||
enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
|
||||
uchar *start = packet->data;
|
||||
uchar *p = start+2;
|
||||
uchar *p = start + 2;
|
||||
putint(p, SV_INITS2C);
|
||||
putint(p, n);
|
||||
putint(p, PROTOCOL_VERSION);
|
||||
putint(p, smapname[0]);
|
||||
sendstring(serverpassword, p);
|
||||
putint(p, clients.length()>maxclients);
|
||||
if(smapname[0])
|
||||
{
|
||||
putint(p, clients.length() > maxclients);
|
||||
if (smapname[0]) {
|
||||
putint(p, SV_MAPCHANGE);
|
||||
sendstring(smapname, p);
|
||||
putint(p, mode);
|
||||
putint(p, SV_ITEMLIST);
|
||||
loopv(sents) if(sents[i].spawned) putint(p, i);
|
||||
loopv(sents) if (sents[i].spawned) putint(p, i);
|
||||
putint(p, -1);
|
||||
};
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p-start);
|
||||
enet_packet_resize(packet, p-start);
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
|
||||
enet_packet_resize(packet, p - start);
|
||||
send(n, packet);
|
||||
};
|
||||
|
||||
void multicast(ENetPacket *packet, int sender)
|
||||
void
|
||||
multicast(ENetPacket *packet, int sender)
|
||||
{
|
||||
loopv(clients)
|
||||
{
|
||||
if(i==sender) continue;
|
||||
if (i == sender)
|
||||
continue;
|
||||
send(i, packet);
|
||||
};
|
||||
};
|
||||
|
||||
void localclienttoserver(ENetPacket *packet)
|
||||
void
|
||||
localclienttoserver(ENetPacket *packet)
|
||||
{
|
||||
process(packet, 0);
|
||||
if(!packet->referenceCount) enet_packet_destroy (packet);
|
||||
if (!packet->referenceCount)
|
||||
enet_packet_destroy(packet);
|
||||
};
|
||||
|
||||
client &addclient()
|
||||
client &
|
||||
addclient()
|
||||
{
|
||||
loopv(clients) if(clients[i].type==ST_EMPTY) return clients[i];
|
||||
loopv(clients) if (clients[i].type == ST_EMPTY) return clients[i];
|
||||
return clients.add();
|
||||
};
|
||||
|
||||
void checkintermission()
|
||||
void
|
||||
checkintermission()
|
||||
{
|
||||
if(!minremain)
|
||||
{
|
||||
interm = lastsec+10;
|
||||
mapend = lastsec+1000;
|
||||
if (!minremain) {
|
||||
interm = lastsec + 10;
|
||||
mapend = lastsec + 1000;
|
||||
};
|
||||
send2(true, -1, SV_TIMEUP, minremain--);
|
||||
};
|
||||
|
||||
void startintermission() { minremain = 0; checkintermission(); };
|
||||
|
||||
void resetserverifempty()
|
||||
void
|
||||
startintermission()
|
||||
{
|
||||
loopv(clients) if(clients[i].type!=ST_EMPTY) return;
|
||||
minremain = 0;
|
||||
checkintermission();
|
||||
};
|
||||
|
||||
void
|
||||
resetserverifempty()
|
||||
{
|
||||
loopv(clients) if (clients[i].type != ST_EMPTY) return;
|
||||
clients.setsize(0);
|
||||
smapname[0] = 0;
|
||||
resetvotes();
|
||||
|
@ -328,19 +377,22 @@ void resetserverifempty()
|
|||
mode = 0;
|
||||
mapreload = false;
|
||||
minremain = 10;
|
||||
mapend = lastsec+minremain*60;
|
||||
mapend = lastsec + minremain * 60;
|
||||
interm = 0;
|
||||
};
|
||||
|
||||
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
|
||||
void
|
||||
serverslice(int seconds,
|
||||
unsigned int timeout) // main server update, called from cube main loop in
|
||||
// sp, or dedicated server loop
|
||||
{
|
||||
loopv(sents) // spawn entities when timer reached
|
||||
{
|
||||
if(sents[i].spawnsecs && (sents[i].spawnsecs -= seconds-lastsec)<=0)
|
||||
{
|
||||
if (sents[i].spawnsecs &&
|
||||
(sents[i].spawnsecs -= seconds - lastsec) <= 0) {
|
||||
sents[i].spawnsecs = 0;
|
||||
sents[i].spawned = true;
|
||||
send2(true, -1, SV_ITEMSPAWN, i);
|
||||
|
@ -349,13 +401,15 @@ void serverslice(int seconds, unsigned int timeout) // main server update, cal
|
|||
|
||||
lastsec = seconds;
|
||||
|
||||
if((mode>1 || (mode==0 && nonlocalclients)) && seconds>mapend-minremain*60) checkintermission();
|
||||
if(interm && seconds>interm)
|
||||
{
|
||||
if ((mode > 1 || (mode == 0 && nonlocalclients)) &&
|
||||
seconds > mapend - minremain * 60)
|
||||
checkintermission();
|
||||
if (interm && seconds > interm) {
|
||||
interm = 0;
|
||||
loopv(clients) if(clients[i].type!=ST_EMPTY)
|
||||
loopv(clients) if (clients[i].type != ST_EMPTY)
|
||||
{
|
||||
send2(true, i, SV_MAPRELOAD, 0); // ask a client to trigger map reload
|
||||
send2(true, i, SV_MAPRELOAD,
|
||||
0); // ask a client to trigger map reload
|
||||
mapreload = true;
|
||||
break;
|
||||
};
|
||||
|
@ -363,106 +417,128 @@ void serverslice(int seconds, unsigned int timeout) // main server update, cal
|
|||
|
||||
resetserverifempty();
|
||||
|
||||
if(!isdedicated) 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, clients.length()>=maxclients);
|
||||
loopv(clients) if (clients[i].type != ST_EMPTY)++ numplayers;
|
||||
serverms(mode, numplayers, minremain, smapname, seconds,
|
||||
clients.length() >= maxclients);
|
||||
|
||||
if(seconds-laststatus>60) // display bandwidth stats, useful for server ops
|
||||
if (seconds - laststatus >
|
||||
60) // display bandwidth stats, useful for server ops
|
||||
{
|
||||
nonlocalclients = 0;
|
||||
loopv(clients) if(clients[i].type==ST_TCPIP) nonlocalclients++;
|
||||
loopv(clients) if (clients[i].type == ST_TCPIP)
|
||||
nonlocalclients++;
|
||||
laststatus = seconds;
|
||||
if(nonlocalclients || bsend || brec) printf("status: %d remote clients, %.1f send, %.1f rec (K/sec)\n", nonlocalclients, bsend/60.0f/1024, brec/60.0f/1024);
|
||||
if (nonlocalclients || bsend || brec)
|
||||
printf("status: %d remote clients, %.1f send, %.1f rec "
|
||||
"(K/sec)\n",
|
||||
nonlocalclients, bsend / 60.0f / 1024,
|
||||
brec / 60.0f / 1024);
|
||||
bsend = brec = 0;
|
||||
};
|
||||
|
||||
ENetEvent event;
|
||||
if(enet_host_service(serverhost, &event, timeout) > 0)
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
{
|
||||
if (enet_host_service(serverhost, &event, timeout) > 0) {
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_CONNECT: {
|
||||
client &c = addclient();
|
||||
c.type = ST_TCPIP;
|
||||
c.peer = event.peer;
|
||||
c.peer->data = (void *)(&c-&clients[0]);
|
||||
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");
|
||||
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]);
|
||||
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);
|
||||
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);
|
||||
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)
|
||||
{
|
||||
if (numplayers > maxclients) {
|
||||
disconnect_client(lastconnect, "maxclients reached");
|
||||
};
|
||||
};
|
||||
#ifndef _WIN32
|
||||
#ifndef _WIN32
|
||||
fflush(stdout);
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
void cleanupserver()
|
||||
void
|
||||
cleanupserver()
|
||||
{
|
||||
if(serverhost) enet_host_destroy(serverhost);
|
||||
if (serverhost)
|
||||
enet_host_destroy(serverhost);
|
||||
};
|
||||
|
||||
void localdisconnect()
|
||||
void
|
||||
localdisconnect()
|
||||
{
|
||||
loopv(clients) if(clients[i].type==ST_LOCAL) clients[i].type = ST_EMPTY;
|
||||
loopv(clients) if (clients[i].type == ST_LOCAL) clients[i].type =
|
||||
ST_EMPTY;
|
||||
};
|
||||
|
||||
void localconnect()
|
||||
void
|
||||
localconnect()
|
||||
{
|
||||
client &c = addclient();
|
||||
c.type = ST_LOCAL;
|
||||
strcpy_s(c.hostname, "local");
|
||||
send_welcome(&c-&clients[0]);
|
||||
send_welcome(&c - &clients[0]);
|
||||
};
|
||||
|
||||
void initserver(bool dedicated, int uprate, char *sdesc, char *ip, char *master, char *passwd, int maxcl)
|
||||
void
|
||||
initserver(bool dedicated, int uprate, char *sdesc, char *ip, char *master,
|
||||
char *passwd, int maxcl)
|
||||
{
|
||||
serverpassword = passwd;
|
||||
maxclients = maxcl;
|
||||
servermsinit(master ? master : "wouter.fov120.com/cube/masterserver/", sdesc, dedicated);
|
||||
servermsinit(master ? master : "wouter.fov120.com/cube/masterserver/",
|
||||
sdesc, dedicated);
|
||||
|
||||
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");
|
||||
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");
|
||||
if (!serverhost)
|
||||
fatal("could not create server host\n");
|
||||
loopi(MAXCLIENTS) serverhost->peers[i].data = (void *)-1;
|
||||
};
|
||||
|
||||
resetserverifempty();
|
||||
|
||||
if(isdedicated) // do not return, this becomes main loop
|
||||
if (isdedicated) // do not return, this becomes main loop
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
|
||||
#endif
|
||||
printf("dedicated server started, waiting for clients...\nCtrl-C to exit\n\n");
|
||||
#endif
|
||||
printf("dedicated server started, waiting for "
|
||||
"clients...\nCtrl-C to exit\n\n");
|
||||
atexit(cleanupserver);
|
||||
atexit(enet_deinitialize);
|
||||
for(;;) serverslice(/*enet_time_get_sec()*/time(NULL), 5);
|
||||
for (;;)
|
||||
serverslice(/*enet_time_get_sec()*/ time(NULL), 5);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
// serverbrowser.cpp: eihrul's concurrent resolver, and server browser window management
|
||||
// serverbrowser.cpp: eihrul's concurrent resolver, and server browser window
|
||||
// management
|
||||
|
||||
#include "cube.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "cube.h"
|
||||
|
||||
struct resolverthread
|
||||
{
|
||||
struct resolverthread {
|
||||
SDL_Thread *thread;
|
||||
char *query;
|
||||
int starttime;
|
||||
};
|
||||
|
||||
struct resolverresult
|
||||
{
|
||||
struct resolverresult {
|
||||
char *query;
|
||||
ENetAddress address;
|
||||
};
|
||||
|
@ -23,22 +22,21 @@ SDL_mutex *resolvermutex;
|
|||
SDL_sem *resolversem;
|
||||
int resolverlimit = 1000;
|
||||
|
||||
int resolverloop(void * data)
|
||||
int
|
||||
resolverloop(void *data)
|
||||
{
|
||||
resolverthread *rt = (resolverthread *)data;
|
||||
for(;;)
|
||||
{
|
||||
for (;;) {
|
||||
SDL_SemWait(resolversem);
|
||||
SDL_LockMutex(resolvermutex);
|
||||
if(resolverqueries.empty())
|
||||
{
|
||||
if (resolverqueries.empty()) {
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
continue;
|
||||
}
|
||||
rt->query = resolverqueries.pop();
|
||||
rt->starttime = lastmillis;
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT };
|
||||
ENetAddress address = {ENET_HOST_ANY, CUBE_SERVINFO_PORT};
|
||||
enet_address_set_host(&address, rt->query);
|
||||
SDL_LockMutex(resolvermutex);
|
||||
resolverresult &rr = resolverresults.add();
|
||||
|
@ -51,14 +49,14 @@ int resolverloop(void * data)
|
|||
return 0;
|
||||
};
|
||||
|
||||
void resolverinit(int threads, int limit)
|
||||
void
|
||||
resolverinit(int threads, int limit)
|
||||
{
|
||||
resolverlimit = limit;
|
||||
resolversem = SDL_CreateSemaphore(0);
|
||||
resolvermutex = SDL_CreateMutex();
|
||||
|
||||
while(threads > 0)
|
||||
{
|
||||
while (threads > 0) {
|
||||
resolverthread &rt = resolverthreads.add();
|
||||
rt.query = NULL;
|
||||
rt.starttime = 0;
|
||||
|
@ -67,23 +65,27 @@ void resolverinit(int threads, int limit)
|
|||
};
|
||||
};
|
||||
|
||||
void resolverstop(resolverthread &rt, bool restart)
|
||||
void
|
||||
resolverstop(resolverthread &rt, bool restart)
|
||||
{
|
||||
SDL_LockMutex(resolvermutex);
|
||||
SDL_KillThread(rt.thread);
|
||||
rt.query = NULL;
|
||||
rt.starttime = 0;
|
||||
rt.thread = NULL;
|
||||
if(restart) rt.thread = SDL_CreateThread(resolverloop, &rt);
|
||||
if (restart)
|
||||
rt.thread = SDL_CreateThread(resolverloop, &rt);
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
};
|
||||
|
||||
void resolverclear()
|
||||
void
|
||||
resolverclear()
|
||||
{
|
||||
SDL_LockMutex(resolvermutex);
|
||||
resolverqueries.setsize(0);
|
||||
resolverresults.setsize(0);
|
||||
while (SDL_SemTryWait(resolversem) == 0);
|
||||
while (SDL_SemTryWait(resolversem) == 0)
|
||||
;
|
||||
loopv(resolverthreads)
|
||||
{
|
||||
resolverthread &rt = resolverthreads[i];
|
||||
|
@ -92,7 +94,8 @@ void resolverclear()
|
|||
SDL_UnlockMutex(resolvermutex);
|
||||
};
|
||||
|
||||
void resolverquery(char *name)
|
||||
void
|
||||
resolverquery(char *name)
|
||||
{
|
||||
SDL_LockMutex(resolvermutex);
|
||||
resolverqueries.add(name);
|
||||
|
@ -100,11 +103,11 @@ void resolverquery(char *name)
|
|||
SDL_UnlockMutex(resolvermutex);
|
||||
};
|
||||
|
||||
bool resolvercheck(char **name, ENetAddress *address)
|
||||
bool
|
||||
resolvercheck(char **name, ENetAddress *address)
|
||||
{
|
||||
SDL_LockMutex(resolvermutex);
|
||||
if(!resolverresults.empty())
|
||||
{
|
||||
if (!resolverresults.empty()) {
|
||||
resolverresult &rr = resolverresults.pop();
|
||||
*name = rr.query;
|
||||
*address = rr.address;
|
||||
|
@ -114,10 +117,8 @@ bool resolvercheck(char **name, ENetAddress *address)
|
|||
loopv(resolverthreads)
|
||||
{
|
||||
resolverthread &rt = resolverthreads[i];
|
||||
if(rt.query)
|
||||
{
|
||||
if(lastmillis - rt.starttime > resolverlimit)
|
||||
{
|
||||
if (rt.query) {
|
||||
if (lastmillis - rt.starttime > resolverlimit) {
|
||||
resolverstop(rt, true);
|
||||
*name = rt.query;
|
||||
SDL_UnlockMutex(resolvermutex);
|
||||
|
@ -129,8 +130,7 @@ bool resolvercheck(char **name, ENetAddress *address)
|
|||
return false;
|
||||
};
|
||||
|
||||
struct serverinfo
|
||||
{
|
||||
struct serverinfo {
|
||||
string name;
|
||||
string full;
|
||||
string map;
|
||||
|
@ -143,11 +143,16 @@ vector<serverinfo> servers;
|
|||
ENetSocket pingsock = ENET_SOCKET_NULL;
|
||||
int lastinfo = 0;
|
||||
|
||||
char *getservername(int n) { return servers[n].name; };
|
||||
|
||||
void addserver(char *servername)
|
||||
char *
|
||||
getservername(int n)
|
||||
{
|
||||
loopv(servers) if(strcmp(servers[i].name, servername)==0) return;
|
||||
return servers[n].name;
|
||||
};
|
||||
|
||||
void
|
||||
addserver(char *servername)
|
||||
{
|
||||
loopv(servers) if (strcmp(servers[i].name, servername) == 0) return;
|
||||
serverinfo &si = servers.insert(0, serverinfo());
|
||||
strcpy_s(si.name, servername);
|
||||
si.full[0] = 0;
|
||||
|
@ -162,7 +167,8 @@ void addserver(char *servername)
|
|||
si.address.port = CUBE_SERVINFO_PORT;
|
||||
};
|
||||
|
||||
void pingservers()
|
||||
void
|
||||
pingservers()
|
||||
{
|
||||
ENetBuffer buf;
|
||||
uchar ping[MAXTRANS];
|
||||
|
@ -170,7 +176,8 @@ void pingservers()
|
|||
loopv(servers)
|
||||
{
|
||||
serverinfo &si = servers[i];
|
||||
if(si.address.host == ENET_HOST_ANY) continue;
|
||||
if (si.address.host == ENET_HOST_ANY)
|
||||
continue;
|
||||
p = ping;
|
||||
putint(p, lastmillis);
|
||||
buf.data = ping;
|
||||
|
@ -180,18 +187,18 @@ void pingservers()
|
|||
lastinfo = lastmillis;
|
||||
};
|
||||
|
||||
void checkresolver()
|
||||
void
|
||||
checkresolver()
|
||||
{
|
||||
char *name = NULL;
|
||||
ENetAddress addr = { ENET_HOST_ANY, CUBE_SERVINFO_PORT };
|
||||
while(resolvercheck(&name, &addr))
|
||||
{
|
||||
if(addr.host == ENET_HOST_ANY) continue;
|
||||
ENetAddress addr = {ENET_HOST_ANY, CUBE_SERVINFO_PORT};
|
||||
while (resolvercheck(&name, &addr)) {
|
||||
if (addr.host == ENET_HOST_ANY)
|
||||
continue;
|
||||
loopv(servers)
|
||||
{
|
||||
serverinfo &si = servers[i];
|
||||
if(name == si.name)
|
||||
{
|
||||
if (name == si.name) {
|
||||
si.address = addr;
|
||||
addr.host = ENET_HOST_ANY;
|
||||
break;
|
||||
|
@ -200,7 +207,8 @@ void checkresolver()
|
|||
}
|
||||
}
|
||||
|
||||
void checkpings()
|
||||
void
|
||||
checkpings()
|
||||
{
|
||||
enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
|
||||
ENetBuffer buf;
|
||||
|
@ -209,18 +217,18 @@ void checkpings()
|
|||
char text[MAXTRANS];
|
||||
buf.data = ping;
|
||||
buf.dataLength = sizeof(ping);
|
||||
while(enet_socket_wait(pingsock, &events, 0) >= 0 && events)
|
||||
{
|
||||
if(enet_socket_receive(pingsock, &addr, &buf, 1) <= 0) return;
|
||||
while (enet_socket_wait(pingsock, &events, 0) >= 0 && events) {
|
||||
if (enet_socket_receive(pingsock, &addr, &buf, 1) <= 0)
|
||||
return;
|
||||
loopv(servers)
|
||||
{
|
||||
serverinfo &si = servers[i];
|
||||
if(addr.host == si.address.host)
|
||||
{
|
||||
if (addr.host == si.address.host) {
|
||||
p = ping;
|
||||
si.ping = lastmillis - getint(p);
|
||||
si.protocol = getint(p);
|
||||
if(si.protocol!=PROTOCOL_VERSION) si.ping = 9998;
|
||||
if (si.protocol != PROTOCOL_VERSION)
|
||||
si.ping = 9998;
|
||||
si.mode = getint(p);
|
||||
si.numplayers = getint(p);
|
||||
si.minremain = getint(p);
|
||||
|
@ -234,40 +242,53 @@ void checkpings()
|
|||
};
|
||||
};
|
||||
|
||||
int sicompare(const serverinfo *a, const serverinfo *b)
|
||||
int
|
||||
sicompare(const serverinfo *a, const serverinfo *b)
|
||||
{
|
||||
return a->ping>b->ping ? 1 : (a->ping<b->ping ? -1 : strcmp(a->name, b->name));
|
||||
return a->ping > b->ping
|
||||
? 1
|
||||
: (a->ping < b->ping ? -1 : strcmp(a->name, b->name));
|
||||
};
|
||||
|
||||
void refreshservers()
|
||||
void
|
||||
refreshservers()
|
||||
{
|
||||
checkresolver();
|
||||
checkpings();
|
||||
if(lastmillis - lastinfo >= 5000) pingservers();
|
||||
if (lastmillis - lastinfo >= 5000)
|
||||
pingservers();
|
||||
servers.sort((void *)sicompare);
|
||||
int maxmenu = 16;
|
||||
loopv(servers)
|
||||
{
|
||||
serverinfo &si = servers[i];
|
||||
if(si.address.host != ENET_HOST_ANY && si.ping != 9999)
|
||||
{
|
||||
if(si.protocol!=PROTOCOL_VERSION) sprintf_s(si.full)("%s [different cube protocol]", si.name);
|
||||
else sprintf_s(si.full)("%d\t%d\t%s, %s: %s %s", si.ping, si.numplayers, si.map[0] ? si.map : "[unknown]", modestr(si.mode), si.name, si.sdesc);
|
||||
}
|
||||
if (si.address.host != ENET_HOST_ANY && si.ping != 9999) {
|
||||
if (si.protocol != PROTOCOL_VERSION)
|
||||
sprintf_s(si.full)(
|
||||
"%s [different cube protocol]", si.name);
|
||||
else
|
||||
{
|
||||
sprintf_s(si.full)(si.address.host != ENET_HOST_ANY ? "%s [waiting for server response]" : "%s [unknown host]\t", si.name);
|
||||
sprintf_s(si.full)("%d\t%d\t%s, %s: %s %s",
|
||||
si.ping, si.numplayers,
|
||||
si.map[0] ? si.map : "[unknown]",
|
||||
modestr(si.mode), si.name, si.sdesc);
|
||||
} else {
|
||||
sprintf_s(si.full)(
|
||||
si.address.host != ENET_HOST_ANY
|
||||
? "%s [waiting for server response]"
|
||||
: "%s [unknown host]\t",
|
||||
si.name);
|
||||
}
|
||||
si.full[50] = 0; // cut off too long server descriptions
|
||||
menumanual(1, i, si.full);
|
||||
if(!--maxmenu) return;
|
||||
if (!--maxmenu)
|
||||
return;
|
||||
};
|
||||
};
|
||||
|
||||
void servermenu()
|
||||
void
|
||||
servermenu()
|
||||
{
|
||||
if(pingsock == ENET_SOCKET_NULL)
|
||||
{
|
||||
if (pingsock == ENET_SOCKET_NULL) {
|
||||
pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL);
|
||||
resolverinit(1, 1000);
|
||||
};
|
||||
|
@ -277,13 +298,19 @@ void servermenu()
|
|||
menuset(1);
|
||||
};
|
||||
|
||||
void updatefrommaster()
|
||||
void
|
||||
updatefrommaster()
|
||||
{
|
||||
const int MAXUPD = 32000;
|
||||
uchar buf[MAXUPD];
|
||||
uchar *reply = retrieveservers(buf, MAXUPD);
|
||||
if(!*reply || strstr((char *)reply, "<html>") || strstr((char *)reply, "<HTML>")) conoutf("master server not replying");
|
||||
else { servers.setsize(0); execute((char *)reply); };
|
||||
if (!*reply || strstr((char *)reply, "<html>") ||
|
||||
strstr((char *)reply, "<HTML>"))
|
||||
conoutf("master server not replying");
|
||||
else {
|
||||
servers.setsize(0);
|
||||
execute((char *)reply);
|
||||
};
|
||||
servermenu();
|
||||
};
|
||||
|
||||
|
@ -291,13 +318,13 @@ COMMAND(addserver, ARG_1STR);
|
|||
COMMAND(servermenu, ARG_NONE);
|
||||
COMMAND(updatefrommaster, ARG_NONE);
|
||||
|
||||
void writeservercfg()
|
||||
void
|
||||
writeservercfg()
|
||||
{
|
||||
FILE *f = fopen("servers.cfg", "w");
|
||||
if(!f) return;
|
||||
if (!f)
|
||||
return;
|
||||
fprintf(f, "// servers connected to are added here automatically\n\n");
|
||||
loopvrev(servers) fprintf(f, "addserver %s\n", servers[i].name);
|
||||
fclose(f);
|
||||
};
|
||||
|
||||
|
||||
|
|
111
src/serverms.cxx
111
src/serverms.cxx
|
@ -4,95 +4,117 @@
|
|||
|
||||
ENetSocket mssock = ENET_SOCKET_NULL;
|
||||
|
||||
void httpgetsend(ENetAddress &ad, char *hostname, char *req, char *ref, char *agent)
|
||||
void
|
||||
httpgetsend(ENetAddress &ad, char *hostname, char *req, char *ref, char *agent)
|
||||
{
|
||||
if(ad.host==ENET_HOST_ANY)
|
||||
{
|
||||
if (ad.host == ENET_HOST_ANY) {
|
||||
printf("looking up %s...\n", hostname);
|
||||
enet_address_set_host(&ad, hostname);
|
||||
if(ad.host==ENET_HOST_ANY) return;
|
||||
if (ad.host == ENET_HOST_ANY)
|
||||
return;
|
||||
};
|
||||
if(mssock!=ENET_SOCKET_NULL) enet_socket_destroy(mssock);
|
||||
if (mssock != ENET_SOCKET_NULL)
|
||||
enet_socket_destroy(mssock);
|
||||
mssock = enet_socket_create(ENET_SOCKET_TYPE_STREAM, NULL);
|
||||
if(mssock==ENET_SOCKET_NULL) { printf("could not open socket\n"); return; };
|
||||
if(enet_socket_connect(mssock, &ad)<0) { printf("could not connect\n"); return; };
|
||||
if (mssock == ENET_SOCKET_NULL) {
|
||||
printf("could not open socket\n");
|
||||
return;
|
||||
};
|
||||
if (enet_socket_connect(mssock, &ad) < 0) {
|
||||
printf("could not connect\n");
|
||||
return;
|
||||
};
|
||||
ENetBuffer buf;
|
||||
sprintf_sd(httpget)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req, hostname, ref, agent);
|
||||
sprintf_sd(httpget)(
|
||||
"GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req,
|
||||
hostname, ref, agent);
|
||||
buf.data = httpget;
|
||||
buf.dataLength = strlen((char *)buf.data);
|
||||
printf("sending request to %s...\n", hostname);
|
||||
enet_socket_send(mssock, NULL, &buf, 1);
|
||||
};
|
||||
|
||||
void httpgetrecieve(ENetBuffer &buf)
|
||||
void
|
||||
httpgetrecieve(ENetBuffer &buf)
|
||||
{
|
||||
if(mssock==ENET_SOCKET_NULL) return;
|
||||
if (mssock == ENET_SOCKET_NULL)
|
||||
return;
|
||||
enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
|
||||
if(enet_socket_wait(mssock, &events, 0) >= 0 && events)
|
||||
{
|
||||
if (enet_socket_wait(mssock, &events, 0) >= 0 && events) {
|
||||
int len = enet_socket_receive(mssock, NULL, &buf, 1);
|
||||
if(len<=0)
|
||||
{
|
||||
if (len <= 0) {
|
||||
enet_socket_destroy(mssock);
|
||||
mssock = ENET_SOCKET_NULL;
|
||||
return;
|
||||
};
|
||||
buf.data = ((char *)buf.data)+len;
|
||||
((char*)buf.data)[0] = 0;
|
||||
buf.data = ((char *)buf.data) + len;
|
||||
((char *)buf.data)[0] = 0;
|
||||
buf.dataLength -= len;
|
||||
};
|
||||
};
|
||||
|
||||
uchar *stripheader(uchar *b)
|
||||
uchar *
|
||||
stripheader(uchar *b)
|
||||
{
|
||||
char *s = strstr((char *)b, "\n\r\n");
|
||||
if(!s) s = strstr((char *)b, "\n\n");
|
||||
if (!s)
|
||||
s = strstr((char *)b, "\n\n");
|
||||
return s ? (uchar *)s : b;
|
||||
};
|
||||
|
||||
ENetAddress masterserver = { ENET_HOST_ANY, 80 };
|
||||
ENetAddress masterserver = {ENET_HOST_ANY, 80};
|
||||
int updmaster = 0;
|
||||
string masterbase;
|
||||
string masterpath;
|
||||
uchar masterrep[MAXTRANS];
|
||||
ENetBuffer masterb;
|
||||
|
||||
void updatemasterserver(int seconds)
|
||||
void
|
||||
updatemasterserver(int seconds)
|
||||
{
|
||||
if(seconds>updmaster) // send alive signal to masterserver every hour of uptime
|
||||
if (seconds >
|
||||
updmaster) // send alive signal to masterserver every hour of uptime
|
||||
{
|
||||
sprintf_sd(path)("%sregister.do?action=add", masterpath);
|
||||
httpgetsend(masterserver, masterbase, path, "cubeserver", "Cube Server");
|
||||
httpgetsend(masterserver, masterbase, path, "cubeserver",
|
||||
"Cube Server");
|
||||
masterrep[0] = 0;
|
||||
masterb.data = masterrep;
|
||||
masterb.dataLength = MAXTRANS-1;
|
||||
updmaster = seconds+60*60;
|
||||
masterb.dataLength = MAXTRANS - 1;
|
||||
updmaster = seconds + 60 * 60;
|
||||
};
|
||||
};
|
||||
|
||||
void checkmasterreply()
|
||||
void
|
||||
checkmasterreply()
|
||||
{
|
||||
bool busy = mssock!=ENET_SOCKET_NULL;
|
||||
bool busy = mssock != ENET_SOCKET_NULL;
|
||||
httpgetrecieve(masterb);
|
||||
if(busy && mssock==ENET_SOCKET_NULL) printf("masterserver reply: %s\n", stripheader(masterrep));
|
||||
if (busy && mssock == ENET_SOCKET_NULL)
|
||||
printf("masterserver reply: %s\n", stripheader(masterrep));
|
||||
};
|
||||
|
||||
uchar *retrieveservers(uchar *buf, int buflen)
|
||||
uchar *
|
||||
retrieveservers(uchar *buf, int buflen)
|
||||
{
|
||||
sprintf_sd(path)("%sretrieve.do?item=list", masterpath);
|
||||
httpgetsend(masterserver, masterbase, path, "cubeserver", "Cube Server");
|
||||
httpgetsend(
|
||||
masterserver, masterbase, path, "cubeserver", "Cube Server");
|
||||
ENetBuffer eb;
|
||||
buf[0] = 0;
|
||||
eb.data = buf;
|
||||
eb.dataLength = buflen-1;
|
||||
while(mssock!=ENET_SOCKET_NULL) httpgetrecieve(eb);
|
||||
eb.dataLength = buflen - 1;
|
||||
while (mssock != ENET_SOCKET_NULL)
|
||||
httpgetrecieve(eb);
|
||||
return stripheader(buf);
|
||||
};
|
||||
|
||||
ENetSocket pongsock = ENET_SOCKET_NULL;
|
||||
string serverdesc;
|
||||
|
||||
void serverms(int mode, int numplayers, int minremain, char *smapname, int seconds, bool isfull)
|
||||
void
|
||||
serverms(int mode, int numplayers, int minremain, char *smapname, int seconds,
|
||||
bool isfull)
|
||||
{
|
||||
checkmasterreply();
|
||||
updatemasterserver(seconds);
|
||||
|
@ -104,11 +126,11 @@ void serverms(int mode, int numplayers, int minremain, char *smapname, int secon
|
|||
int len;
|
||||
enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
|
||||
buf.data = pong;
|
||||
while(enet_socket_wait(pongsock, &events, 0) >= 0 && events)
|
||||
{
|
||||
while (enet_socket_wait(pongsock, &events, 0) >= 0 && events) {
|
||||
buf.dataLength = sizeof(pong);
|
||||
len = enet_socket_receive(pongsock, &addr, &buf, 1);
|
||||
if(len < 0) return;
|
||||
if (len < 0)
|
||||
return;
|
||||
p = &pong[len];
|
||||
putint(p, PROTOCOL_VERSION);
|
||||
putint(p, mode);
|
||||
|
@ -124,18 +146,21 @@ void serverms(int mode, int numplayers, int minremain, char *smapname, int secon
|
|||
};
|
||||
};
|
||||
|
||||
void servermsinit(const char *master, char *sdesc, bool listen)
|
||||
void
|
||||
servermsinit(const char *master, char *sdesc, bool listen)
|
||||
{
|
||||
const char *mid = strstr(master, "/");
|
||||
if(!mid) mid = master;
|
||||
if (!mid)
|
||||
mid = master;
|
||||
strcpy_s(masterpath, mid);
|
||||
strn0cpy(masterbase, master, mid-master+1);
|
||||
strn0cpy(masterbase, master, mid - master + 1);
|
||||
strcpy_s(serverdesc, sdesc);
|
||||
|
||||
if(listen)
|
||||
{
|
||||
ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT };
|
||||
pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, &address);
|
||||
if(pongsock == ENET_SOCKET_NULL) fatal("could not create server info socket\n");
|
||||
if (listen) {
|
||||
ENetAddress address = {ENET_HOST_ANY, CUBE_SERVINFO_PORT};
|
||||
pongsock =
|
||||
enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, &address);
|
||||
if (pongsock == ENET_SOCKET_NULL)
|
||||
fatal("could not create server info socket\n");
|
||||
};
|
||||
};
|
||||
|
|
|
@ -2,55 +2,93 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
// all network traffic is in 32bit ints, which are then compressed using the following simple scheme (assumes that most values are small).
|
||||
// all network traffic is in 32bit ints, which are then compressed using the
|
||||
// following simple scheme (assumes that most values are small).
|
||||
|
||||
void putint(uchar *&p, int n)
|
||||
void
|
||||
putint(uchar *&p, int n)
|
||||
{
|
||||
if(n<128 && n>-127) { *p++ = n; }
|
||||
else if(n<0x8000 && n>=-0x8000) { *p++ = 0x80; *p++ = n; *p++ = n>>8; }
|
||||
else { *p++ = 0x81; *p++ = n; *p++ = n>>8; *p++ = n>>16; *p++ = n>>24; };
|
||||
if (n < 128 && n > -127) {
|
||||
*p++ = n;
|
||||
} else if (n < 0x8000 && n >= -0x8000) {
|
||||
*p++ = 0x80;
|
||||
*p++ = n;
|
||||
*p++ = n >> 8;
|
||||
} else {
|
||||
*p++ = 0x81;
|
||||
*p++ = n;
|
||||
*p++ = n >> 8;
|
||||
*p++ = n >> 16;
|
||||
*p++ = n >> 24;
|
||||
};
|
||||
};
|
||||
|
||||
int getint(uchar *&p)
|
||||
int
|
||||
getint(uchar *&p)
|
||||
{
|
||||
int c = *((char *)p);
|
||||
p++;
|
||||
if(c==-128) { int n = *p++; n |= *((char *)p)<<8; p++; return n;}
|
||||
else if(c==-127) { int n = *p++; n |= *p++<<8; n |= *p++<<16; return n|(*p++<<24); }
|
||||
else return c;
|
||||
if (c == -128) {
|
||||
int n = *p++;
|
||||
n |= *((char *)p) << 8;
|
||||
p++;
|
||||
return n;
|
||||
} else if (c == -127) {
|
||||
int n = *p++;
|
||||
n |= *p++ << 8;
|
||||
n |= *p++ << 16;
|
||||
return n | (*p++ << 24);
|
||||
} else
|
||||
return c;
|
||||
};
|
||||
|
||||
void sendstring(char *t, uchar *&p)
|
||||
void
|
||||
sendstring(char *t, uchar *&p)
|
||||
{
|
||||
while(*t) putint(p, *t++);
|
||||
while (*t)
|
||||
putint(p, *t++);
|
||||
putint(p, 0);
|
||||
};
|
||||
|
||||
const char *modenames[] =
|
||||
{
|
||||
"SP", "DMSP", "ffa/default", "coopedit", "ffa/duel", "teamplay",
|
||||
"instagib", "instagib team", "efficiency", "efficiency team",
|
||||
"insta arena", "insta clan arena", "tactics arena", "tactics clan arena",
|
||||
const char *modenames[] = {
|
||||
"SP",
|
||||
"DMSP",
|
||||
"ffa/default",
|
||||
"coopedit",
|
||||
"ffa/duel",
|
||||
"teamplay",
|
||||
"instagib",
|
||||
"instagib team",
|
||||
"efficiency",
|
||||
"efficiency team",
|
||||
"insta arena",
|
||||
"insta clan arena",
|
||||
"tactics arena",
|
||||
"tactics clan arena",
|
||||
};
|
||||
|
||||
const char *modestr(int n) { return (n>=-2 && n<12) ? modenames[n+2] : "unknown"; };
|
||||
|
||||
char msgsizesl[] = // size inclusive message token, 0 for variable or not-checked sizes
|
||||
const char *
|
||||
modestr(int n)
|
||||
{
|
||||
SV_INITS2C, 4, SV_INITC2S, 0, SV_POS, 12, SV_TEXT, 0, SV_SOUND, 2, SV_CDIS, 2,
|
||||
SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6,
|
||||
SV_DIED, 2, SV_DAMAGE, 4, SV_SHOT, 8, SV_FRAGS, 2,
|
||||
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_TIMEUP, 2, SV_EDITENT, 10, SV_MAPRELOAD, 2, SV_ITEMACC, 2,
|
||||
SV_SENDMAP, 0, SV_RECVMAP, 1, SV_SERVMSG, 0, SV_ITEMLIST, 0,
|
||||
SV_EXT, 0,
|
||||
-1
|
||||
return (n >= -2 && n < 12) ? modenames[n + 2] : "unknown";
|
||||
};
|
||||
|
||||
char msgsizelookup(int msg)
|
||||
char msgsizesl[] = // size inclusive message token, 0 for variable or
|
||||
// not-checked sizes
|
||||
{SV_INITS2C, 4, SV_INITC2S, 0, SV_POS, 12, SV_TEXT, 0, SV_SOUND, 2, SV_CDIS,
|
||||
2, SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6,
|
||||
SV_DIED, 2, SV_DAMAGE, 4, SV_SHOT, 8, SV_FRAGS, 2, 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_TIMEUP, 2, SV_EDITENT, 10,
|
||||
SV_MAPRELOAD, 2, SV_ITEMACC, 2, SV_SENDMAP, 0, SV_RECVMAP, 1,
|
||||
SV_SERVMSG, 0, SV_ITEMLIST, 0, SV_EXT, 0, -1};
|
||||
|
||||
char
|
||||
msgsizelookup(int msg)
|
||||
{
|
||||
for(char *p = msgsizesl; *p>=0; p += 2) if(*p==msg) return p[1];
|
||||
for (char *p = msgsizesl; *p >= 0; p += 2)
|
||||
if (*p == msg)
|
||||
return p[1];
|
||||
return -1;
|
||||
};
|
||||
|
||||
|
@ -60,64 +98,93 @@ string copyname;
|
|||
int copysize;
|
||||
uchar *copydata = NULL;
|
||||
|
||||
void sendmaps(int n, string mapname, int mapsize, uchar *mapdata)
|
||||
void
|
||||
sendmaps(int n, string mapname, int mapsize, uchar *mapdata)
|
||||
{
|
||||
if(mapsize <= 0 || mapsize > 256*256) return;
|
||||
if (mapsize <= 0 || mapsize > 256 * 256)
|
||||
return;
|
||||
strcpy_s(copyname, mapname);
|
||||
copysize = mapsize;
|
||||
if(copydata) free(copydata);
|
||||
if (copydata)
|
||||
free(copydata);
|
||||
copydata = (uchar *)alloc(mapsize);
|
||||
memcpy(copydata, mapdata, mapsize);
|
||||
}
|
||||
|
||||
ENetPacket *recvmap(int n)
|
||||
ENetPacket *
|
||||
recvmap(int n)
|
||||
{
|
||||
if(!copydata) return NULL;
|
||||
ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + copysize, ENET_PACKET_FLAG_RELIABLE);
|
||||
if (!copydata)
|
||||
return NULL;
|
||||
ENetPacket *packet = enet_packet_create(
|
||||
NULL, MAXTRANS + copysize, ENET_PACKET_FLAG_RELIABLE);
|
||||
uchar *start = packet->data;
|
||||
uchar *p = start+2;
|
||||
uchar *p = start + 2;
|
||||
putint(p, SV_RECVMAP);
|
||||
sendstring(copyname, p);
|
||||
putint(p, copysize);
|
||||
memcpy(p, copydata, copysize);
|
||||
p += copysize;
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p-start);
|
||||
enet_packet_resize(packet, p-start);
|
||||
*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
|
||||
enet_packet_resize(packet, p - start);
|
||||
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; };
|
||||
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[])
|
||||
{
|
||||
int uprate = 0, maxcl = 4;
|
||||
char *sdesc = "", *ip = "", *master = NULL, *passwd = "";
|
||||
|
||||
for(int i = 1; i<argc; i++)
|
||||
{
|
||||
for (int i = 1; i < argc; i++) {
|
||||
char *a = &argv[i][2];
|
||||
if(argv[i][0]=='-') switch(argv[i][1])
|
||||
{
|
||||
case 'u': uprate = atoi(a); break;
|
||||
case 'n': sdesc = a; break;
|
||||
case 'i': ip = a; break;
|
||||
case 'm': master = a; break;
|
||||
case 'p': passwd = a; break;
|
||||
case 'c': maxcl = atoi(a); break;
|
||||
default: printf("WARNING: unknown commandline option\n");
|
||||
if (argv[i][0] == '-')
|
||||
switch (argv[i][1]) {
|
||||
case 'u':
|
||||
uprate = atoi(a);
|
||||
break;
|
||||
case 'n':
|
||||
sdesc = a;
|
||||
break;
|
||||
case 'i':
|
||||
ip = a;
|
||||
break;
|
||||
case 'm':
|
||||
master = a;
|
||||
break;
|
||||
case 'p':
|
||||
passwd = a;
|
||||
break;
|
||||
case 'c':
|
||||
maxcl = atoi(a);
|
||||
break;
|
||||
default:
|
||||
printf("WARNING: unknown commandline option\n");
|
||||
};
|
||||
};
|
||||
|
||||
if(enet_initialize()<0) fatal("Unable to initialise network module");
|
||||
if (enet_initialize() < 0)
|
||||
fatal("Unable to initialise network module");
|
||||
initserver(true, uprate, sdesc, ip, master, passwd, maxcl);
|
||||
return 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
|
271
src/sound.cxx
271
src/sound.cxx
|
@ -1,10 +1,12 @@
|
|||
// sound.cpp: uses fmod on windows and sdl_mixer on unix (both had problems on the other platform)
|
||||
// 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
|
||||
// #ifndef _WIN32 // NOTE: fmod not being supported for the moment as it does
|
||||
// not allow stereo pan/vol updating during playback
|
||||
#define USE_MIXER
|
||||
//#endif
|
||||
// #endif
|
||||
|
||||
VARP(soundvol, 0, 255, 255);
|
||||
VARP(musicvol, 0, 128, 255);
|
||||
|
@ -13,95 +15,100 @@ bool nosound = false;
|
|||
#define MAXCHAN 32
|
||||
#define SOUNDFREQ 22050
|
||||
|
||||
struct soundloc { vec loc; bool inuse; } soundlocs[MAXCHAN];
|
||||
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;
|
||||
#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;
|
||||
#include "fmod.h"
|
||||
#define MAXVOL 255
|
||||
FMUSIC_MODULE *mod = NULL;
|
||||
FSOUND_STREAM *stream = NULL;
|
||||
#endif
|
||||
|
||||
void stopsound()
|
||||
void
|
||||
stopsound()
|
||||
{
|
||||
if(nosound) return;
|
||||
if(mod)
|
||||
{
|
||||
#ifdef USE_MIXER
|
||||
if (nosound)
|
||||
return;
|
||||
if (mod) {
|
||||
#ifdef USE_MIXER
|
||||
Mix_HaltMusic();
|
||||
Mix_FreeMusic(mod);
|
||||
#else
|
||||
#else
|
||||
FMUSIC_FreeSong(mod);
|
||||
#endif
|
||||
#endif
|
||||
mod = NULL;
|
||||
};
|
||||
if(stream)
|
||||
{
|
||||
#ifndef USE_MIXER
|
||||
if (stream) {
|
||||
#ifndef USE_MIXER
|
||||
FSOUND_Stream_Close(stream);
|
||||
#endif
|
||||
#endif
|
||||
stream = NULL;
|
||||
};
|
||||
};
|
||||
|
||||
VAR(soundbufferlen, 128, 1024, 4096);
|
||||
|
||||
void initsound()
|
||||
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());
|
||||
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()<FMOD_VERSION) fatal("old FMOD dll");
|
||||
if(!FSOUND_Init(SOUNDFREQ, MAXCHAN, FSOUND_INIT_GLOBALFOCUS))
|
||||
{
|
||||
#else
|
||||
if (FSOUND_GetVersion() < FMOD_VERSION)
|
||||
fatal("old FMOD dll");
|
||||
if (!FSOUND_Init(SOUNDFREQ, MAXCHAN, FSOUND_INIT_GLOBALFOCUS)) {
|
||||
conoutf("sound init failed (FMOD): %d", FSOUND_GetError());
|
||||
nosound = true;
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
void music(char *name)
|
||||
void
|
||||
music(char *name)
|
||||
{
|
||||
if(nosound) return;
|
||||
if (nosound)
|
||||
return;
|
||||
stopsound();
|
||||
if(soundvol && musicvol)
|
||||
{
|
||||
if (soundvol && musicvol) {
|
||||
string sn;
|
||||
strcpy_s(sn, "packages/");
|
||||
strcat_s(sn, name);
|
||||
#ifdef USE_MIXER
|
||||
if(mod = Mix_LoadMUS(path(sn)))
|
||||
{
|
||||
#ifdef USE_MIXER
|
||||
if (mod = Mix_LoadMUS(path(sn))) {
|
||||
Mix_PlayMusic(mod, -1);
|
||||
Mix_VolumeMusic((musicvol*MAXVOL)/255);
|
||||
Mix_VolumeMusic((musicvol * MAXVOL) / 255);
|
||||
};
|
||||
#else
|
||||
if(mod = FMUSIC_LoadSong(path(sn)))
|
||||
{
|
||||
#else
|
||||
if (mod = FMUSIC_LoadSong(path(sn))) {
|
||||
FMUSIC_PlaySong(mod);
|
||||
FMUSIC_SetMasterVolume(mod, musicvol);
|
||||
}
|
||||
else if(stream = FSOUND_Stream_Open(path(sn), FSOUND_LOOP_NORMAL, 0, 0))
|
||||
{
|
||||
} else if (stream = FSOUND_Stream_Open(
|
||||
path(sn), FSOUND_LOOP_NORMAL, 0, 0)) {
|
||||
int chan = FSOUND_Stream_Play(FSOUND_FREE, stream);
|
||||
if(chan>=0) { FSOUND_SetVolume(chan, (musicvol*MAXVOL)/255); FSOUND_SetPaused(chan, false); };
|
||||
}
|
||||
else
|
||||
{
|
||||
if (chan >= 0) {
|
||||
FSOUND_SetVolume(
|
||||
chan, (musicvol * MAXVOL) / 255);
|
||||
FSOUND_SetPaused(chan, false);
|
||||
};
|
||||
} else {
|
||||
conoutf("could not play music: %s", sn);
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -115,112 +122,150 @@ vector<FSOUND_SAMPLE *> samples;
|
|||
|
||||
cvector snames;
|
||||
|
||||
int registersound(char *name)
|
||||
int
|
||||
registersound(char *name)
|
||||
{
|
||||
loopv(snames) if(strcmp(snames[i], name)==0) return i;
|
||||
loopv(snames) if (strcmp(snames[i], name) == 0) return i;
|
||||
snames.add(newstring(name));
|
||||
samples.add(NULL);
|
||||
return samples.length()-1;
|
||||
return samples.length() - 1;
|
||||
};
|
||||
|
||||
COMMAND(registersound, ARG_1EST);
|
||||
|
||||
void cleansound()
|
||||
void
|
||||
cleansound()
|
||||
{
|
||||
if(nosound) return;
|
||||
if (nosound)
|
||||
return;
|
||||
stopsound();
|
||||
#ifdef USE_MIXER
|
||||
#ifdef USE_MIXER
|
||||
Mix_CloseAudio();
|
||||
#else
|
||||
#else
|
||||
FSOUND_Close();
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
VAR(stereo, 0, 1, 1);
|
||||
|
||||
void updatechanvol(int chan, vec *loc)
|
||||
void
|
||||
updatechanvol(int chan, vec *loc)
|
||||
{
|
||||
int vol = soundvol, pan = 255/2;
|
||||
if(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 -= (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
|
||||
vol = (vol * MAXVOL) / 255;
|
||||
#ifdef USE_MIXER
|
||||
Mix_Volume(chan, vol);
|
||||
Mix_SetPanning(chan, 255-pan, pan);
|
||||
#else
|
||||
Mix_SetPanning(chan, 255 - pan, pan);
|
||||
#else
|
||||
FSOUND_SetVolume(chan, vol);
|
||||
FSOUND_SetPan(chan, pan);
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
void newsoundloc(int chan, vec *loc)
|
||||
void
|
||||
newsoundloc(int chan, vec *loc)
|
||||
{
|
||||
assert(chan>=0 && chan<MAXCHAN);
|
||||
assert(chan >= 0 && chan < MAXCHAN);
|
||||
soundlocs[chan].loc = *loc;
|
||||
soundlocs[chan].inuse = true;
|
||||
};
|
||||
|
||||
void updatevol()
|
||||
void
|
||||
updatevol()
|
||||
{
|
||||
if(nosound) return;
|
||||
loopi(MAXCHAN) if(soundlocs[i].inuse)
|
||||
if (nosound)
|
||||
return;
|
||||
loopi(MAXCHAN) if (soundlocs[i].inuse)
|
||||
{
|
||||
#ifdef USE_MIXER
|
||||
if(Mix_Playing(i))
|
||||
#else
|
||||
if(FSOUND_IsPlaying(i))
|
||||
#endif
|
||||
#ifdef USE_MIXER
|
||||
if (Mix_Playing(i))
|
||||
#else
|
||||
if (FSOUND_IsPlaying(i))
|
||||
#endif
|
||||
updatechanvol(i, &soundlocs[i].loc);
|
||||
else soundlocs[i].inuse = false;
|
||||
else
|
||||
soundlocs[i].inuse = false;
|
||||
};
|
||||
};
|
||||
|
||||
void playsoundc(int n) { addmsg(0, 2, SV_SOUND, n); playsound(n); };
|
||||
void
|
||||
playsoundc(int n)
|
||||
{
|
||||
addmsg(0, 2, SV_SOUND, n);
|
||||
playsound(n);
|
||||
};
|
||||
|
||||
int soundsatonce = 0, lastsoundmillis = 0;
|
||||
|
||||
void playsound(int n, vec *loc)
|
||||
void
|
||||
playsound(int n, vec *loc)
|
||||
{
|
||||
if(nosound) return;
|
||||
if(!soundvol) return;
|
||||
if(lastmillis==lastsoundmillis) soundsatonce++; else soundsatonce = 1;
|
||||
if (nosound)
|
||||
return;
|
||||
if (!soundvol)
|
||||
return;
|
||||
if (lastmillis == lastsoundmillis)
|
||||
soundsatonce++;
|
||||
else
|
||||
soundsatonce = 1;
|
||||
lastsoundmillis = lastmillis;
|
||||
if(soundsatonce>5) 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/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; };
|
||||
if (soundsatonce > 5)
|
||||
return; // avoid bursts of sounds with heavy packetloss and in
|
||||
// sp
|
||||
if (n < 0 || n >= samples.length()) {
|
||||
conoutf("unregistered sound: %d", n);
|
||||
return;
|
||||
};
|
||||
|
||||
#ifdef USE_MIXER
|
||||
if (!samples[n]) {
|
||||
sprintf_sd(buf)("packages/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
|
||||
#else
|
||||
int chan = FSOUND_PlaySoundEx(FSOUND_FREE, samples[n], NULL, true);
|
||||
#endif
|
||||
if(chan<0) return;
|
||||
if(loc) newsoundloc(chan, loc);
|
||||
#endif
|
||||
if (chan < 0)
|
||||
return;
|
||||
if (loc)
|
||||
newsoundloc(chan, loc);
|
||||
updatechanvol(chan, loc);
|
||||
#ifndef USE_MIXER
|
||||
#ifndef USE_MIXER
|
||||
FSOUND_SetPaused(chan, false);
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
void sound(int n) { playsound(n, NULL); };
|
||||
void
|
||||
sound(int n)
|
||||
{
|
||||
playsound(n, NULL);
|
||||
};
|
||||
COMMAND(sound, ARG_1INT);
|
||||
|
|
106
src/tools.cxx
106
src/tools.cxx
|
@ -9,28 +9,25 @@ pool::pool()
|
|||
{
|
||||
blocks = 0;
|
||||
allocnext(POOLSIZE);
|
||||
for(int i = 0; i<MAXBUCKETS; i++) reuse[i] = NULL;
|
||||
for (int i = 0; i < MAXBUCKETS; i++)
|
||||
reuse[i] = NULL;
|
||||
};
|
||||
|
||||
void *pool::alloc(size_t size)
|
||||
void *
|
||||
pool::alloc(size_t size)
|
||||
{
|
||||
if(size>MAXREUSESIZE)
|
||||
{
|
||||
if (size > MAXREUSESIZE) {
|
||||
return malloc(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
size = bucket(size);
|
||||
void **r = (void **)reuse[size];
|
||||
if(r)
|
||||
{
|
||||
if (r) {
|
||||
reuse[size] = *r;
|
||||
return (void *)r;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
size <<= PTRBITS;
|
||||
if(left<size) allocnext(POOLSIZE);
|
||||
if (left < size)
|
||||
allocnext(POOLSIZE);
|
||||
char *r = p;
|
||||
p += size;
|
||||
left -= size;
|
||||
|
@ -39,16 +36,14 @@ void *pool::alloc(size_t size)
|
|||
};
|
||||
};
|
||||
|
||||
void pool::dealloc(void *p, size_t size)
|
||||
void
|
||||
pool::dealloc(void *p, size_t size)
|
||||
{
|
||||
if(size>MAXREUSESIZE)
|
||||
{
|
||||
if (size > MAXREUSESIZE) {
|
||||
free(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
size = bucket(size);
|
||||
if(size) // only needed for 0-size free, are there any?
|
||||
if (size) // only needed for 0-size free, are there any?
|
||||
{
|
||||
*((void **)p) = reuse[size];
|
||||
reuse[size] = p;
|
||||
|
@ -56,85 +51,98 @@ void pool::dealloc(void *p, size_t size)
|
|||
};
|
||||
};
|
||||
|
||||
void *pool::realloc(void *p, size_t oldsize, size_t newsize)
|
||||
void *
|
||||
pool::realloc(void *p, size_t oldsize, size_t newsize)
|
||||
{
|
||||
void *np = alloc(newsize);
|
||||
if(!oldsize) return np;
|
||||
memcpy(np, p, newsize>oldsize ? oldsize : newsize);
|
||||
if (!oldsize)
|
||||
return np;
|
||||
memcpy(np, p, newsize > oldsize ? oldsize : newsize);
|
||||
dealloc(p, oldsize);
|
||||
return np;
|
||||
};
|
||||
|
||||
void pool::dealloc_block(void *b)
|
||||
void
|
||||
pool::dealloc_block(void *b)
|
||||
{
|
||||
if(b)
|
||||
{
|
||||
if (b) {
|
||||
dealloc_block(*((char **)b));
|
||||
free(b);
|
||||
};
|
||||
}
|
||||
|
||||
void pool::allocnext(size_t allocsize)
|
||||
void
|
||||
pool::allocnext(size_t allocsize)
|
||||
{
|
||||
char *b = (char *)malloc(allocsize+PTRSIZE);
|
||||
char *b = (char *)malloc(allocsize + PTRSIZE);
|
||||
*((char **)b) = blocks;
|
||||
blocks = b;
|
||||
p = b+PTRSIZE;
|
||||
p = b + PTRSIZE;
|
||||
left = allocsize;
|
||||
};
|
||||
|
||||
char *pool::string(char *s, size_t l)
|
||||
char *
|
||||
pool::string(char *s, size_t l)
|
||||
{
|
||||
char *b = (char *)alloc(l+1);
|
||||
strncpy(b,s,l);
|
||||
char *b = (char *)alloc(l + 1);
|
||||
strncpy(b, s, l);
|
||||
b[l] = 0;
|
||||
return b;
|
||||
};
|
||||
|
||||
pool *gp() // useful for global buffers that need to be initialisation order independant
|
||||
pool *
|
||||
gp() // useful for global buffers that need to be initialisation order
|
||||
// independant
|
||||
{
|
||||
static pool *p = NULL;
|
||||
return p ? p : (p = new pool());
|
||||
};
|
||||
|
||||
|
||||
///////////////////////// misc tools ///////////////////////
|
||||
|
||||
char *path(char *s)
|
||||
char *
|
||||
path(char *s)
|
||||
{
|
||||
for(char *t = s; t = strpbrk(t, "/\\"); *t++ = PATHDIV);
|
||||
for (char *t = s; t = strpbrk(t, "/\\"); *t++ = PATHDIV)
|
||||
;
|
||||
return s;
|
||||
};
|
||||
|
||||
char *loadfile(char *fn, int *size)
|
||||
char *
|
||||
loadfile(char *fn, int *size)
|
||||
{
|
||||
FILE *f = fopen(fn, "rb");
|
||||
if(!f) return NULL;
|
||||
if (!f)
|
||||
return NULL;
|
||||
fseek(f, 0, SEEK_END);
|
||||
int len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
char *buf = (char *)malloc(len+1);
|
||||
if(!buf) return NULL;
|
||||
char *buf = (char *)malloc(len + 1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
buf[len] = 0;
|
||||
size_t rlen = fread(buf, 1, len, f);
|
||||
fclose(f);
|
||||
if(len!=rlen || len<=0)
|
||||
{
|
||||
if (len != rlen || len <= 0) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
};
|
||||
if(size!=NULL) *size = len;
|
||||
if (size != NULL)
|
||||
*size = len;
|
||||
return buf;
|
||||
};
|
||||
|
||||
void endianswap(void *memory, int stride, int length) // little indians as storage format
|
||||
void
|
||||
endianswap(
|
||||
void *memory, int stride, int length) // little indians as storage format
|
||||
{
|
||||
if(*((char *)&stride)) return;
|
||||
loop(w, length) loop(i, stride/2)
|
||||
if (*((char *)&stride))
|
||||
return;
|
||||
loop(w, length) loop(i, stride / 2)
|
||||
{
|
||||
uchar *p = (uchar *)memory+w*stride;
|
||||
uchar *p = (uchar *)memory + w * stride;
|
||||
uchar t = p[i];
|
||||
p[i] = p[stride-i-1];
|
||||
p[stride-i-1] = t;
|
||||
p[i] = p[stride - i - 1];
|
||||
p[stride - i - 1] = t;
|
||||
};
|
||||
}
|
||||
|
|
307
src/tools.h
307
src/tools.h
|
@ -13,12 +13,12 @@
|
|||
#undef gamma
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#ifdef __GNUC__
|
||||
#include <new>
|
||||
#else
|
||||
|
@ -34,20 +34,23 @@ typedef unsigned char uchar;
|
|||
typedef unsigned short ushort;
|
||||
typedef unsigned int uint;
|
||||
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#define rnd(max) (rand()%(max))
|
||||
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define rnd(max) (rand() % (max))
|
||||
#define rndreset() (srand(1))
|
||||
#define rndtime() { loopi(lastmillis&0xF) rnd(i+1); }
|
||||
#define loop(v,m) for(int v = 0; v<(m); v++)
|
||||
#define loopi(m) loop(i,m)
|
||||
#define loopj(m) loop(j,m)
|
||||
#define loopk(m) loop(k,m)
|
||||
#define loopl(m) loop(l,m)
|
||||
#define rndtime() \
|
||||
{ \
|
||||
loopi(lastmillis & 0xF) rnd(i + 1); \
|
||||
}
|
||||
#define loop(v, m) for (int v = 0; v < (m); v++)
|
||||
#define loopi(m) loop(i, m)
|
||||
#define loopj(m) loop(j, m)
|
||||
#define loopk(m) loop(k, m)
|
||||
#define loopl(m) loop(l, m)
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma warning( 3 : 4189 )
|
||||
//#pragma comment(linker,"/OPT:NOWIN98")
|
||||
#pragma warning(3 : 4189)
|
||||
// #pragma comment(linker,"/OPT:NOWIN98")
|
||||
#define PATHDIV '\\'
|
||||
#else
|
||||
#define __cdecl
|
||||
|
@ -55,52 +58,74 @@ typedef unsigned int uint;
|
|||
#define PATHDIV '/'
|
||||
#endif
|
||||
|
||||
|
||||
// easy safe strings
|
||||
|
||||
#define _MAXDEFSTR 260
|
||||
typedef char string[_MAXDEFSTR];
|
||||
|
||||
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) { size_t n = strlen(d); strn0cpy(d+n,s,_MAXDEFSTR-n); };
|
||||
|
||||
inline void formatstring(char *d, const char *fmt, va_list v)
|
||||
inline void
|
||||
strn0cpy(char *d, const char *s, size_t m)
|
||||
{
|
||||
_vsnprintf(d, _MAXDEFSTR, fmt, v);
|
||||
d[_MAXDEFSTR-1] = 0;
|
||||
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)
|
||||
{
|
||||
size_t n = strlen(d);
|
||||
strn0cpy(d + n, s, _MAXDEFSTR - n);
|
||||
};
|
||||
|
||||
struct sprintf_s_f
|
||||
inline void
|
||||
formatstring(char *d, const char *fmt, va_list v)
|
||||
{
|
||||
_vsnprintf(d, _MAXDEFSTR, fmt, v);
|
||||
d[_MAXDEFSTR - 1] = 0;
|
||||
};
|
||||
|
||||
struct sprintf_s_f {
|
||||
char *d;
|
||||
sprintf_s_f(char *str): d(str) {};
|
||||
void operator()(const char* fmt, ...)
|
||||
sprintf_s_f(char *str) : d(str) {};
|
||||
void
|
||||
operator()(const char *fmt, ...)
|
||||
{
|
||||
va_list v;
|
||||
va_start(v, fmt);
|
||||
_vsnprintf(d, _MAXDEFSTR, fmt, v);
|
||||
va_end(v);
|
||||
d[_MAXDEFSTR-1] = 0;
|
||||
d[_MAXDEFSTR - 1] = 0;
|
||||
};
|
||||
};
|
||||
|
||||
#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)
|
||||
|
||||
#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
|
||||
|
||||
#ifdef _MSC_VER
|
||||
inline int fast_f2nat(float a) { // only for positive floats
|
||||
inline int
|
||||
fast_f2nat(float a)
|
||||
{ // only for positive floats
|
||||
static const float fhalf = 0.5f;
|
||||
int retval;
|
||||
|
||||
__asm fld a
|
||||
__asm fsub fhalf
|
||||
__asm fistp retval // perf regalloc?
|
||||
__asm fld a __asm fsub fhalf __asm fistp retval // perf regalloc?
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
@ -108,8 +133,6 @@ inline int fast_f2nat(float a) { // only for positive floats
|
|||
#define fast_f2nat(val) ((int)(val))
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
extern char *path(char *s);
|
||||
extern char *loadfile(char *fn, int *size);
|
||||
extern void endianswap(void *, int, int);
|
||||
|
@ -117,14 +140,19 @@ extern void endianswap(void *, int, int);
|
|||
// memory pool that uses buckets and linear allocation for small objects
|
||||
// VERY fast, and reasonably good memory reuse
|
||||
|
||||
struct pool
|
||||
{
|
||||
struct pool {
|
||||
enum { POOLSIZE = 4096 }; // can be absolutely anything
|
||||
enum { PTRSIZE = sizeof(char *) };
|
||||
enum { MAXBUCKETS = 65 }; // meaning up to size 256 on 32bit pointer systems
|
||||
enum { MAXREUSESIZE = MAXBUCKETS*PTRSIZE-PTRSIZE };
|
||||
inline size_t bucket(size_t s) { return (s+PTRSIZE-1)>>PTRBITS; };
|
||||
enum { PTRBITS = PTRSIZE==2 ? 1 : PTRSIZE==4 ? 2 : 3 };
|
||||
enum {
|
||||
MAXBUCKETS = 65
|
||||
}; // meaning up to size 256 on 32bit pointer systems
|
||||
enum { MAXREUSESIZE = MAXBUCKETS * PTRSIZE - PTRSIZE };
|
||||
inline size_t
|
||||
bucket(size_t s)
|
||||
{
|
||||
return (s + PTRSIZE - 1) >> PTRBITS;
|
||||
};
|
||||
enum { PTRBITS = PTRSIZE == 2 ? 1 : PTRSIZE == 4 ? 2 : 3 };
|
||||
|
||||
char *p;
|
||||
size_t left;
|
||||
|
@ -139,16 +167,27 @@ struct pool
|
|||
void *realloc(void *p, size_t oldsize, size_t newsize);
|
||||
|
||||
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); };
|
||||
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(size_t allocsize);
|
||||
};
|
||||
|
||||
template <class T> struct vector
|
||||
{
|
||||
template <class T> struct vector {
|
||||
T *buf;
|
||||
int alen;
|
||||
int ulen;
|
||||
|
@ -158,69 +197,127 @@ template <class T> struct vector
|
|||
{
|
||||
this->p = gp();
|
||||
alen = 8;
|
||||
buf = (T *)p->alloc(alen*sizeof(T));
|
||||
buf = (T *)p->alloc(alen * sizeof(T));
|
||||
ulen = 0;
|
||||
};
|
||||
|
||||
~vector() { setsize(0); p->dealloc(buf, alen*sizeof(T)); };
|
||||
~vector()
|
||||
{
|
||||
setsize(0);
|
||||
p->dealloc(buf, alen * sizeof(T));
|
||||
};
|
||||
|
||||
vector(vector<T> &v);
|
||||
void operator=(vector<T> &v);
|
||||
|
||||
T &add(const T &x)
|
||||
T &
|
||||
add(const T &x)
|
||||
{
|
||||
if(ulen==alen) realloc();
|
||||
if (ulen == alen)
|
||||
realloc();
|
||||
new (&buf[ulen]) T(x);
|
||||
return buf[ulen++];
|
||||
};
|
||||
|
||||
T &add()
|
||||
T &
|
||||
add()
|
||||
{
|
||||
if(ulen==alen) realloc();
|
||||
if (ulen == alen)
|
||||
realloc();
|
||||
new (&buf[ulen]) T;
|
||||
return buf[ulen++];
|
||||
};
|
||||
|
||||
T &pop() { return buf[--ulen]; };
|
||||
T &last() { return buf[ulen-1]; };
|
||||
bool empty() { return ulen==0; };
|
||||
|
||||
int length() { return ulen; };
|
||||
T &operator[](int i) { assert(i>=0 && i<ulen); return buf[i]; };
|
||||
void setsize(int i) { for(; ulen>i; ulen--) buf[ulen-1].~T(); };
|
||||
T *getbuf() { return buf; };
|
||||
|
||||
void sort(void *cf) { qsort(buf, ulen, sizeof(T), (int (__cdecl *)(const void *,const void *))cf); };
|
||||
|
||||
void realloc()
|
||||
T &
|
||||
pop()
|
||||
{
|
||||
int olen = alen;
|
||||
buf = (T *)p->realloc(buf, olen*sizeof(T), (alen *= 2)*sizeof(T));
|
||||
return buf[--ulen];
|
||||
};
|
||||
T &
|
||||
last()
|
||||
{
|
||||
return buf[ulen - 1];
|
||||
};
|
||||
bool
|
||||
empty()
|
||||
{
|
||||
return ulen == 0;
|
||||
};
|
||||
|
||||
T remove(int i)
|
||||
int
|
||||
length()
|
||||
{
|
||||
return ulen;
|
||||
};
|
||||
T &
|
||||
operator[](int i)
|
||||
{
|
||||
assert(i >= 0 && i < ulen);
|
||||
return buf[i];
|
||||
};
|
||||
void
|
||||
setsize(int i)
|
||||
{
|
||||
for (; ulen > i; ulen--)
|
||||
buf[ulen - 1].~T();
|
||||
};
|
||||
T *
|
||||
getbuf()
|
||||
{
|
||||
return buf;
|
||||
};
|
||||
|
||||
void
|
||||
sort(void *cf)
|
||||
{
|
||||
qsort(buf, ulen, sizeof(T),
|
||||
(int(__cdecl *)(const void *, const void *))cf);
|
||||
};
|
||||
|
||||
void
|
||||
realloc()
|
||||
{
|
||||
int olen = alen;
|
||||
buf = (T *)p->realloc(
|
||||
buf, olen * sizeof(T), (alen *= 2) * sizeof(T));
|
||||
};
|
||||
|
||||
T
|
||||
remove(int i)
|
||||
{
|
||||
T e = buf[i];
|
||||
for(int p = i+1; p<ulen; p++) buf[p-1] = buf[p];
|
||||
for (int p = i + 1; p < ulen; p++)
|
||||
buf[p - 1] = buf[p];
|
||||
ulen--;
|
||||
return e;
|
||||
};
|
||||
|
||||
T &insert(int i, const T &e)
|
||||
T &
|
||||
insert(int i, const T &e)
|
||||
{
|
||||
add(T());
|
||||
for(int p = ulen-1; p>i; p--) buf[p] = buf[p-1];
|
||||
for (int p = ulen - 1; p > i; p--)
|
||||
buf[p] = buf[p - 1];
|
||||
buf[i] = e;
|
||||
return buf[i];
|
||||
};
|
||||
};
|
||||
|
||||
#define loopv(v) if(false) {} else for(int i = 0; i<(v).length(); i++)
|
||||
#define loopvrev(v) if(false) {} else for(int i = (v).length()-1; i>=0; i--)
|
||||
#define loopv(v) \
|
||||
if (false) { \
|
||||
} else \
|
||||
for (int i = 0; i < (v).length(); i++)
|
||||
#define loopvrev(v) \
|
||||
if (false) { \
|
||||
} else \
|
||||
for (int i = (v).length() - 1; i >= 0; i--)
|
||||
|
||||
template <class T> struct hashtable
|
||||
{
|
||||
struct chain { chain *next; char *key; T data; };
|
||||
template <class T> struct hashtable {
|
||||
struct chain {
|
||||
chain *next;
|
||||
char *key;
|
||||
T data;
|
||||
};
|
||||
|
||||
int size;
|
||||
int numelems;
|
||||
|
@ -230,32 +327,36 @@ template <class T> struct hashtable
|
|||
|
||||
hashtable()
|
||||
{
|
||||
this->size = 1<<10;
|
||||
this->size = 1 << 10;
|
||||
this->parent = gp();
|
||||
numelems = 0;
|
||||
table = (chain **)parent->alloc(size*sizeof(T));
|
||||
for(int i = 0; i<size; i++) table[i] = NULL;
|
||||
table = (chain **)parent->alloc(size * sizeof(T));
|
||||
for (int i = 0; i < size; i++)
|
||||
table[i] = NULL;
|
||||
};
|
||||
|
||||
hashtable(hashtable<T> &v);
|
||||
void operator=(hashtable<T> &v);
|
||||
|
||||
T *access(char *key, T *data = NULL)
|
||||
T *
|
||||
access(char *key, T *data = NULL)
|
||||
{
|
||||
unsigned int h = 5381;
|
||||
for(int i = 0, k; k = key[i]; i++) h = ((h<<5)+h)^k; // bernstein k=33 xor
|
||||
h = h&(size-1); // primes not much of an advantage
|
||||
for(chain *c = table[h]; c; c = c->next)
|
||||
{
|
||||
for(char *p1 = key, *p2 = c->key, ch; (ch = *p1++)==*p2++; ) if(!ch) //if(strcmp(key,c->key)==0)
|
||||
for (int i = 0, k; k = key[i]; i++)
|
||||
h = ((h << 5) + h) ^ k; // bernstein k=33 xor
|
||||
h = h & (size - 1); // primes not much of an advantage
|
||||
for (chain *c = table[h]; c; c = c->next) {
|
||||
for (char *p1 = key, *p2 = c->key, ch;
|
||||
(ch = *p1++) == *p2++;)
|
||||
if (!ch) // if(strcmp(key,c->key)==0)
|
||||
{
|
||||
T *d = &c->data;
|
||||
if(data) c->data = *data;
|
||||
if (data)
|
||||
c->data = *data;
|
||||
return d;
|
||||
};
|
||||
};
|
||||
if(data)
|
||||
{
|
||||
if (data) {
|
||||
chain *c = (chain *)parent->alloc(sizeof(chain));
|
||||
c->data = *data;
|
||||
c->key = key;
|
||||
|
@ -267,13 +368,29 @@ template <class T> struct hashtable
|
|||
};
|
||||
};
|
||||
|
||||
|
||||
#define enumerate(ht,t,e,b) loopi(ht->size) for(ht->enumc = ht->table[i]; ht->enumc; ht->enumc = ht->enumc->next) { t e = &ht->enumc->data; b; }
|
||||
#define enumerate(ht, t, e, b) \
|
||||
loopi(ht->size) for (ht->enumc = ht->table[i]; ht->enumc; \
|
||||
ht->enumc = ht->enumc->next) \
|
||||
{ \
|
||||
t e = &ht->enumc->data; \
|
||||
b; \
|
||||
}
|
||||
|
||||
pool *gp();
|
||||
inline char *newstring(char *s) { return gp()->string(s); };
|
||||
inline char *newstring(char *s, size_t l) { return gp()->string(s, l); };
|
||||
inline char *newstringbuf(char *s) { return gp()->stringbuf(s); };
|
||||
inline char *
|
||||
newstring(char *s)
|
||||
{
|
||||
return gp()->string(s);
|
||||
};
|
||||
inline char *
|
||||
newstring(char *s, size_t l)
|
||||
{
|
||||
return gp()->string(s, l);
|
||||
};
|
||||
inline char *
|
||||
newstringbuf(char *s)
|
||||
{
|
||||
return gp()->stringbuf(s);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
356
src/weapon.cxx
356
src/weapon.cxx
|
@ -2,120 +2,154 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
struct guninfo { short sound, attackdelay, damage, projspeed, part, kickamount; char *name; };
|
||||
struct guninfo {
|
||||
short sound, attackdelay, damage, projspeed, part, kickamount;
|
||||
char *name;
|
||||
};
|
||||
|
||||
const int MONSTERDAMAGEFACTOR = 4;
|
||||
const int SGRAYS = 20;
|
||||
const float SGSPREAD = 2;
|
||||
vec sg[SGRAYS];
|
||||
|
||||
guninfo guns[NUMGUNS] =
|
||||
{
|
||||
{ S_PUNCH1, 250, 50, 0, 0, 1, "fist" },
|
||||
{ S_SG, 1400, 10, 0, 0, 20, "shotgun" }, // *SGRAYS
|
||||
{ S_CG, 100, 30, 0, 0, 7, "chaingun" },
|
||||
{ S_RLFIRE, 800, 120, 80, 0, 10, "rocketlauncher" },
|
||||
{ S_RIFLE, 1500, 100, 0, 0, 30, "rifle" },
|
||||
{ S_FLAUNCH, 200, 20, 50, 4, 1, "fireball" },
|
||||
{ S_ICEBALL, 200, 40, 30, 6, 1, "iceball" },
|
||||
{ S_SLIMEBALL, 200, 30, 160, 7, 1, "slimeball" },
|
||||
{ S_PIGR1, 250, 50, 0, 0, 1, "bite" },
|
||||
guninfo guns[NUMGUNS] = {
|
||||
{S_PUNCH1, 250, 50, 0, 0, 1, "fist"},
|
||||
{S_SG, 1400, 10, 0, 0, 20, "shotgun"}, // *SGRAYS
|
||||
{S_CG, 100, 30, 0, 0, 7, "chaingun"},
|
||||
{S_RLFIRE, 800, 120, 80, 0, 10, "rocketlauncher"},
|
||||
{S_RIFLE, 1500, 100, 0, 0, 30, "rifle"},
|
||||
{S_FLAUNCH, 200, 20, 50, 4, 1, "fireball"},
|
||||
{S_ICEBALL, 200, 40, 30, 6, 1, "iceball"},
|
||||
{S_SLIMEBALL, 200, 30, 160, 7, 1, "slimeball"},
|
||||
{S_PIGR1, 250, 50, 0, 0, 1, "bite"},
|
||||
};
|
||||
|
||||
void selectgun(int a, int b, int c)
|
||||
void
|
||||
selectgun(int a, int b, int c)
|
||||
{
|
||||
if(a<-1 || b<-1 || c<-1 || a>=NUMGUNS || b>=NUMGUNS || c>=NUMGUNS) return;
|
||||
if (a < -1 || b < -1 || c < -1 || a >= NUMGUNS || b >= NUMGUNS ||
|
||||
c >= NUMGUNS)
|
||||
return;
|
||||
int s = player1->gunselect;
|
||||
if(a>=0 && s!=a && player1->ammo[a]) s = a;
|
||||
else if(b>=0 && s!=b && player1->ammo[b]) s = b;
|
||||
else if(c>=0 && s!=c && player1->ammo[c]) s = c;
|
||||
else if(s!=GUN_RL && player1->ammo[GUN_RL]) s = GUN_RL;
|
||||
else if(s!=GUN_CG && player1->ammo[GUN_CG]) s = GUN_CG;
|
||||
else if(s!=GUN_SG && player1->ammo[GUN_SG]) s = GUN_SG;
|
||||
else if(s!=GUN_RIFLE && player1->ammo[GUN_RIFLE]) s = GUN_RIFLE;
|
||||
else s = GUN_FIST;
|
||||
if(s!=player1->gunselect) playsoundc(S_WEAPLOAD);
|
||||
if (a >= 0 && s != a && player1->ammo[a])
|
||||
s = a;
|
||||
else if (b >= 0 && s != b && player1->ammo[b])
|
||||
s = b;
|
||||
else if (c >= 0 && s != c && player1->ammo[c])
|
||||
s = c;
|
||||
else if (s != GUN_RL && player1->ammo[GUN_RL])
|
||||
s = GUN_RL;
|
||||
else if (s != GUN_CG && player1->ammo[GUN_CG])
|
||||
s = GUN_CG;
|
||||
else if (s != GUN_SG && player1->ammo[GUN_SG])
|
||||
s = GUN_SG;
|
||||
else if (s != GUN_RIFLE && player1->ammo[GUN_RIFLE])
|
||||
s = GUN_RIFLE;
|
||||
else
|
||||
s = GUN_FIST;
|
||||
if (s != player1->gunselect)
|
||||
playsoundc(S_WEAPLOAD);
|
||||
player1->gunselect = s;
|
||||
//conoutf("%s selected", (int)guns[s].name);
|
||||
// conoutf("%s selected", (int)guns[s].name);
|
||||
};
|
||||
|
||||
int reloadtime(int gun) { return guns[gun].attackdelay; };
|
||||
|
||||
void weapon(char *a1, char *a2, char *a3)
|
||||
int
|
||||
reloadtime(int gun)
|
||||
{
|
||||
selectgun(a1[0] ? atoi(a1) : -1,
|
||||
a2[0] ? atoi(a2) : -1,
|
||||
return guns[gun].attackdelay;
|
||||
};
|
||||
|
||||
void
|
||||
weapon(char *a1, char *a2, char *a3)
|
||||
{
|
||||
selectgun(a1[0] ? atoi(a1) : -1, a2[0] ? atoi(a2) : -1,
|
||||
a3[0] ? atoi(a3) : -1);
|
||||
};
|
||||
|
||||
COMMAND(weapon, ARG_3STR);
|
||||
|
||||
void createrays(vec &from, vec &to) // create random spread of rays for the shotgun
|
||||
void
|
||||
createrays(vec &from, vec &to) // create random spread of rays for the shotgun
|
||||
{
|
||||
vdist(dist, dvec, from, to);
|
||||
float f = dist*SGSPREAD/1000;
|
||||
float f = dist * SGSPREAD / 1000;
|
||||
loopi(SGRAYS)
|
||||
{
|
||||
#define RNDD (rnd(101)-50)*f
|
||||
vec r = { RNDD, RNDD, RNDD };
|
||||
#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
|
||||
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
|
||||
{
|
||||
if (c1 <= 0)
|
||||
p = &from;
|
||||
else {
|
||||
float c2 = dotprod(v, v);
|
||||
if(c2<=c1) p = &to;
|
||||
else
|
||||
{
|
||||
float f = c1/c2;
|
||||
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;
|
||||
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;
|
||||
};
|
||||
|
||||
char *playerincrosshair()
|
||||
char *
|
||||
playerincrosshair()
|
||||
{
|
||||
if(demoplayback) return NULL;
|
||||
if (demoplayback)
|
||||
return NULL;
|
||||
loopv(players)
|
||||
{
|
||||
dynent *o = players[i];
|
||||
if(!o) continue;
|
||||
if(intersect(o, player1->o, worldpos)) return o->name;
|
||||
if (!o)
|
||||
continue;
|
||||
if (intersect(o, player1->o, worldpos))
|
||||
return o->name;
|
||||
};
|
||||
return NULL;
|
||||
};
|
||||
|
||||
const int MAXPROJ = 100;
|
||||
struct projectile { vec o, to; float speed; dynent *owner; int gun; bool inuse, local; };
|
||||
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
|
||||
projreset()
|
||||
{
|
||||
loopi(MAXPROJ) projs[i].inuse = false;
|
||||
};
|
||||
|
||||
void newprojectile(vec &from, vec &to, float speed, bool local, dynent *owner, int gun)
|
||||
void
|
||||
newprojectile(
|
||||
vec &from, vec &to, float speed, bool local, dynent *owner, int gun)
|
||||
{
|
||||
loopi(MAXPROJ)
|
||||
{
|
||||
projectile *p = &projs[i];
|
||||
if(p->inuse) continue;
|
||||
if (p->inuse)
|
||||
continue;
|
||||
p->inuse = true;
|
||||
p->o = from;
|
||||
p->to = to;
|
||||
|
@ -127,11 +161,17 @@ void newprojectile(vec &from, vec &to, float speed, bool local, dynent *owner, i
|
|||
};
|
||||
};
|
||||
|
||||
void hit(int target, int damage, dynent *d, dynent *at)
|
||||
void
|
||||
hit(int target, int damage, dynent *d, dynent *at)
|
||||
{
|
||||
if(d==player1) selfdamage(damage, at==player1 ? -1 : -2, at);
|
||||
else if(d->monsterstate) monsterpain(d, damage, at);
|
||||
else { addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence); playsound(S_PAIN1+rnd(5), &d->o); };
|
||||
if (d == player1)
|
||||
selfdamage(damage, at == player1 ? -1 : -2, at);
|
||||
else if (d->monsterstate)
|
||||
monsterpain(d, damage, 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);
|
||||
};
|
||||
|
@ -139,115 +179,133 @@ void hit(int target, int damage, dynent *d, dynent *at)
|
|||
const float RL_RADIUS = 5;
|
||||
const float RL_DAMRAD = 7; // hack
|
||||
|
||||
void radialeffect(dynent *o, vec &v, int cn, int qdam, dynent *at)
|
||||
void
|
||||
radialeffect(dynent *o, vec &v, int cn, int qdam, dynent *at)
|
||||
{
|
||||
if(o->state!=CS_ALIVE) return;
|
||||
if (o->state != CS_ALIVE)
|
||||
return;
|
||||
vdist(dist, temp, v, o->o);
|
||||
dist -= 2; // account for eye distance imprecision
|
||||
if(dist<RL_DAMRAD)
|
||||
{
|
||||
if(dist<0) dist = 0;
|
||||
int damage = (int)(qdam*(1-(dist/RL_DAMRAD)));
|
||||
if (dist < RL_DAMRAD) {
|
||||
if (dist < 0)
|
||||
dist = 0;
|
||||
int damage = (int)(qdam * (1 - (dist / RL_DAMRAD)));
|
||||
hit(cn, damage, o, at);
|
||||
vmul(temp, (RL_DAMRAD-dist)*damage/800);
|
||||
vmul(temp, (RL_DAMRAD - dist) * damage / 800);
|
||||
vadd(o->vel, temp);
|
||||
};
|
||||
};
|
||||
|
||||
void splash(projectile *p, vec &v, vec &vold, int notthisplayer, int notthismonster, int qdam)
|
||||
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_RL)
|
||||
{
|
||||
if (p->gun != GUN_RL) {
|
||||
playsound(S_FEXPLODE, &v);
|
||||
// no push?
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
playsound(S_RLHIT, &v);
|
||||
newsphere(v, RL_RADIUS, 0);
|
||||
dodynlight(vold, v, 0, 0, p->owner);
|
||||
if(!p->local) return;
|
||||
if (!p->local)
|
||||
return;
|
||||
radialeffect(player1, v, -1, qdam, p->owner);
|
||||
loopv(players)
|
||||
{
|
||||
if(i==notthisplayer) continue;
|
||||
if (i == notthisplayer)
|
||||
continue;
|
||||
dynent *o = players[i];
|
||||
if(!o) continue;
|
||||
if (!o)
|
||||
continue;
|
||||
radialeffect(o, v, i, qdam, p->owner);
|
||||
};
|
||||
dvector &mv = getmonsters();
|
||||
loopv(mv) if(i!=notthismonster) radialeffect(mv[i], v, i, qdam, p->owner);
|
||||
loopv(mv) if (i != notthismonster)
|
||||
radialeffect(mv[i], v, i, qdam, p->owner);
|
||||
};
|
||||
};
|
||||
|
||||
inline void projdamage(dynent *o, projectile *p, vec &v, int i, int im, int qdam)
|
||||
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))
|
||||
{
|
||||
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)
|
||||
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);
|
||||
if(p->owner->monsterstate) qdam /= MONSTERDAMAGEFACTOR;
|
||||
if (!p->inuse)
|
||||
continue;
|
||||
int qdam = guns[p->gun].damage * (p->owner->quadmillis ? 4 : 1);
|
||||
if (p->owner->monsterstate)
|
||||
qdam /= MONSTERDAMAGEFACTOR;
|
||||
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)
|
||||
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;
|
||||
if (!o)
|
||||
continue;
|
||||
projdamage(o, p, v, i, -1, qdam);
|
||||
};
|
||||
if(p->owner!=player1) projdamage(player1, p, v, -1, -1, qdam);
|
||||
if (p->owner != player1)
|
||||
projdamage(player1, p, v, -1, -1, qdam);
|
||||
dvector &mv = getmonsters();
|
||||
loopv(mv) if(!vreject(mv[i]->o, v, 10.0f) && mv[i]!=p->owner) projdamage(mv[i], p, v, -1, i, qdam);
|
||||
loopv(mv) if (!vreject(mv[i]->o, v, 10.0f) &&
|
||||
mv[i] != p->owner)
|
||||
projdamage(mv[i], p, v, -1, i, qdam);
|
||||
};
|
||||
if (p->inuse) {
|
||||
if (time == dtime)
|
||||
splash(p, v, p->o, -1, -1, qdam);
|
||||
else {
|
||||
if (p->gun == GUN_RL) {
|
||||
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);
|
||||
};
|
||||
if(p->inuse)
|
||||
{
|
||||
if(time==dtime) splash(p, v, p->o, -1, -1, qdam);
|
||||
else
|
||||
{
|
||||
if(p->gun==GUN_RL) { 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;
|
||||
};
|
||||
};
|
||||
|
||||
void shootv(int gun, vec &from, vec &to, dynent *d, bool local) // create visual effect from a shot
|
||||
void
|
||||
shootv(int gun, vec &from, vec &to, dynent *d,
|
||||
bool local) // create visual effect from a shot
|
||||
{
|
||||
playsound(guns[gun].sound, d==player1 ? NULL : &d->o);
|
||||
playsound(guns[gun].sound, d == player1 ? NULL : &d->o);
|
||||
int pspeed = 25;
|
||||
switch(gun)
|
||||
{
|
||||
switch (gun) {
|
||||
case GUN_FIST:
|
||||
break;
|
||||
|
||||
case GUN_SG:
|
||||
{
|
||||
case GUN_SG: {
|
||||
loopi(SGRAYS) particle_splash(0, 5, 200, sg[i]);
|
||||
break;
|
||||
};
|
||||
|
||||
case GUN_CG:
|
||||
particle_splash(0, 100, 250, to);
|
||||
//particle_trail(1, 10, from, to);
|
||||
// particle_trail(1, 10, from, to);
|
||||
break;
|
||||
|
||||
case GUN_RL:
|
||||
|
@ -255,7 +313,8 @@ void shootv(int gun, vec &from, vec &to, dynent *d, bool local) // create vi
|
|||
case GUN_ICEBALL:
|
||||
case GUN_SLIMEBALL:
|
||||
pspeed = guns[gun].projspeed;
|
||||
if(d->monsterstate) pspeed /= 2;
|
||||
if (d->monsterstate)
|
||||
pspeed /= 2;
|
||||
newprojectile(from, to, (float)pspeed, local, d, gun);
|
||||
break;
|
||||
|
||||
|
@ -266,39 +325,53 @@ void shootv(int gun, vec &from, vec &to, dynent *d, bool local) // create vi
|
|||
};
|
||||
};
|
||||
|
||||
void hitpush(int target, int damage, dynent *d, dynent *at, vec &from, vec &to)
|
||||
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);
|
||||
vmul(v, damage / dist / 50);
|
||||
vadd(d->vel, v);
|
||||
};
|
||||
|
||||
void raydamage(dynent *o, vec &from, vec &to, dynent *d, int i)
|
||||
void
|
||||
raydamage(dynent *o, vec &from, vec &to, dynent *d, int i)
|
||||
{
|
||||
if(o->state!=CS_ALIVE) return;
|
||||
if (o->state != CS_ALIVE)
|
||||
return;
|
||||
int qdam = guns[d->gunselect].damage;
|
||||
if(d->quadmillis) qdam *= 4;
|
||||
if(d->monsterstate) qdam /= MONSTERDAMAGEFACTOR;
|
||||
if(d->gunselect==GUN_SG)
|
||||
{
|
||||
if (d->quadmillis)
|
||||
qdam *= 4;
|
||||
if (d->monsterstate)
|
||||
qdam /= MONSTERDAMAGEFACTOR;
|
||||
if (d->gunselect == GUN_SG) {
|
||||
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);
|
||||
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 shoot(dynent *d, vec &targ)
|
||||
void
|
||||
shoot(dynent *d, vec &targ)
|
||||
{
|
||||
int attacktime = lastmillis-d->lastaction;
|
||||
if(attacktime<d->gunwait) return;
|
||||
int attacktime = lastmillis - d->lastaction;
|
||||
if (attacktime < d->gunwait)
|
||||
return;
|
||||
d->gunwait = 0;
|
||||
if(!d->attacking) return;
|
||||
if (!d->attacking)
|
||||
return;
|
||||
d->lastaction = lastmillis;
|
||||
d->lastattackgun = d->gunselect;
|
||||
if(!d->ammo[d->gunselect]) { playsoundc(S_NOAMMO); d->gunwait = 250; d->lastattackgun = -1; return; };
|
||||
if(d->gunselect) d->ammo[d->gunselect]--;
|
||||
if (!d->ammo[d->gunselect]) {
|
||||
playsoundc(S_NOAMMO);
|
||||
d->gunwait = 250;
|
||||
d->lastattackgun = -1;
|
||||
return;
|
||||
};
|
||||
if (d->gunselect)
|
||||
d->ammo[d->gunselect]--;
|
||||
vec from = d->o;
|
||||
vec to = targ;
|
||||
from.z -= 0.2f; // below eye
|
||||
|
@ -306,37 +379,42 @@ void shoot(dynent *d, vec &targ)
|
|||
vdist(dist, unitv, from, to);
|
||||
vdiv(unitv, dist);
|
||||
vec kickback = unitv;
|
||||
vmul(kickback, guns[d->gunselect].kickamount*-0.01f);
|
||||
vmul(kickback, guns[d->gunselect].kickamount * -0.01f);
|
||||
vadd(d->vel, kickback);
|
||||
if(d->pitch<80.0f) d->pitch += guns[d->gunselect].kickamount*0.05f;
|
||||
if (d->pitch < 80.0f)
|
||||
d->pitch += guns[d->gunselect].kickamount * 0.05f;
|
||||
|
||||
|
||||
if(d->gunselect==GUN_FIST || d->gunselect==GUN_BITE)
|
||||
{
|
||||
if (d->gunselect == GUN_FIST || d->gunselect == GUN_BITE) {
|
||||
vmul(unitv, 3); // punch range
|
||||
to = from;
|
||||
vadd(to, unitv);
|
||||
};
|
||||
if(d->gunselect==GUN_SG) createrays(from, to);
|
||||
if (d->gunselect == GUN_SG)
|
||||
createrays(from, to);
|
||||
|
||||
if(d->quadmillis && attacktime>200) playsoundc(S_ITEMPUP);
|
||||
if (d->quadmillis && attacktime > 200)
|
||||
playsoundc(S_ITEMPUP);
|
||||
shootv(d->gunselect, from, to, d, true);
|
||||
if(!d->monsterstate) addmsg(1, 8, 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));
|
||||
if (!d->monsterstate)
|
||||
addmsg(1, 8, 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));
|
||||
d->gunwait = guns[d->gunselect].attackdelay;
|
||||
|
||||
if(guns[d->gunselect].projspeed) return;
|
||||
if (guns[d->gunselect].projspeed)
|
||||
return;
|
||||
|
||||
loopv(players)
|
||||
{
|
||||
dynent *o = players[i];
|
||||
if(!o) continue;
|
||||
if (!o)
|
||||
continue;
|
||||
raydamage(o, from, to, d, i);
|
||||
};
|
||||
|
||||
dvector &v = getmonsters();
|
||||
loopv(v) if(v[i]!=d) raydamage(v[i], from, to, d, -2);
|
||||
loopv(v) if (v[i] != d) raydamage(v[i], from, to, d, -2);
|
||||
|
||||
if(d->monsterstate) raydamage(player1, from, to, d, -1);
|
||||
if (d->monsterstate)
|
||||
raydamage(player1, from, to, d, -1);
|
||||
};
|
||||
|
||||
|
||||
|
|
458
src/world.cxx
458
src/world.cxx
|
@ -9,134 +9,215 @@ int sfactor, ssize, cubicsize, mipsize;
|
|||
|
||||
header hdr;
|
||||
|
||||
void settag(int tag, int type) // set all cubes with "tag" to space, if tag is 0 then reset ALL tagged cubes according to type
|
||||
void
|
||||
settag(int tag, int type) // set all cubes with "tag" to space, if tag is 0 then
|
||||
// reset ALL tagged cubes according to type
|
||||
{
|
||||
int maxx = 0, maxy = 0, minx = ssize, miny = ssize;
|
||||
loop(x, ssize) loop(y, ssize)
|
||||
{
|
||||
sqr *s = S(x, y);
|
||||
if(s->tag)
|
||||
{
|
||||
if(tag)
|
||||
{
|
||||
if(tag==s->tag) s->type = SPACE;
|
||||
else continue;
|
||||
}
|
||||
if (s->tag) {
|
||||
if (tag) {
|
||||
if (tag == s->tag)
|
||||
s->type = SPACE;
|
||||
else
|
||||
{
|
||||
continue;
|
||||
} else {
|
||||
s->type = type ? SOLID : SPACE;
|
||||
};
|
||||
if(x>maxx) maxx = x;
|
||||
if(y>maxy) maxy = y;
|
||||
if(x<minx) minx = x;
|
||||
if(y<miny) miny = y;
|
||||
if (x > maxx)
|
||||
maxx = x;
|
||||
if (y > maxy)
|
||||
maxy = y;
|
||||
if (x < minx)
|
||||
minx = x;
|
||||
if (y < miny)
|
||||
miny = y;
|
||||
};
|
||||
};
|
||||
block b = { minx, miny, maxx-minx+1, maxy-miny+1 };
|
||||
if(maxx) remip(b); // remip minimal area of changed geometry
|
||||
block b = {minx, miny, maxx - minx + 1, maxy - miny + 1};
|
||||
if (maxx)
|
||||
remip(b); // remip minimal area of changed geometry
|
||||
};
|
||||
|
||||
void resettagareas() { settag(0, 0); }; // reset for editing or map saving
|
||||
void settagareas() { settag(0, 1); loopv(ents) if(ents[i].type==CARROT) setspawn(i, true); }; // set for playing
|
||||
|
||||
void trigger(int tag, int type, bool savegame)
|
||||
void
|
||||
resettagareas()
|
||||
{
|
||||
if(!tag) return;
|
||||
settag(0, 0);
|
||||
}; // reset for editing or map saving
|
||||
void
|
||||
settagareas()
|
||||
{
|
||||
settag(0, 1);
|
||||
loopv(ents) if (ents[i].type == CARROT) setspawn(i, true);
|
||||
}; // set for playing
|
||||
|
||||
void
|
||||
trigger(int tag, int type, bool savegame)
|
||||
{
|
||||
if (!tag)
|
||||
return;
|
||||
settag(tag, type);
|
||||
if(!savegame && type!=3) playsound(S_RUMBLE);
|
||||
if (!savegame && type != 3)
|
||||
playsound(S_RUMBLE);
|
||||
sprintf_sd(aliasname)("level_trigger_%d", tag);
|
||||
if(identexists(aliasname)) execute(aliasname);
|
||||
if(type==2) endsp(false);
|
||||
if (identexists(aliasname))
|
||||
execute(aliasname);
|
||||
if (type == 2)
|
||||
endsp(false);
|
||||
};
|
||||
|
||||
COMMAND(trigger, ARG_2INT);
|
||||
|
||||
// main geometric mipmapping routine, recursively rebuild mipmaps within block b.
|
||||
// tries to produce cube out of 4 lower level mips as well as possible,
|
||||
// sets defer to 0 if mipped cube is a perfect mip, i.e. can be rendered at this
|
||||
// mip level indistinguishable from its constituent cubes (saves considerable
|
||||
// main geometric mipmapping routine, recursively rebuild mipmaps within block
|
||||
// b. tries to produce cube out of 4 lower level mips as well as possible, sets
|
||||
// defer to 0 if mipped cube is a perfect mip, i.e. can be rendered at this mip
|
||||
// level indistinguishable from its constituent cubes (saves considerable
|
||||
// rendering time if this is possible).
|
||||
|
||||
void remip(block &b, int level)
|
||||
void
|
||||
remip(block &b, int level)
|
||||
{
|
||||
if(level>=SMALLEST_FACTOR) return;
|
||||
int lighterr = getvar("lighterror")*3;
|
||||
if (level >= SMALLEST_FACTOR)
|
||||
return;
|
||||
int lighterr = getvar("lighterror") * 3;
|
||||
sqr *w = wmip[level];
|
||||
sqr *v = wmip[level+1];
|
||||
int ws = ssize>>level;
|
||||
int vs = ssize>>(level+1);
|
||||
sqr *v = wmip[level + 1];
|
||||
int ws = ssize >> level;
|
||||
int vs = ssize >> (level + 1);
|
||||
block s = b;
|
||||
if(s.x&1) { s.x--; s.xs++; };
|
||||
if(s.y&1) { s.y--; s.ys++; };
|
||||
s.xs = (s.xs+1)&~1;
|
||||
s.ys = (s.ys+1)&~1;
|
||||
for(int x = s.x; x<s.x+s.xs; x+=2) for(int y = s.y; y<s.y+s.ys; y+=2)
|
||||
{
|
||||
if (s.x & 1) {
|
||||
s.x--;
|
||||
s.xs++;
|
||||
};
|
||||
if (s.y & 1) {
|
||||
s.y--;
|
||||
s.ys++;
|
||||
};
|
||||
s.xs = (s.xs + 1) & ~1;
|
||||
s.ys = (s.ys + 1) & ~1;
|
||||
for (int x = s.x; x < s.x + s.xs; x += 2)
|
||||
for (int y = s.y; y < s.y + s.ys; y += 2) {
|
||||
sqr *o[4];
|
||||
o[0] = SWS(w,x,y,ws); // the 4 constituent cubes
|
||||
o[1] = SWS(w,x+1,y,ws);
|
||||
o[2] = SWS(w,x+1,y+1,ws);
|
||||
o[3] = SWS(w,x,y+1,ws);
|
||||
sqr *r = SWS(v,x/2,y/2,vs); // the target cube in the higher mip level
|
||||
o[0] = SWS(w, x, y, ws); // the 4 constituent cubes
|
||||
o[1] = SWS(w, x + 1, y, ws);
|
||||
o[2] = SWS(w, x + 1, y + 1, ws);
|
||||
o[3] = SWS(w, x, y + 1, ws);
|
||||
sqr *r = SWS(v, x / 2, y / 2,
|
||||
vs); // the target cube in the higher mip level
|
||||
*r = *o[0];
|
||||
uchar nums[MAXTYPE];
|
||||
loopi(MAXTYPE) nums[i] = 0;
|
||||
loopj(4) nums[o[j]->type]++;
|
||||
r->type = SEMISOLID; // cube contains both solid and space, treated specially in the renderer
|
||||
loopk(MAXTYPE) if(nums[k]==4) r->type = k;
|
||||
if(!SOLID(r))
|
||||
{
|
||||
r->type =
|
||||
SEMISOLID; // cube contains both solid and space,
|
||||
// treated specially in the renderer
|
||||
loopk(MAXTYPE) if (nums[k] == 4) r->type = k;
|
||||
if (!SOLID(r)) {
|
||||
int floor = 127, ceil = -128, num = 0;
|
||||
loopi(4) if(!SOLID(o[i]))
|
||||
loopi(4) if (!SOLID(o[i]))
|
||||
{
|
||||
num++;
|
||||
int fh = o[i]->floor;
|
||||
int ch = o[i]->ceil;
|
||||
if(r->type==SEMISOLID)
|
||||
{
|
||||
if(o[i]->type==FHF) fh -= o[i]->vdelta/4+2; // crap hack, needed for rendering large mips next to hfs
|
||||
if(o[i]->type==CHF) ch += o[i]->vdelta/4+2; // FIXME: needs to somehow take into account middle vertices on higher mips
|
||||
if (r->type == SEMISOLID) {
|
||||
if (o[i]->type == FHF)
|
||||
fh -= o[i]->vdelta / 4 +
|
||||
2; // crap hack,
|
||||
// needed for
|
||||
// rendering
|
||||
// large mips
|
||||
// next to hfs
|
||||
if (o[i]->type == CHF)
|
||||
ch +=
|
||||
o[i]->vdelta / 4 +
|
||||
2; // FIXME: needs
|
||||
// to somehow
|
||||
// take into
|
||||
// account middle
|
||||
// vertices on
|
||||
// higher mips
|
||||
};
|
||||
if(fh<floor) floor = fh; // take lowest floor and highest ceil, so we never have to see missing lower/upper from the side
|
||||
if(ch>ceil) ceil = ch;
|
||||
if (fh < floor)
|
||||
floor =
|
||||
fh; // take lowest floor and
|
||||
// highest ceil, so we
|
||||
// never have to see
|
||||
// missing lower/upper
|
||||
// from the side
|
||||
if (ch > ceil)
|
||||
ceil = ch;
|
||||
};
|
||||
r->floor = floor;
|
||||
r->ceil = ceil;
|
||||
};
|
||||
if(r->type==CORNER) goto mip; // special case: don't ever split even if textures etc are different
|
||||
if (r->type == CORNER)
|
||||
goto mip; // special case: don't ever split even
|
||||
// if textures etc are different
|
||||
r->defer = 1;
|
||||
if(SOLID(r))
|
||||
{
|
||||
if (SOLID(r)) {
|
||||
loopi(3)
|
||||
{
|
||||
if(o[i]->wtex!=o[3]->wtex) goto c; // on an all solid cube, only thing that needs to be equal for a perfect mip is the wall texture
|
||||
if (o[i]->wtex != o[3]->wtex)
|
||||
goto c; // on an all solid cube,
|
||||
// only thing that needs
|
||||
// to be equal for a
|
||||
// perfect mip is the
|
||||
// wall texture
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
loopi(3)
|
||||
{
|
||||
if(o[i]->type!=o[3]->type
|
||||
|| o[i]->floor!=o[3]->floor
|
||||
|| o[i]->ceil!=o[3]->ceil
|
||||
|| o[i]->ftex!=o[3]->ftex
|
||||
|| o[i]->ctex!=o[3]->ctex
|
||||
|| abs(o[i+1]->r-o[0]->r)>lighterr // perfect mip even if light is not exactly equal
|
||||
|| abs(o[i+1]->g-o[0]->g)>lighterr
|
||||
|| abs(o[i+1]->b-o[0]->b)>lighterr
|
||||
|| o[i]->utex!=o[3]->utex
|
||||
|| o[i]->wtex!=o[3]->wtex) goto c;
|
||||
if (o[i]->type != o[3]->type ||
|
||||
o[i]->floor != o[3]->floor ||
|
||||
o[i]->ceil != o[3]->ceil ||
|
||||
o[i]->ftex != o[3]->ftex ||
|
||||
o[i]->ctex != o[3]->ctex ||
|
||||
abs(o[i + 1]->r - o[0]->r) >
|
||||
lighterr // perfect mip even if
|
||||
// light is not exactly
|
||||
// equal
|
||||
|| abs(o[i + 1]->g - o[0]->g) >
|
||||
lighterr ||
|
||||
abs(o[i + 1]->b - o[0]->b) >
|
||||
lighterr ||
|
||||
o[i]->utex != o[3]->utex ||
|
||||
o[i]->wtex != o[3]->wtex)
|
||||
goto c;
|
||||
};
|
||||
if(r->type==CHF || r->type==FHF) // can make a perfect mip out of a hf if slopes lie on one line
|
||||
if (r->type == CHF ||
|
||||
r->type ==
|
||||
FHF) // can make a perfect mip out of a
|
||||
// hf if slopes lie on one line
|
||||
{
|
||||
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
|
||||
|| 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;
|
||||
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 ||
|
||||
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
|
||||
{
|
||||
loopi(4) if (o[i]->defer) goto c;
|
||||
}; // if any of the constituents is not perfect, then
|
||||
// this one isn't either
|
||||
mip:
|
||||
r->defer = 0;
|
||||
c:;
|
||||
|
@ -145,80 +226,105 @@ void remip(block &b, int level)
|
|||
s.y /= 2;
|
||||
s.xs /= 2;
|
||||
s.ys /= 2;
|
||||
remip(s, level+1);
|
||||
remip(s, level + 1);
|
||||
};
|
||||
|
||||
void remipmore(block &b, int level)
|
||||
void
|
||||
remipmore(block &b, int level)
|
||||
{
|
||||
block bb = b;
|
||||
if(bb.x>1) bb.x--;
|
||||
if(bb.y>1) bb.y--;
|
||||
if(bb.xs<ssize-3) bb.xs++;
|
||||
if(bb.ys<ssize-3) bb.ys++;
|
||||
if (bb.x > 1)
|
||||
bb.x--;
|
||||
if (bb.y > 1)
|
||||
bb.y--;
|
||||
if (bb.xs < ssize - 3)
|
||||
bb.xs++;
|
||||
if (bb.ys < ssize - 3)
|
||||
bb.ys++;
|
||||
remip(bb, level);
|
||||
};
|
||||
|
||||
int closestent() // used for delent and edit mode ent display
|
||||
int
|
||||
closestent() // used for delent and edit mode ent display
|
||||
{
|
||||
if(noteditmode()) return -1;
|
||||
if (noteditmode())
|
||||
return -1;
|
||||
int best;
|
||||
float bdist = 99999;
|
||||
loopv(ents)
|
||||
{
|
||||
entity &e = ents[i];
|
||||
if(e.type==NOTUSED) continue;
|
||||
vec v = { e.x, e.y, e.z };
|
||||
if (e.type == NOTUSED)
|
||||
continue;
|
||||
vec v = {e.x, e.y, e.z};
|
||||
vdist(dist, t, player1->o, v);
|
||||
if(dist<bdist)
|
||||
{
|
||||
if (dist < bdist) {
|
||||
best = i;
|
||||
bdist = dist;
|
||||
};
|
||||
};
|
||||
return bdist==99999 ? -1 : best;
|
||||
return bdist == 99999 ? -1 : best;
|
||||
};
|
||||
|
||||
void entproperty(int prop, int amount)
|
||||
void
|
||||
entproperty(int prop, int amount)
|
||||
{
|
||||
int e = closestent();
|
||||
if(e<0) return;
|
||||
switch(prop)
|
||||
{
|
||||
case 0: ents[e].attr1 += amount; break;
|
||||
case 1: ents[e].attr2 += amount; break;
|
||||
case 2: ents[e].attr3 += amount; break;
|
||||
case 3: ents[e].attr4 += amount; break;
|
||||
if (e < 0)
|
||||
return;
|
||||
switch (prop) {
|
||||
case 0:
|
||||
ents[e].attr1 += amount;
|
||||
break;
|
||||
case 1:
|
||||
ents[e].attr2 += amount;
|
||||
break;
|
||||
case 2:
|
||||
ents[e].attr3 += amount;
|
||||
break;
|
||||
case 3:
|
||||
ents[e].attr4 += amount;
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
||||
void delent()
|
||||
void
|
||||
delent()
|
||||
{
|
||||
int e = closestent();
|
||||
if(e<0) { conoutf("no more entities"); return; };
|
||||
if (e < 0) {
|
||||
conoutf("no more entities");
|
||||
return;
|
||||
};
|
||||
int t = ents[e].type;
|
||||
conoutf("%s entity deleted", entnames[t]);
|
||||
ents[e].type = NOTUSED;
|
||||
addmsg(1, 10, SV_EDITENT, e, NOTUSED, 0, 0, 0, 0, 0, 0, 0);
|
||||
if(t==LIGHT) calclight();
|
||||
if (t == LIGHT)
|
||||
calclight();
|
||||
};
|
||||
|
||||
int findtype(char *what)
|
||||
int
|
||||
findtype(char *what)
|
||||
{
|
||||
loopi(MAXENTTYPES) if(strcmp(what, entnames[i])==0) return i;
|
||||
loopi(MAXENTTYPES) if (strcmp(what, entnames[i]) == 0) return i;
|
||||
conoutf("unknown entity type \"%s\"", what);
|
||||
return NOTUSED;
|
||||
}
|
||||
|
||||
entity *newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v4)
|
||||
entity *
|
||||
newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v4)
|
||||
{
|
||||
int type = findtype(what);
|
||||
persistent_entity e = { x, y, z, v1, type, v2, v3, v4 };
|
||||
switch(type)
|
||||
{
|
||||
persistent_entity e = {x, y, z, v1, type, v2, v3, v4};
|
||||
switch (type) {
|
||||
case LIGHT:
|
||||
if(v1>32) v1 = 32;
|
||||
if(!v1) e.attr1 = 16;
|
||||
if(!v2 && !v3 && !v4) e.attr2 = 255;
|
||||
if (v1 > 32)
|
||||
v1 = 32;
|
||||
if (!v1)
|
||||
e.attr1 = 16;
|
||||
if (!v2 && !v3 && !v4)
|
||||
e.attr2 = 255;
|
||||
break;
|
||||
|
||||
case MAPMODEL:
|
||||
|
@ -231,44 +337,55 @@ entity *newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v
|
|||
e.attr1 = (int)player1->yaw;
|
||||
break;
|
||||
};
|
||||
addmsg(1, 10, SV_EDITENT, ents.length(), type, e.x, e.y, e.z, 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();
|
||||
if (type == LIGHT)
|
||||
calclight();
|
||||
return &ents.last();
|
||||
};
|
||||
|
||||
void clearents(char *name)
|
||||
void
|
||||
clearents(char *name)
|
||||
{
|
||||
int type = findtype(name);
|
||||
if(noteditmode() || multiplayer()) return;
|
||||
if (noteditmode() || multiplayer())
|
||||
return;
|
||||
loopv(ents)
|
||||
{
|
||||
entity &e = ents[i];
|
||||
if(e.type==type) e.type = NOTUSED;
|
||||
if (e.type == type)
|
||||
e.type = NOTUSED;
|
||||
};
|
||||
if(type==LIGHT) calclight();
|
||||
if (type == LIGHT)
|
||||
calclight();
|
||||
};
|
||||
|
||||
COMMAND(clearents, ARG_1STR);
|
||||
|
||||
void scalecomp(uchar &c, int intens)
|
||||
void
|
||||
scalecomp(uchar &c, int intens)
|
||||
{
|
||||
int n = c*intens/100;
|
||||
if(n>255) n = 255;
|
||||
int n = c * intens / 100;
|
||||
if (n > 255)
|
||||
n = 255;
|
||||
c = n;
|
||||
};
|
||||
|
||||
void scalelights(int f, int intens)
|
||||
void
|
||||
scalelights(int f, int intens)
|
||||
{
|
||||
loopv(ents)
|
||||
{
|
||||
entity &e = ents[i];
|
||||
if(e.type!=LIGHT) continue;
|
||||
e.attr1 = e.attr1*f/100;
|
||||
if(e.attr1<2) e.attr1 = 2;
|
||||
if(e.attr1>32) e.attr1 = 32;
|
||||
if(intens)
|
||||
{
|
||||
if (e.type != LIGHT)
|
||||
continue;
|
||||
e.attr1 = e.attr1 * f / 100;
|
||||
if (e.attr1 < 2)
|
||||
e.attr1 = 2;
|
||||
if (e.attr1 > 32)
|
||||
e.attr1 = 32;
|
||||
if (intens) {
|
||||
scalecomp(e.attr2, intens);
|
||||
scalecomp(e.attr3, intens);
|
||||
scalecomp(e.attr4, intens);
|
||||
|
@ -279,39 +396,56 @@ void scalelights(int f, int intens)
|
|||
|
||||
COMMAND(scalelights, ARG_2INT);
|
||||
|
||||
int findentity(int type, int index)
|
||||
int
|
||||
findentity(int type, int index)
|
||||
{
|
||||
for(int i = index; i<ents.length(); i++) if(ents[i].type==type) return i;
|
||||
loopj(index) if(ents[j].type==type) return j;
|
||||
for (int i = index; i < ents.length(); i++)
|
||||
if (ents[i].type == type)
|
||||
return i;
|
||||
loopj(index) if (ents[j].type == type) return j;
|
||||
return -1;
|
||||
};
|
||||
|
||||
sqr *wmip[LARGEST_FACTOR*2];
|
||||
sqr *wmip[LARGEST_FACTOR * 2];
|
||||
|
||||
void setupworld(int factor)
|
||||
void
|
||||
setupworld(int factor)
|
||||
{
|
||||
ssize = 1<<(sfactor = factor);
|
||||
cubicsize = ssize*ssize;
|
||||
mipsize = cubicsize*134/100;
|
||||
sqr *w = world = (sqr *)alloc(mipsize*sizeof(sqr));
|
||||
loopi(LARGEST_FACTOR*2) { wmip[i] = w; w += cubicsize>>(i*2); };
|
||||
ssize = 1 << (sfactor = factor);
|
||||
cubicsize = ssize * ssize;
|
||||
mipsize = cubicsize * 134 / 100;
|
||||
sqr *w = world = (sqr *)alloc(mipsize * sizeof(sqr));
|
||||
loopi(LARGEST_FACTOR * 2)
|
||||
{
|
||||
wmip[i] = w;
|
||||
w += cubicsize >> (i * 2);
|
||||
};
|
||||
};
|
||||
|
||||
void empty_world(int factor, bool force) // main empty world creation routine, if passed factor -1 will enlarge old world by 1
|
||||
void
|
||||
empty_world(
|
||||
int factor, bool force) // main empty world creation routine, if passed
|
||||
// factor -1 will enlarge old world by 1
|
||||
{
|
||||
if(!force && noteditmode()) return;
|
||||
if (!force && noteditmode())
|
||||
return;
|
||||
cleardlights();
|
||||
pruneundos();
|
||||
sqr *oldworld = world;
|
||||
bool copy = false;
|
||||
if(oldworld && factor<0) { factor = sfactor+1; copy = true; };
|
||||
if(factor<SMALLEST_FACTOR) factor = SMALLEST_FACTOR;
|
||||
if(factor>LARGEST_FACTOR) factor = LARGEST_FACTOR;
|
||||
if (oldworld && factor < 0) {
|
||||
factor = sfactor + 1;
|
||||
copy = true;
|
||||
};
|
||||
if (factor < SMALLEST_FACTOR)
|
||||
factor = SMALLEST_FACTOR;
|
||||
if (factor > LARGEST_FACTOR)
|
||||
factor = LARGEST_FACTOR;
|
||||
setupworld(factor);
|
||||
|
||||
loop(x,ssize) loop(y,ssize)
|
||||
loop(x, ssize) loop(y, ssize)
|
||||
{
|
||||
sqr *s = S(x,y);
|
||||
sqr *s = S(x, y);
|
||||
s->r = s->g = s->b = 150;
|
||||
s->ftex = DEFAULT_FLOOR;
|
||||
s->ctex = DEFAULT_CEIL;
|
||||
|
@ -328,45 +462,49 @@ void empty_world(int factor, bool force) // main empty world creation routine
|
|||
hdr.headersize = sizeof(header);
|
||||
hdr.sfactor = sfactor;
|
||||
|
||||
if(copy)
|
||||
if (copy) {
|
||||
loop(x, ssize / 2) loop(y, ssize / 2)
|
||||
{
|
||||
loop(x,ssize/2) loop(y,ssize/2)
|
||||
{
|
||||
*S(x+ssize/4, y+ssize/4) = *SWS(oldworld, x, y, ssize/2);
|
||||
*S(x + ssize / 4, y + ssize / 4) =
|
||||
*SWS(oldworld, x, y, ssize / 2);
|
||||
};
|
||||
loopv(ents)
|
||||
{
|
||||
ents[i].x += ssize/4;
|
||||
ents[i].y += ssize/4;
|
||||
ents[i].x += ssize / 4;
|
||||
ents[i].y += ssize / 4;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
strn0cpy(hdr.maptitle, "Untitled Map by Unknown", 128);
|
||||
hdr.waterlevel = -100000;
|
||||
loopi(15) hdr.reserved[i] = 0;
|
||||
loopk(3) loopi(256) hdr.texlists[k][i] = i;
|
||||
ents.setsize(0);
|
||||
block b = { 8, 8, ssize-16, ssize-16 };
|
||||
block b = {8, 8, ssize - 16, ssize - 16};
|
||||
edittypexy(SPACE, b);
|
||||
};
|
||||
|
||||
calclight();
|
||||
startmap("base/unnamed");
|
||||
if(oldworld)
|
||||
{
|
||||
if (oldworld) {
|
||||
free(oldworld);
|
||||
toggleedit();
|
||||
execute("fullbright 1");
|
||||
};
|
||||
};
|
||||
|
||||
void mapenlarge() { empty_world(-1, false); };
|
||||
void newmap(int i) { empty_world(i, false); };
|
||||
void
|
||||
mapenlarge()
|
||||
{
|
||||
empty_world(-1, false);
|
||||
};
|
||||
void
|
||||
newmap(int i)
|
||||
{
|
||||
empty_world(i, false);
|
||||
};
|
||||
|
||||
COMMAND(mapenlarge, ARG_NONE);
|
||||
COMMAND(newmap, ARG_1INT);
|
||||
COMMANDN(recalc, calclight, ARG_NONE);
|
||||
COMMAND(delent, ARG_NONE);
|
||||
COMMAND(entproperty, ARG_2INT);
|
||||
|
||||
|
|
270
src/worldio.cxx
270
src/worldio.cxx
|
@ -2,7 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
void backup(char *name, char *backupname)
|
||||
void
|
||||
backup(char *name, char *backupname)
|
||||
{
|
||||
remove(backupname);
|
||||
rename(name, backupname);
|
||||
|
@ -10,22 +11,21 @@ void backup(char *name, char *backupname)
|
|||
|
||||
string cgzname, bakname, pcfname, mcfname;
|
||||
|
||||
void setnames(char *name)
|
||||
void
|
||||
setnames(char *name)
|
||||
{
|
||||
string pakname, mapname;
|
||||
char *slash = strpbrk(name, "/\\");
|
||||
if(slash)
|
||||
{
|
||||
strn0cpy(pakname, name, slash-name+1);
|
||||
strcpy_s(mapname, slash+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (slash) {
|
||||
strn0cpy(pakname, name, slash - name + 1);
|
||||
strcpy_s(mapname, slash + 1);
|
||||
} else {
|
||||
strcpy_s(pakname, "base");
|
||||
strcpy_s(mapname, name);
|
||||
};
|
||||
sprintf_s(cgzname)("packages/%s/%s.cgz", pakname, mapname);
|
||||
sprintf_s(bakname)("packages/%s/%s_%d.BAK", pakname, mapname, lastmillis);
|
||||
sprintf_s(bakname)(
|
||||
"packages/%s/%s_%d.BAK", pakname, mapname, lastmillis);
|
||||
sprintf_s(pcfname)("packages/%s/package.cfg", pakname);
|
||||
sprintf_s(mcfname)("packages/%s/%s.cfg", pakname, mapname);
|
||||
|
||||
|
@ -33,119 +33,151 @@ void setnames(char *name)
|
|||
path(bakname);
|
||||
};
|
||||
|
||||
// the optimize routines below are here to reduce the detrimental effects of messy mapping by
|
||||
// setting certain properties (vdeltas and textures) to neighbouring values wherever there is no
|
||||
// visible difference. This allows the mipmapper to generate more efficient mips.
|
||||
// the reason it is done on save is to reduce the amount spend in the mipmapper (as that is done
|
||||
// in realtime).
|
||||
// the optimize routines below are here to reduce the detrimental effects of
|
||||
// messy mapping by setting certain properties (vdeltas and textures) to
|
||||
// neighbouring values wherever there is no visible difference. This allows the
|
||||
// mipmapper to generate more efficient mips. the reason it is done on save is
|
||||
// to reduce the amount spend in the mipmapper (as that is done in realtime).
|
||||
|
||||
inline bool nhf(sqr *s) { return s->type!=FHF && s->type!=CHF; };
|
||||
inline bool
|
||||
nhf(sqr *s)
|
||||
{
|
||||
return s->type != FHF && s->type != CHF;
|
||||
};
|
||||
|
||||
void voptimize() // reset vdeltas on non-hf cubes
|
||||
void
|
||||
voptimize() // reset vdeltas on non-hf cubes
|
||||
{
|
||||
loop(x, ssize) loop(y, ssize)
|
||||
{
|
||||
sqr *s = S(x, y);
|
||||
if(x && y) { if(nhf(s) && nhf(S(x-1, y)) && nhf(S(x-1, y-1)) && nhf(S(x, y-1))) s->vdelta = 0; }
|
||||
else s->vdelta = 0;
|
||||
if (x && y) {
|
||||
if (nhf(s) && nhf(S(x - 1, y)) &&
|
||||
nhf(S(x - 1, y - 1)) && nhf(S(x, y - 1)))
|
||||
s->vdelta = 0;
|
||||
} else
|
||||
s->vdelta = 0;
|
||||
};
|
||||
};
|
||||
|
||||
void topt(sqr *s, bool &wf, bool &uf, int &wt, int &ut)
|
||||
void
|
||||
topt(sqr *s, bool &wf, bool &uf, int &wt, int &ut)
|
||||
{
|
||||
sqr *o[4];
|
||||
o[0] = SWS(s,0,-1,ssize);
|
||||
o[1] = SWS(s,0,1,ssize);
|
||||
o[2] = SWS(s,1,0,ssize);
|
||||
o[3] = SWS(s,-1,0,ssize);
|
||||
o[0] = SWS(s, 0, -1, ssize);
|
||||
o[1] = SWS(s, 0, 1, ssize);
|
||||
o[2] = SWS(s, 1, 0, ssize);
|
||||
o[3] = SWS(s, -1, 0, ssize);
|
||||
wf = true;
|
||||
uf = true;
|
||||
if(SOLID(s))
|
||||
{
|
||||
loopi(4) if(!SOLID(o[i]))
|
||||
if (SOLID(s)) {
|
||||
loopi(4) if (!SOLID(o[i]))
|
||||
{
|
||||
wf = false;
|
||||
wt = s->wtex;
|
||||
ut = s->utex;
|
||||
return;
|
||||
};
|
||||
}
|
||||
else
|
||||
} else {
|
||||
loopi(4) if (!SOLID(o[i]))
|
||||
{
|
||||
loopi(4) if(!SOLID(o[i]))
|
||||
{
|
||||
if(o[i]->floor<s->floor) { wt = s->wtex; wf = false; };
|
||||
if(o[i]->ceil>s->ceil) { ut = s->utex; uf = false; };
|
||||
if (o[i]->floor < s->floor) {
|
||||
wt = s->wtex;
|
||||
wf = false;
|
||||
};
|
||||
if (o[i]->ceil > s->ceil) {
|
||||
ut = s->utex;
|
||||
uf = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
void toptimize() // FIXME: only does 2x2, make atleast for 4x4 also
|
||||
void
|
||||
toptimize() // FIXME: only does 2x2, make atleast for 4x4 also
|
||||
{
|
||||
bool wf[4], uf[4];
|
||||
sqr *s[4];
|
||||
for(int x = 2; x<ssize-4; x += 2) for(int y = 2; y<ssize-4; y += 2)
|
||||
{
|
||||
s[0] = S(x,y);
|
||||
for (int x = 2; x < ssize - 4; x += 2)
|
||||
for (int y = 2; y < ssize - 4; y += 2) {
|
||||
s[0] = S(x, y);
|
||||
int wt = s[0]->wtex, ut = s[0]->utex;
|
||||
topt(s[0], wf[0], uf[0], wt, ut);
|
||||
topt(s[1] = SWS(s[0],0,1,ssize), wf[1], uf[1], wt, ut);
|
||||
topt(s[2] = SWS(s[0],1,1,ssize), wf[2], uf[2], wt, ut);
|
||||
topt(s[3] = SWS(s[0],1,0,ssize), wf[3], uf[3], wt, ut);
|
||||
topt(s[1] = SWS(s[0], 0, 1, ssize), wf[1], uf[1], wt,
|
||||
ut);
|
||||
topt(s[2] = SWS(s[0], 1, 1, ssize), wf[2], uf[2], wt,
|
||||
ut);
|
||||
topt(s[3] = SWS(s[0], 1, 0, ssize), wf[3], uf[3], wt,
|
||||
ut);
|
||||
loopi(4)
|
||||
{
|
||||
if(wf[i]) s[i]->wtex = wt;
|
||||
if(uf[i]) s[i]->utex = ut;
|
||||
if (wf[i])
|
||||
s[i]->wtex = wt;
|
||||
if (uf[i])
|
||||
s[i]->utex = ut;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// these two are used by getmap/sendmap.. transfers compressed maps directly
|
||||
|
||||
void writemap(char *mname, int msize, uchar *mdata)
|
||||
void
|
||||
writemap(char *mname, int msize, uchar *mdata)
|
||||
{
|
||||
setnames(mname);
|
||||
backup(cgzname, bakname);
|
||||
FILE *f = fopen(cgzname, "wb");
|
||||
if(!f) { conoutf("could not write map to %s", cgzname); return; };
|
||||
if (!f) {
|
||||
conoutf("could not write map to %s", cgzname);
|
||||
return;
|
||||
};
|
||||
fwrite(mdata, 1, msize, f);
|
||||
fclose(f);
|
||||
conoutf("wrote map %s as file %s", mname, cgzname);
|
||||
}
|
||||
|
||||
uchar *readmap(char *mname, int *msize)
|
||||
uchar *
|
||||
readmap(char *mname, int *msize)
|
||||
{
|
||||
setnames(mname);
|
||||
uchar *mdata = (uchar *)loadfile(cgzname, msize);
|
||||
if(!mdata) { conoutf("could not read map %s", cgzname); return NULL; };
|
||||
if (!mdata) {
|
||||
conoutf("could not read map %s", cgzname);
|
||||
return NULL;
|
||||
};
|
||||
return mdata;
|
||||
}
|
||||
|
||||
// save map as .cgz file. uses 2 layers of compression: first does simple run-length
|
||||
// encoding and leaves out data for certain kinds of cubes, then zlib removes the
|
||||
// last bits of redundancy. Both passes contribute greatly to the miniscule map sizes.
|
||||
// save map as .cgz file. uses 2 layers of compression: first does simple
|
||||
// run-length encoding and leaves out data for certain kinds of cubes, then zlib
|
||||
// removes the last bits of redundancy. Both passes contribute greatly to the
|
||||
// miniscule map sizes.
|
||||
|
||||
void save_world(char *mname)
|
||||
void
|
||||
save_world(char *mname)
|
||||
{
|
||||
resettagareas(); // wouldn't be able to reproduce tagged areas otherwise
|
||||
voptimize();
|
||||
toptimize();
|
||||
if(!*mname) mname = getclientmap();
|
||||
if (!*mname)
|
||||
mname = getclientmap();
|
||||
setnames(mname);
|
||||
backup(cgzname, bakname);
|
||||
gzFile f = gzopen(cgzname, "wb9");
|
||||
if(!f) { conoutf("could not write map to %s", cgzname); return; };
|
||||
if (!f) {
|
||||
conoutf("could not write map to %s", cgzname);
|
||||
return;
|
||||
};
|
||||
hdr.version = MAPVERSION;
|
||||
hdr.numents = 0;
|
||||
loopv(ents) if(ents[i].type!=NOTUSED) hdr.numents++;
|
||||
loopv(ents) if (ents[i].type != NOTUSED) hdr.numents++;
|
||||
header tmp = hdr;
|
||||
endianswap(&tmp.version, sizeof(int), 4);
|
||||
endianswap(&tmp.waterlevel, sizeof(int), 16);
|
||||
gzwrite(f, &tmp, sizeof(header));
|
||||
loopv(ents)
|
||||
{
|
||||
if(ents[i].type!=NOTUSED)
|
||||
{
|
||||
if (ents[i].type != NOTUSED) {
|
||||
entity tmp = ents[i];
|
||||
endianswap(&tmp, sizeof(short), 4);
|
||||
gzwrite(f, &tmp, sizeof(persistent_entity));
|
||||
|
@ -153,39 +185,42 @@ void save_world(char *mname)
|
|||
};
|
||||
sqr *t = NULL;
|
||||
int sc = 0;
|
||||
#define spurge while(sc) { gzputc(f, 255); if(sc>255) { gzputc(f, 255); sc -= 255; } else { gzputc(f, sc); sc = 0; } };
|
||||
#define spurge \
|
||||
while (sc) { \
|
||||
gzputc(f, 255); \
|
||||
if (sc > 255) { \
|
||||
gzputc(f, 255); \
|
||||
sc -= 255; \
|
||||
} else { \
|
||||
gzputc(f, sc); \
|
||||
sc = 0; \
|
||||
} \
|
||||
};
|
||||
loopk(cubicsize)
|
||||
{
|
||||
sqr *s = &world[k];
|
||||
#define c(f) (s->f==t->f)
|
||||
#define c(f) (s->f == t->f)
|
||||
// 4 types of blocks, to compress a bit:
|
||||
// 255 (2): same as previous block + count
|
||||
// 254 (3): same as previous, except light // deprecated
|
||||
// SOLID (5)
|
||||
// anything else (9)
|
||||
|
||||
if(SOLID(s))
|
||||
{
|
||||
if(t && c(type) && c(wtex) && c(vdelta))
|
||||
{
|
||||
if (SOLID(s)) {
|
||||
if (t && c(type) && c(wtex) && c(vdelta)) {
|
||||
sc++;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
spurge;
|
||||
gzputc(f, s->type);
|
||||
gzputc(f, s->wtex);
|
||||
gzputc(f, s->vdelta);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
if(t && c(type) && c(floor) && c(ceil) && c(ctex) && c(ftex) && c(utex) && c(wtex) && c(vdelta) && c(tag))
|
||||
{
|
||||
} else {
|
||||
if (t && c(type) && c(floor) && c(ceil) && c(ctex) &&
|
||||
c(ftex) && c(utex) && c(wtex) && c(vdelta) &&
|
||||
c(tag)) {
|
||||
sc++;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
spurge;
|
||||
gzputc(f, s->type);
|
||||
gzputc(f, s->floor);
|
||||
|
@ -206,26 +241,31 @@ void save_world(char *mname)
|
|||
settagareas();
|
||||
};
|
||||
|
||||
void load_world(char *mname) // still supports all map formats that have existed since the earliest cube betas!
|
||||
void
|
||||
load_world(char *mname) // still supports all map formats that have existed
|
||||
// since the earliest cube betas!
|
||||
{
|
||||
stopifrecording();
|
||||
cleardlights();
|
||||
pruneundos();
|
||||
setnames(mname);
|
||||
gzFile f = gzopen(cgzname, "rb9");
|
||||
if(!f) { conoutf("could not read map %s", cgzname); return; };
|
||||
gzread(f, &hdr, sizeof(header)-sizeof(int)*16);
|
||||
if (!f) {
|
||||
conoutf("could not read map %s", cgzname);
|
||||
return;
|
||||
};
|
||||
gzread(f, &hdr, sizeof(header) - sizeof(int) * 16);
|
||||
endianswap(&hdr.version, sizeof(int), 4);
|
||||
if(strncmp(hdr.head, "CUBE", 4)!=0) fatal("while reading map: header malformatted");
|
||||
if(hdr.version>MAPVERSION) fatal("this map requires a newer version of cube");
|
||||
if(sfactor<SMALLEST_FACTOR || sfactor>LARGEST_FACTOR) fatal("illegal map size");
|
||||
if(hdr.version>=4)
|
||||
{
|
||||
gzread(f, &hdr.waterlevel, sizeof(int)*16);
|
||||
if (strncmp(hdr.head, "CUBE", 4) != 0)
|
||||
fatal("while reading map: header malformatted");
|
||||
if (hdr.version > MAPVERSION)
|
||||
fatal("this map requires a newer version of cube");
|
||||
if (sfactor < SMALLEST_FACTOR || sfactor > LARGEST_FACTOR)
|
||||
fatal("illegal map size");
|
||||
if (hdr.version >= 4) {
|
||||
gzread(f, &hdr.waterlevel, sizeof(int) * 16);
|
||||
endianswap(&hdr.waterlevel, sizeof(int), 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
hdr.waterlevel = -100000;
|
||||
};
|
||||
ents.setsize(0);
|
||||
|
@ -235,10 +275,11 @@ void load_world(char *mname) // still supports all map formats that have
|
|||
gzread(f, &e, sizeof(persistent_entity));
|
||||
endianswap(&e, sizeof(short), 4);
|
||||
e.spawned = false;
|
||||
if(e.type==LIGHT)
|
||||
{
|
||||
if(!e.attr2) e.attr2 = 255; // needed for MAPVERSION<=2
|
||||
if(e.attr1>32) e.attr1 = 32; // 12_03 and below
|
||||
if (e.type == LIGHT) {
|
||||
if (!e.attr2)
|
||||
e.attr2 = 255; // needed for MAPVERSION<=2
|
||||
if (e.attr1 > 32)
|
||||
e.attr1 = 32; // 12_03 and below
|
||||
};
|
||||
};
|
||||
free(world);
|
||||
|
@ -250,12 +291,11 @@ void load_world(char *mname) // still supports all map formats that have
|
|||
{
|
||||
sqr *s = &world[k];
|
||||
int type = gzgetc(f);
|
||||
switch(type)
|
||||
{
|
||||
case 255:
|
||||
{
|
||||
switch (type) {
|
||||
case 255: {
|
||||
int n = gzgetc(f);
|
||||
for(int i = 0; i<n; i++, k++) memcpy(&world[k], t, sizeof(sqr));
|
||||
for (int i = 0; i < n; i++, k++)
|
||||
memcpy(&world[k], t, sizeof(sqr));
|
||||
k--;
|
||||
break;
|
||||
};
|
||||
|
@ -266,12 +306,14 @@ void load_world(char *mname) // still supports all map formats that have
|
|||
gzgetc(f);
|
||||
break;
|
||||
};
|
||||
case SOLID:
|
||||
{
|
||||
case SOLID: {
|
||||
s->type = SOLID;
|
||||
s->wtex = gzgetc(f);
|
||||
s->vdelta = gzgetc(f);
|
||||
if(hdr.version<=2) { gzgetc(f); gzgetc(f); };
|
||||
if (hdr.version <= 2) {
|
||||
gzgetc(f);
|
||||
gzgetc(f);
|
||||
};
|
||||
s->ftex = DEFAULT_FLOOR;
|
||||
s->ctex = DEFAULT_CEIL;
|
||||
s->utex = s->wtex;
|
||||
|
@ -280,44 +322,51 @@ void load_world(char *mname) // still supports all map formats that have
|
|||
s->ceil = 16;
|
||||
break;
|
||||
};
|
||||
default:
|
||||
{
|
||||
if(type<0 || type>=MAXTYPE)
|
||||
{
|
||||
default: {
|
||||
if (type < 0 || type >= MAXTYPE) {
|
||||
sprintf_sd(t)("%d @ %d", type, k);
|
||||
fatal("while reading map: type out of range: ", t);
|
||||
fatal("while reading map: type out of range: ",
|
||||
t);
|
||||
};
|
||||
s->type = type;
|
||||
s->floor = gzgetc(f);
|
||||
s->ceil = gzgetc(f);
|
||||
if(s->floor>=s->ceil) s->floor = s->ceil-1; // for pre 12_13
|
||||
if (s->floor >= s->ceil)
|
||||
s->floor = s->ceil - 1; // for pre 12_13
|
||||
s->wtex = gzgetc(f);
|
||||
s->ftex = gzgetc(f);
|
||||
s->ctex = gzgetc(f);
|
||||
if(hdr.version<=2) { gzgetc(f); gzgetc(f); };
|
||||
if (hdr.version <= 2) {
|
||||
gzgetc(f);
|
||||
gzgetc(f);
|
||||
};
|
||||
s->vdelta = gzgetc(f);
|
||||
s->utex = (hdr.version>=2) ? gzgetc(f) : s->wtex;
|
||||
s->tag = (hdr.version>=5) ? gzgetc(f) : 0;
|
||||
s->utex = (hdr.version >= 2) ? gzgetc(f) : s->wtex;
|
||||
s->tag = (hdr.version >= 5) ? gzgetc(f) : 0;
|
||||
s->type = type;
|
||||
};
|
||||
};
|
||||
s->defer = 0;
|
||||
t = s;
|
||||
texuse[s->wtex] = 1;
|
||||
if(!SOLID(s)) texuse[s->utex] = texuse[s->ftex] = texuse[s->ctex] = 1;
|
||||
if (!SOLID(s))
|
||||
texuse[s->utex] = texuse[s->ftex] = texuse[s->ctex] = 1;
|
||||
};
|
||||
gzclose(f);
|
||||
calclight();
|
||||
settagareas();
|
||||
int xs, ys;
|
||||
loopi(256) if(texuse) lookuptexture(i, xs, ys);
|
||||
conoutf("read map %s (%d milliseconds)", cgzname, SDL_GetTicks()-lastmillis);
|
||||
loopi(256) if (texuse) lookuptexture(i, xs, ys);
|
||||
conoutf("read map %s (%d milliseconds)", cgzname,
|
||||
SDL_GetTicks() - lastmillis);
|
||||
conoutf("%s", hdr.maptitle);
|
||||
startmap(mname);
|
||||
loopl(256)
|
||||
{
|
||||
sprintf_sd(aliasname)("level_trigger_%d", l); // can this be done smarter?
|
||||
if(identexists(aliasname)) alias(aliasname, "");
|
||||
sprintf_sd(aliasname)(
|
||||
"level_trigger_%d", l); // can this be done smarter?
|
||||
if (identexists(aliasname))
|
||||
alias(aliasname, "");
|
||||
};
|
||||
execfile("data/default_map_settings.cfg");
|
||||
execfile(pcfname);
|
||||
|
@ -325,4 +374,3 @@ void load_world(char *mname) // still supports all map formats that have
|
|||
};
|
||||
|
||||
COMMANDN(savemap, save_world, ARG_1STR);
|
||||
|
||||
|
|
|
@ -4,58 +4,71 @@
|
|||
|
||||
extern bool hasoverbright;
|
||||
|
||||
VAR(lightscale,1,4,100);
|
||||
VAR(lightscale, 1, 4, 100);
|
||||
|
||||
void lightray(float bx, float by, persistent_entity &light) // done in realtime, needs to be fast
|
||||
void
|
||||
lightray(float bx, float by,
|
||||
persistent_entity &light) // done in realtime, needs to be fast
|
||||
{
|
||||
float lx = light.x+(rnd(21)-10)*0.1f;
|
||||
float ly = light.y+(rnd(21)-10)*0.1f;
|
||||
float dx = bx-lx;
|
||||
float dy = by-ly;
|
||||
float dist = (float)sqrt(dx*dx+dy*dy);
|
||||
if(dist<1.0f) return;
|
||||
float lx = light.x + (rnd(21) - 10) * 0.1f;
|
||||
float ly = light.y + (rnd(21) - 10) * 0.1f;
|
||||
float dx = bx - lx;
|
||||
float dy = by - ly;
|
||||
float dist = (float)sqrt(dx * dx + dy * dy);
|
||||
if (dist < 1.0f)
|
||||
return;
|
||||
int reach = light.attr1;
|
||||
int steps = (int)(reach*reach*1.6f/dist); // can change this for speedup/quality?
|
||||
int steps = (int)(reach * reach * 1.6f /
|
||||
dist); // can change this for speedup/quality?
|
||||
const int PRECBITS = 12;
|
||||
const float PRECF = 4096.0f;
|
||||
int x = (int)(lx*PRECF);
|
||||
int y = (int)(ly*PRECF);
|
||||
int l = light.attr2<<PRECBITS;
|
||||
int stepx = (int)(dx/(float)steps*PRECF);
|
||||
int stepy = (int)(dy/(float)steps*PRECF);
|
||||
int stepl = fast_f2nat(l/(float)steps); // incorrect: light will fade quicker if near edge of the world
|
||||
int x = (int)(lx * PRECF);
|
||||
int y = (int)(ly * PRECF);
|
||||
int l = light.attr2 << PRECBITS;
|
||||
int stepx = (int)(dx / (float)steps * PRECF);
|
||||
int stepy = (int)(dy / (float)steps * PRECF);
|
||||
int stepl =
|
||||
fast_f2nat(l / (float)steps); // incorrect: light will fade quicker
|
||||
// if near edge of the world
|
||||
|
||||
if(hasoverbright)
|
||||
{
|
||||
if (hasoverbright) {
|
||||
l /= lightscale;
|
||||
stepl /= lightscale;
|
||||
|
||||
if(light.attr3 || light.attr4) // coloured light version, special case because most lights are white
|
||||
if (light.attr3 ||
|
||||
light.attr4) // coloured light version, special case because
|
||||
// most lights are white
|
||||
{
|
||||
int dimness = rnd((255-(light.attr2+light.attr3+light.attr4)/3)/16+1);
|
||||
x += stepx*dimness;
|
||||
y += stepy*dimness;
|
||||
int dimness = rnd(
|
||||
(255 -
|
||||
(light.attr2 + light.attr3 + light.attr4) / 3) /
|
||||
16 +
|
||||
1);
|
||||
x += stepx * dimness;
|
||||
y += stepy * dimness;
|
||||
|
||||
if(OUTBORD(x>>PRECBITS, y>>PRECBITS)) return;
|
||||
if (OUTBORD(x >> PRECBITS, y >> PRECBITS))
|
||||
return;
|
||||
|
||||
int g = light.attr3<<PRECBITS;
|
||||
int stepg = fast_f2nat(g/(float)steps);
|
||||
int b = light.attr4<<PRECBITS;
|
||||
int stepb = fast_f2nat(b/(float)steps);
|
||||
int g = light.attr3 << PRECBITS;
|
||||
int stepg = fast_f2nat(g / (float)steps);
|
||||
int b = light.attr4 << PRECBITS;
|
||||
int stepb = fast_f2nat(b / (float)steps);
|
||||
g /= lightscale;
|
||||
stepg /= lightscale;
|
||||
b /= lightscale;
|
||||
stepb /= lightscale;
|
||||
loopi(steps)
|
||||
{
|
||||
sqr *s = S(x>>PRECBITS, y>>PRECBITS);
|
||||
int tl = (l>>PRECBITS)+s->r;
|
||||
s->r = tl>255 ? 255 : tl;
|
||||
tl = (g>>PRECBITS)+s->g;
|
||||
s->g = tl>255 ? 255 : tl;
|
||||
tl = (b>>PRECBITS)+s->b;
|
||||
s->b = tl>255 ? 255 : tl;
|
||||
if(SOLID(s)) return;
|
||||
sqr *s = S(x >> PRECBITS, y >> PRECBITS);
|
||||
int tl = (l >> PRECBITS) + s->r;
|
||||
s->r = tl > 255 ? 255 : tl;
|
||||
tl = (g >> PRECBITS) + s->g;
|
||||
s->g = tl > 255 ? 255 : tl;
|
||||
tl = (b >> PRECBITS) + s->b;
|
||||
s->b = tl > 255 ? 255 : tl;
|
||||
if (SOLID(s))
|
||||
return;
|
||||
x += stepx;
|
||||
y += stepy;
|
||||
l -= stepl;
|
||||
|
@ -65,71 +78,84 @@ void lightray(float bx, float by, persistent_entity &light) // done in realt
|
|||
stepg -= 25;
|
||||
stepb -= 25;
|
||||
};
|
||||
}
|
||||
else // white light, special optimized version
|
||||
} else // white light, special optimized version
|
||||
{
|
||||
int dimness = rnd((255-light.attr2)/16+1);
|
||||
x += stepx*dimness;
|
||||
y += stepy*dimness;
|
||||
int dimness = rnd((255 - light.attr2) / 16 + 1);
|
||||
x += stepx * dimness;
|
||||
y += stepy * dimness;
|
||||
|
||||
if(OUTBORD(x>>PRECBITS, y>>PRECBITS)) return;
|
||||
if (OUTBORD(x >> PRECBITS, y >> PRECBITS))
|
||||
return;
|
||||
|
||||
loopi(steps)
|
||||
{
|
||||
sqr *s = S(x>>PRECBITS, y>>PRECBITS);
|
||||
int tl = (l>>PRECBITS)+s->r;
|
||||
s->r = s->g = s->b = tl>255 ? 255 : tl;
|
||||
if(SOLID(s)) return;
|
||||
sqr *s = S(x >> PRECBITS, y >> PRECBITS);
|
||||
int tl = (l >> PRECBITS) + s->r;
|
||||
s->r = s->g = s->b = tl > 255 ? 255 : tl;
|
||||
if (SOLID(s))
|
||||
return;
|
||||
x += stepx;
|
||||
y += stepy;
|
||||
l -= stepl;
|
||||
stepl -= 25;
|
||||
};
|
||||
};
|
||||
}
|
||||
else // the old (white) light code, here for the few people with old video cards that don't support overbright
|
||||
} else // the old (white) light code, here for the few people with old
|
||||
// video cards that don't support overbright
|
||||
{
|
||||
loopi(steps)
|
||||
{
|
||||
sqr *s = S(x>>PRECBITS, y>>PRECBITS);
|
||||
int light = l>>PRECBITS;
|
||||
if(light>s->r) s->r = s->g = s->b = (uchar)light;
|
||||
if(SOLID(s)) return;
|
||||
sqr *s = S(x >> PRECBITS, y >> PRECBITS);
|
||||
int light = l >> PRECBITS;
|
||||
if (light > s->r)
|
||||
s->r = s->g = s->b = (uchar)light;
|
||||
if (SOLID(s))
|
||||
return;
|
||||
x += stepx;
|
||||
y += stepy;
|
||||
l -= stepl;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
void calclightsource(persistent_entity &l)
|
||||
void
|
||||
calclightsource(persistent_entity &l)
|
||||
{
|
||||
int reach = l.attr1;
|
||||
int sx = l.x-reach;
|
||||
int ex = l.x+reach;
|
||||
int sy = l.y-reach;
|
||||
int ey = l.y+reach;
|
||||
int sx = l.x - reach;
|
||||
int ex = l.x + reach;
|
||||
int sy = l.y - reach;
|
||||
int ey = l.y + reach;
|
||||
|
||||
rndreset();
|
||||
|
||||
const float s = 0.8f;
|
||||
|
||||
for(float sx2 = (float)sx; sx2<=ex; sx2+=s*2) { lightray(sx2, (float)sy, l); lightray(sx2, (float)ey, l); };
|
||||
for(float sy2 = sy+s; sy2<=ey-s; sy2+=s*2) { lightray((float)sx, sy2, l); lightray((float)ex, sy2, l); };
|
||||
for (float sx2 = (float)sx; sx2 <= ex; sx2 += s * 2) {
|
||||
lightray(sx2, (float)sy, l);
|
||||
lightray(sx2, (float)ey, l);
|
||||
};
|
||||
for (float sy2 = sy + s; sy2 <= ey - s; sy2 += s * 2) {
|
||||
lightray((float)sx, sy2, l);
|
||||
lightray((float)ex, sy2, l);
|
||||
};
|
||||
|
||||
rndtime();
|
||||
};
|
||||
|
||||
void postlightarea(block &a) // median filter, smooths out random noise in light and makes it more mipable
|
||||
void
|
||||
postlightarea(block &a) // median filter, smooths out random noise in light and
|
||||
// makes it more mipable
|
||||
{
|
||||
loop(x,a.xs) loop(y,a.ys) // assumes area not on edge of world
|
||||
loop(x, a.xs) loop(y, a.ys) // assumes area not on edge of world
|
||||
{
|
||||
sqr *s = S(x+a.x,y+a.y);
|
||||
#define median(m) s->m = (s->m*2 + SW(s,1,0)->m*2 + SW(s,0,1)->m*2 \
|
||||
+ SW(s,-1,0)->m*2 + SW(s,0,-1)->m*2 \
|
||||
+ SW(s,1,1)->m + SW(s,1,-1)->m \
|
||||
+ SW(s,-1,1)->m + SW(s,-1,-1)->m)/14; // median is 4/2/1 instead
|
||||
sqr *s = S(x + a.x, y + a.y);
|
||||
#define median(m) \
|
||||
s->m = \
|
||||
(s->m * 2 + SW(s, 1, 0)->m * 2 + SW(s, 0, 1)->m * 2 + \
|
||||
SW(s, -1, 0)->m * 2 + SW(s, 0, -1)->m * 2 + SW(s, 1, 1)->m + \
|
||||
SW(s, 1, -1)->m + SW(s, -1, 1)->m + SW(s, -1, -1)->m) / \
|
||||
14; // median is 4/2/1 instead
|
||||
median(r);
|
||||
median(g);
|
||||
median(b);
|
||||
|
@ -138,21 +164,23 @@ void postlightarea(block &a) // median filter, smooths out random noise in li
|
|||
remip(a);
|
||||
};
|
||||
|
||||
void calclight()
|
||||
void
|
||||
calclight()
|
||||
{
|
||||
loop(x,ssize) loop(y,ssize)
|
||||
loop(x, ssize) loop(y, ssize)
|
||||
{
|
||||
sqr *s = S(x,y);
|
||||
sqr *s = S(x, y);
|
||||
s->r = s->g = s->b = 10;
|
||||
};
|
||||
|
||||
loopv(ents)
|
||||
{
|
||||
entity &e = ents[i];
|
||||
if(e.type==LIGHT) calclightsource(e);
|
||||
if (e.type == LIGHT)
|
||||
calclightsource(e);
|
||||
};
|
||||
|
||||
block b = { 1, 1, ssize-2, ssize-2 };
|
||||
block b = {1, 1, ssize - 2, ssize - 2};
|
||||
postlightarea(b);
|
||||
setvar("fullbright", 0);
|
||||
};
|
||||
|
@ -161,54 +189,69 @@ VARP(dynlight, 0, 16, 32);
|
|||
|
||||
vector<block *> dlights;
|
||||
|
||||
void cleardlights()
|
||||
void
|
||||
cleardlights()
|
||||
{
|
||||
while(!dlights.empty())
|
||||
{
|
||||
while (!dlights.empty()) {
|
||||
block *backup = dlights.pop();
|
||||
blockpaste(*backup);
|
||||
free(backup);
|
||||
};
|
||||
};
|
||||
|
||||
void dodynlight(vec &vold, vec &v, int reach, int strength, dynent *owner)
|
||||
void
|
||||
dodynlight(vec &vold, vec &v, int reach, int strength, dynent *owner)
|
||||
{
|
||||
if(!reach) reach = dynlight;
|
||||
if(owner->monsterstate) reach = reach/2;
|
||||
if(!reach) return;
|
||||
if(v.x<0 || v.y<0 || v.x>ssize || v.y>ssize) return;
|
||||
if (!reach)
|
||||
reach = dynlight;
|
||||
if (owner->monsterstate)
|
||||
reach = reach / 2;
|
||||
if (!reach)
|
||||
return;
|
||||
if (v.x < 0 || v.y < 0 || v.x > ssize || v.y > ssize)
|
||||
return;
|
||||
|
||||
int creach = reach+16; // dependant on lightray random offsets!
|
||||
block b = { (int)v.x-creach, (int)v.y-creach, creach*2+1, creach*2+1 };
|
||||
int creach = reach + 16; // dependant on lightray random offsets!
|
||||
block b = {(int)v.x - creach, (int)v.y - creach, creach * 2 + 1,
|
||||
creach * 2 + 1};
|
||||
|
||||
if(b.x<1) b.x = 1;
|
||||
if(b.y<1) b.y = 1;
|
||||
if(b.xs+b.x>ssize-2) b.xs = ssize-2-b.x;
|
||||
if(b.ys+b.y>ssize-2) b.ys = ssize-2-b.y;
|
||||
if (b.x < 1)
|
||||
b.x = 1;
|
||||
if (b.y < 1)
|
||||
b.y = 1;
|
||||
if (b.xs + b.x > ssize - 2)
|
||||
b.xs = ssize - 2 - b.x;
|
||||
if (b.ys + b.y > ssize - 2)
|
||||
b.ys = ssize - 2 - b.y;
|
||||
|
||||
dlights.add(blockcopy(b)); // backup area before rendering in dynlight
|
||||
|
||||
persistent_entity l = { (int)v.x, (int)v.y, (int)v.z, reach, LIGHT, strength, 0, 0 };
|
||||
persistent_entity l = {
|
||||
(int)v.x, (int)v.y, (int)v.z, reach, LIGHT, strength, 0, 0};
|
||||
calclightsource(l);
|
||||
postlightarea(b);
|
||||
};
|
||||
|
||||
// utility functions also used by editing code
|
||||
|
||||
block *blockcopy(block &s)
|
||||
block *
|
||||
blockcopy(block &s)
|
||||
{
|
||||
block *b = (block *)alloc(sizeof(block)+s.xs*s.ys*sizeof(sqr));
|
||||
block *b = (block *)alloc(sizeof(block) + s.xs * s.ys * sizeof(sqr));
|
||||
*b = s;
|
||||
sqr *q = (sqr *)(b+1);
|
||||
for(int x = s.x; x<s.xs+s.x; x++) for(int y = s.y; y<s.ys+s.y; y++) *q++ = *S(x,y);
|
||||
sqr *q = (sqr *)(b + 1);
|
||||
for (int x = s.x; x < s.xs + s.x; x++)
|
||||
for (int y = s.y; y < s.ys + s.y; y++)
|
||||
*q++ = *S(x, y);
|
||||
return b;
|
||||
};
|
||||
|
||||
void blockpaste(block &b)
|
||||
void
|
||||
blockpaste(block &b)
|
||||
{
|
||||
sqr *q = (sqr *)((&b)+1);
|
||||
for(int x = b.x; x<b.xs+b.x; x++) for(int y = b.y; y<b.ys+b.y; y++) *S(x,y) = *q++;
|
||||
sqr *q = (sqr *)((&b) + 1);
|
||||
for (int x = b.x; x < b.xs + b.x; x++)
|
||||
for (int y = b.y; y < b.ys + b.y; y++)
|
||||
*S(x, y) = *q++;
|
||||
remipmore(b);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -8,79 +8,116 @@ float rdist[NUMRAYS];
|
|||
bool ocull = true;
|
||||
float odist = 256;
|
||||
|
||||
void toggleocull() { ocull = !ocull; };
|
||||
void
|
||||
toggleocull()
|
||||
{
|
||||
ocull = !ocull;
|
||||
};
|
||||
|
||||
COMMAND(toggleocull, ARG_NONE);
|
||||
|
||||
// constructs occlusion map: cast rays in all directions on the 2d plane and record distance.
|
||||
// done exactly once per frame.
|
||||
// constructs occlusion map: cast rays in all directions on the 2d plane and
|
||||
// record distance. done exactly once per frame.
|
||||
|
||||
void computeraytable(float vx, float vy)
|
||||
void
|
||||
computeraytable(float vx, float vy)
|
||||
{
|
||||
if(!ocull) return;
|
||||
if (!ocull)
|
||||
return;
|
||||
|
||||
odist = getvar("fog")*1.5f;
|
||||
odist = getvar("fog") * 1.5f;
|
||||
|
||||
float apitch = (float)fabs(player1->pitch);
|
||||
float af = getvar("fov")/2+apitch/1.5f+3;
|
||||
float byaw = (player1->yaw-90+af)/360*PI2;
|
||||
float syaw = (player1->yaw-90-af)/360*PI2;
|
||||
float af = getvar("fov") / 2 + apitch / 1.5f + 3;
|
||||
float byaw = (player1->yaw - 90 + af) / 360 * PI2;
|
||||
float syaw = (player1->yaw - 90 - af) / 360 * PI2;
|
||||
|
||||
loopi(NUMRAYS)
|
||||
{
|
||||
float angle = i*PI2/NUMRAYS;
|
||||
if((apitch>45 // must be bigger if fov>120
|
||||
|| (angle<byaw && angle>syaw)
|
||||
|| (angle<byaw-PI2 && angle>syaw-PI2)
|
||||
|| (angle<byaw+PI2 && angle>syaw+PI2))
|
||||
&& !OUTBORD(vx, vy)
|
||||
&& !SOLID(S(fast_f2nat(vx), fast_f2nat(vy)))) // try to avoid tracing ray if outside of frustrum
|
||||
float angle = i * PI2 / NUMRAYS;
|
||||
if ((apitch > 45 // must be bigger if fov>120
|
||||
|| (angle < byaw && angle > syaw) ||
|
||||
(angle < byaw - PI2 && angle > syaw - PI2) ||
|
||||
(angle < byaw + PI2 && angle > syaw + PI2)) &&
|
||||
!OUTBORD(vx, vy) &&
|
||||
!SOLID(S(fast_f2nat(vx),
|
||||
fast_f2nat(vy)))) // try to avoid tracing ray if outside
|
||||
// of frustrum
|
||||
{
|
||||
float ray = i*8/(float)NUMRAYS;
|
||||
float ray = i * 8 / (float)NUMRAYS;
|
||||
float dx, dy;
|
||||
if(ray>1 && ray<3) { dx = -(ray-2); dy = 1; }
|
||||
else if(ray>=3 && ray<5) { dx = -1; dy = -(ray-4); }
|
||||
else if(ray>=5 && ray<7) { dx = ray-6; dy = -1; }
|
||||
else { dx = 1; dy = ray>4 ? ray-8 : ray; };
|
||||
if (ray > 1 && ray < 3) {
|
||||
dx = -(ray - 2);
|
||||
dy = 1;
|
||||
} else if (ray >= 3 && ray < 5) {
|
||||
dx = -1;
|
||||
dy = -(ray - 4);
|
||||
} else if (ray >= 5 && ray < 7) {
|
||||
dx = ray - 6;
|
||||
dy = -1;
|
||||
} else {
|
||||
dx = 1;
|
||||
dy = ray > 4 ? ray - 8 : ray;
|
||||
};
|
||||
float sx = vx;
|
||||
float sy = vy;
|
||||
for(;;)
|
||||
{
|
||||
for (;;) {
|
||||
sx += dx;
|
||||
sy += dy;
|
||||
if(SOLID(S(fast_f2nat(sx), fast_f2nat(sy)))) // 90% of time spend in this function is on this line
|
||||
if (SOLID(S(fast_f2nat(sx),
|
||||
fast_f2nat(
|
||||
sy)))) // 90% of time spend in this
|
||||
// function is on this line
|
||||
{
|
||||
rdist[i] = (float)(fabs(sx-vx)+fabs(sy-vy));
|
||||
rdist[i] = (float)(fabs(sx - vx) +
|
||||
fabs(sy - vy));
|
||||
break;
|
||||
};
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
rdist[i] = 2;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// test occlusion for a cube... one of the most computationally expensive functions in the engine
|
||||
// as its done for every cube and entity, but its effect is more than worth it!
|
||||
// test occlusion for a cube... one of the most computationally expensive
|
||||
// functions in the engine as its done for every cube and entity, but its effect
|
||||
// is more than worth it!
|
||||
|
||||
inline float ca(float x, float y) { return x>y ? y/x : 2-x/y; };
|
||||
inline float ma(float x, float y) { return x==0 ? (y>0 ? 2 : -2) : y/x; };
|
||||
|
||||
int isoccluded(float vx, float vy, float cx, float cy, float csize) // v = viewer, c = cube to test
|
||||
inline float
|
||||
ca(float x, float y)
|
||||
{
|
||||
if(!ocull) return 0;
|
||||
return x > y ? y / x : 2 - x / y;
|
||||
};
|
||||
inline float
|
||||
ma(float x, float y)
|
||||
{
|
||||
return x == 0 ? (y > 0 ? 2 : -2) : y / x;
|
||||
};
|
||||
|
||||
float nx = vx, ny = vy; // n = point on the border of the cube that is closest to v
|
||||
if(nx<cx) nx = cx;
|
||||
else if(nx>cx+csize) nx = cx+csize;
|
||||
if(ny<cy) ny = cy;
|
||||
else if(ny>cy+csize) ny = cy+csize;
|
||||
float xdist = (float)fabs(nx-vx);
|
||||
float ydist = (float)fabs(ny-vy);
|
||||
if(xdist>odist || ydist>odist) return 2;
|
||||
float dist = xdist+ydist-1; // 1 needed?
|
||||
int
|
||||
isoccluded(float vx, float vy, float cx, float cy,
|
||||
float csize) // v = viewer, c = cube to test
|
||||
{
|
||||
if (!ocull)
|
||||
return 0;
|
||||
|
||||
float
|
||||
nx = vx,
|
||||
ny = vy; // n = point on the border of the cube that is closest to v
|
||||
if (nx < cx)
|
||||
nx = cx;
|
||||
else if (nx > cx + csize)
|
||||
nx = cx + csize;
|
||||
if (ny < cy)
|
||||
ny = cy;
|
||||
else if (ny > cy + csize)
|
||||
ny = cy + csize;
|
||||
float xdist = (float)fabs(nx - vx);
|
||||
float ydist = (float)fabs(ny - vy);
|
||||
if (xdist > odist || ydist > odist)
|
||||
return 2;
|
||||
float dist = xdist + ydist - 1; // 1 needed?
|
||||
|
||||
// ABC
|
||||
// D E
|
||||
|
@ -88,49 +125,80 @@ int isoccluded(float vx, float vy, float cx, float cy, float csize) // v = v
|
|||
|
||||
// - check middle cube? BG
|
||||
|
||||
// find highest and lowest angle in the occlusion map that this cube spans, based on its most left and right
|
||||
// points on the border from the viewer pov... I see no easier way to do this than this silly code below
|
||||
// find highest and lowest angle in the occlusion map that this cube
|
||||
// spans, based on its most left and right points on the border from the
|
||||
// viewer pov... I see no easier way to do this than this silly code
|
||||
// below
|
||||
|
||||
float h, l;
|
||||
if(cx<=vx) // ABDFG
|
||||
if (cx <= vx) // ABDFG
|
||||
{
|
||||
if(cx+csize<vx) // ADF
|
||||
if (cx + csize < vx) // ADF
|
||||
{
|
||||
if(cy<=vy) // AD
|
||||
if (cy <= vy) // AD
|
||||
{
|
||||
if(cy+csize<vy) { h = ca(-(cx-vx), -(cy+csize-vy))+4; l = ca(-(cx+csize-vx), -(cy-vy))+4; } // A
|
||||
else { h = ma(-(cx+csize-vx), -(cy+csize-vy))+4; l = ma(-(cx+csize-vx), -(cy-vy))+4; } // D
|
||||
}
|
||||
else { h = ca(cy+csize-vy, -(cx+csize-vx))+2; l = ca(cy-vy, -(cx-vx))+2; }; // F
|
||||
}
|
||||
else // BG
|
||||
if (cy + csize < vy) {
|
||||
h = ca(-(cx - vx), -(cy + csize - vy)) +
|
||||
4;
|
||||
l = ca(-(cx + csize - vx), -(cy - vy)) +
|
||||
4;
|
||||
} // A
|
||||
else {
|
||||
h = ma(-(cx + csize - vx),
|
||||
-(cy + csize - vy)) +
|
||||
4;
|
||||
l = ma(-(cx + csize - vx), -(cy - vy)) +
|
||||
4;
|
||||
} // D
|
||||
} else {
|
||||
h = ca(cy + csize - vy, -(cx + csize - vx)) + 2;
|
||||
l = ca(cy - vy, -(cx - vx)) + 2;
|
||||
}; // F
|
||||
} else // BG
|
||||
{
|
||||
if(cy<=vy)
|
||||
{
|
||||
if(cy+csize<vy) { h = ma(-(cy+csize-vy), cx-vx)+6; l = ma(-(cy+csize-vy), cx+csize-vx)+6; } // B
|
||||
else return 0;
|
||||
}
|
||||
else { h = ma(cy-vy, -(cx+csize-vx))+2; l = ma(cy-vy, -(cx-vx))+2; }; // G
|
||||
if (cy <= vy) {
|
||||
if (cy + csize < vy) {
|
||||
h = ma(-(cy + csize - vy), cx - vx) + 6;
|
||||
l = ma(-(cy + csize - vy),
|
||||
cx + csize - vx) +
|
||||
6;
|
||||
} // B
|
||||
else
|
||||
return 0;
|
||||
} else {
|
||||
h = ma(cy - vy, -(cx + csize - vx)) + 2;
|
||||
l = ma(cy - vy, -(cx - vx)) + 2;
|
||||
}; // G
|
||||
};
|
||||
}
|
||||
else // CEH
|
||||
} else // CEH
|
||||
{
|
||||
if(cy<=vy) // CE
|
||||
if (cy <= vy) // CE
|
||||
{
|
||||
if(cy+csize<vy) { h = ca(-(cy-vy), cx-vx)+6; l = ca(-(cy+csize-vy), cx+csize-vx)+6; } // C
|
||||
else { h = ma(cx-vx, cy-vy); l = ma(cx-vx, cy+csize-vy); }; // E
|
||||
}
|
||||
else { h = ca(cx+csize-vx, cy-vy); l = ca(cx-vx, cy+csize-vy); }; // H
|
||||
if (cy + csize < vy) {
|
||||
h = ca(-(cy - vy), cx - vx) + 6;
|
||||
l = ca(-(cy + csize - vy), cx + csize - vx) + 6;
|
||||
} // C
|
||||
else {
|
||||
h = ma(cx - vx, cy - vy);
|
||||
l = ma(cx - vx, cy + csize - vy);
|
||||
}; // E
|
||||
} else {
|
||||
h = ca(cx + csize - vx, cy - vy);
|
||||
l = ca(cx - vx, cy + csize - vy);
|
||||
}; // H
|
||||
};
|
||||
int si = fast_f2nat(h*(NUMRAYS/8))+NUMRAYS; // get indexes into occlusion map from angles
|
||||
int ei = fast_f2nat(l*(NUMRAYS/8))+NUMRAYS+1;
|
||||
if(ei<=si) ei += NUMRAYS;
|
||||
int si = fast_f2nat(h * (NUMRAYS / 8)) +
|
||||
NUMRAYS; // get indexes into occlusion map from angles
|
||||
int ei = fast_f2nat(l * (NUMRAYS / 8)) + NUMRAYS + 1;
|
||||
if (ei <= si)
|
||||
ei += NUMRAYS;
|
||||
|
||||
for(int i = si; i<=ei; i++)
|
||||
{
|
||||
if(dist<rdist[i&(NUMRAYS-1)]) return 0; // if any value in this segment of the occlusion map is further away then cube is not occluded
|
||||
for (int i = si; i <= ei; i++) {
|
||||
if (dist < rdist[i & (NUMRAYS - 1)])
|
||||
return 0; // if any value in this segment of the
|
||||
// occlusion map is further away then cube is
|
||||
// not occluded
|
||||
};
|
||||
|
||||
return 1; // cube is entirely occluded
|
||||
};
|
||||
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
// worldrender.cpp: goes through all cubes in top down quad tree fashion, determines what has to
|
||||
// be rendered and how (depending on neighbouring cubes), then calls functions in rendercubes.cpp
|
||||
// worldrender.cpp: goes through all cubes in top down quad tree fashion,
|
||||
// determines what has to be rendered and how (depending on neighbouring cubes),
|
||||
// then calls functions in rendercubes.cpp
|
||||
|
||||
#include "cube.h"
|
||||
|
||||
void render_wall(sqr *o, sqr *s, int x1, int y1, int x2, int y2, int mip, sqr *d1, sqr *d2, bool topleft)
|
||||
void
|
||||
render_wall(sqr *o, sqr *s, int x1, int y1, int x2, int y2, int mip, sqr *d1,
|
||||
sqr *d2, bool topleft)
|
||||
{
|
||||
if(SOLID(o) || o->type==SEMISOLID)
|
||||
{
|
||||
if (SOLID(o) || o->type == SEMISOLID) {
|
||||
float c1 = s->floor;
|
||||
float c2 = s->floor;
|
||||
if(s->type==FHF) { c1 -= d1->vdelta/4.0f; c2 -= d2->vdelta/4.0f; };
|
||||
if (s->type == FHF) {
|
||||
c1 -= d1->vdelta / 4.0f;
|
||||
c2 -= d2->vdelta / 4.0f;
|
||||
};
|
||||
float f1 = s->ceil;
|
||||
float f2 = s->ceil;
|
||||
if(s->type==CHF) { f1 += d1->vdelta/4.0f; f2 += d2->vdelta/4.0f; };
|
||||
//if(f1-c1<=0 && f2-c2<=0) return;
|
||||
render_square(o->wtex, c1, c2, f1, f2, x1<<mip, y1<<mip, x2<<mip, y2<<mip, 1<<mip, d1, d2, topleft);
|
||||
if (s->type == CHF) {
|
||||
f1 += d1->vdelta / 4.0f;
|
||||
f2 += d2->vdelta / 4.0f;
|
||||
};
|
||||
// if(f1-c1<=0 && f2-c2<=0) return;
|
||||
render_square(o->wtex, c1, c2, f1, f2, x1 << mip, y1 << mip,
|
||||
x2 << mip, y2 << mip, 1 << mip, d1, d2, topleft);
|
||||
return;
|
||||
};
|
||||
{
|
||||
|
@ -22,38 +31,36 @@ void render_wall(sqr *o, sqr *s, int x1, int y1, int x2, int y2, int mip, sqr *d
|
|||
float f2 = s->floor;
|
||||
float c1 = o->floor;
|
||||
float c2 = o->floor;
|
||||
if(o->type==FHF && s->type!=FHF)
|
||||
{
|
||||
c1 -= d1->vdelta/4.0f;
|
||||
c2 -= d2->vdelta/4.0f;
|
||||
if (o->type == FHF && s->type != FHF) {
|
||||
c1 -= d1->vdelta / 4.0f;
|
||||
c2 -= d2->vdelta / 4.0f;
|
||||
}
|
||||
if(s->type==FHF && o->type!=FHF)
|
||||
{
|
||||
f1 -= d1->vdelta/4.0f;
|
||||
f2 -= d2->vdelta/4.0f;
|
||||
if (s->type == FHF && o->type != FHF) {
|
||||
f1 -= d1->vdelta / 4.0f;
|
||||
f2 -= d2->vdelta / 4.0f;
|
||||
}
|
||||
if(f1>=c1 && f2>=c2) goto skip;
|
||||
render_square(o->wtex, f1, f2, c1, c2, x1<<mip, y1<<mip, x2<<mip, y2<<mip, 1<<mip, d1, d2, topleft);
|
||||
if (f1 >= c1 && f2 >= c2)
|
||||
goto skip;
|
||||
render_square(o->wtex, f1, f2, c1, c2, x1 << mip, y1 << mip,
|
||||
x2 << mip, y2 << mip, 1 << mip, d1, d2, topleft);
|
||||
};
|
||||
skip:
|
||||
{
|
||||
skip: {
|
||||
float f1 = o->ceil;
|
||||
float f2 = o->ceil;
|
||||
float c1 = s->ceil;
|
||||
float c2 = s->ceil;
|
||||
if(o->type==CHF && s->type!=CHF)
|
||||
{
|
||||
f1 += d1->vdelta/4.0f;
|
||||
f2 += d2->vdelta/4.0f;
|
||||
if (o->type == CHF && s->type != CHF) {
|
||||
f1 += d1->vdelta / 4.0f;
|
||||
f2 += d2->vdelta / 4.0f;
|
||||
} else if (s->type == CHF && o->type != CHF) {
|
||||
c1 += d1->vdelta / 4.0f;
|
||||
c2 += d2->vdelta / 4.0f;
|
||||
}
|
||||
else if(s->type==CHF && o->type!=CHF)
|
||||
{
|
||||
c1 += d1->vdelta/4.0f;
|
||||
c2 += d2->vdelta/4.0f;
|
||||
}
|
||||
if(c1<=f1 && c2<=f2) return;
|
||||
render_square(o->utex, f1, f2, c1, c2, x1<<mip, y1<<mip, x2<<mip, y2<<mip, 1<<mip, d1, d2, topleft);
|
||||
};
|
||||
if (c1 <= f1 && c2 <= f2)
|
||||
return;
|
||||
render_square(o->utex, f1, f2, c1, c2, x1 << mip, y1 << mip, x2 << mip,
|
||||
y2 << mip, 1 << mip, d1, d2, topleft);
|
||||
};
|
||||
};
|
||||
|
||||
const int MAX_MIP = 5; // 32x32 unit blocks
|
||||
|
@ -66,225 +73,286 @@ int min_lod;
|
|||
|
||||
int stats[LARGEST_FACTOR];
|
||||
|
||||
// detect those cases where a higher mip solid has a visible wall next to lower mip cubes
|
||||
// (used for wall rendering below)
|
||||
// detect those cases where a higher mip solid has a visible wall next to lower
|
||||
// mip cubes (used for wall rendering below)
|
||||
|
||||
bool issemi(int mip, int x, int y, int x1, int y1, int x2, int y2)
|
||||
bool
|
||||
issemi(int mip, int x, int y, int x1, int y1, int x2, int y2)
|
||||
{
|
||||
if(!(mip--)) return true;
|
||||
if (!(mip--))
|
||||
return true;
|
||||
sqr *w = wmip[mip];
|
||||
int msize = ssize>>mip;
|
||||
int msize = ssize >> mip;
|
||||
x *= 2;
|
||||
y *= 2;
|
||||
switch(SWS(w, x+x1, y+y1, msize)->type)
|
||||
{
|
||||
case SEMISOLID: if(issemi(mip, x+x1, y+y1, x1, y1, x2, y2)) return true;
|
||||
switch (SWS(w, x + x1, y + y1, msize)->type) {
|
||||
case SEMISOLID:
|
||||
if (issemi(mip, x + x1, y + y1, x1, y1, x2, y2))
|
||||
return true;
|
||||
case CORNER:
|
||||
case SOLID: break;
|
||||
default: return true;
|
||||
case SOLID:
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
};
|
||||
switch(SWS(w, x+x2, y+y2, msize)->type)
|
||||
{
|
||||
case SEMISOLID: if(issemi(mip, x+x2, y+y2, x1, y1, x2, y2)) return true;
|
||||
switch (SWS(w, x + x2, y + y2, msize)->type) {
|
||||
case SEMISOLID:
|
||||
if (issemi(mip, x + x2, y + y2, x1, y1, x2, y2))
|
||||
return true;
|
||||
case CORNER:
|
||||
case SOLID: break;
|
||||
default: return true;
|
||||
case SOLID:
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
bool render_floor, render_ceil;
|
||||
|
||||
// the core recursive function, renders a rect of cubes at a certain mip level from a viewer perspective
|
||||
// call itself for lower mip levels, on most modern machines however this function will use the higher
|
||||
// mip levels only for perfect mips.
|
||||
// the core recursive function, renders a rect of cubes at a certain mip level
|
||||
// from a viewer perspective call itself for lower mip levels, on most modern
|
||||
// machines however this function will use the higher mip levels only for
|
||||
// perfect mips.
|
||||
|
||||
void render_seg_new(float vx, float vy, float vh, int mip, int x, int y, int xs, int ys)
|
||||
void
|
||||
render_seg_new(
|
||||
float vx, float vy, float vh, int mip, int x, int y, int xs, int ys)
|
||||
{
|
||||
sqr *w = wmip[mip];
|
||||
int sz = ssize>>mip;
|
||||
int vxx = ((int)vx+(1<<mip)/2)>>mip;
|
||||
int vyy = ((int)vy+(1<<mip)/2)>>mip;
|
||||
int lx = vxx-lodleft; // these mark the rect inside the current rest that we want to render using a lower mip level
|
||||
int ly = vyy-lodtop;
|
||||
int rx = vxx+lodright;
|
||||
int ry = vyy+lodbot;
|
||||
int sz = ssize >> mip;
|
||||
int vxx = ((int)vx + (1 << mip) / 2) >> mip;
|
||||
int vyy = ((int)vy + (1 << mip) / 2) >> mip;
|
||||
int lx =
|
||||
vxx - lodleft; // these mark the rect inside the current rest that
|
||||
// we want to render using a lower mip level
|
||||
int ly = vyy - lodtop;
|
||||
int rx = vxx + lodright;
|
||||
int ry = vyy + lodbot;
|
||||
|
||||
float fsize = (float)(1<<mip);
|
||||
for(int ox = x; ox<xs; ox++) for(int oy = y; oy<ys; oy++) // first collect occlusion information for this block
|
||||
float fsize = (float)(1 << mip);
|
||||
for (int ox = x; ox < xs; ox++)
|
||||
for (int oy = y; oy < ys;
|
||||
oy++) // first collect occlusion information for this block
|
||||
{
|
||||
SWS(w,ox,oy,sz)->occluded = isoccluded(player1->o.x, player1->o.y, (float)(ox<<mip), (float)(oy<<mip), fsize);
|
||||
SWS(w, ox, oy, sz)->occluded =
|
||||
isoccluded(player1->o.x, player1->o.y,
|
||||
(float)(ox << mip), (float)(oy << mip), fsize);
|
||||
};
|
||||
|
||||
int pvx = (int)vx>>mip;
|
||||
int pvy = (int)vy>>mip;
|
||||
if(pvx>=0 && pvy>=0 && pvx<sz && pvy<sz)
|
||||
{
|
||||
//SWS(w,vxx,vyy,sz)->occluded = 0;
|
||||
SWS(w, pvx, pvy, sz)->occluded = 0; // player cell never occluded
|
||||
int pvx = (int)vx >> mip;
|
||||
int pvy = (int)vy >> mip;
|
||||
if (pvx >= 0 && pvy >= 0 && pvx < sz && pvy < sz) {
|
||||
// SWS(w,vxx,vyy,sz)->occluded = 0;
|
||||
SWS(w, pvx, pvy, sz)->occluded =
|
||||
0; // player cell never occluded
|
||||
};
|
||||
|
||||
#define df(x) s->floor-(x->vdelta/4.0f)
|
||||
#define dc(x) s->ceil+(x->vdelta/4.0f)
|
||||
#define df(x) s->floor - (x->vdelta / 4.0f)
|
||||
#define dc(x) s->ceil + (x->vdelta / 4.0f)
|
||||
|
||||
// loop through the rect 3 times (for floor/ceil/walls seperately, to facilitate dynamic stripify)
|
||||
// for each we skip occluded cubes (occlusion at higher mip levels is a big time saver!).
|
||||
// during the first loop (ceil) we collect cubes that lie within the lower mip rect and are
|
||||
// also deferred, and render them recursively. Anything left (perfect mips and higher lods) we
|
||||
// render here.
|
||||
// loop through the rect 3 times (for floor/ceil/walls seperately, to
|
||||
// facilitate dynamic stripify) for each we skip occluded cubes
|
||||
// (occlusion at higher mip levels is a big time saver!). during the
|
||||
// first loop (ceil) we collect cubes that lie within the lower mip rect
|
||||
// and are also deferred, and render them recursively. Anything left
|
||||
// (perfect mips and higher lods) we render here.
|
||||
|
||||
#define LOOPH {for(int xx = x; xx<xs; xx++) for(int yy = y; yy<ys; yy++) { \
|
||||
sqr *s = SWS(w,xx,yy,sz); if(s->occluded==1) continue; \
|
||||
if(s->defer && !s->occluded && mip && xx>=lx && xx<rx && yy>=ly && yy<ry)
|
||||
#define LOOPD sqr *t = SWS(s,1,0,sz); \
|
||||
sqr *u = SWS(s,1,1,sz); \
|
||||
sqr *v = SWS(s,0,1,sz); \
|
||||
#define LOOPH \
|
||||
{ \
|
||||
for (int xx = x; xx < xs; xx++) \
|
||||
for (int yy = y; yy < ys; yy++) { \
|
||||
sqr *s = SWS(w, xx, yy, sz); \
|
||||
if (s->occluded == 1) \
|
||||
continue; \
|
||||
if (s->defer && !s->occluded && mip && \
|
||||
xx >= lx && xx < rx && yy >= ly && \
|
||||
yy < ry)
|
||||
#define LOOPD \
|
||||
sqr *t = SWS(s, 1, 0, sz); \
|
||||
sqr *u = SWS(s, 1, 1, sz); \
|
||||
sqr *v = SWS(s, 0, 1, sz);
|
||||
|
||||
LOOPH // ceils
|
||||
{
|
||||
int start = yy;
|
||||
sqr *next;
|
||||
while(yy<ys-1 && (next = SWS(w,xx,yy+1,sz))->defer && !next->occluded) yy++; // collect 2xN rect of lower mip
|
||||
render_seg_new(vx, vy, vh, mip-1, xx*2, start*2, xx*2+2, yy*2+2);
|
||||
while (yy < ys - 1 && (next = SWS(w, xx, yy + 1, sz))->defer &&
|
||||
!next->occluded)
|
||||
yy++; // collect 2xN rect of lower mip
|
||||
render_seg_new(vx, vy, vh, mip - 1, xx * 2, start * 2,
|
||||
xx * 2 + 2, yy * 2 + 2);
|
||||
continue;
|
||||
};
|
||||
stats[mip]++;
|
||||
LOOPD
|
||||
if((s->type==SPACE || s->type==FHF) && s->ceil>=vh && render_ceil)
|
||||
render_flat(s->ctex, xx<<mip, yy<<mip, 1<<mip, s->ceil, s, t, u, v, true);
|
||||
if(s->type==CHF) //if(s->ceil>=vh)
|
||||
render_flatdelta(s->ctex, xx<<mip, yy<<mip, 1<<mip, dc(s), dc(t), dc(u), dc(v), s, t, u, v, true);
|
||||
}};
|
||||
if ((s->type == SPACE || s->type == FHF) && s->ceil >= vh &&
|
||||
render_ceil)
|
||||
render_flat(s->ctex, xx << mip, yy << mip, 1 << mip, s->ceil, s,
|
||||
t, u, v, true);
|
||||
if (s->type == CHF) // if(s->ceil>=vh)
|
||||
render_flatdelta(s->ctex, xx << mip, yy << mip, 1 << mip, dc(s),
|
||||
dc(t), dc(u), dc(v), s, t, u, v, true);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
LOOPH continue; // floors
|
||||
LOOPD
|
||||
if((s->type==SPACE || s->type==CHF) && s->floor<=vh && render_floor)
|
||||
{
|
||||
render_flat(s->ftex, xx<<mip, yy<<mip, 1<<mip, s->floor, s, t, u, v, false);
|
||||
if(s->floor<hdr.waterlevel && !SOLID(s)) addwaterquad(xx<<mip, yy<<mip, 1<<mip);
|
||||
};
|
||||
if(s->type==FHF)
|
||||
{
|
||||
render_flatdelta(s->ftex, xx<<mip, yy<<mip, 1<<mip, df(s), df(t), df(u), df(v), s, t, u, v, false);
|
||||
if(s->floor-s->vdelta/4.0f<hdr.waterlevel && !SOLID(s)) addwaterquad(xx<<mip, yy<<mip, 1<<mip);
|
||||
};
|
||||
}};
|
||||
LOOPH continue; // floors
|
||||
LOOPD
|
||||
if ((s->type == SPACE || s->type == CHF) && s->floor <= vh && render_floor) {
|
||||
render_flat(s->ftex, xx << mip, yy << mip, 1 << mip, s->floor, s, t, u,
|
||||
v, false);
|
||||
if (s->floor < hdr.waterlevel && !SOLID(s))
|
||||
addwaterquad(xx << mip, yy << mip, 1 << mip);
|
||||
};
|
||||
if (s->type == FHF) {
|
||||
render_flatdelta(s->ftex, xx << mip, yy << mip, 1 << mip, df(s), df(t),
|
||||
df(u), df(v), s, t, u, v, false);
|
||||
if (s->floor - s->vdelta / 4.0f < hdr.waterlevel && !SOLID(s))
|
||||
addwaterquad(xx << mip, yy << mip, 1 << mip);
|
||||
};
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
LOOPH continue; // walls
|
||||
LOOPD
|
||||
// w
|
||||
// zSt
|
||||
// vu
|
||||
LOOPH continue; // walls
|
||||
LOOPD
|
||||
// w
|
||||
// zSt
|
||||
// vu
|
||||
|
||||
sqr *w = SWS(s,0,-1,sz);
|
||||
sqr *z = SWS(s,-1,0,sz);
|
||||
bool normalwall = true;
|
||||
sqr *w = SWS(s, 0, -1, sz);
|
||||
sqr *z = SWS(s, -1, 0, sz);
|
||||
bool normalwall = true;
|
||||
|
||||
if(s->type==CORNER)
|
||||
{
|
||||
if (s->type == CORNER) {
|
||||
// cull also
|
||||
bool topleft = true;
|
||||
sqr *h1 = NULL;
|
||||
sqr *h2 = NULL;
|
||||
if(SOLID(z))
|
||||
{
|
||||
if(SOLID(w)) { render_wall(w, h2 = s, xx+1, yy, xx, yy+1, mip, t, v, false); topleft = false; }
|
||||
else if(SOLID(v)) { render_wall(v, h2 = s, xx, yy, xx+1, yy+1, mip, s, u, false); };
|
||||
}
|
||||
else if(SOLID(t))
|
||||
{
|
||||
if(SOLID(w)) { render_wall(w, h1 = s, xx+1, yy+1, xx, yy, mip, u, s, false); }
|
||||
else if(SOLID(v)) { render_wall(v, h1 = s, xx, yy+1, xx+1, yy, mip, v, t, false); topleft = false; };
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SOLID(z)) {
|
||||
if (SOLID(w)) {
|
||||
render_wall(w, h2 = s, xx + 1, yy, xx, yy + 1, mip, t,
|
||||
v, false);
|
||||
topleft = false;
|
||||
} else if (SOLID(v)) {
|
||||
render_wall(v, h2 = s, xx, yy, xx + 1, yy + 1, mip, s,
|
||||
u, false);
|
||||
};
|
||||
} else if (SOLID(t)) {
|
||||
if (SOLID(w)) {
|
||||
render_wall(w, h1 = s, xx + 1, yy + 1, xx, yy, mip, u,
|
||||
s, false);
|
||||
} else if (SOLID(v)) {
|
||||
render_wall(v, h1 = s, xx, yy + 1, xx + 1, yy, mip, v,
|
||||
t, false);
|
||||
topleft = false;
|
||||
};
|
||||
} else {
|
||||
normalwall = false;
|
||||
bool wv = w->ceil-w->floor < v->ceil-v->floor;
|
||||
if(z->ceil-z->floor < t->ceil-t->floor)
|
||||
{
|
||||
if(wv) { render_wall(h1 = s, h2 = v, xx+1, yy, xx, yy+1, mip, t, v, false); topleft = false; }
|
||||
else { render_wall(h1 = s, h2 = w, xx, yy, xx+1, yy+1, mip, s, u, false); };
|
||||
}
|
||||
else
|
||||
{
|
||||
if(wv) { render_wall(h2 = s, h1 = v, xx+1, yy+1, xx, yy, mip, u, s, false); }
|
||||
else { render_wall(h2 = s, h1 = w, xx, yy+1, xx+1, yy, mip, v, t, false); topleft = false; };
|
||||
bool wv = w->ceil - w->floor < v->ceil - v->floor;
|
||||
if (z->ceil - z->floor < t->ceil - t->floor) {
|
||||
if (wv) {
|
||||
render_wall(h1 = s, h2 = v, xx + 1, yy, xx,
|
||||
yy + 1, mip, t, v, false);
|
||||
topleft = false;
|
||||
} else {
|
||||
render_wall(h1 = s, h2 = w, xx, yy, xx + 1,
|
||||
yy + 1, mip, s, u, false);
|
||||
};
|
||||
} else {
|
||||
if (wv) {
|
||||
render_wall(h2 = s, h1 = v, xx + 1, yy + 1, xx,
|
||||
yy, mip, u, s, false);
|
||||
} else {
|
||||
render_wall(h2 = s, h1 = w, xx, yy + 1, xx + 1,
|
||||
yy, mip, v, t, false);
|
||||
topleft = false;
|
||||
};
|
||||
};
|
||||
render_tris(xx<<mip, yy<<mip, 1<<mip, topleft, h1, h2, s, t, u, v);
|
||||
}
|
||||
|
||||
if(normalwall)
|
||||
{
|
||||
bool inner = xx!=sz-1 && yy!=sz-1;
|
||||
|
||||
if(xx>=vxx && xx!=0 && yy!=sz-1 && !SOLID(z) && (!SOLID(s) || z->type!=CORNER)
|
||||
&& (z->type!=SEMISOLID || issemi(mip, xx-1, yy, 1, 0, 1, 1)))
|
||||
render_wall(s, z, xx, yy, xx, yy+1, mip, s, v, true);
|
||||
if(xx<=vxx && inner && !SOLID(t) && (!SOLID(s) || t->type!=CORNER)
|
||||
&& (t->type!=SEMISOLID || issemi(mip, xx+1, yy, 0, 0, 0, 1)))
|
||||
render_wall(s, t, xx+1, yy, xx+1, yy+1, mip, t, u, false);
|
||||
if(yy>=vyy && yy!=0 && xx!=sz-1 && !SOLID(w) && (!SOLID(s) || w->type!=CORNER)
|
||||
&& (w->type!=SEMISOLID || issemi(mip, xx, yy-1, 0, 1, 1, 1)))
|
||||
render_wall(s, w, xx, yy, xx+1, yy, mip, s, t, false);
|
||||
if(yy<=vyy && inner && !SOLID(v) && (!SOLID(s) || v->type!=CORNER)
|
||||
&& (v->type!=SEMISOLID || issemi(mip, xx, yy+1, 0, 0, 1, 0)))
|
||||
render_wall(s, v, xx, yy+1, xx+1, yy+1, mip, v, u, true);
|
||||
};
|
||||
}};
|
||||
render_tris(
|
||||
xx << mip, yy << mip, 1 << mip, topleft, h1, h2, s, t, u, v);
|
||||
}
|
||||
|
||||
if (normalwall) {
|
||||
bool inner = xx != sz - 1 && yy != sz - 1;
|
||||
|
||||
if (xx >= vxx && xx != 0 && yy != sz - 1 && !SOLID(z) &&
|
||||
(!SOLID(s) || z->type != CORNER) &&
|
||||
(z->type != SEMISOLID || issemi(mip, xx - 1, yy, 1, 0, 1, 1)))
|
||||
render_wall(s, z, xx, yy, xx, yy + 1, mip, s, v, true);
|
||||
if (xx <= vxx && inner && !SOLID(t) &&
|
||||
(!SOLID(s) || t->type != CORNER) &&
|
||||
(t->type != SEMISOLID || issemi(mip, xx + 1, yy, 0, 0, 0, 1)))
|
||||
render_wall(s, t, xx + 1, yy, xx + 1, yy + 1, mip, t, u, false);
|
||||
if (yy >= vyy && yy != 0 && xx != sz - 1 && !SOLID(w) &&
|
||||
(!SOLID(s) || w->type != CORNER) &&
|
||||
(w->type != SEMISOLID || issemi(mip, xx, yy - 1, 0, 1, 1, 1)))
|
||||
render_wall(s, w, xx, yy, xx + 1, yy, mip, s, t, false);
|
||||
if (yy <= vyy && inner && !SOLID(v) &&
|
||||
(!SOLID(s) || v->type != CORNER) &&
|
||||
(v->type != SEMISOLID || issemi(mip, xx, yy + 1, 0, 0, 1, 0)))
|
||||
render_wall(s, v, xx, yy + 1, xx + 1, yy + 1, mip, v, u, true);
|
||||
};
|
||||
}
|
||||
}
|
||||
;
|
||||
}
|
||||
;
|
||||
|
||||
void distlod(int &low, int &high, int angle, float widef)
|
||||
void
|
||||
distlod(int &low, int &high, int angle, float widef)
|
||||
{
|
||||
float f = 90.0f/lod/widef;
|
||||
low = (int)((90-angle)/f);
|
||||
high = (int)(angle/f);
|
||||
if(low<min_lod) low = min_lod;
|
||||
if(high<min_lod) high = min_lod;
|
||||
float f = 90.0f / lod / widef;
|
||||
low = (int)((90 - angle) / f);
|
||||
high = (int)(angle / f);
|
||||
if (low < min_lod)
|
||||
low = min_lod;
|
||||
if (high < min_lod)
|
||||
high = min_lod;
|
||||
};
|
||||
|
||||
// does some out of date view frustrum optimisation that doesn't contribute much anymore
|
||||
// does some out of date view frustrum optimisation that doesn't contribute much
|
||||
// anymore
|
||||
|
||||
void render_world(float vx, float vy, float vh, int yaw, int pitch, float fov, int w, int h)
|
||||
void
|
||||
render_world(
|
||||
float vx, float vy, float vh, int yaw, int pitch, float fov, int w, int h)
|
||||
{
|
||||
loopi(LARGEST_FACTOR) stats[i] = 0;
|
||||
min_lod = MIN_LOD+abs(pitch)/12;
|
||||
yaw = 360-yaw;
|
||||
float widef = fov/75.0f;
|
||||
int cdist = abs(yaw%90-45);
|
||||
if(cdist<7) // hack to avoid popup at high fovs at 45 yaw
|
||||
min_lod = MIN_LOD + abs(pitch) / 12;
|
||||
yaw = 360 - yaw;
|
||||
float widef = fov / 75.0f;
|
||||
int cdist = abs(yaw % 90 - 45);
|
||||
if (cdist < 7) // hack to avoid popup at high fovs at 45 yaw
|
||||
{
|
||||
min_lod = max(min_lod, (int)(MIN_LOD+(10-cdist)/1.0f*widef)); // less if lod worked better
|
||||
min_lod = max(min_lod,
|
||||
(int)(MIN_LOD + (10 - cdist) / 1.0f *
|
||||
widef)); // less if lod worked better
|
||||
widef = 1.0f;
|
||||
};
|
||||
lod = MAX_LOD;
|
||||
lodtop = lodbot = lodleft = lodright = min_lod;
|
||||
if(yaw>45 && yaw<=135)
|
||||
{
|
||||
if (yaw > 45 && yaw <= 135) {
|
||||
lodleft = lod;
|
||||
distlod(lodtop, lodbot, yaw-45, widef);
|
||||
}
|
||||
else if(yaw>135 && yaw<=225)
|
||||
{
|
||||
distlod(lodtop, lodbot, yaw - 45, widef);
|
||||
} else if (yaw > 135 && yaw <= 225) {
|
||||
lodbot = lod;
|
||||
distlod(lodleft, lodright, yaw-135, widef);
|
||||
}
|
||||
else if(yaw>225 && yaw<=315)
|
||||
{
|
||||
distlod(lodleft, lodright, yaw - 135, widef);
|
||||
} else if (yaw > 225 && yaw <= 315) {
|
||||
lodright = lod;
|
||||
distlod(lodbot, lodtop, yaw-225, widef);
|
||||
}
|
||||
else
|
||||
{
|
||||
distlod(lodbot, lodtop, yaw - 225, widef);
|
||||
} else {
|
||||
lodtop = lod;
|
||||
distlod(lodright, lodleft, yaw<=45 ? yaw+45 : yaw-315, widef);
|
||||
distlod(
|
||||
lodright, lodleft, yaw <= 45 ? yaw + 45 : yaw - 315, widef);
|
||||
};
|
||||
float hyfov = fov*h/w/2;
|
||||
render_floor = pitch<hyfov;
|
||||
render_ceil = -pitch<hyfov;
|
||||
float hyfov = fov * h / w / 2;
|
||||
render_floor = pitch < hyfov;
|
||||
render_ceil = -pitch < hyfov;
|
||||
|
||||
render_seg_new(vx, vy, vh, MAX_MIP, 0, 0, ssize>>MAX_MIP, ssize>>MAX_MIP);
|
||||
render_seg_new(
|
||||
vx, vy, vh, MAX_MIP, 0, 0, ssize >> MAX_MIP, ssize >> MAX_MIP);
|
||||
mipstats(stats[0], stats[1], stats[2]);
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue