Cube  Check-in [6f5dd50626]

Overview
Comment:Improve clang-format
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 6f5dd506262655e5d3a6732794f955c8476b19a80fa03d5ebb6744ed8b18c963
User & Date: js on 2025-03-08 02:38:40
Other Links: manifest | tags
Context
2025-03-08
03:05
More style cleanup check-in: 753ff34122 user: js tags: trunk
02:38
Improve clang-format check-in: 6f5dd50626 user: js tags: trunk
01:09
Remove memory pool check-in: 0097baa3a7 user: js tags: trunk
Changes

Modified src/.clang-format from [a045e80592] to [2a796d79cc].

1
2
3


4













5
6


7
8
9
10
11
12
13
14
15
16
17






18
19
IndentWidth: 8
TabWidth: 8
UseTab: ForIndentation


BreakBeforeBraces: Linux













AlwaysBreakAfterReturnType: AllDefinitions
AlignAfterOpenBracket: DontAlign


ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
ObjCPropertyAttributeOrder: [
    class, direct,
    readonly, readwrite,
    nullable, nonnull, null_resettable, null_unspecified,
    assign, retain, strong, copy, weak, unsafe_unretained,
    atomic, nonatomic,
    getter, setter
]






IndentPPDirectives: AfterHash
PPIndentWidth: 1



>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>


>
>











>
>
>
>
>
>


1
2
3
4
5
6
7
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
IndentWidth: 8
TabWidth: 8
UseTab: ForIndentation
ColumnLimit: 80
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BraceWrapping:
  AfterClass: false
  AfterControlStatement: false
  AfterEnum: false
  AfterFunction: true
  AfterNamespace: false
  AfterObjCDeclaration: true
  AfterStruct: false
  AfterUnion: false
  AfterExternBlock: false
  BeforeCatch: false
  BeforeElse: false
AlwaysBreakAfterReturnType: AllDefinitions
AlignAfterOpenBracket: DontAlign
AlignEscapedNewlines: Left
AlignOperands: DontAlign
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
ObjCPropertyAttributeOrder: [
    class, direct,
    readonly, readwrite,
    nullable, nonnull, null_resettable, null_unspecified,
    assign, retain, strong, copy, weak, unsafe_unretained,
    atomic, nonatomic,
    getter, setter
]
SpaceBeforeInheritanceColon: false
QualifierAlignment: Left
#RemoveEmptyLinesInUnwrappedLines: true
RemoveSemicolon: true
CompactNamespaces: true
SortIncludes: CaseSensitive
IndentPPDirectives: AfterHash
PPIndentWidth: 1

Modified src/Alias.h from [257d20d71a] to [6c97091132].

1
2
3
4
5
6
7
8
9
10
11
12
#import "Identifier.h"

OF_ASSUME_NONNULL_BEGIN

@interface Alias : Identifier
@property (copy, nonatomic) OFString *action;
@property (readonly, nonatomic) bool persisted;

