1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: main.cpp
5 	Desc: contains various miscellaneous functions
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include "main.hpp"
13 #include "hash.hpp"
14 #include "entity.hpp"
15 #include "prng.hpp"
16 
17 // main definitions
18 Sint32 xres = 960;
19 Sint32 yres = 600;
20 int mainloop = 1;
21 bool initialized = false;
22 Uint32 ticks = 0;
23 bool stop = false;
24 char datadir[PATH_MAX];
25 char outputdir[PATH_MAX];
26 
27 // language stuff
28 char languageCode[32] = { 0 };
29 char** language = nullptr;
30 
31 // input stuff
32 int reversemouse = 0;
33 real_t mousespeed = 32;
34 Uint32 impulses[NUMIMPULSES];
35 Uint32 joyimpulses[NUM_JOY_IMPULSES];
36 Uint32 lastkeypressed = 0;
37 Sint8 keystatus[512];
38 char* inputstr = nullptr;
39 int inputlen = 0;
40 Sint8 mousestatus[6];
41 Sint8 joystatus[NUM_JOY_STATUS];
42 Sint8 joy_trigger_status[NUM_JOY_TRIGGER_STATUS];
43 Entity** clickmap = nullptr;
44 bool capture_mouse = true;
45 string lastname;
46 int lastCreatedCharacterClass = -1;
47 int lastCreatedCharacterAppearance = -1;
48 int lastCreatedCharacterSex = -1;
49 int lastCreatedCharacterRace = -1;
50 
51 // net stuff
52 Uint32 clientplayer = 0;
53 int numplayers = 0;
54 int clientnum = 0;
55 int multiplayer = -1;
56 SteamStat_t g_SteamStats[NUM_STEAM_STATISTICS] =
57 {
58 	{ 1, STEAM_STAT_INT, "STAT_BOULDER_DEATHS" },
59 	{ 2, STEAM_STAT_INT, "STAT_WORTHLESS_GLASS" },
60 	{ 3, STEAM_STAT_INT, "STAT_TOUGH_AS_NAILS" },
61 	{ 4, STEAM_STAT_INT, "STAT_UNSTOPPABLE_FORCE" },
62 	{ 5, STEAM_STAT_INT, "STAT_GAMES_STARTED" },
63 	{ 6, STEAM_STAT_INT, "STAT_GAMES_WON" },
64 	{ 7, STEAM_STAT_INT, "STAT_BOMBARDIER" },
65 	{ 8, STEAM_STAT_INT, "STAT_IN_THE_MIX" },
66 	{ 9, STEAM_STAT_INT, "STAT_FREE_REFILLS" },
67 	{ 10, STEAM_STAT_INT, "STAT_TAKE_THIS_OUTSIDE" },
68 	{ 11, STEAM_STAT_INT, "STAT_ALTER_EGO" },
69 	{ 12, STEAM_STAT_INT, "STAT_BLOOD_SPORT" },
70 	{ 13, STEAM_STAT_INT, "STAT_BAD_BLOOD" },
71 	{ 14, STEAM_STAT_INT, "STAT_IRON_GUT" },
72 	{ 15, STEAM_STAT_INT, "STAT_BOTTLE_NOSED" },
73 	{ 16, STEAM_STAT_INT, "STAT_BARFIGHT_CHAMP" },
74 	{ 17, STEAM_STAT_INT, "STAT_VOLATILE" },
75 	{ 18, STEAM_STAT_INT, "STAT_SURROGATES" },
76 	{ 19, STEAM_STAT_INT, "STAT_KILL_COMMAND" },
77 	{ 20, STEAM_STAT_INT, "STAT_TRASH_COMPACTOR" },
78 	{ 21, STEAM_STAT_INT, "STAT_SPICY" },
79 	{ 22, STEAM_STAT_INT, "STAT_SERIAL_THRILLA" },
80 	{ 23, STEAM_STAT_INT, "STAT_TRADITION" },
81 	{ 24, STEAM_STAT_INT, "STAT_POP_QUIZ" },
82 	{ 25, STEAM_STAT_INT, "STAT_DYSLEXIA" },
83 	{ 26, STEAM_STAT_INT, "STAT_BOOKWORM" },
84 	{ 27, STEAM_STAT_INT, "STAT_MONARCH" },
85 	{ 28, STEAM_STAT_INT, "STAT_SUPER_SHREDDER" },
86 	{ 29, STEAM_STAT_INT, "STAT_FIXER_UPPER" },
87 	{ 30, STEAM_STAT_INT, "STAT_TORCHERER" },
88 	{ 31, STEAM_STAT_INT, "STAT_MANY_PEDI_PALP" },
89 	{ 32, STEAM_STAT_INT, "STAT_5000_SECOND_RULE" },
90 	{ 33, STEAM_STAT_INT, "STAT_SOCIAL_BUTTERFLY" },
91 	{ 34, STEAM_STAT_INT, "STAT_ROLL_THE_BONES" },
92 	{ 35, STEAM_STAT_INT, "STAT_COWBOY_FROM_HELL" },
93 	{ 36, STEAM_STAT_INT, "STAT_SELF_FLAGELLATION" },
94 	{ 37, STEAM_STAT_INT, "STAT_CHOPPING_BLOCK" },
95 	{ 38, STEAM_STAT_INT, "STAT_IF_YOU_LOVE_SOMETHING" },
96 	{ 39, STEAM_STAT_INT, "STAT_RAGE_AGAINST" },
97 	{ 40, STEAM_STAT_INT, "STAT_GUERILLA_RADIO" },
98 	{ 41, STEAM_STAT_INT, "STAT_FASCIST" },
99 	{ 42, STEAM_STAT_INT, "STAT_ITS_A_LIVING" },
100 	{ 43, STEAM_STAT_INT, "STAT_OVERCLOCKED" },
101 	{ 44, STEAM_STAT_INT, "STAT_BACK_TO_BASICS" },
102 	{ 45, STEAM_STAT_INT, "STAT_EXTRA_CREDIT" },
103 	{ 46, STEAM_STAT_INT, "STAT_EXTRA_CREDIT_LVLS" },
104 	{ 47, STEAM_STAT_INT, "STAT_DIPLOMA" },
105 	{ 48, STEAM_STAT_INT, "STAT_DIPLOMA_LVLS" },
106 	{ 49, STEAM_STAT_INT, "STAT_TUTORIAL_ENTERED" }
107 };
108 
109 SteamStat_t g_SteamGlobalStats[NUM_GLOBAL_STEAM_STATISTICS] =
110 {
111 	{ 1, STEAM_STAT_INT,  "STAT_GLOBAL_GAMES_STARTED" },
112 	{ 2, STEAM_STAT_INT,  "STAT_GLOBAL_GAMES_WON" },
113 	{ 3, STEAM_STAT_INT,  "STAT_GLOBAL_BOULDER_DEATHS" },
114 	{ 4, STEAM_STAT_INT,  "STAT_GLOBAL_HERX_SLAIN" },
115 	{ 5, STEAM_STAT_INT,  "STAT_GLOBAL_BAPHOMET_SLAIN" },
116 	{ 6, STEAM_STAT_INT,  "STAT_GLOBAL_TWINSICE_SLAIN" },
117 	{ 7, STEAM_STAT_INT,  "STAT_GLOBAL_DEATHS_HUMAN" },
118 	{ 8, STEAM_STAT_INT,  "STAT_GLOBAL_DEATHS_RAT" },
119 	{ 9, STEAM_STAT_INT,  "STAT_GLOBAL_DEATHS_GOBLIN" },
120 	{ 10, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_SLIME" },
121 	{ 11, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_TROLL" },
122 	{ 12, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_SPIDER" },
123 	{ 13, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_GHOUL" },
124 	{ 14, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_SKELETON" },
125 	{ 15, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_SCORPION" },
126 	{ 16, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_IMP" },
127 	{ 17, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_GNOME" },
128 	{ 18, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_DEMON" },
129 	{ 19, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_SUCCUBUS" },
130 	{ 20, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_LICH" },
131 	{ 21, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_MINOTAUR" },
132 	{ 22, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_DEVIL" },
133 	{ 23, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_SHOPKEEPER" },
134 	{ 24, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_KOBOLD" },
135 	{ 25, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_SCARAB" },
136 	{ 26, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_CRYSTALGOLEM" },
137 	{ 27, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_INCUBUS" },
138 	{ 28, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_VAMPIRE" },
139 	{ 29, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_SHADOW" },
140 	{ 30, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_COCKATRICE" },
141 	{ 31, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_INSECTOID" },
142 	{ 32, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_GOATMAN" },
143 	{ 33, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_AUTOMATON" },
144 	{ 34, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_LICHICE" },
145 	{ 35, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_LICHFIRE" },
146 	{ 36, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_SENTRYBOT" },
147 	{ 37, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_SPELLBOT" },
148 	{ 38, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_GYROBOT" },
149 	{ 39, STEAM_STAT_INT, "STAT_GLOBAL_DEATHS_DUMMYBOT" },
150 	{ 40, STEAM_STAT_INT, "STAT_GLOBAL_TWINSFIRE_SLAIN" },
151 	{ 41, STEAM_STAT_INT, "STAT_GLOBAL_SHOPKEEPERS_SLAIN" },
152 	{ 42, STEAM_STAT_INT, "STAT_GLOBAL_MINOTAURS_SLAIN" },
153 	{ 43, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL_ENTERED" },
154 	{ 44, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL1_COMPLETED" },
155 	{ 45, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL2_COMPLETED" },
156 	{ 46, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL3_COMPLETED" },
157 	{ 47, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL4_COMPLETED" },
158 	{ 48, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL5_COMPLETED" },
159 	{ 49, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL6_COMPLETED" },
160 	{ 50, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL7_COMPLETED" },
161 	{ 51, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL8_COMPLETED" },
162 	{ 52, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL9_COMPLETED" },
163 	{ 53, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL10_COMPLETED" },
164 	{ 54, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL1_ATTEMPTS" },
165 	{ 55, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL2_ATTEMPTS" },
166 	{ 56, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL3_ATTEMPTS" },
167 	{ 57, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL4_ATTEMPTS" },
168 	{ 58, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL5_ATTEMPTS" },
169 	{ 59, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL6_ATTEMPTS" },
170 	{ 60, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL7_ATTEMPTS" },
171 	{ 61, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL8_ATTEMPTS" },
172 	{ 62, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL9_ATTEMPTS" },
173 	{ 63, STEAM_STAT_INT, "STAT_GLOBAL_TUTORIAL10_ATTEMPTS" },
174 	{ 64, STEAM_STAT_INT, "STAT_GLOBAL_DISABLE" },
175 	{ 65, STEAM_STAT_INT, "STAT_GLOBAL_PROMO" },
176 	{ 66, STEAM_STAT_INT, "STAT_GLOBAL_PROMO_INTERACT" }
177 };
178 
179 #ifdef STEAMWORKS
180 bool directConnect = false;
181 CSteamLeaderboards* g_SteamLeaderboards = NULL;
182 CSteamWorkshop* g_SteamWorkshop = NULL;
183 CSteamStatistics* g_SteamStatistics = NULL;
184 #else
185 bool directConnect = true;
186 #endif
187 char address[64];
188 IPaddress net_server;
189 IPaddress* net_clients = nullptr;
190 UDPsocket net_sock = nullptr;
191 TCPsocket net_tcpsock = nullptr;
192 UDPpacket* net_packet = nullptr;
193 TCPsocket* net_tcpclients = nullptr;
194 SDLNet_SocketSet tcpset = nullptr;
195 list_t safePacketsSent;
196 std::unordered_map<int, Uint32> safePacketsReceivedMap[MAXPLAYERS];
197 bool receivedclientnum = false;
198 char const * window_title = nullptr;
199 bool softwaremode = false;
200 SDL_TimerID timer;
201 SDL_Window* screen = nullptr;
202 #ifdef APPLE
203 SDL_Renderer* renderer = nullptr;
204 #else
205 SDL_GLContext renderer;
206 #endif
207 SDL_Surface* mainsurface = nullptr;
208 SDL_Event event;
209 bool firstmouseevent = true;
210 int fullscreen = 0;
211 bool borderless = false;
212 bool smoothlighting = false;
213 list_t removedEntities;
214 list_t entitiesToDelete[MAXPLAYERS];
215 Entity* client_selected[MAXPLAYERS] = {nullptr, nullptr, nullptr, nullptr};
216 bool inrange[MAXPLAYERS];
217 Sint32 client_classes[MAXPLAYERS];
218 Uint32 client_keepalive[MAXPLAYERS];
219 Uint16 portnumber;
220 bool client_disconnected[MAXPLAYERS];
221 list_t entitiesdeleted;
222 
223 // fps
224 bool showfps = false;
225 real_t t, ot = 0.0, frameval[AVERAGEFRAMES];
226 Uint32 cycles = 0, pingtime = 0;
227 Uint32 timesync = 0;
228 real_t fps = 0.0;
229 
230 // world sim data
231 Sint32 camx = 0, camy = 0;
232 Sint32 ocamx = 0, ocamy = 0;
233 Sint32 newcamx, newcamy;
234 Uint32 entity_uids = 1, lastEntityUIDs = 1;
235 view_t cameras[MAXPLAYERS];
236 view_t menucam;
237 map_t map;
238 voxel_t** models = nullptr;
239 list_t button_l;
240 list_t light_l;
241 Uint32 mapseed;
242 bool* shoparea = nullptr;
243 real_t globalLightModifier = 0.f;
244 real_t globalLightTelepathyModifier = 0.f;
245 int globalLightSmoothingRate = 1;
246 int globalLightModifierActive = 0;
247 
248 // game variables
249 bool shootmode = false;
250 Sint8 minimap[MINIMAP_MAX_DIMENSION][MINIMAP_MAX_DIMENSION];
251 bool loadnextlevel = false;
252 int skipLevelsOnLoad = 0;
253 bool loadingSameLevelAsCurrent = false;
254 std::string loadCustomNextMap = "";
255 Uint32 forceMapSeed = 0;
256 bool loading = false;
257 int currentlevel = 0, minotaurlevel = 0;
258 bool secretlevel = false;
259 bool darkmap = false;
260 bool skipintro = false;
261 bool broadcast = false;
262 bool nohud = false;
263 bool noclip = false, godmode = false, buddhamode = false;
264 bool everybodyfriendly = false;
265 bool combat = false, combattoggle = false;
266 bool assailant[MAXPLAYERS];
267 bool oassailant[MAXPLAYERS];
268 int assailantTimer[MAXPLAYERS] = { 0, 0, 0, 0 };
269 Uint32 nummonsters = 0;
270 bool gamePaused = false;
271 bool intro = true;
272 int introstage = -1;
273 bool movie = false;
274 int kills[NUMMONSTERS];
275 
276 // messages
277 list_t messages;
278 list_t command_history;
279 node_t* chosen_command = nullptr;
280 bool command = false;
281 char command_str[128];
282 
283 // editor variables
284 int drawlayer = OBSTACLELAYER, drawx = 0, drawy = 0, odrawx = 0, odrawy = 0;
285 int alllayers = 0;
286 int scroll = 0;
287 char layerstatus[20];
288 int menuVisible = 0;
289 int subwindow = 0;
290 int subx1, subx2, suby1, suby2;
291 char subtext[1024];
292 int toolbox = 1;
293 int statusbar = 1;
294 int viewsprites = 1;
295 int showgrid = 0;
296 int hovertext = 1;
297 int selectedTile = 0;
298 int tilepalette = 0;
299 int spritepalette = 0;
300 int mclick = 0;
301 int selectedTool = 0; // 0: Pencil 1: Point 2: Brush 3: Select 4: Fill
302 int allowediting = 0; // only turned on when the mouse is over paintable screen region
303 int openwindow = 0, savewindow = 0, newwindow = 0;
304 int slidery = 0, slidersize = 16;
305 int menuDisappear = 0;
306 int selectedFile = 0;
307 char** d_names = nullptr;
308 unsigned long d_names_length = 0;
309 char filename[128];
310 char foldername[128];
311 char oldfilename[128];
312 char message[256];
313 int messagetime = 0;
314 char widthtext[4], heighttext[4], nametext[32], authortext[32], skyboxtext[32];
315 char mapflagtext[MAPFLAGTEXTS][32];
316 char spriteProperties[32][128];
317 char tmpSpriteProperties[32][128];
318 int editproperty = 0;
319 SDL_Cursor* cursorArrow, *cursorPencil, *cursorPoint, *cursorBrush, *cursorSelect, *cursorFill;
320 int* palette;
321 
322 // video definitions
323 polymodel_t* polymodels = nullptr;
324 bool useModelCache = false;
325 list_t ttfTextHash[HASH_SIZE];
326 TTF_Font* ttf8 = nullptr;
327 TTF_Font* ttf12 = nullptr;
328 TTF_Font* ttf16 = nullptr;
329 SDL_Surface* font8x8_bmp = nullptr;
330 SDL_Surface* font12x12_bmp = nullptr;
331 SDL_Surface* font16x16_bmp = nullptr;
332 SDL_Surface* fancyWindow_bmp = nullptr;
333 SDL_Surface** sprites = nullptr;
334 SDL_Surface** tiles = nullptr;
335 std::unordered_map<std::string, SDL_Surface*> achievementImages;
336 std::unordered_map<std::string, std::string> achievementNames;
337 std::unordered_map<std::string, std::string> achievementDesc;
338 std::unordered_set<std::string> achievementHidden;
339 std::set<std::pair<std::string, std::string>, Comparator> achievementNamesSorted;
340 std::unordered_map<std::string, int> achievementProgress; // ->second is the associated achievement stat index
341 std::unordered_map<std::string, int64_t> achievementUnlockTime;
342 
343 std::unordered_set<std::string> achievementUnlockedLookup;
344 Uint32 imgref = 1, vboref = 1;
345 const Uint32 ttfTextCacheLimit = 9000;
346 GLuint* texid = nullptr;
347 bool disablevbos = false;
348 Uint32 fov = 65;
349 Uint32 fpsLimit = 60;
350 //GLuint *vboid=nullptr, *vaoid=nullptr;
351 SDL_Surface** allsurfaces;
352 Uint32 numsprites, numtiles, nummodels;
353 bool *animatedtiles = nullptr;
354 bool *lavatiles = nullptr;
355 bool *swimmingtiles = nullptr;
356 int rscale = 1;
357 real_t vidgamma = 1.0f;
358 real_t* zbuffer = nullptr;
359 Sint32* lightmap = nullptr;
360 Sint32* lightmapSmoothed = nullptr;
361 bool* vismap = nullptr;
362 bool mode3d = false;
363 bool verticalSync = false;
364 bool showStatusEffectIcons = true;
365 bool minimapPingMute = false;
366 bool mute_audio_on_focus_lost = false;
367 bool mute_player_monster_sounds = false;
368 int minimapTransparencyForeground = 0;
369 int minimapTransparencyBackground = 0;
370 int minimapScale = 4;
371 int minimapObjectZoom = 0;
372 int minimapScaleQuickToggle = 0;
373 
374 // audio definitions
375 int audio_rate = 22050;
376 Uint16 audio_format = AUDIO_S16;
377 int audio_channels = 2;
378 int audio_buffers = 512;
379 int sfxvolume = 64;
380 int sfxAmbientVolume = 64;
381 int sfxEnvironmentVolume = 64;
382 int musvolume = 48;
383 
384 // fun stuff
385 SDL_Surface* title_bmp = nullptr;
386 SDL_Surface* logo_bmp = nullptr;
387 SDL_Surface* cursor_bmp = nullptr;
388 SDL_Surface* cross_bmp = nullptr;
389 int shaking = 0, bobbing = 0;
390 bool fadeout = false, fadefinished = false;
391 int fadealpha = 0;
392 cameravars_t cameravars[MAXPLAYERS];
393 
394 // misc definitions
395 char tempstr[1024];
396 char maptoload[256], configtoload[256];
397 bool loadingmap = false, genmap = false, loadingconfig = false;
398 bool deleteallbuttons = false;
399 Uint32 cursorflash = 0;
400 
401 bool no_sound = false;
402 
403 //Entity *players[4];
404 
405 hit_t hit;
406 
407 /*-------------------------------------------------------------------------------
408 
409 	longestline
410 
411 	returns the longest line of characters in a string (stopping for
412 	newlines)
413 
414 -------------------------------------------------------------------------------*/
415 
longestline(char const * const str)416 int longestline(char const * const str)
417 {
418 	int c, x = 0, result = 0;
419 	for ( c = 0; c < strlen(str); c++ )
420 	{
421 		if ( str[c] == 10 )
422 		{
423 			x = 0;
424 			continue;
425 		}
426 		x++;
427 		result = std::max(x, result);
428 	}
429 	return result;
430 }
431 
432 /*-------------------------------------------------------------------------------
433 
434 	concatedStringLength
435 
436 	returns the length of all the given strings combined together
437 	e.g. concatedStringLength("chicken %s", "potato")
438 
439 -------------------------------------------------------------------------------*/
440 
concatedStringLength(char * str,...)441 int concatedStringLength(char* str, ...)
442 {
443 	va_list argptr;
444 	char newstr[1024] = { 0 };
445 
446 	int result = 0;
447 
448 	va_start(argptr, str);
449 	vsnprintf(newstr, 1023, str, argptr);
450 	va_end(argptr);
451 
452 	return strlen(newstr);
453 }
454 
455 /*-------------------------------------------------------------------------------
456 
457 	sgn
458 
459 	returns the sign of the given double (positive or negative);
460 
461 -------------------------------------------------------------------------------*/
462 
sgn(real_t x)463 int sgn(real_t x)
464 {
465 	return (x > 0) - (x < 0);
466 }
467 
468 /*-------------------------------------------------------------------------------
469 
470 	numdigits
471 
472 	return the number of digits of the given int (includes the sign if negative)
473 
474 -------------------------------------------------------------------------------*/
475 
numdigits_sint16(Sint16 x)476 int numdigits_sint16(Sint16 x)
477 {
478 	return snprintf(nullptr, 0, "%d", x);
479 }
480 
481 /*-------------------------------------------------------------------------------
482 
483 	printlog
484 
485 	prints the given formatted text to the log file
486 
487 -------------------------------------------------------------------------------*/
488 
printlog(const char * str,...)489 void printlog(const char* str, ...)
490 {
491 	char newstr[1024] = { 0 };
492 	va_list argptr;
493 
494 	// format the content
495 	va_start( argptr, str );
496 	vsnprintf( newstr, 1023, str, argptr );
497 	va_end( argptr );
498 
499 	// timestamp the message
500 	time_t timer;
501 	char buffer[32];
502 	struct tm* tm_info;
503 	time(&timer);
504 	tm_info = localtime(&timer);
505 	strftime( buffer, 32, "%H-%M-%S", tm_info );
506 
507 	// print to the log
508 	if ( newstr[strlen(newstr) - 1] != '\n' )
509 	{
510 		int c = strlen(newstr);
511 		newstr[c] = '\n';
512 		newstr[c + 1] = 0;
513 	}
514 	fprintf( stderr, "[%s] %s", buffer, newstr );
515 	fflush( stderr );
516 }
517