1 /*
2 Copyright © 2011-2012 Clint Bellanger
3 Copyright © 2012 Stefan Beller
4 Copyright © 2013 Henrik Andersson
5 Copyright © 2013 Kurt Rinnert
6 Copyright © 2012-2016 Justin Jacobs
7
8 This file is part of FLARE.
9
10 FLARE is free software: you can redistribute it and/or modify it under the terms
11 of the GNU General Public License as published by the Free Software Foundation,
12 either version 3 of the License, or (at your option) any later version.
13
14 FLARE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
16 PARTICULAR PURPOSE. See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along with
19 FLARE. If not, see http://www.gnu.org/licenses/
20 */
21
22 #include "Avatar.h"
23 #include "EngineSettings.h"
24 #include "InputState.h"
25 #include "MessageEngine.h"
26 #include "Platform.h"
27 #include "Settings.h"
28 #include "SharedResources.h"
29 #include "Utils.h"
30 #include "UtilsFileSystem.h"
31 #include "UtilsMath.h"
32 #include "UtilsParsing.h"
33
34 #include <cmath>
35 #include <stdarg.h>
36 #include <ctype.h>
37 #include <iomanip>
38 #include <iostream>
39 #include <locale>
40 #include <string.h>
41
42 int Utils::LOCK_INDEX = 0;
43
44 bool Utils::LOG_FILE_INIT = false;
45 bool Utils::LOG_FILE_CREATED = false;
46 std::string Utils::LOG_PATH;
47 std::queue<std::pair<SDL_LogPriority, std::string> > Utils::LOG_MSG;
48
49 /**
50 * Point: A simple x/y coordinate structure
51 */
Point()52 Point::Point()
53 : x(0)
54 , y(0)
55 {}
56
Point(int _x,int _y)57 Point::Point(int _x, int _y)
58 : x(_x)
59 , y(_y)
60 {}
61
Point(const FPoint & _fp)62 Point::Point(const FPoint& _fp)
63 : x(static_cast<int>(_fp.x))
64 , y(static_cast<int>(_fp.y))
65 {}
66
67 /**
68 * FPoint: A floating point version of Point
69 */
FPoint()70 FPoint::FPoint()
71 : x(0)
72 , y(0)
73 {}
74
FPoint(float _x,float _y)75 FPoint::FPoint(float _x, float _y)
76 : x(_x)
77 , y(_y)
78 {}
79
FPoint(Point _p)80 FPoint::FPoint(Point _p)
81 : x(static_cast<float>(_p.x))
82 , y(static_cast<float>(_p.y))
83 {}
84
align()85 void FPoint::align() {
86 // this rounds the float values to the nearest multiple of 1/(2^4)
87 // 1/(2^4) was chosen because it's a "nice" floating point number, removing 99% of rounding errors
88 x = floorf(x / 0.0625f) * 0.0625f;
89 y = floorf(y / 0.0625f) * 0.0625f;
90 }
91
92 /**
93 * Rect: A rectangle defined by the top-left x/y and the width/height
94 */
Rect()95 Rect::Rect()
96 : x(0)
97 , y(0)
98 , w(0)
99 , h(0)
100 {}
101
Rect(int _x,int _y,int _w,int _h)102 Rect::Rect(int _x, int _y, int _w, int _h)
103 : x(_x)
104 , y(_y)
105 , w(_w)
106 , h(_h)
107 {}
108
Rect(const SDL_Rect & _r)109 Rect::Rect(const SDL_Rect& _r)
110 : x(_r.x)
111 , y(_r.y)
112 , w(_r.w)
113 , h(_r.h)
114 {}
115
operator SDL_Rect() const116 Rect::operator SDL_Rect() const {
117 SDL_Rect r;
118 r.x = x;
119 r.y = y;
120 r.w = w;
121 r.h = h;
122 return r;
123 }
124
125 /**
126 * Color: RGBA color; defaults to 100% opaque black
127 */
Color()128 Color::Color()
129 : r(0)
130 , g(0)
131 , b(0)
132 , a(255)
133 {}
134
Color(Uint8 _r,Uint8 _g,Uint8 _b,Uint8 _a)135 Color::Color(Uint8 _r, Uint8 _g, Uint8 _b, Uint8 _a)
136 : r(_r)
137 , g(_g)
138 , b(_b)
139 , a(_a)
140 {}
141
operator SDL_Color() const142 Color::operator SDL_Color() const {
143 SDL_Color c;
144 c.r = r;
145 c.g = g;
146 c.b = b;
147 c.a = a;
148 return c;
149 }
150
operator ==(const Color & other)151 bool Color::operator ==(const Color &other) {
152 return r == other.r && g == other.g && b == other.b && a == other.a;
153 }
154
operator !=(const Color & other)155 bool Color::operator !=(const Color &other) {
156 return !((*this) == other);
157 }
158
encodeRGBA()159 uint32_t Color::encodeRGBA() {
160 uint32_t result = static_cast<uint32_t>(a);
161 result |= static_cast<uint32_t>(r) << 24;
162 result |= static_cast<uint32_t>(g) << 16;
163 result |= static_cast<uint32_t>(b) << 8;
164 return result;
165 }
166
decodeRGBA(const uint32_t encoded)167 void Color::decodeRGBA(const uint32_t encoded) {
168 a = static_cast<uint8_t>(encoded);
169 r = static_cast<uint8_t>(encoded >> 24);
170 g = static_cast<uint8_t>(encoded >> 16);
171 b = static_cast<uint8_t>(encoded >> 8);
172 }
173
Timer(unsigned _duration)174 Timer::Timer(unsigned _duration)
175 : current(0)
176 , duration(_duration)
177 {
178 }
179
getCurrent()180 unsigned Timer::getCurrent() {
181 return current;
182 }
183
getDuration()184 unsigned Timer::getDuration() {
185 return duration;
186 }
187
setCurrent(unsigned val)188 void Timer::setCurrent(unsigned val) {
189 current = val;
190 if (current > duration)
191 current = duration;
192 }
193
setDuration(unsigned val)194 void Timer::setDuration(unsigned val) {
195 current = duration = val;
196 }
197
tick()198 bool Timer::tick() {
199 if (current > 0)
200 current--;
201
202 if (current == 0)
203 return true;
204
205 return false;
206 }
207
isEnd()208 bool Timer::isEnd() {
209 return current == 0;
210 }
211
isBegin()212 bool Timer::isBegin() {
213 return current == duration;
214 }
215
reset(int type)216 void Timer::reset(int type) {
217 if (type == Timer::END)
218 current = 0;
219 else if (type == Timer::BEGIN)
220 current = duration;
221 }
222
isWholeSecond()223 bool Timer::isWholeSecond() {
224 return current % settings->max_frames_per_sec == 0;
225 }
226
screenToMap(int x,int y,float camx,float camy)227 FPoint Utils::screenToMap(int x, int y, float camx, float camy) {
228 FPoint r;
229 if (eset->tileset.orientation == eset->tileset.TILESET_ISOMETRIC) {
230 float scrx = float(x - settings->view_w_half) * 0.5f;
231 float scry = float(y - settings->view_h_half) * 0.5f;
232
233 r.x = (eset->tileset.units_per_pixel_x * scrx) + (eset->tileset.units_per_pixel_y * scry) + camx;
234 r.y = (eset->tileset.units_per_pixel_y * scry) - (eset->tileset.units_per_pixel_x * scrx) + camy;
235 }
236 else {
237 r.x = static_cast<float>(x - settings->view_w_half) * (eset->tileset.units_per_pixel_x) + camx;
238 r.y = static_cast<float>(y - settings->view_h_half) * (eset->tileset.units_per_pixel_y) + camy;
239 }
240 return r;
241 }
242
243 /**
244 * Returns a point (in map units) of a given (x,y) tupel on the screen
245 * when the camera is at a given position.
246 */
mapToScreen(float x,float y,float camx,float camy)247 Point Utils::mapToScreen(float x, float y, float camx, float camy) {
248 Point r;
249
250 // adjust to the center of the viewport
251 // we do this calculation first to avoid negative integer division
252 float adjust_x = (settings->view_w_half + 0.5f) * eset->tileset.units_per_pixel_x;
253 float adjust_y = (settings->view_h_half + 0.5f) * eset->tileset.units_per_pixel_y;
254
255 if (eset->tileset.orientation == eset->tileset.TILESET_ISOMETRIC) {
256 r.x = int(floorf(((x - camx - y + camy + adjust_x)/eset->tileset.units_per_pixel_x)+0.5f));
257 r.y = int(floorf(((x - camx + y - camy + adjust_y)/eset->tileset.units_per_pixel_y)+0.5f));
258 }
259 else if (eset->tileset.orientation == eset->tileset.TILESET_ORTHOGONAL) {
260 r.x = int((x - camx + adjust_x)/eset->tileset.units_per_pixel_x);
261 r.y = int((y - camy + adjust_y)/eset->tileset.units_per_pixel_y);
262 }
263 return r;
264 }
265
266 /**
267 * Apply parameter distance to position and direction
268 */
calcVector(const FPoint & pos,int direction,float dist)269 FPoint Utils::calcVector(const FPoint& pos, int direction, float dist) {
270 FPoint p;
271 p.x = pos.x;
272 p.y = pos.y;
273
274 float dist_straight = dist;
275 float dist_diag = dist * 0.7071f; // 1/sqrt(2)
276
277 switch (direction) {
278 case 0:
279 p.x -= dist_diag;
280 p.y += dist_diag;
281 break;
282 case 1:
283 p.x -= dist_straight;
284 break;
285 case 2:
286 p.x -= dist_diag;
287 p.y -= dist_diag;
288 break;
289 case 3:
290 p.y -= dist_straight;
291 break;
292 case 4:
293 p.x += dist_diag;
294 p.y -= dist_diag;
295 break;
296 case 5:
297 p.x += dist_straight;
298 break;
299 case 6:
300 p.x += dist_diag;
301 p.y += dist_diag;
302 break;
303 case 7:
304 p.y += dist_straight;
305 break;
306 }
307 return p;
308 }
309
calcDist(const FPoint & p1,const FPoint & p2)310 float Utils::calcDist(const FPoint& p1, const FPoint& p2) {
311 return sqrtf((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
312 }
313
314 /**
315 * is target within the area defined by center and radius?
316 */
isWithinRadius(const FPoint & center,float radius,const FPoint & target)317 bool Utils::isWithinRadius(const FPoint& center, float radius, const FPoint& target) {
318 return (calcDist(center, target) < radius);
319 }
320
321 /**
322 * is target within the area defined by rectangle r?
323 */
isWithinRect(const Rect & r,const Point & target)324 bool Utils::isWithinRect(const Rect& r, const Point& target) {
325 return target.x >= r.x && target.y >= r.y && target.x < r.x+r.w && target.y < r.y+r.h;
326 }
327
calcDirection(float x0,float y0,float x1,float y1)328 unsigned char Utils::calcDirection(float x0, float y0, float x1, float y1) {
329 float theta = calcTheta(x0, y0, x1, y1);
330 float val = theta / (static_cast<float>(M_PI)/4);
331 int dir = static_cast<int>(((val < 0) ? ceilf(val-0.5f) : floorf(val+0.5f)) + 4);
332 dir = (dir + 1) % 8;
333 if (dir >= 0 && dir < 8)
334 return static_cast<unsigned char>(dir);
335 else
336 return 0;
337 }
338
339 // convert cartesian to polar theta where (x1,x2) is the origin
calcTheta(float x1,float y1,float x2,float y2)340 float Utils::calcTheta(float x1, float y1, float x2, float y2) {
341 // calculate base angle
342 float dx = x2 - x1;
343 float dy = y2 - y1;
344 float exact_dx = x2 - x1;
345 float theta;
346
347 // convert cartesian to polar coordinates
348 if (exact_dx == 0) {
349 if (dy > 0.0) theta = static_cast<float>(M_PI)/2.0f;
350 else theta = static_cast<float>(-M_PI)/2.0f;
351 }
352 else {
353 theta = atanf(dy/dx);
354 if (dx < 0.0 && dy >= 0.0) theta += static_cast<float>(M_PI);
355 if (dx < 0.0 && dy < 0.0) theta -= static_cast<float>(M_PI);
356 }
357 return theta;
358 }
359
abbreviateKilo(int amount)360 std::string Utils::abbreviateKilo(int amount) {
361 std::stringstream ss;
362 if (amount < 1000)
363 ss << amount;
364 else
365 ss << (amount/1000) << msg->get("k");
366
367 return ss.str();
368 }
369
alignToScreenEdge(int alignment,Rect * r)370 void Utils::alignToScreenEdge(int alignment, Rect *r) {
371 if (!r) return;
372
373 if (alignment == ALIGN_TOPLEFT) {
374 // do nothing
375 }
376 else if (alignment == ALIGN_TOP) {
377 r->x = (settings->view_w_half - r->w/2) + r->x;
378 }
379 else if (alignment == ALIGN_TOPRIGHT) {
380 r->x = (settings->view_w - r->w) + r->x;
381 }
382 else if (alignment == ALIGN_LEFT) {
383 r->y = (settings->view_h_half - r->h/2) + r->y;
384 }
385 else if (alignment == ALIGN_CENTER) {
386 r->x = (settings->view_w_half - r->w/2) + r->x;
387 r->y = (settings->view_h_half - r->h/2) + r->y;
388 }
389 else if (alignment == ALIGN_RIGHT) {
390 r->x = (settings->view_w - r->w) + r->x;
391 r->y = (settings->view_h_half - r->h/2) + r->y;
392 }
393 else if (alignment == ALIGN_BOTTOMLEFT) {
394 r->y = (settings->view_h - r->h) + r->y;
395 }
396 else if (alignment == ALIGN_BOTTOM) {
397 r->x = (settings->view_w_half - r->w/2) + r->x;
398 r->y = (settings->view_h - r->h) + r->y;
399 }
400 else if (alignment == ALIGN_BOTTOMRIGHT) {
401 r->x = (settings->view_w - r->w) + r->x;
402 r->y = (settings->view_h - r->h) + r->y;
403 }
404 else if (alignment == ALIGN_FRAME_TOPLEFT) {
405 r->x = ((settings->view_w - eset->resolutions.frame_w)/2) + r->x;
406 r->y = ((settings->view_h - eset->resolutions.frame_h)/2) + r->y;
407 }
408 else if (alignment == ALIGN_FRAME_TOP) {
409 r->x = ((settings->view_w - eset->resolutions.frame_w)/2) + (eset->resolutions.frame_w/2 - r->w/2) + r->x;
410 r->y = ((settings->view_h - eset->resolutions.frame_h)/2) + r->y;
411 }
412 else if (alignment == ALIGN_FRAME_TOPRIGHT) {
413 r->x = ((settings->view_w - eset->resolutions.frame_w)/2) + (eset->resolutions.frame_w - r->w) + r->x;
414 r->y = ((settings->view_h - eset->resolutions.frame_h)/2) + r->y;
415 }
416 else if (alignment == ALIGN_FRAME_LEFT) {
417 r->x = ((settings->view_w - eset->resolutions.frame_w)/2) + r->x;
418 r->y = ((settings->view_h - eset->resolutions.frame_h)/2) + (eset->resolutions.frame_h/2 - r->h/2) + r->y;
419 }
420 else if (alignment == ALIGN_FRAME_CENTER) {
421 r->x = ((settings->view_w - eset->resolutions.frame_w)/2) + (eset->resolutions.frame_w/2 - r->w/2) + r->x;
422 r->y = ((settings->view_h - eset->resolutions.frame_h)/2) + (eset->resolutions.frame_h/2 - r->h/2) + r->y;
423 }
424 else if (alignment == ALIGN_FRAME_RIGHT) {
425 r->x = ((settings->view_w - eset->resolutions.frame_w)/2) + (eset->resolutions.frame_w - r->w) + r->x;
426 r->y = ((settings->view_h - eset->resolutions.frame_h)/2) + (eset->resolutions.frame_h/2 - r->h/2) + r->y;
427 }
428 else if (alignment == ALIGN_FRAME_BOTTOMLEFT) {
429 r->x = ((settings->view_w - eset->resolutions.frame_w)/2) + r->x;
430 r->y = ((settings->view_h - eset->resolutions.frame_h)/2) + (eset->resolutions.frame_h - r->h) + r->y;
431 }
432 else if (alignment == ALIGN_FRAME_BOTTOM) {
433 r->x = ((settings->view_w - eset->resolutions.frame_w)/2) + (eset->resolutions.frame_w/2 - r->w/2) + r->x;
434 r->y = ((settings->view_h - eset->resolutions.frame_h)/2) + (eset->resolutions.frame_h - r->h) + r->y;
435 }
436 else if (alignment == ALIGN_FRAME_BOTTOMRIGHT) {
437 r->x = ((settings->view_w - eset->resolutions.frame_w)/2) + (eset->resolutions.frame_w - r->w) + r->x;
438 r->y = ((settings->view_h - eset->resolutions.frame_h)/2) + (eset->resolutions.frame_h - r->h) + r->y;
439 }
440 else {
441 // do nothing
442 }
443 }
444
445 /**
446 * These functions provide a unified way to log messages, printf-style
447 */
logInfo(const char * format,...)448 void Utils::logInfo(const char* format, ...) {
449 va_list args;
450
451 va_start(args, format);
452 SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, format, args);
453 va_end(args);
454
455 char file_buf[BUFSIZ];
456 va_start(args, format);
457 vsnprintf(file_buf, BUFSIZ, format, args);
458 va_end(args);
459
460 if (!LOG_FILE_INIT) {
461 LOG_MSG.push(std::pair<SDL_LogPriority, std::string>(SDL_LOG_PRIORITY_INFO, std::string(file_buf)));
462 }
463 else if (LOG_FILE_CREATED) {
464 FILE *log_file = fopen(LOG_PATH.c_str(), "a");
465 if (log_file) {
466 fprintf(log_file, "INFO: ");
467 fprintf(log_file, "%s", file_buf);
468 fprintf(log_file, "\n");
469 fclose(log_file);
470 }
471 }
472
473 }
474
logError(const char * format,...)475 void Utils::logError(const char* format, ...) {
476 va_list args;
477
478 va_start(args, format);
479 SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_ERROR, format, args);
480 va_end(args);
481
482 char file_buf[BUFSIZ];
483 va_start(args, format);
484 vsnprintf(file_buf, BUFSIZ, format, args);
485 va_end(args);
486
487 if (!LOG_FILE_INIT) {
488 LOG_MSG.push(std::pair<SDL_LogPriority, std::string>(SDL_LOG_PRIORITY_ERROR, std::string(file_buf)));
489 }
490 else if (LOG_FILE_CREATED) {
491 FILE *log_file = fopen(LOG_PATH.c_str(), "a");
492 if (log_file) {
493 fprintf(log_file, "ERROR: ");
494 fprintf(log_file, "%s", file_buf);
495 fprintf(log_file, "\n");
496 fclose(log_file);
497 }
498 }
499 }
500
logErrorDialog(const char * dialog_text,...)501 void Utils::logErrorDialog(const char* dialog_text, ...) {
502 char pre_buf[BUFSIZ];
503 char buf[BUFSIZ];
504 snprintf(pre_buf, BUFSIZ, "%s%s", "FLARE Error\n", dialog_text);
505
506 va_list args;
507 va_start(args, dialog_text);
508 vsnprintf(buf, BUFSIZ, pre_buf, args);
509 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "FLARE Error", buf, NULL);
510 va_end(args);
511 }
512
createLogFile()513 void Utils::createLogFile() {
514 LOG_PATH = settings->path_conf + "/flare_log.txt";
515
516 // always create a new log file on each launch
517 if (Filesystem::fileExists(LOG_PATH)) {
518 Filesystem::removeFile(LOG_PATH);
519 }
520
521 FILE *log_file = fopen(LOG_PATH.c_str(), "w+");
522 if (log_file) {
523 LOG_FILE_CREATED = true;
524 fprintf(log_file, "### Flare log file\n\n");
525
526 while (!LOG_MSG.empty()) {
527 if (LOG_MSG.front().first == SDL_LOG_PRIORITY_INFO)
528 fprintf(log_file, "INFO: ");
529 else if (LOG_MSG.front().first == SDL_LOG_PRIORITY_ERROR)
530 fprintf(log_file, "ERROR: ");
531
532 fprintf(log_file, "%s", LOG_MSG.front().second.c_str());
533 fprintf(log_file, "\n");
534
535 LOG_MSG.pop();
536 }
537 fclose(log_file);
538 }
539 else {
540 while (!LOG_MSG.empty())
541 LOG_MSG.pop();
542
543 logError("Utils: Could not create log file.");
544 }
545
546 LOG_FILE_INIT = true;
547 }
548
Exit(int code)549 void Utils::Exit(int code) {
550 SDL_Quit();
551 lockFileWrite(-1);
552 exit(code);
553 }
554
createSaveDir(int slot)555 void Utils::createSaveDir(int slot) {
556 // game slots are currently 1-4
557 if (slot == 0) return;
558
559 std::stringstream ss;
560 ss << settings->path_user << "saves/" << eset->misc.save_prefix << "/";
561
562 Filesystem::createDir(ss.str());
563
564 ss << slot;
565 Filesystem::createDir(ss.str());
566 }
567
removeSaveDir(int slot)568 void Utils::removeSaveDir(int slot) {
569 // game slots are currently 1-4
570 if (slot == 0) return;
571
572 std::stringstream ss;
573 ss << settings->path_user << "saves/" << eset->misc.save_prefix << "/" << slot;
574
575 if (Filesystem::isDirectory(ss.str())) {
576 Filesystem::removeDirRecursive(ss.str());
577 }
578 }
579
resizeToScreen(int w,int h,bool crop,int align)580 Rect Utils::resizeToScreen(int w, int h, bool crop, int align) {
581 Rect r;
582
583 // fit to height
584 float ratio = settings->view_h / static_cast<float>(h);
585 r.w = static_cast<int>(static_cast<float>(w) * ratio);
586 r.h = settings->view_h;
587
588 if (!crop) {
589 // fit to width
590 if (r.w > settings->view_w) {
591 ratio = settings->view_w / static_cast<float>(w);
592 r.h = static_cast<int>(static_cast<float>(h) * ratio);
593 r.w = settings->view_w;
594 }
595 }
596
597 alignToScreenEdge(align, &r);
598
599 return r;
600 }
601
stringFindCaseInsensitive(const std::string & _a,const std::string & _b)602 size_t Utils::stringFindCaseInsensitive(const std::string &_a, const std::string &_b) {
603 std::string a;
604 std::string b;
605
606 for (size_t i=0; i<_a.size(); ++i) {
607 a += static_cast<char>(tolower(static_cast<int>(_a[i])));
608 }
609
610 for (size_t i=0; i<_b.size(); ++i) {
611 b += static_cast<char>(tolower(static_cast<int>(_b[i])));
612 }
613
614 return a.find(b);
615 }
616
floatToString(const float value,size_t precision)617 std::string Utils::floatToString(const float value, size_t precision) {
618 std::stringstream ss;
619 ss << value;
620 std::string temp = ss.str();
621
622 size_t decimal = temp.find(".");
623 if (decimal != std::string::npos && temp.length() > decimal + precision + 1) {
624 temp = temp.substr(0, decimal + precision + 1);
625 }
626
627 return temp;
628 }
629
getDurationString(const int duration,size_t precision)630 std::string Utils::getDurationString(const int duration, size_t precision) {
631 float real_duration = static_cast<float>(duration) / settings->max_frames_per_sec;
632 std::string temp = floatToString(real_duration, precision);
633
634 if (real_duration == 1.f) {
635 return msg->get("%s second", temp);
636 }
637 else {
638 return msg->get("%s seconds", temp);
639 }
640 }
641
substituteVarsInString(const std::string & _s,Avatar * avatar)642 std::string Utils::substituteVarsInString(const std::string &_s, Avatar* avatar) {
643 std::string s = _s;
644
645 size_t begin = s.find("${");
646 while (begin != std::string::npos) {
647 size_t end = s.find("}");
648
649 if (end == std::string::npos)
650 break;
651
652 size_t var_len = end-begin+1;
653 std::string var = s.substr(begin,var_len);
654
655 if (avatar && var == "${AVATAR_NAME}") {
656 s.replace(begin, var_len, avatar->stats.name);
657 }
658 else if (avatar && var == "${AVATAR_CLASS}") {
659 s.replace(begin, var_len, avatar->stats.getShortClass());
660 }
661 else if (var == "${INPUT_MOVEMENT}") {
662 s.replace(begin, var_len, inpt->getMovementString());
663 }
664 else if (var == "${INPUT_ATTACK}") {
665 s.replace(begin, var_len, inpt->getAttackString());
666 }
667 else if (var == "${INPUT_CONTINUE}") {
668 s.replace(begin, var_len, inpt->getContinueString());
669 }
670 else {
671 logError("'%s' is not a valid string variable name.", var.c_str());
672 // strip the brackets from the variable
673 s.replace(begin, var_len, var.substr(2, var.length()-3));
674 }
675
676 begin = s.find("${");
677 }
678
679 return s;
680 }
681
682 /**
683 * Keep two points within a certain range
684 */
clampDistance(float range,const FPoint & src,const FPoint & target)685 FPoint Utils::clampDistance(float range, const FPoint& src, const FPoint& target) {
686 FPoint limit_target = target;
687
688 if (range > 0) {
689 if (src.x+range < target.x)
690 limit_target.x = src.x+range;
691 if (src.x-range > target.x)
692 limit_target.x = src.x-range;
693 if (src.y+range < target.y)
694 limit_target.y = src.y+range;
695 if (src.y-range > target.y)
696 limit_target.y = src.y-range;
697 }
698
699 return limit_target;
700 }
701
702 /**
703 * Compares two rectangles and returns true if they overlap
704 */
rectsOverlap(const Rect & a,const Rect & b)705 bool Utils::rectsOverlap(const Rect &a, const Rect &b) {
706 Point a_1(a.x, a.y);
707 Point a_2(a.x + a.w, a.y);
708 Point a_3(a.x, a.y + a.h);
709 Point a_4(a.x + a.w, a.y + a.h);
710
711 Point b_1(b.x, b.y);
712 Point b_2(b.x + b.w, b.y);
713 Point b_3(b.x, b.y + b.h);
714 Point b_4(b.x + b.w, b.y + b.h);
715
716 bool a_in_b = isWithinRect(b, a_1) || isWithinRect(b, a_2) || isWithinRect(b, a_3) || isWithinRect(b, a_4);
717 bool b_in_a = isWithinRect(a, b_1) || isWithinRect(a, b_2) || isWithinRect(a, b_3) || isWithinRect(a, b_4);
718
719 return a_in_b || b_in_a;
720 }
721
rotateDirection(int direction,int val)722 int Utils::rotateDirection(int direction, int val) {
723 direction += val;
724 if (direction > 7)
725 direction -= 7;
726 else if (direction < 0)
727 direction += 7;
728
729 return direction;
730 }
731
getTimeString(const unsigned long time)732 std::string Utils::getTimeString(const unsigned long time) {
733 std::stringstream ss;
734 unsigned long hours = (time / 60) / 60;
735 if (hours < 100)
736 ss << std::setfill('0') << std::setw(2) << hours;
737 else
738 ss << hours;
739
740 ss << ":";
741 unsigned long minutes = (time / 60) % 60;
742 ss << std::setfill('0') << std::setw(2) << minutes;
743
744 ss << ":";
745 unsigned long seconds = time % 60;
746 ss << std::setfill('0') << std::setw(2) << seconds;
747
748 return ss.str();
749 }
750
hashString(const std::string & str)751 unsigned long Utils::hashString(const std::string& str) {
752 std::locale loc;
753 const std::collate<char>& coll = std::use_facet<std::collate<char> >(loc);
754 return coll.hash(str.data(), str.data() + str.length());
755 }
756
strdup(const std::string & str)757 char* Utils::strdup(const std::string& str) {
758 size_t length = str.length() + 1;
759 char *x = static_cast<char*>(malloc(length));
760 if (!x)
761 return NULL;
762 memcpy(x, str.c_str(), length);
763 return x;
764 }
765
lockFileRead()766 void Utils::lockFileRead() {
767 if (!platform.has_lock_file)
768 return;
769
770 std::string lock_file_path = Filesystem::convertSlashes(settings->path_conf + "flare_lock");
771
772 std::ifstream infile;
773 infile.open(lock_file_path.c_str(), std::ios::in);
774
775 while (infile.good()) {
776 std::string line = Parse::getLine(infile);
777
778 if (line.length() == 0 || line.at(0) == '#')
779 continue;
780
781 LOCK_INDEX = Parse::toInt(line);
782 }
783
784 infile.close();
785 infile.clear();
786
787 if (LOCK_INDEX < 0)
788 LOCK_INDEX = 0;
789 }
790
lockFileWrite(int increment)791 void Utils::lockFileWrite(int increment) {
792 if (!platform.has_lock_file)
793 return;
794
795 std::string lock_file_path = settings->path_conf + "flare_lock";
796
797 if (increment < 0) {
798 if (LOCK_INDEX == 0)
799 return;
800
801 // refresh LOCK_INDEX in case any other instances were closed while this instance was running
802 lockFileRead();
803 }
804
805 std::ofstream outfile;
806 outfile.open(lock_file_path.c_str(), std::ios::out);
807 if (outfile.is_open()) {
808 LOCK_INDEX += increment;
809 outfile << "# Flare lock file. Counts instances of Flare" << std::endl;
810 outfile << LOCK_INDEX << std::endl;
811 outfile.close();
812 outfile.clear();
813 }
814 }
815
lockFileCheck()816 void Utils::lockFileCheck() {
817 if (!platform.has_lock_file)
818 return;
819
820 LOCK_INDEX = 0;
821
822 lockFileRead();
823
824 if (LOCK_INDEX > 0){
825 const SDL_MessageBoxButtonData buttons[] = {
826 { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT|SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 0, "Quit" },
827 { 0, 1, "Continue" },
828 { 0, 2, "Reset" },
829 { 0, 3, "Safe Video" },
830 };
831 const SDL_MessageBoxData messageboxdata = {
832 SDL_MESSAGEBOX_INFORMATION,
833 NULL,
834 "Flare",
835 "Flare is unable to launch properly. This may be because it did not exit properly, or because there is another instance running.\n\nIf Flare crashed, it is recommended to try 'Safe Video' mode. This will try launching Flare with the minimum video settings.\n\nIf Flare is already running, you may:\n- 'Quit' Flare (safe, recommended)\n- 'Continue' to launch another copy of Flare.\n- 'Reset' the counter which tracks the number of copies of Flare that are currently running.\n If this dialog is shown every time you launch Flare, this option should fix it.",
836 static_cast<int>(SDL_arraysize(buttons)),
837 buttons,
838 NULL
839 };
840 int buttonid = 0;
841 SDL_ShowMessageBox(&messageboxdata, &buttonid);
842 if (buttonid == 0) {
843 lockFileWrite(1);
844 Exit(1);
845 }
846 else if (buttonid == 2) {
847 LOCK_INDEX = 0;
848 }
849 else if (buttonid == 3) {
850 LOCK_INDEX = 0;
851 settings->safe_video = true;
852 }
853 }
854
855 lockFileWrite(1);
856 }
857
858