- (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName:(OFString *)name
                      action:(OFString *)action
                   persisted:(bool)persisted;




|







1
2
3
4
5
6
7
8
9
10
11
12
#import "Identifier.h"

OF_ASSUME_NONNULL_BEGIN

@interface Alias: Identifier
@property (copy, nonatomic) OFString *action;
@property (readonly, nonatomic) bool persisted;

- (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName:(OFString *)name
                      action:(OFString *)action
                   persisted:(bool)persisted;

Modified src/Command.h from [d308bef816] to [e066e0d794].

1
2
3
4
5
6
7
8
9
10
11
12
#import "Identifier.h"

OF_ASSUME_NONNULL_BEGIN

@interface Command : Identifier
@property (readonly, nonatomic) void (*function)();
@property (readonly, nonatomic) int argumentsTypes;

- (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName:(OFString *)name
                    function:(void (*)())function
              argumentsTypes:(int)argumentsTypes;




|







1
2
3
4
5
6
7
8
9
10
11
12
#import "Identifier.h"

OF_ASSUME_NONNULL_BEGIN

@interface Command: Identifier
@property (readonly, nonatomic) void (*function)();
@property (readonly, nonatomic) int argumentsTypes;

- (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName:(OFString *)name
                    function:(void (*)())function
              argumentsTypes:(int)argumentsTypes;

Modified src/Cube.mm from [aeae4b6df1] to [00b4da043c].

1
2
3
4
5
6
7
8
9
10

11
12
13
14
15
16
17
// main.cpp: initialisation & main loop

#include "cube.h"

OF_APPLICATION_DELEGATE(Cube)

VARF(gamespeed, 10, 100, 1000, if (multiplayer()) gamespeed = 100);
VARP(minmillis, 0, 5, 1000);

@implementation Cube {

	int _width, _height;
}

+ (Cube *)sharedInstance
{
	return (Cube *)OFApplication.sharedApplication.delegate;
}









|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// main.cpp: initialisation & main loop

#include "cube.h"

OF_APPLICATION_DELEGATE(Cube)

VARF(gamespeed, 10, 100, 1000, if (multiplayer()) gamespeed = 100);
VARP(minmillis, 0, 5, 1000);

@implementation Cube
{
	int _width, _height;
}

+ (Cube *)sharedInstance
{
	return (Cube *)OFApplication.sharedApplication.delegate;
}

Modified src/Identifier.h from [9069c402ca] to [f570e25a18].

1
2
3
4
5
6
7
8
9
10
11
12
#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@interface Identifier : OFObject
@property (readonly, copy, nonatomic) OFString *name;

- (instancetype)init OF_UNAVAILABLE;
- (instancetype)initWithName:(OFString *)name;
@end

OF_ASSUME_NONNULL_END




|







1
2
3
4
5
6
7
8
9
10
11
12
#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@interface Identifier: OFObject
@property (readonly, copy, nonatomic) OFString *name;

- (instancetype)init OF_UNAVAILABLE;
- (instancetype)initWithName:(OFString *)name;
@end

OF_ASSUME_NONNULL_END

Modified src/KeyMapping.h from [791a955c77] to [995386e6ec].

1
2
3
4
5
6
7
8
9
10
11
12
#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@interface KeyMapping : OFObject
@property (readonly) int code;
@property (readonly, nonatomic) OFString *name;
@property (copy, nonatomic) OFString *action;

- (instancetype)initWithCode:(int)code name:(OFString *)name;
@end





|







1
2
3
4
5
6
7
8
9
10
11
12
#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@interface KeyMapping: OFObject
@property (readonly) int code;
@property (readonly, nonatomic) OFString *name;
@property (copy, nonatomic) OFString *action;

- (instancetype)initWithCode:(int)code name:(OFString *)name;
@end

Modified src/MD2.h from [e16b607519] to [3eecbab1e8].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@class MapModelInfo;

@interface MD2 : OFObject
@property (nonatomic) MapModelInfo *mmi;
@property (copy, nonatomic) OFString *loadname;
@property (nonatomic) int mdlnum;
@property (nonatomic) bool loaded;

- (bool)loadWithIRI:(OFIRI *)IRI;
- (void)renderWithLight:(OFVector3D)light






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@class MapModelInfo;

@interface MD2: OFObject
@property (nonatomic) MapModelInfo *mmi;
@property (copy, nonatomic) OFString *loadname;
@property (nonatomic) int mdlnum;
@property (nonatomic) bool loaded;

- (bool)loadWithIRI:(OFIRI *)IRI;
- (void)renderWithLight:(OFVector3D)light

Modified src/MD2.mm from [150ffcd79c] to [074abf7ae2].

26
27
28
29
30
31
32
33

34
35
36
37
38
39
40

static float
snap(int sn, float f)
{
	return sn ? (float)(((int)(f + sn * 0.5f)) & (~(sn - 1))) : f;
}

@implementation MD2 {

	int _numGlCommands;
	int *_glCommands;
	int _numTriangles;
	int _frameSize;
	int _numFrames;
	int _numVerts;
	char *_frames;







|
>







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

static float
snap(int sn, float f)
{
	return sn ? (float)(((int)(f + sn * 0.5f)) & (~(sn - 1))) : f;
}

@implementation MD2
{
	int _numGlCommands;
	int *_glCommands;
	int _numTriangles;
	int _frameSize;
	int _numFrames;
	int _numVerts;
	char *_frames;

Modified src/MapModelInfo.h from [340f29da11] to [5ed60ec67c].

1
2
3
4
5
6
7
8
9
10
11
12
#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@interface MapModelInfo : OFObject
@property (nonatomic) int rad, h, zoff, snap;
@property (copy, nonatomic) OFString *name;

- (instancetype)initWithRad:(int)rad
                          h:(int)h
                       zoff:(int)zoff
                       snap:(int)snap




|







1
2
3
4
5
6
7
8
9
10
11
12
#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@interface MapModelInfo: OFObject
@property (nonatomic) int rad, h, zoff, snap;
@property (copy, nonatomic) OFString *name;

- (instancetype)initWithRad:(int)rad
                          h:(int)h
                       zoff:(int)zoff
                       snap:(int)snap

Modified src/Menu.h from [63596d2912] to [5fa014f8d6].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@class MenuItem;

@interface Menu : OFObject
@property (readonly, nonatomic) OFString *name;
@property (readonly) OFMutableArray<MenuItem *> *items;
@property (nonatomic) int mwidth;
@property (nonatomic) int menusel;

- (instancetype)initWithName:(OFString *)name;
@end






|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

@class MenuItem;

@interface Menu: OFObject
@property (readonly, nonatomic) OFString *name;
@property (readonly) OFMutableArray<MenuItem *> *items;
@property (nonatomic) int mwidth;
@property (nonatomic) int menusel;

- (instancetype)initWithName:(OFString *)name;
@end

Modified src/MenuItem.h from [58b3d87ceb] to [3f56310839].

1
2
3
4
5
6
7
#import <ObjFW/ObjFW.h>

@interface MenuItem : OFObject
@property (readonly, nonatomic) OFString *text, *action;

- (instancetype)initWithText:(OFString *)text action:(OFString *)action;
@end


|




1
2
3
4
5
6
7
#import <ObjFW/ObjFW.h>

@interface MenuItem: OFObject
@property (readonly, nonatomic) OFString *text, *action;

- (instancetype)initWithText:(OFString *)text action:(OFString *)action;
@end

Modified src/Variable.h from [d1b2d68346] to [82779ddfda].

1
2
3
4
5
6
7
8
9
10
11
12
#import "Identifier.h"

OF_ASSUME_NONNULL_BEGIN

@interface Variable : Identifier
@property (readonly, nonatomic) int min, max;
@property (readonly, nonatomic) int *storage;
@property (readonly, nonatomic) void (*__cdecl function)();
@property (readonly, nonatomic) bool persisted;

- (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName:(OFString *)name




|







1
2
3
4
5
6
7
8
9
10
11
12
#import "Identifier.h"

OF_ASSUME_NONNULL_BEGIN

@interface Variable: Identifier
@property (readonly, nonatomic) int min, max;
@property (readonly, nonatomic) int *storage;
@property (readonly, nonatomic) void (*__cdecl function)();
@property (readonly, nonatomic) bool persisted;

- (instancetype)initWithName:(OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName:(OFString *)name

Modified src/client.mm from [da4c60f968] to [89ff04e5c2].

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
			putint(p, SV_MAPCHANGE);
			sendstring(toservermap, p);
			toservermap = @"";
			putint(p, nextmode);
		} else {
			putint(p, SV_POS);
			putint(p, clientnum);
			putint(
			    p, (int)(d->o.x *
			             DMF)); // quantize coordinates to 1/16th
			                    // of a cube, between 1 and 3 bytes
			putint(p, (int)(d->o.y * DMF));
			putint(p, (int)(d->o.z * DMF));
			putint(p, (int)(d->yaw * DAF));
			putint(p, (int)(d->pitch * DAF));
			putint(p, (int)(d->roll * DAF));
			putint(
			    p, (int)(d->vel.x * DVF)); // quantize to 1/100,
			                               // almost always 1 byte
			putint(p, (int)(d->vel.y * DVF));
			putint(p, (int)(d->vel.z * DVF));
			// pack rest in 1 byte: strafe:2, move:2, onfloor:1,
			// state:3
			putint(
			    p, (d->strafe & 3) | ((d->move & 3) << 2) |
			           (((int)d->onfloor) << 4) |
			           ((editmode ? CS_EDITING : d->state) << 5));

			if (senditemstoserver) {
				packet->flags = ENET_PACKET_FLAG_RELIABLE;
				putint(p, SV_ITEMLIST);
				if (!m_noitems)
					putitems(p);
				putint(p, -1);







|
|
|
|












|
|
|
|







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
			putint(p, SV_MAPCHANGE);
			sendstring(toservermap, p);
			toservermap = @"";
			putint(p, nextmode);
		} else {
			putint(p, SV_POS);
			putint(p, clientnum);
			putint(p,
			    (int)(d->o.x *
			        DMF)); // quantize coordinates to 1/16th
			               // of a cube, between 1 and 3 bytes
			putint(p, (int)(d->o.y * DMF));
			putint(p, (int)(d->o.z * DMF));
			putint(p, (int)(d->yaw * DAF));
			putint(p, (int)(d->pitch * DAF));
			putint(p, (int)(d->roll * DAF));
			putint(
			    p, (int)(d->vel.x * DVF)); // quantize to 1/100,
			                               // almost always 1 byte
			putint(p, (int)(d->vel.y * DVF));
			putint(p, (int)(d->vel.z * DVF));
			// pack rest in 1 byte: strafe:2, move:2, onfloor:1,
			// state:3
			putint(p,
			    (d->strafe & 3) | ((d->move & 3) << 2) |
			        (((int)d->onfloor) << 4) |
			        ((editmode ? CS_EDITING : d->state) << 5));

			if (senditemstoserver) {
				packet->flags = ENET_PACKET_FLAG_RELIABLE;
				putint(p, SV_ITEMLIST);
				if (!m_noitems)
					putitems(p);
				putint(p, -1);
408
409
410
411
412
413
414
415

		case ENET_EVENT_TYPE_DISCONNECT:
			if (disconnecting)
				disconnect();
			else
				server_err();
			return;
		}
};








<
>
408
409
410
411
412
413
414

415
		case ENET_EVENT_TYPE_DISCONNECT:
			if (disconnecting)
				disconnect();
			else
				server_err();
			return;
		}

}

Modified src/clientextras.mm from [45d297fe4b] to [0e321e84f3].

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
extern int democlientnum;

void
renderclients()
{
	dynent *d;
	loopv(players) if ((d = players[i]) &&
	                   (!demoplayback || i != democlientnum))
	    renderclient(d, isteam(player1->team, d->team), @"monster/ogro",
	        false, 1.0f);
}

// creation of scoreboard pseudo-menu

bool scoreson = false;

void







|
|
<







74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
extern int democlientnum;

void
renderclients()
{
	dynent *d;
	loopv(players) if ((d = players[i]) &&
	    (!demoplayback || i != democlientnum)) renderclient(d,
	    isteam(player1->team, d->team), @"monster/ogro", false, 1.0f);

}

// creation of scoreboard pseudo-menu

bool scoreson = false;

void

Modified src/clientgame.mm from [2ddec801b6] to [6689f47248].

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);

Modified src/clients2c.mm from [a511b2eec3] to [15295b6bce].

48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
			d->o.x += dx < 0 ? r - fx : -(r - fx);
	};
	int lagtime = lastmillis - d->lastupdate;
	if (lagtime) {
		d->plag = (d->plag * 5 + lagtime) / 6;
		d->lastupdate = lastmillis;
	};
};


void
localservertoclient(
    uchar *buf, int len) // processes any updates from the server
{
	if (ENET_NET_TO_HOST_16(*(ushort *)buf) != len)
		neterr(@"packet length");







<
>







48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
			d->o.x += dx < 0 ? r - fx : -(r - fx);
	};
	int lagtime = lastmillis - d->lastupdate;
	if (lagtime) {
		d->plag = (d->plag * 5 + lagtime) / 6;
		d->lastupdate = lastmillis;
	};

}

void
localservertoclient(
    uchar *buf, int len) // processes any updates from the server
{
	if (ENET_NET_TO_HOST_16(*(ushort *)buf) != len)
		neterr(@"packet length");

Modified src/console.mm from [de4b6af5c1] to [54ce1aa50b].

74
75
76
77
78
79
80
81

82
83
84
85
86
87

88
89
90
91
92
93
94
}

void
renderconsole() // render buffer taking into account time & scrolling
{
	int nd = 0;
	char *refs[ndraw];
	loopv(conlines) if (conskip ? i >= conskip - 1 ||

	                                  i >= conlines.length() - ndraw
	                            : lastmillis - conlines[i].outtime < 20000)
	{
		refs[nd++] = conlines[i].cref;
		if (nd == ndraw)
			break;

	}
	@autoreleasepool {
		loopj(nd)
		{
			draw_text(@(refs[j]), FONTH / 3,
			    (FONTH / 4 * 5) * (nd - j - 1) + FONTH / 3, 2);
		}







|
>
|
|
<
|
|
|
>







74
75
76
77
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94
95
}

void
renderconsole() // render buffer taking into account time & scrolling
{
	int nd = 0;
	char *refs[ndraw];
	loopv(conlines)
	{
		if (conskip ? i >= conskip - 1 || i >= conlines.length() - ndraw
		            : lastmillis - conlines[i].outtime < 20000) {

			refs[nd++] = conlines[i].cref;
			if (nd == ndraw)
				break;
		}
	}
	@autoreleasepool {
		loopj(nd)
		{
			draw_text(@(refs[j]), FONTH / 3,
			    (FONTH / 4 * 5) * (nd - j - 1) + FONTH / 3, 2);
		}

Modified src/cube.h from [d285e7fbb6] to [4e3991eb16].

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"

Modified src/editing.mm from [e060139dd3] to [f5920b6aeb].

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,

Modified src/entities.mm from [8e9438e362] to [6e39a8fac1].

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;

}

Modified src/monster.mm from [6306bf3f2c] to [9d16bae907].

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);
}

Modified src/physics.mm from [33d03da6be] to [5d3f7bf679].

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));

}

