︙ | | | ︙ | |
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
d->k_left = false;
d->k_right = false;
d->k_up = false;
d->k_down = false;
d->jumpnext = false;
d->strafe = 0;
d->move = 0;
};
void
spawnstate(dynent *d) // reset player state not persistent accross spawns
{
resetmovement(d);
d->vel.x = d->vel.y = d->vel.z = 0;
d->onfloor = false;
|
<
>
|
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
d->k_left = false;
d->k_right = false;
d->k_up = false;
d->k_down = false;
d->jumpnext = false;
d->strafe = 0;
d->move = 0;
}
void
spawnstate(dynent *d) // reset player state not persistent accross spawns
{
resetmovement(d);
d->vel.x = d->vel.y = d->vel.z = 0;
d->onfloor = false;
|
︙ | | | ︙ | |
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
d->gunselect = GUN_CG;
};
d->ammo[GUN_CG] /= 2;
};
} else {
d->ammo[GUN_SG] = 5;
};
};
dynent *
newdynent() // create a new blank player or monster
{
dynent *d = (dynent *)malloc(sizeof(dynent));
d->o.x = 0;
d->o.y = 0;
|
<
>
|
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
d->gunselect = GUN_CG;
};
d->ammo[GUN_CG] /= 2;
};
} else {
d->ammo[GUN_SG] = 5;
};
}
dynent *
newdynent() // create a new blank player or monster
{
dynent *d = (dynent *)malloc(sizeof(dynent));
d->o.x = 0;
d->o.y = 0;
|
︙ | | | ︙ | |
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
d->monsterstate = 0;
d->name[0] = d->team[0] = 0;
d->blocked = false;
d->lifesequence = 0;
d->state = CS_ALIVE;
spawnstate(d);
return d;
};
void
respawnself()
{
spawnplayer(player1);
showscores(false);
};
void
arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam)
{
if (d->state != CS_DEAD) {
alive++;
if (lastteam && strcmp(lastteam, d->team))
oneteam = false;
lastteam = d->team;
} else {
dead++;
};
};
int arenarespawnwait = 0;
int arenadetectwait = 0;
void
arenarespawn()
{
|
<
>
<
>
<
>
|
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
d->monsterstate = 0;
d->name[0] = d->team[0] = 0;
d->blocked = false;
d->lifesequence = 0;
d->state = CS_ALIVE;
spawnstate(d);
return d;
}
void
respawnself()
{
spawnplayer(player1);
showscores(false);
}
void
arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam)
{
if (d->state != CS_DEAD) {
alive++;
if (lastteam && strcmp(lastteam, d->team))
oneteam = false;
lastteam = d->team;
} else {
dead++;
};
}
int arenarespawnwait = 0;
int arenadetectwait = 0;
void
arenarespawn()
{
|
︙ | | | ︙ | |
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
else
conoutf(@"everyone died!");
arenarespawnwait = lastmillis + 5000;
arenadetectwait = lastmillis + 10000;
player1->roll = 0;
};
};
};
void
zapdynent(dynent *&d)
{
if (d)
free(d);
d = NULL;
};
extern int democlientnum;
void
otherplayers()
{
loopv(players) if (players[i])
{
const int lagtime = lastmillis - players[i]->lastupdate;
if (lagtime > 1000 && players[i]->state == CS_ALIVE) {
players[i]->state = CS_LAGGED;
continue;
};
if (lagtime && players[i]->state != CS_DEAD &&
(!demoplayback || i != democlientnum))
moveplayer(
players[i], 2, false); // use physics to extrapolate
// player position
};
};
void
respawn()
{
if (player1->state == CS_DEAD) {
player1->attacking = false;
if (m_arena) {
|
<
>
<
>
<
<
>
>
|
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
else
conoutf(@"everyone died!");
arenarespawnwait = lastmillis + 5000;
arenadetectwait = lastmillis + 10000;
player1->roll = 0;
};
};
}
void
zapdynent(dynent *&d)
{
if (d)
free(d);
d = NULL;
}
extern int democlientnum;
void
otherplayers()
{
loopv(players) if (players[i])
{
const int lagtime = lastmillis - players[i]->lastupdate;
if (lagtime > 1000 && players[i]->state == CS_ALIVE) {
players[i]->state = CS_LAGGED;
continue;
};
if (lagtime && players[i]->state != CS_DEAD &&
(!demoplayback || i != democlientnum))
moveplayer(
players[i], 2, false); // use physics to extrapolate
// player position
}
}
void
respawn()
{
if (player1->state == CS_DEAD) {
player1->attacking = false;
if (m_arena) {
|
︙ | | | ︙ | |
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
|
if (!demoplayback) {
monsterthink();
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) {
moveplayer(player1, 20, true);
checkitems();
};
c2sinfo(player1); // do this last, to reduce the
// effective frame lag
|
|
<
|
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
|
if (!demoplayback) {
monsterthink();
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) {
moveplayer(player1, 20, true);
checkitems();
};
c2sinfo(player1); // do this last, to reduce the
// effective frame lag
|
︙ | | | ︙ | |
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
|
return;
d->o.x -= dx;
d->o.y -= dy;
}
conoutf(@"can't find entity spawn spot! (%d, %d)", (int)d->o.x,
(int)d->o.y);
// leave ent at original pos, possibly stuck
};
int spawncycle = -1;
int fixspawn = 2;
void
spawnplayer(dynent *d) // place at random spawn. also used by monsters!
{
|
<
>
|
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
|
return;
d->o.x -= dx;
d->o.y -= dy;
}
conoutf(@"can't find entity spawn spot! (%d, %d)", (int)d->o.x,
(int)d->o.y);
// leave ent at original pos, possibly stuck
}
int spawncycle = -1;
int fixspawn = 2;
void
spawnplayer(dynent *d) // place at random spawn. also used by monsters!
{
|
︙ | | | ︙ | |
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
|
} else {
d->o.x = d->o.y = (float)ssize / 2;
d->o.z = 4;
};
entinmap(d);
spawnstate(d);
d->state = CS_ALIVE;
};
// movement input code
#define dir(name, v, d, s, os) \
void name(bool isdown) \
{ \
player1->s = isdown; \
player1->v = isdown ? d : (player1->os ? -(d) : 0); \
player1->lastmove = lastmillis; \
};
dir(backward, move, -1, k_down, k_up);
dir(forward, move, 1, k_up, k_down);
dir(left, strafe, 1, k_left, k_right);
dir(right, strafe, -1, k_right, k_left);
void
attack(bool on)
{
if (intermission)
return;
if (editmode)
editdrag(on);
else if (player1->attacking = on)
respawn();
};
void
jumpn(bool on)
{
if (!intermission && (player1->jumpnext = on))
respawn();
};
COMMAND(backward, ARG_DOWN)
COMMAND(forward, ARG_DOWN)
COMMAND(left, ARG_DOWN)
COMMAND(right, ARG_DOWN)
COMMANDN(jump, jumpn, ARG_DOWN)
COMMAND(attack, ARG_DOWN)
|
<
>
|
|
|
|
|
|
<
>
<
>
<
>
|
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
|
} else {
d->o.x = d->o.y = (float)ssize / 2;
d->o.z = 4;
};
entinmap(d);
spawnstate(d);
d->state = CS_ALIVE;
}
// movement input code
#define dir(name, v, d, s, os) \
void name(bool isdown) \
{ \
player1->s = isdown; \
player1->v = isdown ? d : (player1->os ? -(d) : 0); \
player1->lastmove = lastmillis; \
}
dir(backward, move, -1, k_down, k_up);
dir(forward, move, 1, k_up, k_down);
dir(left, strafe, 1, k_left, k_right);
dir(right, strafe, -1, k_right, k_left);
void
attack(bool on)
{
if (intermission)
return;
if (editmode)
editdrag(on);
else if (player1->attacking = on)
respawn();
}
void
jumpn(bool on)
{
if (!intermission && (player1->jumpnext = on))
respawn();
}
COMMAND(backward, ARG_DOWN)
COMMAND(forward, ARG_DOWN)
COMMAND(left, ARG_DOWN)
COMMAND(right, ARG_DOWN)
COMMANDN(jump, jumpn, ARG_DOWN)
COMMAND(attack, ARG_DOWN)
|
︙ | | | ︙ | |
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
|
player1->pitch = MAXPITCH;
if (player1->pitch < -MAXPITCH)
player1->pitch = -MAXPITCH;
while (player1->yaw < 0.0f)
player1->yaw += 360.0f;
while (player1->yaw >= 360.0f)
player1->yaw -= 360.0f;
};
void
mousemove(int dx, int dy)
{
if (player1->state == CS_DEAD || intermission)
return;
const float SENSF = 33.0f; // try match quake sens
player1->yaw += (dx / SENSF) * (sensitivity / (float)sensitivityscale);
player1->pitch -= (dy / SENSF) *
(sensitivity / (float)sensitivityscale) *
(invmouse ? -1 : 1);
fixplayer1range();
};
// damage arriving from the network, monsters, yourself, all ends up here.
void
selfdamage(int damage, int actor, dynent *act)
{
if (player1->state != CS_ALIVE || editmode || intermission)
return;
damageblend(damage);
demoblend(damage);
int ad = damage * (player1->armourtype + 1) * 20 /
100; // let armour absorb when possible
if (ad > player1->armour)
ad = player1->armour;
player1->armour -= ad;
damage -= ad;
float droll = damage / 0.5f;
player1->roll +=
player1->roll > 0
? droll
: (player1->roll < 0
? -droll
: (rnd(2) ? droll
: -droll)); // give player a kick depending
// on amount of damage
if ((player1->health -= damage) <= 0) {
if (actor == -2) {
conoutf(@"you got killed by %s!", act->name);
} else if (actor == -1) {
actor = getclientnum();
conoutf(@"you suicided!");
addmsg(1, 2, SV_FRAGS, --player1->frags);
|
<
>
|
<
<
>
|
|
<
|
|
|
|
|
|
|
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
|
player1->pitch = MAXPITCH;
if (player1->pitch < -MAXPITCH)
player1->pitch = -MAXPITCH;
while (player1->yaw < 0.0f)
player1->yaw += 360.0f;
while (player1->yaw >= 360.0f)
player1->yaw -= 360.0f;
}
void
mousemove(int dx, int dy)
{
if (player1->state == CS_DEAD || intermission)
return;
const float SENSF = 33.0f; // try match quake sens
player1->yaw += (dx / SENSF) * (sensitivity / (float)sensitivityscale);
player1->pitch -= (dy / SENSF) *
(sensitivity / (float)sensitivityscale) * (invmouse ? -1 : 1);
fixplayer1range();
}
// damage arriving from the network, monsters, yourself, all ends up here.
void
selfdamage(int damage, int actor, dynent *act)
{
if (player1->state != CS_ALIVE || editmode || intermission)
return;
damageblend(damage);
demoblend(damage);
int ad = damage * (player1->armourtype + 1) * 20 /
100; // let armour absorb when possible
if (ad > player1->armour)
ad = player1->armour;
player1->armour -= ad;
damage -= ad;
float droll = damage / 0.5f;
player1->roll += player1->roll > 0
? droll
: (player1->roll < 0
? -droll
: (rnd(2) ? droll
: -droll)); // give player a kick depending
// on amount of damage
if ((player1->health -= damage) <= 0) {
if (actor == -2) {
conoutf(@"you got killed by %s!", act->name);
} else if (actor == -1) {
actor = getclientnum();
conoutf(@"you suicided!");
addmsg(1, 2, SV_FRAGS, --player1->frags);
|
︙ | | | ︙ | |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// one big bad include file for the whole engine... nasty!
#import <ObjFW/ObjFW.h>
#define gamma gamma__
#include <SDL2/SDL.h>
#undef gamma
#include "tools.h"
@interface Cube : OFObject <OFApplicationDelegate>
@property (class, readonly, nonatomic) Cube *sharedInstance;
@property (readonly, nonatomic) SDL_Window *window;
@property (readonly, nonatomic) OFIRI *gameDataIRI, *userDataIRI;
@property (nonatomic) bool repeatsKeys;
@property (nonatomic) int framesInMap;
@end
|
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// one big bad include file for the whole engine... nasty!
#import <ObjFW/ObjFW.h>
#define gamma gamma__
#include <SDL2/SDL.h>
#undef gamma
#include "tools.h"
@interface Cube: OFObject <OFApplicationDelegate>
@property (class, readonly, nonatomic) Cube *sharedInstance;
@property (readonly, nonatomic) SDL_Window *window;
@property (readonly, nonatomic) OFIRI *gameDataIRI, *userDataIRI;
@property (nonatomic) bool repeatsKeys;
@property (nonatomic) int framesInMap;
@end
|
︙ | | | ︙ | |
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
{
short x, y, z; // cube aligned position
short attr1;
uchar type; // type is one of the above
uchar attr2, attr3, attr4;
};
struct entity : public persistent_entity {
bool spawned; // the only dynamic state of a map entity
};
#define MAPVERSION 5 // bump if map format changes, see worldio.cpp
struct header // map file format header
{
|
|
|
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
{
short x, y, z; // cube aligned position
short attr1;
uchar type; // type is one of the above
uchar attr2, attr3, attr4;
};
struct entity: public persistent_entity {
bool spawned; // the only dynamic state of a map entity
};
#define MAPVERSION 5 // bump if map format changes, see worldio.cpp
struct header // map file format header
{
|
︙ | | | ︙ | |
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
#define SW(w, x, y) SWS(w, x, y, ssize)
#define S(x, y) SW(world, x, y) // convenient lookup of a lowest mip cube
#define SMALLEST_FACTOR 6 // determines number of mips there can be
#define DEFAULT_FACTOR 8
#define LARGEST_FACTOR 11 // 10 is already insane
#define SOLID(x) ((x)->type == SOLID)
#define MINBORD 2 // 2 cubes from the edge of the world are always solid
#define OUTBORD(x, y) \
((x) < MINBORD || (y) < MINBORD || (x) >= ssize - MINBORD || \
(y) >= ssize - MINBORD)
struct block {
int x, y, xs, ys;
};
enum {
|
|
|
|
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
|
#define SW(w, x, y) SWS(w, x, y, ssize)
#define S(x, y) SW(world, x, y) // convenient lookup of a lowest mip cube
#define SMALLEST_FACTOR 6 // determines number of mips there can be
#define DEFAULT_FACTOR 8
#define LARGEST_FACTOR 11 // 10 is already insane
#define SOLID(x) ((x)->type == SOLID)
#define MINBORD 2 // 2 cubes from the edge of the world are always solid
#define OUTBORD(x, y) \
((x) < MINBORD || (y) < MINBORD || (x) >= ssize - MINBORD || \
(y) >= ssize - MINBORD)
struct block {
int x, y, xs, ys;
};
enum {
|
︙ | | | ︙ | |
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
int trigger; // millis at which transition to another monsterstate takes
// place
OFVector3D 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
enum { A_BLUE, A_GREEN, A_YELLOW }; // armour types... take 20/40/60 % off
enum {
M_NONE = 0,
M_SEARCH,
M_HOME,
|
|
|
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
int trigger; // millis at which transition to another monsterstate takes
// place
OFVector3D 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
enum { A_BLUE, A_GREEN, A_YELLOW }; // armour types... take 20/40/60 % off
enum {
M_NONE = 0,
M_SEARCH,
M_HOME,
|
︙ | | | ︙ | |
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
|
#define PIXELTAB (VIRTW / 12)
#define PI (3.1415927f)
#define PI2 (2 * PI)
// simplistic vector ops
#define dotprod(u, v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)
#define vmul(u, f) \
{ \
(u).x *= (f); \
(u).y *= (f); \
(u).z *= (f); \
}
#define vdiv(u, f) \
{ \
(u).x /= (f); \
(u).y /= (f); \
(u).z /= (f); \
}
#define vadd(u, v) \
{ \
(u).x += (v).x; \
(u).y += (v).y; \
(u).z += (v).z; \
};
#define vsub(u, v) \
{ \
(u).x -= (v).x; \
(u).y -= (v).y; \
(u).z -= (v).z; \
};
#define vdist(d, v, e, s) \
OFVector3D 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 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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
|
#define PIXELTAB (VIRTW / 12)
#define PI (3.1415927f)
#define PI2 (2 * PI)
// simplistic vector ops
#define dotprod(u, v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)
#define vmul(u, f) \
{ \
(u).x *= (f); \
(u).y *= (f); \
(u).z *= (f); \
}
#define vdiv(u, f) \
{ \
(u).x /= (f); \
(u).y /= (f); \
(u).z /= (f); \
}
#define vadd(u, v) \
{ \
(u).x += (v).x; \
(u).y += (v).y; \
(u).z += (v).z; \
};
#define vsub(u, v) \
{ \
(u).x -= (v).x; \
(u).y -= (v).y; \
(u).z -= (v).z; \
};
#define vdist(d, v, e, s) \
OFVector3D 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 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)
|
︙ | | | ︙ | |
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
|
ARG_1EST,
ARG_2EST,
ARG_VARI
};
// nasty macros for registering script functions, abuses globals to avoid
// excessive infrastructure
#define COMMANDN(name, fun, nargs) \
OF_CONSTRUCTOR() \
{ \
enqueueInit(^{ \
addcommand(@ #name, (void (*)())fun, nargs); \
}); \
}
#define COMMAND(name, nargs) COMMANDN(name, name, nargs)
#define VARP(name, min, cur, max) \
int name; \
OF_CONSTRUCTOR() \
{ \
enqueueInit(^{ \
name = variable( \
@ #name, min, cur, max, &name, NULL, true); \
}); \
}
#define VAR(name, min, cur, max) \
int name; \
OF_CONSTRUCTOR() \
{ \
enqueueInit(^{ \
name = variable( \
@ #name, min, cur, max, &name, NULL, false); \
}); \
}
#define VARF(name, min, cur, max, body) \
void var_##name(); \
static int name; \
OF_CONSTRUCTOR() \
{ \
enqueueInit(^{ \
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; \
OF_CONSTRUCTOR() \
{ \
enqueueInit(^{ \
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"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
|
ARG_1EST,
ARG_2EST,
ARG_VARI
};
// nasty macros for registering script functions, abuses globals to avoid
// excessive infrastructure
#define COMMANDN(name, fun, nargs) \
OF_CONSTRUCTOR() \
{ \
enqueueInit(^{ \
addcommand(@ #name, (void (*)())fun, nargs); \
}); \
}
#define COMMAND(name, nargs) COMMANDN(name, name, nargs)
#define VARP(name, min, cur, max) \
int name; \
OF_CONSTRUCTOR() \
{ \
enqueueInit(^{ \
name = variable( \
@ #name, min, cur, max, &name, NULL, true); \
}); \
}
#define VAR(name, min, cur, max) \
int name; \
OF_CONSTRUCTOR() \
{ \
enqueueInit(^{ \
name = variable( \
@ #name, min, cur, max, &name, NULL, false); \
}); \
}
#define VARF(name, min, cur, max, body) \
void var_##name(); \
static int name; \
OF_CONSTRUCTOR() \
{ \
enqueueInit(^{ \
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; \
OF_CONSTRUCTOR() \
{ \
enqueueInit(^{ \
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"
|
︙ | | | ︙ | |
︙ | | | ︙ | |
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
};
});
}
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); \
}
int cx, cy, ch;
int curedittex[] = {-1, -1, -1};
bool dragging = false;
|
|
|
|
|
|
|
|
|
|
|
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
};
});
}
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); \
}
int cx, cy, ch;
int curedittex[] = {-1, -1, -1};
bool dragging = false;
|
︙ | | | ︙ | |
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
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()
{
correctsel();
if (!editmode)
conoutf(@"this function is only allowed in edit mode");
return !editmode;
}
bool
noselection()
{
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;
void
selectpos(int x, int y, int xs, int ys)
{
block s = {x, y, xs, ys};
sel = s;
selh = 0;
correctsel();
};
void
makesel()
{
block s = {min(lastx, cx), min(lasty, cy), abs(lastx - cx) + 1,
abs(lasty - cy) + 1};
sel = s;
selh = max(lasth, ch);
correctsel();
if (selset)
rtex = *S(sel.x, sel.y);
};
VAR(flrceil, 0, 0, 2);
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);
};
void
cursorupdate() // called every frame from hud
{
flrceil = ((int)(player1->pitch >= 0)) * 2;
volatile float x =
|
<
>
|
|
|
|
|
|
<
>
<
>
|
<
|
<
<
>
|
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
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()
{
correctsel();
if (!editmode)
conoutf(@"this function is only allowed in edit mode");
return !editmode;
}
bool
noselection()
{
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;
void
selectpos(int x, int y, int xs, int ys)
{
block s = {x, y, xs, ys};
sel = s;
selh = 0;
correctsel();
}
void
makesel()
{
block s = {min(lastx, cx), min(lasty, cy), abs(lastx - cx) + 1,
abs(lasty - cy) + 1};
sel = s;
selh = max(lasth, ch);
correctsel();
if (selset)
rtex = *S(sel.x, sel.y);
}
VAR(flrceil, 0, 0, 2);
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);
}
void
cursorupdate() // called every frame from hud
{
flrceil = ((int)(player1->pitch >= 0)) * 2;
volatile float x =
|
︙ | | | ︙ | |
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
|
ch = (int)ih;
};
if (selset) {
linestyle(GRIDS, 0xFF, 0x40, 0x40);
box(sel, (float)selh, (float)selh, (float)selh, (float)selh);
};
};
vector<block *> undos; // unlimited undo
VARP(undomegs, 0, 1, 10); // bounded by n megs
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));
};
};
void
makeundo()
{
undos.add(blockcopy(sel));
pruneundos(undomegs << 20);
};
void
editundo()
{
EDITMP;
if (undos.empty()) {
conoutf(@"nothing more to undo");
|
<
>
<
<
>
>
<
>
|
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
ch = (int)ih;
};
if (selset) {
linestyle(GRIDS, 0xFF, 0x40, 0x40);
box(sel, (float)selh, (float)selh, (float)selh, (float)selh);
};
}
vector<block *> undos; // unlimited undo
VARP(undomegs, 0, 1, 10); // bounded by n megs
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));
}
}
void
makeundo()
{
undos.add(blockcopy(sel));
pruneundos(undomegs << 20);
}
void
editundo()
{
EDITMP;
if (undos.empty()) {
conoutf(@"nothing more to undo");
|
︙ | | | ︙ | |
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
|
void
copy()
{
EDITSELMP;
if (copybuf)
free(copybuf);
copybuf = blockcopy(sel);
};
void
paste()
{
EDITMP;
if (!copybuf) {
conoutf(@"nothing to paste");
|
<
>
|
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
|
void
copy()
{
EDITSELMP;
if (copybuf)
free(copybuf);
copybuf = blockcopy(sel);
}
void
paste()
{
EDITMP;
if (!copybuf) {
conoutf(@"nothing to paste");
|
︙ | | | ︙ | |
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
|
uchar *p = hdr.texlists[i];
int t = p[c];
for (int a = c - 1; a >= 0; a--)
p[a + 1] = p[a];
p[0] = t;
curedittex[i] = -1;
};
};
};
void
editdrag(bool isdown)
{
if (dragging = isdown) {
lastx = cx;
lasty = cy;
lasth = ch;
selset = false;
tofronttex();
};
makesel();
};
// 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.
void
editheightxy(bool isfloor, int amount, block &sel)
{
loopselxy(
if (isfloor) {
s->floor += amount;
if (s->floor >= s->ceil)
s->floor = s->ceil - 1;
} else {
s->ceil += amount;
if (s->ceil <= s->floor)
s->ceil = s->floor + 1;
});
};
void
editheight(int flr, int amount)
{
EDITSEL;
bool isfloor = flr == 0;
editheightxy(isfloor, amount, sel);
|
<
<
>
>
<
>
<
>
|
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
|
uchar *p = hdr.texlists[i];
int t = p[c];
for (int a = c - 1; a >= 0; a--)
p[a + 1] = p[a];
p[0] = t;
curedittex[i] = -1;
};
}
}
void
editdrag(bool isdown)
{
if (dragging = isdown) {
lastx = cx;
lasty = cy;
lasth = ch;
selset = false;
tofronttex();
};
makesel();
}
// 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.
void
editheightxy(bool isfloor, int amount, block &sel)
{
loopselxy(
if (isfloor) {
s->floor += amount;
if (s->floor >= s->ceil)
s->floor = s->ceil - 1;
} else {
s->ceil += amount;
if (s->ceil <= s->floor)
s->ceil = s->floor + 1;
});
}
void
editheight(int flr, int amount)
{
EDITSEL;
bool isfloor = flr == 0;
editheightxy(isfloor, amount, sel);
|
︙ | | | ︙ | |
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
|
case 2:
s->ctex = t;
break;
case 3:
s->utex = t;
break;
});
};
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;
int i = curedittex[atype];
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()
{
EDITSELMP;
loop(x, ssize) loop(y, ssize)
{
|
<
>
<
>
|
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
|
case 2:
s->ctex = t;
break;
case 3:
s->utex = t;
break;
});
}
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;
int i = curedittex[atype];
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()
{
EDITSELMP;
loop(x, ssize) loop(y, ssize)
{
|
︙ | | | ︙ | |
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
|
s->ctex = lasttex;
break;
case 3:
if (s->utex == rtex.utex)
s->utex = lasttex;
break;
};
};
block b = {0, 0, ssize, ssize};
remip(b);
};
void
edittypexy(int type, block &sel)
{
loopselxy(s->type = type);
};
void
edittype(int type)
{
EDITSEL;
if (type == CORNER &&
(sel.xs != sel.ys || sel.xs == 3 || sel.xs > 4 && sel.xs != 8 ||
|
<
>
<
>
<
>
|
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
|
s->ctex = lasttex;
break;
case 3:
if (s->utex == rtex.utex)
s->utex = lasttex;
break;
};
}
block b = {0, 0, ssize, ssize};
remip(b);
}
void
edittypexy(int type, block &sel)
{
loopselxy(s->type = type);
}
void
edittype(int type)
{
EDITSEL;
if (type == CORNER &&
(sel.xs != sel.ys || sel.xs == 3 || sel.xs > 4 && sel.xs != 8 ||
|
︙ | | | ︙ | |
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
|
if (isfloor)
s->floor = low;
else
s->ceil = hi;
if (s->floor >= s->ceil)
s->floor = s->ceil - 1;
});
};
void
equalize(int flr)
{
bool isfloor = flr == 0;
EDITSEL;
editequalisexy(isfloor, sel);
addmsg(1, 6, SV_EDITE, sel.x, sel.y, sel.xs, sel.ys, isfloor);
}
COMMAND(equalize, ARG_1INT)
void
setvdeltaxy(int delta, block &sel)
{
loopselxy(s->vdelta = max(s->vdelta + delta, 0));
remipmore(sel);
};
void
setvdelta(int delta)
{
EDITSEL;
setvdeltaxy(delta, sel);
addmsg(1, 6, SV_EDITD, sel.x, sel.y, sel.xs, sel.ys, delta);
};
const int MAXARCHVERT = 50;
int archverts[MAXARCHVERT][MAXARCHVERT];
bool archvinit = false;
void
archvertex(int span, int vert, int delta)
{
if (!archvinit) {
archvinit = true;
loop(s, MAXARCHVERT) loop(v, MAXARCHVERT) archverts[s][v] = 0;
};
if (span >= MAXARCHVERT || vert >= MAXARCHVERT || span < 0 || vert < 0)
return;
archverts[span][vert] = delta;
};
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)));
remipmore(sel);
};
void
slope(int xd, int yd)
{
EDITSELMP;
int off = 0;
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);
remipmore(sel);
};
void
perlin(int scale, int seed, int psize)
{
EDITSELMP;
sel.xs++;
sel.ys++;
makeundo();
sel.xs--;
sel.ys--;
perlinarea(sel, scale, seed, psize);
sel.xs++;
sel.ys++;
remipmore(sel);
sel.xs--;
sel.ys--;
};
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)
{
EDITSELMP;
loopselxy(s->tag = tag);
};
void
newent(OFString *what, OFString *a1, OFString *a2, OFString *a3, OFString *a4)
{
EDITSEL;
@autoreleasepool {
newentity(sel.x, sel.y, (int)player1->o.z, what,
|
<
>
<
>
<
>
<
>
<
|
|
|
|
|
<
>
<
>
<
>
<
>
|
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
|
if (isfloor)
s->floor = low;
else
s->ceil = hi;
if (s->floor >= s->ceil)
s->floor = s->ceil - 1;
});
}
void
equalize(int flr)
{
bool isfloor = flr == 0;
EDITSEL;
editequalisexy(isfloor, sel);
addmsg(1, 6, SV_EDITE, sel.x, sel.y, sel.xs, sel.ys, isfloor);
}
COMMAND(equalize, ARG_1INT)
void
setvdeltaxy(int delta, block &sel)
{
loopselxy(s->vdelta = max(s->vdelta + delta, 0));
remipmore(sel);
}
void
setvdelta(int delta)
{
EDITSEL;
setvdeltaxy(delta, sel);
addmsg(1, 6, SV_EDITD, sel.x, sel.y, sel.xs, sel.ys, delta);
}
const int MAXARCHVERT = 50;
int archverts[MAXARCHVERT][MAXARCHVERT];
bool archvinit = false;
void
archvertex(int span, int vert, int delta)
{
if (!archvinit) {
archvinit = true;
loop(s, MAXARCHVERT) loop(v, MAXARCHVERT) archverts[s][v] = 0;
};
if (span >= MAXARCHVERT || vert >= MAXARCHVERT || span < 0 || vert < 0)
return;
archverts[span][vert] = delta;
}
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)));
remipmore(sel);
}
void
slope(int xd, int yd)
{
EDITSELMP;
int off = 0;
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);
remipmore(sel);
}
void
perlin(int scale, int seed, int psize)
{
EDITSELMP;
sel.xs++;
sel.ys++;
makeundo();
sel.xs--;
sel.ys--;
perlinarea(sel, scale, seed, psize);
sel.xs++;
sel.ys++;
remipmore(sel);
sel.xs--;
sel.ys--;
}
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)
{
EDITSELMP;
loopselxy(s->tag = tag);
}
void
newent(OFString *what, OFString *a1, OFString *a2, OFString *a3, OFString *a4)
{
EDITSEL;
@autoreleasepool {
newentity(sel.x, sel.y, (int)player1->o.z, what,
|
︙ | | | ︙ | |
︙ | | | ︙ | |
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
void
renderent(entity &e, OFString *mdlname, float z, float yaw, int frame = 0,
int numf = 1, int basetime = 0, float speed = 10.0f)
{
rendermodel(mdlname, frame, numf, 0, 1.1f, e.x, z + S(e.x, e.y)->floor,
e.y, yaw, 0, false, 1.0f, speed, 0, basetime);
};
void
renderentities()
{
if (lastmillis > triggertime + 1000)
triggertime = 0;
loopv(ents)
|
<
>
|
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
void
renderent(entity &e, OFString *mdlname, float z, float yaw, int frame = 0,
int numf = 1, int basetime = 0, float speed = 10.0f)
{
rendermodel(mdlname, frame, numf, 0, 1.1f, e.x, z + S(e.x, e.y)->floor,
e.y, yaw, 0, false, 1.0f, speed, 0, basetime);
}
void
renderentities()
{
if (lastmillis > triggertime + 1000)
triggertime = 0;
loopv(ents)
|
︙ | | | ︙ | |
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
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));
break;
case 4:
renderent(e, @"switch2", 3,
(float)e.attr3 * 90,
|
|
|
|
>
|
|
|
|
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
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));
break;
case 4:
renderent(e, @"switch2", 3,
(float)e.attr3 * 90,
|
︙ | | | ︙ | |
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
: 0,
(e.spawned || !triggertime) ? 1
: 30,
triggertime, 35.0f);
break;
};
};
};
};
struct itemstat {
int add, max, sound;
} itemstats[] = {
10,
50,
S_ITEMAMMO,
|
<
<
>
>
|
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
: 0,
(e.spawned || !triggertime) ? 1
: 30,
triggertime, 35.0f);
break;
};
};
}
}
struct itemstat {
int add, max, sound;
} itemstats[] = {
10,
50,
S_ITEMAMMO,
|
︙ | | | ︙ | |
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
S_ITEMPUP,
};
void
baseammo(int gun)
{
player1->ammo[gun] = itemstats[gun - 1].add * 2;
};
// these two functions are called when the server acknowledges that you really
// picked up the item (in multiplayer someone may grab it before you).
void
radditem(int i, int &v)
{
itemstat &is = itemstats[ents[i].type - I_SHELLS];
ents[i].spawned = false;
v += is.add;
if (v > is.max)
v = is.max;
playsoundc(is.sound);
};
void
realpickup(int n, dynent *d)
{
switch (ents[n].type) {
case I_SHELLS:
radditem(n, d->ammo[1]);
|
<
>
<
>
|
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
S_ITEMPUP,
};
void
baseammo(int gun)
{
player1->ammo[gun] = itemstats[gun - 1].add * 2;
}
// these two functions are called when the server acknowledges that you really
// picked up the item (in multiplayer someone may grab it before you).
void
radditem(int i, int &v)
{
itemstat &is = itemstats[ents[i].type - I_SHELLS];
ents[i].spawned = false;
v += is.add;
if (v > is.max)
v = is.max;
playsoundc(is.sound);
}
void
realpickup(int n, dynent *d)
{
switch (ents[n].type) {
case I_SHELLS:
radditem(n, d->ammo[1]);
|
︙ | | | ︙ | |
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
|
d->pitch = 0;
d->vel.x = d->vel.y = d->vel.z = 0;
entinmap(d);
playsoundc(S_TELEPORT);
break;
};
};
};
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
|
<
>
|
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
|
d->pitch = 0;
d->vel.x = d->vel.y = d->vel.z = 0;
entinmap(d);
playsoundc(S_TELEPORT);
break;
};
};
}
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
|
︙ | | | ︙ | |
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
|
}
}
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)
{
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;
};
|
|
<
>
<
>
|
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
|
}
}
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)
{
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;
}
|
︙ | | | ︙ | |
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
m->state = CS_ALIVE;
m->anger = 0;
@autoreleasepool {
strcpy_s(m->name, t->name.UTF8String);
}
monsters.add(m);
return m;
};
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;
};
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
{
loopv(monsters) free(monsters[i]);
monsters.setsize(0);
|
<
>
<
>
|
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
m->state = CS_ALIVE;
m->anger = 0;
@autoreleasepool {
strcpy_s(m->name, t->name.UTF8String);
}
monsters.add(m);
return m;
}
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;
};
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
{
loopv(monsters) free(monsters[i]);
monsters.setsize(0);
|
︙ | | | ︙ | |
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
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;
entinmap(m);
monstertotal++;
};
};
};
bool
los(float lx, float ly, float lz, float bx, float by, float bz,
OFVector3D &v) // height-correct line of sight for monster shooting/seeing
{
if (OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by))
return false;
|
>
|
<
<
>
|
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
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;
entinmap(m);
monstertotal++;
}
};
}
bool
los(float lx, float ly, float lz, float bx, float by, float bz,
OFVector3D &v) // height-correct line of sight for monster shooting/seeing
{
if (OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by))
return false;
|
︙ | | | ︙ | |
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
v.y = y;
v.z = rz;
x += dx / (float)steps;
y += dy / (float)steps;
i++;
};
return i >= steps;
};
bool
enemylos(dynent *m, OFVector3D &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);
};
// 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
{
m->monsterstate = state;
m->move = moving;
n = n * 130 / 100;
m->trigger = lastmillis + n - skill * (n / 16) + rnd(r + 1);
};
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;
};
void
monsteraction(
dynent *m) // main AI thinking routine, called every frame for every monster
{
if (m->enemy->state == CS_DEAD) {
m->enemy = player1;
|
<
>
<
>
<
>
<
>
|
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
v.y = y;
v.z = rz;
x += dx / (float)steps;
y += dy / (float)steps;
i++;
};
return i >= steps;
}
bool
enemylos(dynent *m, OFVector3D &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);
}
// 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
{
m->monsterstate = state;
m->move = moving;
n = n * 130 / 100;
m->trigger = lastmillis + n - skill * (n / 16) + rnd(r + 1);
}
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;
}
void
monsteraction(
dynent *m) // main AI thinking routine, called every frame for every monster
{
if (m->enemy->state == CS_DEAD) {
m->enemy = player1;
|
︙ | | | ︙ | |
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
|
vdist(disttoenemy, vectoenemy, m->o, m->enemy->o);
m->pitch = atan2(m->enemy->o.z - m->o.z, disttoenemy) * 180 / PI;
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)
{
m->jumpnext = true;
} 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) ;)
transition(m, M_SEARCH, 1, 400, 1000);
};
};
|
|
|
|
|
|
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
|
vdist(disttoenemy, vectoenemy, m->o, m->enemy->o);
m->pitch = atan2(m->enemy->o.z - m->o.z, disttoenemy) * 180 / PI;
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)
{
m->jumpnext = true;
} 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) ;)
transition(m, M_SEARCH, 1, 400, 1000);
};
};
|
︙ | | | ︙ | |
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
|
};
};
};
break;
};
moveplayer(m, 1, false); // use physics to move monster
};
void
monsterpain(dynent *m, int damage, dynent *d)
{
if (d->monsterstate) // a monster hit us
{
if (m != d) // guard for RL guys shooting themselves :)
|
<
>
|
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
|
};
};
};
break;
};
moveplayer(m, 1, false); // use physics to move monster
}
void
monsterpain(dynent *m, int damage, dynent *d)
{
if (d->monsterstate) // a monster hit us
{
if (m != d) // guard for RL guys shooting themselves :)
|
︙ | | | ︙ | |
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
|
playsound(monstertypes[m->mtype].diesound, &m->o);
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)
{
conoutf(allkilled ? @"you have cleared the map!"
: @"you reached the exit!");
conoutf(@"score: %d kills in %d seconds", numkilled,
|
<
>
|
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
|
playsound(monstertypes[m->mtype].diesound, &m->o);
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)
{
conoutf(allkilled ? @"you have cleared the map!"
: @"you reached the exit!");
conoutf(@"score: %d kills in %d seconds", numkilled,
|
︙ | | | ︙ | |
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
|
else
{
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]);
};
};
loopv(monsters) if (monsters[i]->state == CS_ALIVE)
monsteraction(monsters[i]);
}
void
monsterrender()
{
loopv(monsters) renderclient(monsters[i], false,
monstertypes[monsters[i]->mtype].mdlname, monsters[i]->mtype == 5,
monstertypes[monsters[i]->mtype].mscale / 10.0f);
}
|
<
<
>
>
|
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
|
else
{
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]);
}
}
loopv(monsters) if (monsters[i]->state == CS_ALIVE)
monsteraction(monsters[i]);
}
void
monsterrender()
{
loopv(monsters) renderclient(monsters[i], false,
monstertypes[monsters[i]->mtype].mdlname, monsters[i]->mtype == 5,
monstertypes[monsters[i]->mtype].mscale / 10.0f);
}
|
︙ | | | ︙ | |
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
if (d->monsterstate)
return false; // hack
headspace = d->o.z - o->o.z - o->aboveeye - d->eyeheight;
if (headspace < 0)
headspace = 10;
};
return true;
};
bool
cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by,
int &bs) // recursively collide with a mipmapped corner cube
{
sqr *w = wmip[mip];
int sz = ssize >> mip;
bool stest =
SOLID(SWS(w, x + dx, y, sz)) && SOLID(SWS(w, x, y + dy, sz));
mip++;
x /= 2;
y /= 2;
if (SWS(wmip[mip], x, y, ssize >> mip)->type == CORNER) {
bx = x << 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
{
loopv(ents)
{
entity &e = ents[i];
|
<
>
<
>
|
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
if (d->monsterstate)
return false; // hack
headspace = d->o.z - o->o.z - o->aboveeye - d->eyeheight;
if (headspace < 0)
headspace = 10;
};
return true;
}
bool
cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by,
int &bs) // recursively collide with a mipmapped corner cube
{
sqr *w = wmip[mip];
int sz = ssize >> mip;
bool stest =
SOLID(SWS(w, x + dx, y, sz)) && SOLID(SWS(w, x, y + dy, sz));
mip++;
x /= 2;
y /= 2;
if (SWS(wmip[mip], x, y, ssize >> mip)->type == CORNER) {
bx = x << 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
{
loopv(ents)
{
entity &e = ents[i];
|
︙ | | | ︙ | |
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
const float fy2 = d->o.y + d->radius;
const int x1 = fast_f2nat(fx1);
const int y1 = fast_f2nat(fy1);
const int x2 = fast_f2nat(fx2);
const int y2 = fast_f2nat(fy2);
float hi = 127, lo = -128;
float minfloor = (d->monsterstate && !spawn && d->health > 100)
? d->o.z - d->eyeheight - 4.5f
: -1000.0f; // big monsters are afraid of heights,
// unless angry :)
for (int x = x1; x <= x2; x++)
for (int y = y1; y <= y2; y++) // collide with map
{
if (OUTBORD(x, y))
return false;
sqr *s = S(x, y);
|
|
|
|
|
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
const float fy2 = d->o.y + d->radius;
const int x1 = fast_f2nat(fx1);
const int y1 = fast_f2nat(fy1);
const int x2 = fast_f2nat(fx2);
const int y2 = fast_f2nat(fy2);
float hi = 127, lo = -128;
float minfloor = (d->monsterstate && !spawn && d->health > 100)
? d->o.z - d->eyeheight - 4.5f
: -1000.0f; // big monsters are afraid of heights,
// unless angry :)
for (int x = x1; x <= x2; x++)
for (int y = y1; y <= y2; y++) // collide with map
{
if (OUTBORD(x, y))
return false;
sqr *s = S(x, y);
|
︙ | | | ︙ | |
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
};
case FHF: // FIXME: too simplistic collision with
// slopes, makes it feels like tiny stairs
floor -= (s->vdelta + S(x + 1, y)->vdelta +
S(x, y + 1)->vdelta +
S(x + 1, y + 1)->vdelta) /
16.0f;
break;
case CHF:
ceil += (s->vdelta + S(x + 1, y)->vdelta +
S(x, y + 1)->vdelta +
S(x + 1, y + 1)->vdelta) /
16.0f;
};
if (ceil < hi)
hi = ceil;
if (floor > lo)
lo = floor;
if (floor < minfloor)
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 (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;
headspace -= 0.01f;
mmcollide(d, hi, lo); // collide with map models
if (spawn) {
d->o.z = lo + d->eyeheight; // just drop to floor (sideeffect)
d->onfloor = true;
|
|
|
<
>
|
|
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
};
case FHF: // FIXME: too simplistic collision with
// slopes, makes it feels like tiny stairs
floor -= (s->vdelta + S(x + 1, y)->vdelta +
S(x, y + 1)->vdelta +
S(x + 1, y + 1)->vdelta) /
16.0f;
break;
case CHF:
ceil += (s->vdelta + S(x + 1, y)->vdelta +
S(x, y + 1)->vdelta +
S(x + 1, y + 1)->vdelta) /
16.0f;
};
if (ceil < hi)
hi = ceil;
if (floor > lo)
lo = floor;
if (floor < minfloor)
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 (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;
headspace -= 0.01f;
mmcollide(d, hi, lo); // collide with map models
if (spawn) {
d->o.z = lo + d->eyeheight; // just drop to floor (sideeffect)
d->onfloor = true;
|
︙ | | | ︙ | |
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
|
return true;
}
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
{
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
void
moveplayer(dynent *pl, int moveres, bool local, int curtime)
|
<
>
<
>
|
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
|
return true;
}
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
{
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
void
moveplayer(dynent *pl, int moveres, bool local, int curtime)
|
︙ | | | ︙ | |
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
|
}; // dampen velocity change even harder, gives
// correct water feel
if (local)
playsoundc(S_JUMP);
else if (pl->monsterstate)
playsound(S_JUMP, &pl->o);
} else if (pl->timeinair >
800) // if we land after long time must have
// been a high jump, make thud sound
{
if (local)
playsoundc(S_LAND);
else if (pl->monsterstate)
playsound(S_LAND, &pl->o);
};
pl->timeinair = 0;
} 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
loopi(moveres) // discrete steps collision detection & sliding
{
// try move forward
pl->o.x += f * d.x;
pl->o.y += f * d.y;
|
|
|
|
<
|
<
|
|
<
|
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
|
}; // dampen velocity change even harder, gives
// correct water feel
if (local)
playsoundc(S_JUMP);
else if (pl->monsterstate)
playsound(S_JUMP, &pl->o);
} else if (pl->timeinair >
800) // if we land after long time must have
// been a high jump, make thud sound
{
if (local)
playsoundc(S_LAND);
else if (pl->monsterstate)
playsound(S_LAND, &pl->o);
};
pl->timeinair = 0;
} 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
loopi(moveres) // discrete steps collision detection & sliding
{
// try move forward
pl->o.x += f * d.x;
pl->o.y += f * d.y;
|
︙ | | | ︙ | |
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
|
pl->o.y -= f * d.y;
if (collide(pl, false, drop, rise)) {
d.y = d.x = 0;
continue;
};
pl->o.z -= f * d.z;
break;
};
};
// detect wether player is outside map, used for skipping zbuffer clear
// mostly
if (pl->o.x < 0 || pl->o.x >= ssize || pl->o.y < 0 || pl->o.y > ssize) {
pl->outsidemap = true;
} else {
sqr *s = S((int)pl->o.x, (int)pl->o.y);
pl->outsidemap =
SOLID(s) ||
pl->o.z < s->floor - (s->type == FHF ? s->vdelta / 4 : 0) ||
pl->o.z > s->ceil + (s->type == CHF ? s->vdelta / 4 : 0);
};
// automatically apply smooth roll when strafing
if (pl->strafe == 0) {
|
<
>
|
<
|
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
|
pl->o.y -= f * d.y;
if (collide(pl, false, drop, rise)) {
d.y = d.x = 0;
continue;
};
pl->o.z -= f * d.z;
break;
}
};
// detect wether player is outside map, used for skipping zbuffer clear
// mostly
if (pl->o.x < 0 || pl->o.x >= ssize || pl->o.y < 0 || pl->o.y > ssize) {
pl->outsidemap = true;
} else {
sqr *s = S((int)pl->o.x, (int)pl->o.y);
pl->outsidemap = SOLID(s) ||
pl->o.z < s->floor - (s->type == FHF ? s->vdelta / 4 : 0) ||
pl->o.z > s->ceil + (s->type == CHF ? s->vdelta / 4 : 0);
};
// automatically apply smooth roll when strafing
if (pl->strafe == 0) {
|
︙ | | | ︙ | |
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
|
if (!pl->inwater && water) {
playsound(S_SPLASH2, &pl->o);
pl->vel.z = 0;
} else if (pl->inwater && !water)
playsound(S_SPLASH1, &pl->o);
pl->inwater = water;
};
void
moveplayer(dynent *pl, int moveres, bool local)
{
loopi(physicsrepeat) moveplayer(pl, moveres, local,
i ? curtime / physicsrepeat
: curtime - curtime / physicsrepeat * (physicsrepeat - 1));
};
|
<
>
<
>
|
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
|
if (!pl->inwater && water) {
playsound(S_SPLASH2, &pl->o);
pl->vel.z = 0;
} else if (pl->inwater && !water)
playsound(S_SPLASH1, &pl->o);
pl->inwater = water;
}
void
moveplayer(dynent *pl, int moveres, bool local)
{
loopi(physicsrepeat) moveplayer(pl, moveres, local,
i ? curtime / physicsrepeat
: curtime - curtime / physicsrepeat * (physicsrepeat - 1));
}
|
︙ | | | ︙ | |
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
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.
#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 vert(v1, v2, v3, ls, t1, t2) \
{ \
vertf((float)(v1), (float)(v2), (float)(v3), ls, t1, t2); \
}
int nquads;
const float TEXTURESCALE = 32.0f;
bool floorstrip = false, deltastrip = false;
int oh, oy, ox, ogltex; // the o* vars are used by the stripification
int ol3r, ol3g, ol3b, ol4r, ol4g, ol4b;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
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.
#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 vert(v1, v2, v3, ls, t1, t2) \
{ \
vertf((float)(v1), (float)(v2), (float)(v3), ls, t1, t2); \
}
int nquads;
const float TEXTURESCALE = 32.0f;
bool floorstrip = false, deltastrip = false;
int oh, oy, ox, ogltex; // the o* vars are used by the stripification
int ol3r, ol3g, ol3b, ol4r, ol4g, ol4b;
|
︙ | | | ︙ | |
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
void
mipstats(int a, int b, int c)
{
if (showm)
conoutf(@"1x1/2x2/4x4: %d / %d / %d", a, b, c);
}
#define stripend() \
{ \
if (floorstrip || deltastrip) { \
addstrip(ogltex, firstindex, curvert - firstindex); \
floorstrip = deltastrip = false; \
}; \
};
void
finishstrips()
{
stripend();
};
sqr sbright, sdark;
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
|
|
|
|
|
|
|
<
>
|
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
void
mipstats(int a, int b, int c)
{
if (showm)
conoutf(@"1x1/2x2/4x4: %d / %d / %d", a, b, c);
}
#define stripend() \
{ \
if (floorstrip || deltastrip) { \
addstrip(ogltex, firstindex, curvert - firstindex); \
floorstrip = deltastrip = false; \
}; \
};
void
finishstrips()
{
stripend();
}
sqr sbright, sdark;
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
|
︙ | | | ︙ | |
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
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;
if (first) // start strip here
{
stripend();
firstindex = curvert;
ogltex = gltex;
oh = h;
|
|
|
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
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;
if (first) // start strip here
{
stripend();
firstindex = curvert;
ogltex = gltex;
oh = h;
|
︙ | | | ︙ | |
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
} 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
{
vertcheck();
|
<
>
|
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
} 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
{
vertcheck();
|
︙ | | | ︙ | |
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
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
{
stripend();
vertcheck();
|
<
>
|
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
|
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
{
stripend();
vertcheck();
|
︙ | | | ︙ | |
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
|
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);
nquads++;
};
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);
};
};
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();
|
<
>
<
>
|
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
|
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);
nquads++;
}
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);
};
}
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();
|
︙ | | | ︙ | |
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
|
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);
};
int wx1, wy1, wx2, wy2;
VAR(watersubdiv, 1, 4, 64);
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)
{
vertcheck();
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)
{
|
<
>
<
>
<
>
<
>
|
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
|
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);
}
int wx1, wy1, wx2, wy2;
VAR(watersubdiv, 1, 4, 64);
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)
{
vertcheck();
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)
{
|
︙ | | | ︙ | |
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
|
glDrawArrays(GL_TRIANGLE_STRIP, curvert -= n, n);
};
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
return nquads;
};
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) {
|
<
>
|
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
|
glDrawArrays(GL_TRIANGLE_STRIP, curvert -= n, n);
};
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
return nquads;
}
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) {
|
︙ | | | ︙ | |
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
|
if (y < wy1)
wy1 = y;
if (x2 > wx2)
wx2 = x2;
if (y2 > wy2)
wy2 = y2;
};
};
void
resetcubes()
{
if (!verts)
reallocv();
floorstrip = deltastrip = false;
wx1 = -1;
nquads = 0;
sbright.r = sbright.g = sbright.b = 255;
sdark.r = sdark.g = sdark.b = 0;
};
|
<
>
<
>
|
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
|
if (y < wy1)
wy1 = y;
if (x2 > wx2)
wx2 = x2;
if (y2 > wy2)
wy2 = y2;
};
}
void
resetcubes()
{
if (!verts)
reallocv();
floorstrip = deltastrip = false;
wx1 = -1;
nquads = 0;
sbright.r = sbright.g = sbright.b = 255;
sdark.r = sdark.g = sdark.b = 0;
}
|
︙ | | | ︙ | |
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
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)x2, z2, (float)y2);
glEnd();
xtraverts += 4;
};
void
linestyle(float width, int r, int g, int b)
{
glLineWidth(width);
glColor3ub(r, g, b);
};
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);
glEnd();
xtraverts += 4;
};
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);
glEnd();
xtraverts += 4;
};
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);
|
<
>
<
>
<
>
<
>
|
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
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)x2, z2, (float)y2);
glEnd();
xtraverts += 4;
}
void
linestyle(float width, int r, int g, int b)
{
glLineWidth(width);
glColor3ub(r, g, b);
}
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);
glEnd();
xtraverts += 4;
}
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);
glEnd();
xtraverts += 4;
}
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);
|
︙ | | | ︙ | |
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
glVertex2i(x1, y2);
glEnd();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
xtraverts += 8;
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glDepthMask(GL_TRUE);
};
const int MAXSPHERES = 50;
struct sphere {
OFVector3D o;
float size, max;
int type;
sphere *next;
};
sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL;
bool sinit = false;
void
newsphere(OFVector3D &o, float max, int type)
{
if (!sinit) {
loopi(MAXSPHERES)
{
spheres[i].next = sempty;
sempty = &spheres[i];
};
sinit = true;
};
if (sempty) {
sphere *p = sempty;
sempty = p->next;
p->o = o;
p->max = max;
p->size = 1;
p->type = type;
p->next = slist;
slist = p;
};
};
void
renderspheres(int time)
{
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
<
>
<
>
<
>
|
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
glVertex2i(x1, y2);
glEnd();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
xtraverts += 8;
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glDepthMask(GL_TRUE);
}
const int MAXSPHERES = 50;
struct sphere {
OFVector3D o;
float size, max;
int type;
sphere *next;
};
sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL;
bool sinit = false;
void
newsphere(OFVector3D &o, float max, int type)
{
if (!sinit) {
loopi(MAXSPHERES)
{
spheres[i].next = sempty;
sempty = &spheres[i];
}
sinit = true;
};
if (sempty) {
sphere *p = sempty;
sempty = p->next;
p->o = o;
p->max = max;
p->size = 1;
p->type = type;
p->next = slist;
slist = p;
};
}
void
renderspheres(int time)
{
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
︙ | | | ︙ | |
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
p->size += time / 100.0f;
pp = &p->next;
};
};
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
};
string closeent;
OFString *entnames[] = {
@"none?",
@"light",
@"playerstart",
@"shells",
|
<
>
|
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
p->size += time / 100.0f;
pp = &p->next;
};
};
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
}
string closeent;
OFString *entnames[] = {
@"none?",
@"light",
@"playerstart",
@"shells",
|
︙ | | | ︙ | |
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
|
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
float
depthcorrect(float 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
|
<
>
<
>
|
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
|
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
float
depthcorrect(float 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
|
︙ | | | ︙ | |
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
|
&worldx, &worldz, &worldy);
worldpos.x = (float)worldx;
worldpos.y = (float)worldy;
worldpos.z = (float)worldz;
OFVector3D r = OFMakeVector3D(mm[0], mm[4], mm[8]);
OFVector3D u = OFMakeVector3D(mm[1], mm[5], mm[9]);
setorient(r, u);
};
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;
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);
glEnd();
xtraverts += 4;
};
void
invertperspective()
{
// 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];
glLoadMatrixd(inv);
};
VARP(crosshairsize, 0, 15, 50);
int dblend = 0;
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)
{
|
<
>
<
>
<
>
<
>
|
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
|
&worldx, &worldz, &worldy);
worldpos.x = (float)worldx;
worldpos.y = (float)worldy;
worldpos.z = (float)worldz;
OFVector3D r = OFMakeVector3D(mm[0], mm[4], mm[8]);
OFVector3D u = OFMakeVector3D(mm[1], mm[5], mm[9]);
setorient(r, u);
}
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;
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);
glEnd();
xtraverts += 4;
}
void
invertperspective()
{
// 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];
glLoadMatrixd(inv);
}
VARP(crosshairsize, 0, 15, 50);
int dblend = 0;
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)
{
|
︙ | | | ︙ | |
︙ | | | ︙ | |
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
fatal(@"glu sphere");
gluQuadricDrawStyle(qsphere, GLU_FILL);
gluQuadricOrientation(qsphere, GLU_INSIDE);
gluQuadricTexture(qsphere, GL_TRUE);
glNewList(1, GL_COMPILE);
gluSphere(qsphere, 1, 12, 6);
glEndList();
};
void
cleangl()
{
if (qsphere)
gluDeleteQuadric(qsphere);
}
|
<
>
|
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
fatal(@"glu sphere");
gluQuadricDrawStyle(qsphere, GLU_FILL);
gluQuadricOrientation(qsphere, GLU_INSIDE);
gluQuadricTexture(qsphere, GL_TRUE);
glNewList(1, GL_COMPILE);
gluSphere(qsphere, 1, 12, 6);
glEndList();
}
void
cleangl()
{
if (qsphere)
gluDeleteQuadric(qsphere);
}
|
︙ | | | ︙ | |
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
int mapping[256][MAXFRAMES]; // ( cube texture, frame ) -> ( opengl id, name )
string mapname[256][MAXFRAMES];
void
purgetextures()
{
loopi(256) loop(j, MAXFRAMES) mapping[i][j] = 0;
};
int curtexnum = 0;
void
texturereset()
{
curtexnum = 0;
|
<
>
|
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
int mapping[256][MAXFRAMES]; // ( cube texture, frame ) -> ( opengl id, name )
string mapname[256][MAXFRAMES];
void
purgetextures()
{
loopi(256) loop(j, MAXFRAMES) mapping[i][j] = 0;
}
int curtexnum = 0;
void
texturereset()
{
curtexnum = 0;
|
︙ | | | ︙ | |
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
|
if (hasoverbright) {
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
glTexEnvi(
GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);
};
};
int skyoglid;
struct strip {
int tex, start, num;
};
vector<strip> strips;
|
<
>
|
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
|
if (hasoverbright) {
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
glTexEnvi(
GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);
};
}
int skyoglid;
struct strip {
int tex, start, num;
};
vector<strip> strips;
|
︙ | | | ︙ | |
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
|
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);
};
VARP(fov, 10, 105, 120);
int xtraverts;
VAR(fog, 64, 180, 1024);
VAR(fogcolour, 0, 0x8099B3, 0xFFFFFF);
|
<
>
|
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
|
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);
}
VARP(fov, 10, 105, 120);
int xtraverts;
VAR(fog, 64, 180, 1024);
VAR(fogcolour, 0, 0x8099B3, 0xFFFFFF);
|
︙ | | | ︙ | |
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
|
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fovy, aspect, 0.15f, farplane);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_CULL_FACE);
};
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;
|
<
>
|
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
|
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(fovy, aspect, 0.15f, farplane);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_CULL_FACE);
}
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;
|
︙ | | | ︙ | |
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
|
fovy += (float)sin(lastmillis / 1000.0) * 2.0f;
aspect += (float)sin(lastmillis / 1000.0 + PI) * 0.1f;
glFogi(GL_FOG_START, 0);
glFogi(GL_FOG_END, (fog + 96) / 8);
};
glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) |
GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
int farplane = fog * 5 / 2;
gluPerspective(fovy, aspect, 0.15f, farplane);
glMatrixMode(GL_MODELVIEW);
|
|
|
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
|
fovy += (float)sin(lastmillis / 1000.0) * 2.0f;
aspect += (float)sin(lastmillis / 1000.0 + PI) * 0.1f;
glFogi(GL_FOG_START, 0);
glFogi(GL_FOG_END, (fog + 96) / 8);
};
glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) |
GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
int farplane = fog * 5 / 2;
gluPerspective(fovy, aspect, 0.15f, farplane);
glMatrixMode(GL_MODELVIEW);
|
︙ | | | ︙ | |
486
487
488
489
490
491
492
493
|
glDisable(GL_TEXTURE_2D);
gl_drawhud(w, h, (int)curfps, nquads, curvert, underwater);
glEnable(GL_CULL_FACE);
glEnable(GL_FOG);
};
|
<
>
|
486
487
488
489
490
491
492
493
|
glDisable(GL_TEXTURE_2D);
gl_drawhud(w, h, (int)curfps, nquads, curvert, underwater);
glEnable(GL_CULL_FACE);
glEnable(GL_FOG);
}
|
︙ | | | ︙ | |
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
|
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);
};
restoreserverstate(ents);
gzread(f, player1, sizeof(dynent));
player1->lastaction = lastmillis;
int nmonsters = gzgeti();
dvector &monsters = getmonsters();
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;
};
restoremonsterstate();
int nplayers = gzgeti();
loopi(nplayers) if (!gzget())
{
dynent *d = getclient(i);
assert(d);
gzread(f, d, sizeof(dynent));
};
conoutf(@"savegame restored");
if (demoloading)
startdemo();
else
stop();
}
|
<
>
|
<
<
>
<
>
|
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
|
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);
}
restoreserverstate(ents);
gzread(f, player1, sizeof(dynent));
player1->lastaction = lastmillis;
int nmonsters = gzgeti();
dvector &monsters = getmonsters();
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;
}
restoremonsterstate();
int nplayers = gzgeti();
loopi(nplayers) if (!gzget())
{
dynent *d = getclient(i);
assert(d);
gzread(f, d, sizeof(dynent));
}
conoutf(@"savegame restored");
if (demoloading)
startdemo();
else
stop();
}
|
︙ | | | ︙ | |
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
|
COMMAND(record, ARG_1STR)
void
demodamage(int damage, OFVector3D &o)
{
ddamage = damage;
dorig = o;
};
void
demoblend(int damage)
{
bdamage = damage;
};
void
incomingdemodata(uchar *buf, int len, bool extras)
{
if (!demorecording)
return;
gzputi(lastmillis - starttime);
|
<
>
<
>
|
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
|
COMMAND(record, ARG_1STR)
void
demodamage(int damage, OFVector3D &o)
{
ddamage = damage;
dorig = o;
}
void
demoblend(int damage)
{
bdamage = damage;
}
void
incomingdemodata(uchar *buf, int len, bool extras)
{
if (!demorecording)
return;
gzputi(lastmillis - starttime);
|
︙ | | | ︙ | |
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
|
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)
{
while (b->yaw - a->yaw > 180)
a->yaw += 360;
while (b->yaw - a->yaw < -180)
a->yaw -= 360;
};
void
demoplaybackstep()
{
while (demoplayback && lastmillis >= playbacktime) {
int len = gzgeti();
if (len < 1 || len > MAXTRANS) {
|
<
>
<
>
|
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
|
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)
{
while (b->yaw - a->yaw > 180)
a->yaw += 360;
while (b->yaw - a->yaw < -180)
a->yaw -= 360;
}
void
demoplaybackstep()
{
while (demoplayback && lastmillis >= playbacktime) {
int len = gzgeti();
if (len < 1 || len > MAXTRANS) {
|
︙ | | | ︙ | |
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
|
readdemotime();
}
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];
*player1 = *b;
if (a != b) // interpolate pos & angles
{
dynent *c = b;
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);
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
|
|
|
|
<
|
|
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
|
readdemotime();
}
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];
*player1 = *b;
if (a != b) // interpolate pos & angles
{
dynent *c = b;
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);
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
|
︙ | | | ︙ | |
︙ | | | ︙ | |
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
restoreserverstate(
vector<entity> &ents) // hack: called from savegame code, only works in SP
{
loopv(sents)
{
sents[i].spawned = ents[i].spawned;
sents[i].spawnsecs = 0;
};
};
int interm = 0, minremain = 0, mapend = 0;
bool mapreload = false;
static OFString *serverpassword = @"";
bool isdedicated;
|
<
<
>
>
|
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
restoreserverstate(
vector<entity> &ents) // hack: called from savegame code, only works in SP
{
loopv(sents)
{
sents[i].spawned = ents[i].spawned;
sents[i].spawnsecs = 0;
}
}
int interm = 0, minremain = 0, mapend = 0;
bool mapreload = false;
static OFString *serverpassword = @"";
bool isdedicated;
|
︙ | | | ︙ | |
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
break;
};
case ST_LOCAL:
localservertoclient(packet->data, packet->dataLength);
break;
};
};
void
send2(bool rel, int cn, int a, int b)
{
ENetPacket *packet =
enet_packet_create(NULL, 32, rel ? ENET_PACKET_FLAG_RELIABLE : 0);
uchar *start = packet->data;
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);
};
void
sendservmsg(OFString *msg)
{
ENetPacket *packet = enet_packet_create(
NULL, _MAXDEFSTR + 10, ENET_PACKET_FLAG_RELIABLE);
uchar *start = packet->data;
|
<
>
<
>
|
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
break;
};
case ST_LOCAL:
localservertoclient(packet->data, packet->dataLength);
break;
};
}
void
send2(bool rel, int cn, int a, int b)
{
ENetPacket *packet =
enet_packet_create(NULL, 32, rel ? ENET_PACKET_FLAG_RELIABLE : 0);
uchar *start = packet->data;
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);
}
void
sendservmsg(OFString *msg)
{
ENetPacket *packet = enet_packet_create(
NULL, _MAXDEFSTR + 10, ENET_PACKET_FLAG_RELIABLE);
uchar *start = packet->data;
|
︙ | | | ︙ | |
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
void
disconnect_client(int n, char *reason)
{
printf("disconnecting client (%s) [%s]\n", clients[n].hostname, reason);
enet_peer_disconnect(clients[n].peer);
clients[n].type = ST_EMPTY;
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
{
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()
{
loopv(clients) clients[i].mapvote[0] = 0;
};
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)
{
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
@autoreleasepool {
OFString *msg =
[OFString stringWithFormat:
@"%s suggests %@ on map %s (set map to vote)",
clients[sender].name, modestr(reqmode), map];
sendservmsg(msg);
}
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)
void
process(ENetPacket *packet, int sender) // sender may be -1
|
<
>
<
>
<
>
<
>
<
>
<
>
|
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
void
disconnect_client(int n, char *reason)
{
printf("disconnecting client (%s) [%s]\n", clients[n].hostname, reason);
enet_peer_disconnect(clients[n].peer);
clients[n].type = ST_EMPTY;
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
{
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()
{
loopv(clients) clients[i].mapvote[0] = 0;
}
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)
{
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
@autoreleasepool {
OFString *msg =
[OFString stringWithFormat:
@"%s suggests %@ on map %s (set map to vote)",
clients[sender].name, modestr(reqmode), map];
sendservmsg(msg);
}
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)
void
process(ENetPacket *packet, int sender) // sender may be -1
|
︙ | | | ︙ | |
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
|
};
if (p > end) {
disconnect_client(sender, "end of packet");
return;
};
multicast(packet, sender);
};
void
send_welcome(int n)
{
ENetPacket *packet =
enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
uchar *start = packet->data;
|
<
>
|
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
|
};
if (p > end) {
disconnect_client(sender, "end of packet");
return;
};
multicast(packet, sender);
}
void
send_welcome(int n)
{
ENetPacket *packet =
enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
uchar *start = packet->data;
|
︙ | | | ︙ | |
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
|
multicast(ENetPacket *packet, int sender)
{
loopv(clients)
{
if (i == sender)
continue;
send(i, packet);
};
};
void
localclienttoserver(ENetPacket *packet)
{
process(packet, 0);
if (!packet->referenceCount)
enet_packet_destroy(packet);
};
client &
addclient()
{
loopv(clients) if (clients[i].type == ST_EMPTY) return clients[i];
return clients.add();
};
void
checkintermission()
{
if (!minremain) {
interm = lastsec + 10;
mapend = lastsec + 1000;
};
send2(true, -1, SV_TIMEUP, minremain--);
};
void
startintermission()
{
minremain = 0;
checkintermission();
};
void
resetserverifempty()
{
loopv(clients) if (clients[i].type != ST_EMPTY) return;
clients.setsize(0);
smapname = @"";
|
<
<
>
>
<
>
<
>
<
>
<
>
|
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
|
multicast(ENetPacket *packet, int sender)
{
loopv(clients)
{
if (i == sender)
continue;
send(i, packet);
}
}
void
localclienttoserver(ENetPacket *packet)
{
process(packet, 0);
if (!packet->referenceCount)
enet_packet_destroy(packet);
}
client &
addclient()
{
loopv(clients) if (clients[i].type == ST_EMPTY) return clients[i];
return clients.add();
}
void
checkintermission()
{
if (!minremain) {
interm = lastsec + 10;
mapend = lastsec + 1000;
};
send2(true, -1, SV_TIMEUP, minremain--);
}
void
startintermission()
{
minremain = 0;
checkintermission();
}
void
resetserverifempty()
{
loopv(clients) if (clients[i].type != ST_EMPTY) return;
clients.setsize(0);
smapname = @"";
|
︙ | | | ︙ | |
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
|
{
if (sents[i].spawnsecs &&
(sents[i].spawnsecs -= seconds - lastsec) <= 0) {
sents[i].spawnsecs = 0;
sents[i].spawned = true;
send2(true, -1, SV_ITEMSPAWN, i);
};
};
lastsec = seconds;
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)
{
send2(true, i, SV_MAPRELOAD,
0); // ask a client to trigger map reload
mapreload = true;
break;
};
};
resetserverifempty();
if (!isdedicated)
return; // below is network only
|
<
>
<
>
|
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
|
{
if (sents[i].spawnsecs &&
(sents[i].spawnsecs -= seconds - lastsec) <= 0) {
sents[i].spawnsecs = 0;
sents[i].spawned = true;
send2(true, -1, SV_ITEMSPAWN, i);
};
}
lastsec = seconds;
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)
{
send2(true, i, SV_MAPRELOAD,
0); // ask a client to trigger map reload
mapreload = true;
break;
}
};
resetserverifempty();
if (!isdedicated)
return; // below is network only
|
︙ | | | ︙ | |
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
|
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]);
char hn[1024];
strcpy_s(
c.hostname, (enet_address_get_host(&c.peer->address,
hn, sizeof(hn)) == 0)
? hn
: "localhost");
printf("client connected (%s)\n", c.hostname);
send_welcome(lastconnect = &c - &clients[0]);
break;
}
case ENET_EVENT_TYPE_RECEIVE:
brec += event.packet->dataLength;
process(event.packet, (intptr_t)event.peer->data);
|
|
|
|
|
|
|
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
|
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]);
char hn[1024];
strcpy_s(c.hostname,
(enet_address_get_host(
&c.peer->address, hn, sizeof(hn)) == 0)
? hn
: "localhost");
printf("client connected (%s)\n", c.hostname);
send_welcome(lastconnect = &c - &clients[0]);
break;
}
case ENET_EVENT_TYPE_RECEIVE:
brec += event.packet->dataLength;
process(event.packet, (intptr_t)event.peer->data);
|
︙ | | | ︙ | |
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
|
if (numplayers > maxclients) {
disconnect_client(lastconnect, "maxclients reached");
};
};
#ifndef _WIN32
fflush(stdout);
#endif
};
void
cleanupserver()
{
if (serverhost)
enet_host_destroy(serverhost);
};
void
localdisconnect()
{
loopv(clients) if (clients[i].type == ST_LOCAL) clients[i].type =
ST_EMPTY;
};
void
localconnect()
{
client &c = addclient();
c.type = ST_LOCAL;
strcpy_s(c.hostname, "local");
send_welcome(&c - &clients[0]);
};
void
initserver(bool dedicated, int uprate, OFString *sdesc, OFString *ip,
OFString *master, OFString *passwd, int maxcl)
{
serverpassword = passwd;
maxclients = maxcl;
|
<
>
<
>
<
>
<
>
|
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
|
if (numplayers > maxclients) {
disconnect_client(lastconnect, "maxclients reached");
};
};
#ifndef _WIN32
fflush(stdout);
#endif
}
void
cleanupserver()
{
if (serverhost)
enet_host_destroy(serverhost);
}
void
localdisconnect()
{
loopv(clients) if (clients[i].type == ST_LOCAL) clients[i].type =
ST_EMPTY;
}
void
localconnect()
{
client &c = addclient();
c.type = ST_LOCAL;
strcpy_s(c.hostname, "local");
send_welcome(&c - &clients[0]);
}
void
initserver(bool dedicated, int uprate, OFString *sdesc, OFString *ip,
OFString *master, OFString *passwd, int maxcl)
{
serverpassword = passwd;
maxclients = maxcl;
|
︙ | | | ︙ | |
550
551
552
553
554
555
556
557
|
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);
};
};
|
<
>
|
550
551
552
553
554
555
556
557
|
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);
};
}
|
︙ | | | ︙ | |
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
rr.query = rt->query;
rr.address = address;
rt->query = NULL;
rt->starttime = 0;
SDL_UnlockMutex(resolvermutex);
};
return 0;
};
void
resolverinit(int threads, int limit)
{
resolverlimit = limit;
resolversem = SDL_CreateSemaphore(0);
resolvermutex = SDL_CreateMutex();
|
<
>
|
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
rr.query = rt->query;
rr.address = address;
rt->query = NULL;
rt->starttime = 0;
SDL_UnlockMutex(resolvermutex);
};
return 0;
}
void
resolverinit(int threads, int limit)
{
resolverlimit = limit;
resolversem = SDL_CreateSemaphore(0);
resolvermutex = SDL_CreateMutex();
|
︙ | | | ︙ | |
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
resolverresults.setsize(0);
while (SDL_SemTryWait(resolversem) == 0)
;
loopv(resolverthreads)
{
resolverthread &rt = resolverthreads[i];
resolverstop(rt, true);
};
SDL_UnlockMutex(resolvermutex);
};
void
resolverquery(char *name)
{
SDL_LockMutex(resolvermutex);
resolverqueries.add(name);
SDL_SemPost(resolversem);
SDL_UnlockMutex(resolvermutex);
};
bool
resolvercheck(char **name, ENetAddress *address)
{
SDL_LockMutex(resolvermutex);
if (!resolverresults.empty()) {
resolverresult &rr = resolverresults.pop();
|
<
>
<
>
<
>
|
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
resolverresults.setsize(0);
while (SDL_SemTryWait(resolversem) == 0)
;
loopv(resolverthreads)
{
resolverthread &rt = resolverthreads[i];
resolverstop(rt, true);
}
SDL_UnlockMutex(resolvermutex);
}
void
resolverquery(char *name)
{
SDL_LockMutex(resolvermutex);
resolverqueries.add(name);
SDL_SemPost(resolversem);
SDL_UnlockMutex(resolvermutex);
}
bool
resolvercheck(char **name, ENetAddress *address)
{
SDL_LockMutex(resolvermutex);
if (!resolverresults.empty()) {
resolverresult &rr = resolverresults.pop();
|
︙ | | | ︙ | |
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
if (lastmillis - rt.starttime > resolverlimit) {
resolverstop(rt, true);
*name = rt.query;
SDL_UnlockMutex(resolvermutex);
return true;
};
};
};
SDL_UnlockMutex(resolvermutex);
return false;
};
struct serverinfo {
string name;
string full;
string map;
string sdesc;
int mode, numplayers, ping, protocol, minremain;
|
<
>
<
>
|
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
if (lastmillis - rt.starttime > resolverlimit) {
resolverstop(rt, true);
*name = rt.query;
SDL_UnlockMutex(resolvermutex);
return true;
};
};
}
SDL_UnlockMutex(resolvermutex);
return false;
}
struct serverinfo {
string name;
string full;
string map;
string sdesc;
int mode, numplayers, ping, protocol, minremain;
|
︙ | | | ︙ | |
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
void
addserver(OFString *servername_)
{
@autoreleasepool {
const char *servername = servername_.UTF8String;
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;
si.mode = 0;
si.numplayers = 0;
si.ping = 9999;
si.protocol = 0;
|
|
|
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
void
addserver(OFString *servername_)
{
@autoreleasepool {
const char *servername = servername_.UTF8String;
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;
si.mode = 0;
si.numplayers = 0;
si.ping = 9999;
si.protocol = 0;
|
︙ | | | ︙ | |
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
if (si.address.host == ENET_HOST_ANY)
continue;
p = ping;
putint(p, lastmillis);
buf.data = ping;
buf.dataLength = p - ping;
enet_socket_send(pingsock, &si.address, &buf, 1);
};
lastinfo = lastmillis;
};
void
checkresolver()
{
char *name = NULL;
ENetAddress addr = {ENET_HOST_ANY, CUBE_SERVINFO_PORT};
while (resolvercheck(&name, &addr)) {
|
<
>
<
>
|
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
if (si.address.host == ENET_HOST_ANY)
continue;
p = ping;
putint(p, lastmillis);
buf.data = ping;
buf.dataLength = p - ping;
enet_socket_send(pingsock, &si.address, &buf, 1);
}
lastinfo = lastmillis;
}
void
checkresolver()
{
char *name = NULL;
ENetAddress addr = {ENET_HOST_ANY, CUBE_SERVINFO_PORT};
while (resolvercheck(&name, &addr)) {
|
︙ | | | ︙ | |
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
}
}
int
sicompare(const serverinfo *a, const serverinfo *b)
{
return a->ping > b->ping
? 1
: (a->ping < b->ping ? -1 : strcmp(a->name, b->name));
}
void
refreshservers()
{
checkresolver();
checkpings();
|
|
|
|
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
|
}
}
int
sicompare(const serverinfo *a, const serverinfo *b)
{
return a->ping > b->ping
? 1
: (a->ping < b->ping ? -1 : strcmp(a->name, b->name));
}
void
refreshservers()
{
checkresolver();
checkpings();
|
︙ | | | ︙ | |
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
|
si.numplayers,
si.map[0] ? si.map : "[unknown]",
modestr(si.mode).UTF8String,
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
@autoreleasepool {
menumanual(1, i, @(si.full));
|
|
<
|
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
|
si.numplayers,
si.map[0] ? si.map : "[unknown]",
modestr(si.mode).UTF8String,
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
@autoreleasepool {
menumanual(1, i, @(si.full));
|
︙ | | | ︙ | |
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
|
pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL);
resolverinit(1, 1000);
};
resolverclear();
loopv(servers) resolverquery(servers[i].name);
refreshservers();
menuset(1);
};
void
updatefrommaster()
{
const int MAXUPD = 32000;
uchar buf[MAXUPD];
uchar *reply = retrieveservers(buf, MAXUPD);
|
<
>
|
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
|
pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL);
resolverinit(1, 1000);
};
resolverclear();
loopv(servers) resolverquery(servers[i].name);
refreshservers();
menuset(1);
}
void
updatefrommaster()
{
const int MAXUPD = 32000;
uchar buf[MAXUPD];
uchar *reply = retrieveservers(buf, MAXUPD);
|
︙ | | | ︙ | |
︙ | | | ︙ | |
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
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
};
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)
{
if (!tag)
return;
|
<
>
<
>
|
|
|
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
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
}
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)
{
if (!tag)
return;
|
︙ | | | ︙ | |
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
|
{
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 (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
r->defer = 1;
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
};
} 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 (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 -
|
|
|
|
|
|
<
|
<
>
<
>
|
<
>
|
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
{
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 (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
r->defer = 1;
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
}
} 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 (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 -
|
︙ | | | ︙ | |
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
|
c:;
};
s.x /= 2;
s.y /= 2;
s.xs /= 2;
s.ys /= 2;
remip(s, level + 1);
};
void
remipmore(block &b, int level)
{
block bb = b;
if (bb.x > 1)
bb.x--;
if (bb.y > 1)
bb.y--;
if (bb.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
{
if (noteditmode())
return -1;
int best;
float bdist = 99999;
loopv(ents)
{
entity &e = ents[i];
if (e.type == NOTUSED)
continue;
OFVector3D v = OFMakeVector3D(e.x, e.y, e.z);
vdist(dist, t, player1->o, v);
if (dist < bdist) {
best = i;
bdist = dist;
};
};
return bdist == 99999 ? -1 : best;
};
void
entproperty(int prop, int amount)
{
int e = closestent();
if (e < 0)
return;
|
<
>
<
>
<
>
<
>
|
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
|
c:;
};
s.x /= 2;
s.y /= 2;
s.xs /= 2;
s.ys /= 2;
remip(s, level + 1);
}
void
remipmore(block &b, int level)
{
block bb = b;
if (bb.x > 1)
bb.x--;
if (bb.y > 1)
bb.y--;
if (bb.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
{
if (noteditmode())
return -1;
int best;
float bdist = 99999;
loopv(ents)
{
entity &e = ents[i];
if (e.type == NOTUSED)
continue;
OFVector3D v = OFMakeVector3D(e.x, e.y, e.z);
vdist(dist, t, player1->o, v);
if (dist < bdist) {
best = i;
bdist = dist;
};
}
return bdist == 99999 ? -1 : best;
}
void
entproperty(int prop, int amount)
{
int e = closestent();
if (e < 0)
return;
|
︙ | | | ︙ | |
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
|
case 2:
ents[e].attr3 += amount;
break;
case 3:
ents[e].attr4 += amount;
break;
};
};
void
delent()
{
int e = closestent();
if (e < 0) {
conoutf(@"no more entities");
|
<
>
|
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
|
case 2:
ents[e].attr3 += amount;
break;
case 3:
ents[e].attr4 += amount;
break;
};
}
void
delent()
{
int e = closestent();
if (e < 0) {
conoutf(@"no more entities");
|
︙ | | | ︙ | |
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
|
};
addmsg(1, 10, SV_EDITENT, ents.length(), type, e.x, e.y, e.z, e.attr1,
e.attr2, e.attr3, e.attr4);
ents.add(*((entity *)&e)); // unsafe!
if (type == LIGHT)
calclight();
return &ents.last();
};
void
clearents(OFString *name)
{
int type = findtype(name);
if (noteditmode() || multiplayer())
return;
loopv(ents)
{
entity &e = ents[i];
if (e.type == type)
e.type = NOTUSED;
};
if (type == LIGHT)
calclight();
}
COMMAND(clearents, ARG_1STR)
void
scalecomp(uchar &c, int intens)
{
int n = c * intens / 100;
if (n > 255)
n = 255;
c = n;
};
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) {
scalecomp(e.attr2, intens);
scalecomp(e.attr3, intens);
scalecomp(e.attr4, intens);
};
};
calclight();
}
COMMAND(scalelights, ARG_2INT)
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;
return -1;
};
sqr *wmip[LARGEST_FACTOR * 2];
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);
};
};
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())
|
<
>
<
>
<
>
<
>
<
>
<
<
>
>
|
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
|
};
addmsg(1, 10, SV_EDITENT, ents.length(), type, e.x, e.y, e.z, e.attr1,
e.attr2, e.attr3, e.attr4);
ents.add(*((entity *)&e)); // unsafe!
if (type == LIGHT)
calclight();
return &ents.last();
}
void
clearents(OFString *name)
{
int type = findtype(name);
if (noteditmode() || multiplayer())
return;
loopv(ents)
{
entity &e = ents[i];
if (e.type == type)
e.type = NOTUSED;
}
if (type == LIGHT)
calclight();
}
COMMAND(clearents, ARG_1STR)
void
scalecomp(uchar &c, int intens)
{
int n = c * intens / 100;
if (n > 255)
n = 255;
c = n;
}
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) {
scalecomp(e.attr2, intens);
scalecomp(e.attr3, intens);
scalecomp(e.attr4, intens);
};
}
calclight();
}
COMMAND(scalelights, ARG_2INT)
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;
return -1;
}
sqr *wmip[LARGEST_FACTOR * 2];
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);
}
}
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())
|
︙ | | | ︙ | |
︙ | | | ︙ | |
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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?
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);
|
|
|
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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?
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);
|
︙ | | | ︙ | |
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
y += stepy;
l -= stepl;
g -= stepg;
b -= stepb;
stepl -= 25;
stepg -= 25;
stepb -= 25;
};
} else // white light, special optimized version
{
int dimness = rnd((255 - light.attr2) / 16 + 1);
x += stepx * dimness;
y += stepy * dimness;
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;
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
{
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;
x += stepx;
y += stepy;
l -= stepl;
};
};
};
void
calclightsource(persistent_entity &l)
{
int reach = l.attr1;
int sx = l.x - reach;
int ex = l.x + reach;
|
<
>
<
>
>
|
<
<
>
|
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
y += stepy;
l -= stepl;
g -= stepg;
b -= stepb;
stepl -= 25;
stepg -= 25;
stepb -= 25;
}
} else // white light, special optimized version
{
int dimness = rnd((255 - light.attr2) / 16 + 1);
x += stepx * dimness;
y += stepy * dimness;
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;
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
{
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;
x += stepx;
y += stepy;
l -= stepl;
}
};
}
void
calclightsource(persistent_entity &l)
{
int reach = l.attr1;
int sx = l.x - reach;
int ex = l.x + reach;
|
︙ | | | ︙ | |
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
};
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
{
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
median(r);
median(g);
median(b);
};
remip(a);
};
void
calclight()
{
loop(x, ssize) loop(y, ssize)
{
sqr *s = S(x, y);
|
<
>
|
|
|
|
|
<
|
>
<
>
|
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
};
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
{
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
median(r);
median(g);
median(b);
}
remip(a);
}
void
calclight()
{
loop(x, ssize) loop(y, ssize)
{
sqr *s = S(x, y);
|
︙ | | | ︙ | |
︙ | | | ︙ | |
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
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);
};
};
const int MAX_MIP = 5; // 32x32 unit blocks
const int MIN_LOD = 2;
const int LOW_LOD = 25;
const int MAX_LOD = 1000;
int lod = 40, lodtop, lodbot, lodleft, lodright;
|
<
>
|
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
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);
};
}
const int MAX_MIP = 5; // 32x32 unit blocks
const int MIN_LOD = 2;
const int LOW_LOD = 25;
const int MAX_LOD = 1000;
int lod = 40, lodtop, lodbot, lodleft, lodright;
|
︙ | | | ︙ | |
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
case CORNER:
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.
|
<
>
|
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
case CORNER:
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.
|
︙ | | | ︙ | |
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
// 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);
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);
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
>
|
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
|
// 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);
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);
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)
|
︙ | | | ︙ | |
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
|
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
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 = 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) {
lodleft = lod;
distlod(lodtop, lodbot, yaw - 45, widef);
|
<
>
|
>
|
|
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
|
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
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 = 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) {
lodleft = lod;
distlod(lodtop, lodbot, yaw - 45, widef);
|
︙ | | | ︙ | |
351
352
353
354
355
356
357
358
|
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);
mipstats(stats[0], stats[1], stats[2]);
};
|
<
>
|
352
353
354
355
356
357
358
359
|
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);
mipstats(stats[0], stats[1], stats[2]);
}
|