Modified src/rendercubes.mm from [3909c7f58d] to [e0804e2cb2].

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;

}

Modified src/renderextras.mm from [2f71e0ae26] to [0c686b4d39].

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)
{

Modified src/rendergl.mm from [7db200290d] to [5b95ce281e].

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);

}

Modified src/renderparticles.mm from [d554171715] to [9f00935137].

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
newparticle(OFVector3D &o, OFVector3D &d, int fade, int type)
{
	if (!parinit) {
		loopi(MAXPARTICLES)
		{
			particles[i].next = parempty;
			parempty = &particles[i];
		};

		parinit = true;
	};
	if (parempty) {
		particle *p = parempty;
		parempty = p->next;
		p->o = o;
		p->d = d;
		p->fade = fade;
		p->type = type;
		p->millis = lastmillis;
		p->next = parlist;
		parlist = p;
	};
};


VAR(demotracking, 0, 0, 1);
VARP(particlesize, 20, 100, 500);

OFVector3D right, up;

void







<
>













<
>







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
newparticle(OFVector3D &o, OFVector3D &d, int fade, int type)
{
	if (!parinit) {
		loopi(MAXPARTICLES)
		{
			particles[i].next = parempty;
			parempty = &particles[i];

		}
		parinit = true;
	};
	if (parempty) {
		particle *p = parempty;
		parempty = p->next;
		p->o = o;
		p->d = d;
		p->fade = fade;
		p->type = type;
		p->millis = lastmillis;
		p->next = parlist;
		parlist = p;
	};

}

VAR(demotracking, 0, 0, 1);
VARP(particlesize, 20, 100, 500);

OFVector3D right, up;

void
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
		if (numrender++ > maxparticles || (p->fade -= time) < 0) {
			*pp = p->next;
			p->next = parempty;
			parempty = p;
		} else {
			if (pt->gr)
				p->o.z -= ((lastmillis - p->millis) / 3.0f) *
				          curtime / (pt->gr * 10000);
			OFVector3D a = p->d;
			vmul(a, time);
			vdiv(a, 20000.0f);
			vadd(p->o, a);
			pp = &p->next;
		}
	}

	glEnable(GL_FOG);
	glDisable(GL_BLEND);
	glDepthMask(GL_TRUE);
};


void
particle_splash(int type, int num, int fade, OFVector3D &p)
{
	loopi(num)
	{
		const int radius = type == 5 ? 50 : 150;







|











<
>







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
		if (numrender++ > maxparticles || (p->fade -= time) < 0) {
			*pp = p->next;
			p->next = parempty;
			parempty = p;
		} else {
			if (pt->gr)
				p->o.z -= ((lastmillis - p->millis) / 3.0f) *
				    curtime / (pt->gr * 10000);
			OFVector3D a = p->d;
			vmul(a, time);
			vdiv(a, 20000.0f);
			vadd(p->o, a);
			pp = &p->next;
		}
	}

	glEnable(GL_FOG);
	glDisable(GL_BLEND);
	glDepthMask(GL_TRUE);

}

void
particle_splash(int type, int num, int fade, OFVector3D &p)
{
	loopi(num)
	{
		const int radius = type == 5 ? 50 : 150;

Modified src/rndmap.mm from [b9a3803fd5] to [db18b3b402].

1
2
3
4
5
6
7
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
// rndmap.cpp: perlin noise landscape generation and some experimental random
// map stuff, currently not used

#include "cube.h"

float
noise(int x, int y, int seed)
{
	int n = x + y * 57;
	n = (n << 13) ^ n;
	return 1.0f -
	       ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) /
	           1073741824.0f;
}

float
smoothednoise(int x, int y, int seed)
{
	float corners =
	    (noise(x - 1, y - 1, seed) + noise(x + 1, y - 1, seed) +
	        noise(x - 1, y + 1, seed) + noise(x + 1, y + 1, seed)) /
	    16;
	float sides = (noise(x - 1, y, seed) + noise(x + 1, y, seed) +
	                  noise(x, y - 1, seed) + noise(x, y + 1, seed)) /
	              8;
	float center = noise(x, y, seed) / 4;
	return corners + sides + center;
}

float
interpolate(float a, float b, float x)
{











|
|











|







1
2
3
4
5
6
7
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
// rndmap.cpp: perlin noise landscape generation and some experimental random
// map stuff, currently not used

#include "cube.h"

float
noise(int x, int y, int seed)
{
	int n = x + y * 57;
	n = (n << 13) ^ n;
	return 1.0f -
	    ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) /
	    1073741824.0f;
}

float
smoothednoise(int x, int y, int seed)
{
	float corners =
	    (noise(x - 1, y - 1, seed) + noise(x + 1, y - 1, seed) +
	        noise(x - 1, y + 1, seed) + noise(x + 1, y + 1, seed)) /
	    16;
	float sides = (noise(x - 1, y, seed) + noise(x + 1, y, seed) +
	                  noise(x, y - 1, seed) + noise(x, y + 1, seed)) /
	    8;
	float center = noise(x, y, seed) / 4;
	return corners + sides + center;
}

float
interpolate(float a, float b, float x)
{
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
88
89

{
	float total = 0;
	int seed = 0;
	for (int i = 0; i < 7; i++) {
		float frequency = (float)(2 ^ i);
		float amplitude = (float)pow(pers, i);
		total += interpolatednoise(x * frequency, y * frequency, seed) *
		         amplitude;
		seed += seedstep;
	}
	return total;
}

void
perlinarea(block &b, int scale, int seed, int psize)
{
	srand(seed);
	seed = rnd(10000);
	if (!scale)
		scale = 10;
	for (int x = b.x; x <= b.x + b.xs; x++)
		for (int y = b.y; y <= b.y + b.ys; y++) {
			sqr *s = S(x, y);
			if (!SOLID(s) && x != b.x + b.xs && y != b.y + b.ys)
				s->type = FHF;
			s->vdelta =
			    (int)(perlinnoise_2D(x / ((float)scale) + seed,
			              y / ((float)scale) + seed, 1000, 0.01f) *
			              50 +
			          25);
			if (s->vdelta > 128)
				s->vdelta = 0;
		};
};








|




















|
|



<
>
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
88

89
{
	float total = 0;
	int seed = 0;
	for (int i = 0; i < 7; i++) {
		float frequency = (float)(2 ^ i);
		float amplitude = (float)pow(pers, i);
		total += interpolatednoise(x * frequency, y * frequency, seed) *
		    amplitude;
		seed += seedstep;
	}
	return total;
}

void
perlinarea(block &b, int scale, int seed, int psize)
{
	srand(seed);
	seed = rnd(10000);
	if (!scale)
		scale = 10;
	for (int x = b.x; x <= b.x + b.xs; x++)
		for (int y = b.y; y <= b.y + b.ys; y++) {
			sqr *s = S(x, y);
			if (!SOLID(s) && x != b.x + b.xs && y != b.y + b.ys)
				s->type = FHF;
			s->vdelta =
			    (int)(perlinnoise_2D(x / ((float)scale) + seed,
			              y / ((float)scale) + seed, 1000, 0.01f) *
			            50 +
			        25);
			if (s->vdelta > 128)
				s->vdelta = 0;
		};

}

Modified src/savegamedemo.mm from [38fcd6b7f4] to [d7af13e4bd].

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

Modified src/server.mm from [133bbad38c] to [2b7d5c9bfe].

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);
	};

}

Modified src/serverbrowser.mm from [c6e27aadf2] to [4bf1a6646d].

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);

Modified src/serverms.mm from [063897f679] to [21c02ed323].

47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63

64
65
66
67
68
69
70
			mssock = ENET_SOCKET_NULL;
			return;
		};
		buf.data = ((char *)buf.data) + len;
		((char *)buf.data)[0] = 0;
		buf.dataLength -= len;
	};
};


uchar *
stripheader(uchar *b)
{
	char *s = strstr((char *)b, "\n\r\n");
	if (!s)
		s = strstr((char *)b, "\n\n");
	return s ? (uchar *)s : b;
};


ENetAddress masterserver = {ENET_HOST_ANY, 80};
int updmaster = 0;
string masterbase;
string masterpath;
uchar masterrep[MAXTRANS];
ENetBuffer masterb;







<
>








<
>







47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62

63
64
65
66
67
68
69
70
			mssock = ENET_SOCKET_NULL;
			return;
		};
		buf.data = ((char *)buf.data) + len;
		((char *)buf.data)[0] = 0;
		buf.dataLength -= len;
	};

}

uchar *
stripheader(uchar *b)
{
	char *s = strstr((char *)b, "\n\r\n");
	if (!s)
		s = strstr((char *)b, "\n\n");
	return s ? (uchar *)s : b;

}

ENetAddress masterserver = {ENET_HOST_ANY, 80};
int updmaster = 0;
string masterbase;
string masterpath;
uchar masterrep[MAXTRANS];
ENetBuffer masterb;
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
		httpgetsend(masterserver, masterbase, path, "cubeserver",
		    "Cube Server");
		masterrep[0] = 0;
		masterb.data = masterrep;
		masterb.dataLength = MAXTRANS - 1;
		updmaster = seconds + 60 * 60;
	};
};


void
checkmasterreply()
{
	bool busy = mssock != ENET_SOCKET_NULL;
	httpgetrecieve(masterb);
	if (busy && mssock == ENET_SOCKET_NULL)
		printf("masterserver reply: %s\n", stripheader(masterrep));
};


uchar *
retrieveservers(uchar *buf, int buflen)
{
	sprintf_sd(path)("%sretrieve.do?item=list", masterpath);
	httpgetsend(
	    masterserver, masterbase, path, "cubeserver", "Cube Server");
	ENetBuffer eb;
	buf[0] = 0;
	eb.data = buf;
	eb.dataLength = buflen - 1;
	while (mssock != ENET_SOCKET_NULL)
		httpgetrecieve(eb);
	return stripheader(buf);
};


ENetSocket pongsock = ENET_SOCKET_NULL;
static OFString *serverdesc;

void
serverms(int mode, int numplayers, int minremain, OFString *smapname,
    int seconds, bool isfull)







<
>








<
>














<
>







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
		httpgetsend(masterserver, masterbase, path, "cubeserver",
		    "Cube Server");
		masterrep[0] = 0;
		masterb.data = masterrep;
		masterb.dataLength = MAXTRANS - 1;
		updmaster = seconds + 60 * 60;
	};

}

void
checkmasterreply()
{
	bool busy = mssock != ENET_SOCKET_NULL;
	httpgetrecieve(masterb);
	if (busy && mssock == ENET_SOCKET_NULL)
		printf("masterserver reply: %s\n", stripheader(masterrep));

}

uchar *
retrieveservers(uchar *buf, int buflen)
{
	sprintf_sd(path)("%sretrieve.do?item=list", masterpath);
	httpgetsend(
	    masterserver, masterbase, path, "cubeserver", "Cube Server");
	ENetBuffer eb;
	buf[0] = 0;
	eb.data = buf;
	eb.dataLength = buflen - 1;
	while (mssock != ENET_SOCKET_NULL)
		httpgetrecieve(eb);
	return stripheader(buf);

}

ENetSocket pongsock = ENET_SOCKET_NULL;
static OFString *serverdesc;

void
serverms(int mode, int numplayers, int minremain, OFString *smapname,
    int seconds, bool isfull)

Modified src/serverutil.mm from [e5f6cbf888] to [9a3fdd3f13].

17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
	} else {
		*p++ = 0x81;
		*p++ = n;
		*p++ = n >> 8;
		*p++ = n >> 16;
		*p++ = n >> 24;
	};
};


int
getint(uchar *&p)
{
	int c = *((char *)p);
	p++;
	if (c == -128) {







<
>







17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
	} else {
		*p++ = 0x81;
		*p++ = n;
		*p++ = n >> 8;
		*p++ = n >> 16;
		*p++ = n >> 24;
	};

}

int
getint(uchar *&p)
{
	int c = *((char *)p);
	p++;
	if (c == -128) {
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
char
msgsizelookup(int msg)
{
	for (char *p = msgsizesl; *p >= 0; p += 2)
		if (*p == msg)
			return p[1];
	return -1;
};


// sending of maps between clients

static OFString *copyname;
int copysize;
uchar *copydata = NULL;








<
>







89
90
91
92
93
94
95

96
97
98
99
100
101
102
103
char
msgsizelookup(int msg)
{
	for (char *p = msgsizesl; *p >= 0; p += 2)
		if (*p == msg)
			return p[1];
	return -1;

}

// sending of maps between clients

static OFString *copyname;
int copysize;
uchar *copydata = NULL;

Modified src/sound.mm from [2ceb58ac38] to [af1eb7e101].

48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
	};
	if (stream) {
#ifndef USE_MIXER
		FSOUND_Stream_Close(stream);
#endif
		stream = NULL;
	};
};


VAR(soundbufferlen, 128, 1024, 4096);

void
initsound()
{
	memset(soundlocs, 0, sizeof(soundloc) * MAXCHAN);







<
>







48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
	};
	if (stream) {
#ifndef USE_MIXER
		FSOUND_Stream_Close(stream);
#endif
		stream = NULL;
	};

}

VAR(soundbufferlen, 128, 1024, 4096);

void
initsound()
{
	memset(soundlocs, 0, sizeof(soundloc) * MAXCHAN);
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
205
206
207
208
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
		return;
	stopsound();
#ifdef USE_MIXER
	Mix_CloseAudio();
#else
	FSOUND_Close();
#endif
};


VAR(stereo, 0, 1, 1);

void
updatechanvol(int chan, OFVector3D *loc)
{
	int vol = soundvol, pan = 255 / 2;
	if (loc) {
		vdist(dist, v, *loc, player1->o);
		vol -= (int)(dist * 3 * soundvol /
		             255); // simple mono distance attenuation
		if (stereo && (v.x != 0 || v.y != 0)) {
			float yaw = -atan2(v.x, v.y) -
			            player1->yaw *
			                (PI / 180.0f); // relative angle of
			                               // sound along X-Y axis
			pan = int(255.9f * (0.5 * sin(yaw) +
			                       0.5f)); // range is from 0 (left)
			                               // to 255 (right)
		};
	};
	vol = (vol * MAXVOL) / 255;
#ifdef USE_MIXER
	Mix_Volume(chan, vol);
	Mix_SetPanning(chan, 255 - pan, pan);
#else
	FSOUND_SetVolume(chan, vol);
	FSOUND_SetPan(chan, pan);
#endif
};


void
newsoundloc(int chan, OFVector3D *loc)
{
	assert(chan >= 0 && chan < MAXCHAN);
	soundlocs[chan].loc = *loc;
	soundlocs[chan].inuse = true;
};


void
updatevol()
{
	if (nosound)
		return;
	loopi(MAXCHAN) if (soundlocs[i].inuse)
	{
#ifdef USE_MIXER
		if (Mix_Playing(i))
#else
		if (FSOUND_IsPlaying(i))
#endif
			updatechanvol(i, &soundlocs[i].loc);
		else
			soundlocs[i].inuse = false;
	};
};



void
playsoundc(int n)
{
	addmsg(0, 2, SV_SOUND, n);
	playsound(n);
};


int soundsatonce = 0, lastsoundmillis = 0;

void
playsound(int n, OFVector3D *loc)
{
	if (nosound)







<
>










|


|
|
|
|
|
|










<
>







<
>
















<
<
>
>






<
>







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
205
206
207
208
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
		return;
	stopsound();
#ifdef USE_MIXER
	Mix_CloseAudio();
#else
	FSOUND_Close();
#endif

}

VAR(stereo, 0, 1, 1);

void
updatechanvol(int chan, OFVector3D *loc)
{
	int vol = soundvol, pan = 255 / 2;
	if (loc) {
		vdist(dist, v, *loc, player1->o);
		vol -= (int)(dist * 3 * soundvol /
		    255); // simple mono distance attenuation
		if (stereo && (v.x != 0 || v.y != 0)) {
			float yaw = -atan2(v.x, v.y) -
			    player1->yaw *
			        (PI / 180.0f); // relative angle of
			                       // sound along X-Y axis
			pan = int(255.9f *
			    (0.5 * sin(yaw) + 0.5f)); // range is from 0 (left)
			                              // to 255 (right)
		};
	};
	vol = (vol * MAXVOL) / 255;
#ifdef USE_MIXER
	Mix_Volume(chan, vol);
	Mix_SetPanning(chan, 255 - pan, pan);
#else
	FSOUND_SetVolume(chan, vol);
	FSOUND_SetPan(chan, pan);
#endif

}

void
newsoundloc(int chan, OFVector3D *loc)
{
	assert(chan >= 0 && chan < MAXCHAN);
	soundlocs[chan].loc = *loc;
	soundlocs[chan].inuse = true;

}

void
updatevol()
{
	if (nosound)
		return;
	loopi(MAXCHAN) if (soundlocs[i].inuse)
	{
#ifdef USE_MIXER
		if (Mix_Playing(i))
#else
		if (FSOUND_IsPlaying(i))
#endif
			updatechanvol(i, &soundlocs[i].loc);
		else
			soundlocs[i].inuse = false;


	}
}

void
playsoundc(int n)
{
	addmsg(0, 2, SV_SOUND, n);
	playsound(n);

}

int soundsatonce = 0, lastsoundmillis = 0;

void
playsound(int n, OFVector3D *loc)
{
	if (nosound)

Modified src/tools.h from [a99286d792] to [fd4e5832e2].

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
typedef unsigned short ushort;
typedef unsigned int uint;

#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define rnd(max) (rand() % (max))
#define rndreset() (srand(1))
#define rndtime()                                                              \
	{                                                                      \
		loopi(lastmillis & 0xF) rnd(i + 1);                            \
	}
#define loop(v, m) for (int v = 0; v < (m); v++)
#define loopi(m) loop(i, m)
#define loopj(m) loop(j, m)
#define loopk(m) loop(k, m)
#define loopl(m) loop(l, m)








|
|
|







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
typedef unsigned short ushort;
typedef unsigned int uint;

#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define rnd(max) (rand() % (max))
#define rndreset() (srand(1))
#define rndtime()                                   \
	{                                           \
		loopi(lastmillis & 0xF) rnd(i + 1); \
	}
#define loop(v, m) for (int v = 0; v < (m); v++)
#define loopi(m) loop(i, m)
#define loopj(m) loop(j, m)
#define loopk(m) loop(k, m)
#define loopl(m) loop(l, m)

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
		_vsnprintf(d, _MAXDEFSTR, fmt, v);
		va_end(v);
		d[_MAXDEFSTR - 1] = 0;
	}
};

#define sprintf_s(d) sprintf_s_f((char *)d)
#define sprintf_sd(d)                                                          \
	string d;                                                              \
	sprintf_s(d)
#define sprintf_sdlv(d, last, fmt)                                             \
	string d;                                                              \
	{                                                                      \
		va_list ap;                                                    \
		va_start(ap, last);                                            \
		formatstring(d, fmt, ap);                                      \
		va_end(ap);                                                    \
	}
#define sprintf_sdv(d, fmt) sprintf_sdlv(d, fmt, fmt)

#define fast_f2nat(val) ((int)(val))

extern void endianswap(void *, int, int);








|
|

|
|
|
|
|
|
|







98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
		_vsnprintf(d, _MAXDEFSTR, fmt, v);
		va_end(v);
		d[_MAXDEFSTR - 1] = 0;
	}
};

#define sprintf_s(d) sprintf_s_f((char *)d)
#define sprintf_sd(d) \
	string d;     \
	sprintf_s(d)
#define sprintf_sdlv(d, last, fmt)        \
	string d;                         \
	{                                 \
		va_list ap;               \
		va_start(ap, last);       \
		formatstring(d, fmt, ap); \
		va_end(ap);               \
	}
#define sprintf_sdv(d, fmt) sprintf_sdlv(d, fmt, fmt)

#define fast_f2nat(val) ((int)(val))

extern void endianswap(void *, int, int);

232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
		for (int p = ulen - 1; p > i; p--)
			buf[p] = buf[p - 1];
		buf[i] = e;
		return buf[i];
	}
};

#define loopv(v)                                                               \
	if (false) {                                                           \
	} else                                                                 \
		for (int i = 0; i < (v).length(); i++)
#define loopvrev(v)                                                            \
	if (false) {                                                           \
	} else                                                                 \
		for (int i = (v).length() - 1; i >= 0; i--)

#endif







|
|
|

|
|
|



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
		for (int p = ulen - 1; p > i; p--)
			buf[p] = buf[p - 1];
		buf[i] = e;
		return buf[i];
	}
};

#define loopv(v)     \
	if (false) { \
	} else       \
		for (int i = 0; i < (v).length(); i++)
#define loopvrev(v)  \
	if (false) { \
	} else       \
		for (int i = (v).length() - 1; i >= 0; i--)

#endif

Modified src/tools.mm from [d7e6269b15] to [22d3464477].

13
14
15
16
17
18
19
20

21
		return;
	loop(w, length) loop(i, stride / 2)
	{
		uchar *p = (uchar *)memory + w * stride;
		uchar t = p[i];
		p[i] = p[stride - i - 1];
		p[stride - i - 1] = t;
	};

}







<
>

13
14
15
16
17
18
19

20
21
		return;
	loop(w, length) loop(i, stride / 2)
	{
		uchar *p = (uchar *)memory + w * stride;
		uchar t = p[i];
		p[i] = p[stride - i - 1];
		p[stride - i - 1] = t;

	}
}

Modified src/weapon.mm from [a7650f3ece] to [93de7b2cbf].

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
			vmul(v, f);
			vadd(v, from);
			p = &v;
		}
	}

	return (p->x <= d->o.x + d->radius && p->x >= d->o.x - d->radius &&
	        p->y <= d->o.y + d->radius && p->y >= d->o.y - d->radius &&
	        p->z <= d->o.z + d->aboveeye && p->z >= d->o.z - d->eyeheight);
}

OFString *
playerincrosshair()
{
	if (demoplayback)
		return NULL;







|
|







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
			vmul(v, f);
			vadd(v, from);
			p = &v;
		}
	}

	return (p->x <= d->o.x + d->radius && p->x >= d->o.x - d->radius &&
	    p->y <= d->o.y + d->radius && p->y >= d->o.y - d->radius &&
	    p->z <= d->o.z + d->aboveeye && p->z >= d->o.z - d->eyeheight);
}

OFString *
playerincrosshair()
{
	if (demoplayback)
		return NULL;
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
					continue;
				projdamage(o, p, v, i, -1, qdam);
			}
			if (p->owner != player1)
				projdamage(player1, p, v, -1, -1, qdam);
			dvector &mv = getmonsters();
			loopv(mv) if (!vreject(mv[i]->o, v, 10.0f) &&
			              mv[i] != p->owner)
			    projdamage(mv[i], p, v, -1, i, qdam);
		}
		if (p->inuse) {
			if (time == dtime)
				splash(p, v, p->o, -1, -1, qdam);
			else {
				if (p->gun == GUN_RL) {







|







265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
					continue;
				projdamage(o, p, v, i, -1, qdam);
			}
			if (p->owner != player1)
				projdamage(player1, p, v, -1, -1, qdam);
			dvector &mv = getmonsters();
			loopv(mv) if (!vreject(mv[i]->o, v, 10.0f) &&
			    mv[i] != p->owner)
			    projdamage(mv[i], p, v, -1, i, qdam);
		}
		if (p->inuse) {
			if (time == dtime)
				splash(p, v, p->o, -1, -1, qdam);
			else {
				if (p->gun == GUN_RL) {

Modified src/world.mm from [cee211ba17] to [50f4e28f8c].

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())

Modified src/worldio.mm from [fcad2200c2] to [09e55787b5].

181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
				entity tmp = ents[i];
				endianswap(&tmp, sizeof(short), 4);
				gzwrite(f, &tmp, sizeof(persistent_entity));
			}
		}
		sqr *t = NULL;
		int sc = 0;
#define spurge                                                                 \
	while (sc) {                                                           \
		gzputc(f, 255);                                                \
		if (sc > 255) {                                                \
			gzputc(f, 255);                                        \
			sc -= 255;                                             \
		} else {                                                       \
			gzputc(f, sc);                                         \
			sc = 0;                                                \
		}                                                              \
	}
		loopk(cubicsize)
		{
			sqr *s = &world[k];
#define c(f) (s->f == t->f)
			// 4 types of blocks, to compress a bit:
			// 255 (2): same as previous block + count







|
|
|
|
|
|
|
|
|
|







181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
				entity tmp = ents[i];
				endianswap(&tmp, sizeof(short), 4);
				gzwrite(f, &tmp, sizeof(persistent_entity));
			}
		}
		sqr *t = NULL;
		int sc = 0;
#define spurge                          \
	while (sc) {                    \
		gzputc(f, 255);         \
		if (sc > 255) {         \
			gzputc(f, 255); \
			sc -= 255;      \
		} else {                \
			gzputc(f, sc);  \
			sc = 0;         \
		}                       \
	}
		loopk(cubicsize)
		{
			sqr *s = &world[k];
#define c(f) (s->f == t->f)
			// 4 types of blocks, to compress a bit:
			// 255 (2): same as previous block + count

Modified src/worldlight.mm from [93b7010f75] to [3bb65afc34].

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);

Modified src/worldocull.mm from [8bad2cc9f5] to [7b61604ca1].

65
66
67
68
69
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
				sy += dy;
				if (SOLID(S(fast_f2nat(sx),
				        fast_f2nat(
				            sy)))) // 90% of time spend in this
				                   // function is on this line
				{
					rdist[i] = (float)(fabs(sx - vx) +
					                   fabs(sy - vy));
					break;
				};
			};
		} else {
			rdist[i] = 2;
		};
	};
};



// test occlusion for a cube... one of the most computationally expensive
// functions in the engine as its done for every cube and entity, but its effect
// is more than worth it!

inline float
ca(float x, float y)
{
	return x > y ? y / x : 2 - x / y;
};

inline float
ma(float x, float y)
{
	return x == 0 ? (y > 0 ? 2 : -2) : y / x;
};


int
isoccluded(float vx, float vy, float cx, float cy,
    float csize) // v = viewer, c = cube to test
{
	if (!ocull)
		return 0;







|






<
<
>
>









<
>




<
>







65
66
67
68
69
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
				sy += dy;
				if (SOLID(S(fast_f2nat(sx),
				        fast_f2nat(
				            sy)))) // 90% of time spend in this
				                   // function is on this line
				{
					rdist[i] = (float)(fabs(sx - vx) +
					    fabs(sy - vy));
					break;
				};
			};
		} else {
			rdist[i] = 2;
		};


	}
}

// test occlusion for a cube... one of the most computationally expensive
// functions in the engine as its done for every cube and entity, but its effect
// is more than worth it!

inline float
ca(float x, float y)
{
	return x > y ? y / x : 2 - x / y;

}
inline float
ma(float x, float y)
{
	return x == 0 ? (y > 0 ? 2 : -2) : y / x;

}

int
isoccluded(float vx, float vy, float cx, float cy,
    float csize) // v = viewer, c = cube to test
{
	if (!ocull)
		return 0;
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203

			}; // E
		} else {
			h = ca(cx + csize - vx, cy - vy);
			l = ca(cx - vx, cy + csize - vy);
		}; // H
	};
	int si = fast_f2nat(h * (NUMRAYS / 8)) +
	         NUMRAYS; // get indexes into occlusion map from angles
	int ei = fast_f2nat(l * (NUMRAYS / 8)) + NUMRAYS + 1;
	if (ei <= si)
		ei += NUMRAYS;

	for (int i = si; i <= ei; i++) {
		if (dist < rdist[i & (NUMRAYS - 1)])
			return 0; // if any value in this segment of the
			          // occlusion map is further away then cube is
			          // not occluded
	};

	return 1; // cube is entirely occluded
};








|












<
>
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202

203
			}; // E
		} else {
			h = ca(cx + csize - vx, cy - vy);
			l = ca(cx - vx, cy + csize - vy);
		}; // H
	};
	int si = fast_f2nat(h * (NUMRAYS / 8)) +
	    NUMRAYS; // get indexes into occlusion map from angles
	int ei = fast_f2nat(l * (NUMRAYS / 8)) + NUMRAYS + 1;
	if (ei <= si)
		ei += NUMRAYS;

	for (int i = si; i <= ei; i++) {
		if (dist < rdist[i & (NUMRAYS - 1)])
			return 0; // if any value in this segment of the
			          // occlusion map is further away then cube is
			          // not occluded
	};

	return 1; // cube is entirely occluded

}

Modified src/worldrender.mm from [a8374cc3dc] to [5690f47554].

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]);

}