1 ////////////////////////////////////////////////////////////////////////////////
2 // Scorched3D (c) 2000-2011
3 //
4 // This file is part of Scorched3D.
5 //
6 // Scorched3D is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
10 //
11 // Scorched3D is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20
21 #include <stdlib.h>
22 #include <wx/wx.h>
23 #include <wx/image.h>
24 #include <wx/process.h>
25 #include <wx/txtstrm.h>
26 #include <wx/msgdlg.h>
27 #include <wx/dcbuffer.h>
28 #include <wx/splash.h>
29 #define WIN32_LEAN_AND_MEAN
30 #include <windows.h>
31 #include <SDL/SDL.h>
32 #include <wxdialogs/MainDialog.h>
33 #include <wxdialogs/DisplayDialog.h>
34 #include <wxdialogs/ServerSDialog.h>
35 #include <wxdialogs/TrueTypeFont.h>
36 #include <scorched/ScorchedParams.h>
37 #include <graph/OptionsDisplay.h>
38 #include <common/Defines.h>
39
40 extern char scorched3dAppName[128];
41 static wxFrame *mainDialog = 0;
42 bool wxWindowExit = false;
43
44 enum
45 {
46 ID_MAIN_TIMER
47 };
48
convertString(const std::string & input)49 wxString convertString(const std::string &input)
50 {
51 wxString result(input.c_str(), wxConvUTF8);
52 return result;
53 }
54
addTitleToWindow(wxWindow * parent,wxSizer * sizer,const char * fileName,int buttonId)55 void addTitleToWindow(
56 wxWindow *parent,
57 wxSizer *sizer,
58 const char *fileName,
59 int buttonId)
60 {
61 wxBitmap scorchedBitmap;
62 if (scorchedBitmap.LoadFile(wxString(fileName, wxConvUTF8),
63 wxBITMAP_TYPE_BMP) &&
64 scorchedBitmap.Ok())
65 {
66 wxBitmapButton *button = new wxBitmapButton(
67 parent, buttonId, scorchedBitmap);
68 wxBoxSizer *boxSizer = new wxBoxSizer(wxHORIZONTAL);
69 boxSizer->Add(button, 0, wxALL, 5);
70 sizer->Add(boxSizer, 0, wxALIGN_CENTER | wxALL, 5);
71 }
72 }
73
74 static SDL_mutex *messageMutex_ = 0;
75 static std::string messageString_;
76 static int exitCode_ = 0;
77
78 class ScorchedProcess : public wxProcess
79 {
80 public:
ScorchedProcess(bool server)81 ScorchedProcess(bool server) :
82 wxProcess(!server?wxPROCESS_REDIRECT:0),
83 server_(server)
84 {
85 if (server_) serverProcessesRunning_++;
86 else clientProcessesRunning_++;
87 }
88
OnTerminate(int pid,int status)89 virtual void OnTerminate(int pid, int status)
90 {
91 if (server_) serverProcessesRunning_--;
92 else clientProcessesRunning_--;
93
94 if (status != 0)
95 {
96 SDL_LockMutex(messageMutex_);
97 if (server_) exitCode_ = 64; // So it doesn't say to load failsafe
98 else exitCode_ = status;
99
100 if (status != 64)
101 {
102 messageString_ = "The Scorched3d process "
103 "terminated unexpectedly.\n";
104 }
105 else
106 {
107 messageString_ = "The Scorched3d process "
108 "terminated due to configuration errors.\n";
109 }
110 while (IsInputAvailable())
111 {
112 wxTextInputStream tis(*GetInputStream());
113 wxString line = tis.ReadLine();
114 messageString_.append((const char *) line.mb_str(wxConvUTF8));
115 messageString_.append("\n");
116 }
117 SDL_UnlockMutex(messageMutex_);
118 }
119 Detach();
120 wxProcess::OnTerminate(pid, status);
121 }
122
getClientProcessesRunning()123 static unsigned int getClientProcessesRunning()
124 {
125 return clientProcessesRunning_;
126 }
127
getServerProcessesRunning()128 static unsigned int getServerProcessesRunning()
129 {
130 return serverProcessesRunning_;
131 }
132
133 protected:
134 bool server_;
135 static unsigned int clientProcessesRunning_;
136 static unsigned int serverProcessesRunning_;
137 };
138
139 unsigned int ScorchedProcess::clientProcessesRunning_(0);
140 unsigned int ScorchedProcess::serverProcessesRunning_(0);
141
runScorched3D(const char * text,bool server)142 void runScorched3D(const char *text, bool server)
143 {
144 if (!server &&
145 ScorchedProcess::getClientProcessesRunning() > 0)
146 {
147 if (::wxMessageBox(
148 wxT("You are already running the game, do you wish to run another copy?"),
149 wxT("Scorched3D"),
150 wxCANCEL | wxOK | wxICON_EXCLAMATION) == wxCANCEL)
151 {
152 return;
153 }
154 }
155
156 std::string exeName = S3D::getExeName();
157 const char *exePart = strstr(exeName.c_str(), ".exe");
158 if (exePart) ((char *)exePart)[0] = '\0';
159 exePart = strstr(exeName.c_str(), ".EXE");
160 if (exePart) ((char *)exePart)[0] = '\0';
161
162 char path[1024];
163 snprintf(path, 1024, "\"%s%s%s\" %s -settingsdir %s %s",
164 exeName.c_str(),
165 (server?"s":"c"),
166 (exePart?".exe":""),
167 (ScorchedParams::instance()->getAllowExceptions()?" -allowexceptions":""),
168 ScorchedParams::instance()->getSettingsDir(),
169 text);
170
171 ScorchedProcess *process = new ScorchedProcess(server);
172 long result = ::wxExecute(wxString(path, wxConvUTF8), wxEXEC_ASYNC, process);
173 if (result == 0)
174 {
175 delete process;
176 S3D::dialogMessage(scorched3dAppName, S3D::formatStringBuffer(
177 "Error: Failed to execute scorched3d using commandline :-\n"
178 "%s",
179 path));
180 }
181 }
182
addButtonToWindow(int id,const char * text,const char * bitmapName,wxWindow * parent,wxSizer * sizer,wxObjectRefData * data)183 wxButton *addButtonToWindow(
184 int id,
185 const char *text,
186 const char *bitmapName,
187 wxWindow *parent,
188 wxSizer *sizer,
189 wxObjectRefData *data)
190 {
191 wxButton *button = 0;
192 wxBitmap bitmap;
193 if (bitmap.LoadFile(wxString(bitmapName, wxConvUTF8), wxBITMAP_TYPE_BMP) &&
194 bitmap.Ok())
195 {
196 button = new wxBitmapButton(parent, id, bitmap);
197 }
198 else
199 {
200 button = new wxButton(parent, id, wxT("Select"));
201 }
202 if (data) button->SetRefData(data);
203
204 wxStaticText *staticText = new wxStaticText(
205 parent, -1,
206 wxString(text, wxConvUTF8));
207
208 sizer->Add(button, 0, wxRIGHT, 5);
209 sizer->Add(staticText, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5);
210
211 return button;
212 }
213
214 class MainFrame: public wxFrame
215 {
216 public:
217 MainFrame();
218
219 void onTimer(wxTimerEvent &event);
220 void onPaint(wxPaintEvent& event);
221 void onMotion(wxMouseEvent &event);
222 void onEraseBackground(wxEraseEvent &event);
223
224 void onDisplayButton();
225 void onQuitButton();
226 void onSingleButton();
227 void onServerButton();
228 void onDonateClick();
229 void onHelpButton();
230
231 private:
232 DECLARE_EVENT_TABLE()
233
234 struct ImageData
235 {
236 int x, y;
237 wxImage loadedImage;
238 wxImage descriptionImage;
239 wxBitmap cachedBitmap1;
240 wxBitmap cachedBitmap2;
241 wxBitmap cachedDescription;
242 };
243
244 wxTimer timer_;
245 wxBitmap backdropBitmap_;
246 wxImage backdropImage_;
247 std::list<ImageData *> images_;
248 long mouseX_, mouseY_;
249 int lastPos_;
250
251 void generateCachedImage(int x, int y,
252 wxImage &src, wxBitmap &destBitamp,
253 bool highlight = false);
254 };
255
BEGIN_EVENT_TABLE(MainFrame,wxFrame)256 BEGIN_EVENT_TABLE(MainFrame, wxFrame)
257 EVT_TIMER(ID_MAIN_TIMER, MainFrame::onTimer)
258 EVT_PAINT(MainFrame::onPaint)
259 EVT_MOUSE_EVENTS(MainFrame::onMotion)
260 EVT_ERASE_BACKGROUND(MainFrame::onEraseBackground)
261 END_EVENT_TABLE()
262
263 MainFrame::MainFrame() :
264 wxFrame((wxFrame *)NULL, -1, wxString(scorched3dAppName, wxConvUTF8),
265 wxDefaultPosition, wxDefaultSize,
266 wxMINIMIZE_BOX | wxCAPTION | wxSYSTEM_MENU),
267 mouseX_(0), mouseY_(0), lastPos_(-1)
268 {
269 if (!messageMutex_) messageMutex_ = SDL_CreateMutex();
270
271 // Set the frame's icon
272 wxIcon icon(convertString(S3D::getDataFile("data/images/tank2.ico")), wxBITMAP_TYPE_ICO);
273 SetIcon(icon);
274
275 #if wxCHECK_VERSION(2, 6, 0)
276 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
277 #endif
278
279 // Load the backdrop bitmaps
280 if (!backdropImage_.LoadFile(
281 convertString(S3D::getDataFile("data/images/backdrop.gif")),
282 wxBITMAP_TYPE_GIF))
283 {
284 S3D::dialogMessage("Scorched", "Failed to load backdrop");
285 }
286 backdropBitmap_ = wxBitmap(backdropImage_, -1);
287
288 // Load all of the button bitmaps
289 struct ImageDefinition
290 {
291 const char *name;
292 const char *description;
293 int x, y;
294 } imageDefinitions[] = {
295 "Play", "- Play a game.", 30, 150,
296 "Start Server", "- Start a LAN or internet server.", 30, 180,
297 "Settings", "- Change the display, sound or other settings.", 30, 210,
298 "Help", "- View the online help.", 30, 260,
299 "Donate", "- Show support for Scorched3D.", 30, 290,
300 "Quit", "- Exit the game.", 30, 340
301 };
302
303 TrueTypeFont largeImageFont(S3D::getDataFile("data/fonts/dejavusans.ttf"), 14);
304 TrueTypeFont smallImageFont(S3D::getDataFile("data/fonts/dejavusans.ttf"), 12);
305
306 for (int i=0; i<sizeof(imageDefinitions) / sizeof(ImageDefinition); i++)
307 {
308 ImageData *image = new ImageData();
309 image->x = imageDefinitions[i].x;
310 image->y = imageDefinitions[i].y;
311
312 if (!largeImageFont.getImageForText(imageDefinitions[i].name, image->loadedImage) ||
313 !smallImageFont.getImageForText(imageDefinitions[i].description, image->descriptionImage))
314 {
315 S3D::dialogMessage("Scorched",
316 S3D::formatStringBuffer("Failed to load button %s", imageDefinitions[i].name));
317 }
318 else
319 {
320 images_.push_back(image);
321 }
322 }
323
324 // Setup timer
325 timer_.SetOwner(this, ID_MAIN_TIMER);
326 timer_.Start(1000, false);
327
328 // use the sizer for layout
329 // Create the positioning sizer
330 wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
331 topsizer->SetMinSize(533, 400);
332 SetSizer(topsizer);
333 topsizer->SetSizeHints(this); // set size hints to honour minimum size
334
335 CentreOnScreen();
336 }
337
onMotion(wxMouseEvent & event)338 void MainFrame::onMotion(wxMouseEvent &event)
339 {
340 mouseX_ = event.m_x;
341 mouseY_ = event.m_y;
342
343 int foundPos = -1;
344 int pos = 0;
345 std::list<ImageData *>::iterator itor;
346 for (itor = images_.begin();
347 itor != images_.end();
348 ++itor, pos++)
349 {
350 ImageData *imageData = (*itor);
351 if (mouseX_ > imageData->x &&
352 mouseY_ > imageData->y &&
353 mouseX_ < imageData->x + imageData->cachedBitmap1.GetWidth() &&
354 mouseY_ < imageData->y + imageData->cachedBitmap1.GetHeight())
355 {
356 foundPos = pos;
357 }
358 }
359
360 if (event.ButtonDown() &&
361 foundPos != -1)
362 {
363 switch (foundPos)
364 {
365 case 0:
366 onSingleButton();
367 break;
368 case 1:
369 onServerButton();
370 break;
371 case 2:
372 onDisplayButton();
373 break;
374 case 3:
375 onHelpButton();
376 break;
377 case 4:
378 onDonateClick();
379 break;
380 case 5:
381 onQuitButton();
382 break;
383 }
384 }
385
386 if (lastPos_ != foundPos)
387 {
388 lastPos_ = foundPos;
389 Refresh();
390 }
391 }
392
generateCachedImage(int x,int y,wxImage & src,wxBitmap & destBitamp,bool highlight)393 void MainFrame::generateCachedImage(int x, int y,
394 wxImage &src, wxBitmap &destBitamp,
395 bool highlight)
396 {
397 wxImage dest(
398 src.GetWidth(),
399 src.GetHeight());
400
401 unsigned char *backdropdata = backdropImage_.GetData();
402 backdropdata +=
403 3 * x +
404 3 * y * backdropImage_.GetWidth();
405
406 unsigned char *srcdata = src.GetData();
407 unsigned char *destdata = dest.GetData();
408 for (int y=0; y<src.GetHeight(); y++)
409 {
410 unsigned char *backdropstart = backdropdata;
411 for (int x=0; x<src.GetWidth(); x++)
412 {
413 float alpha = srcdata[0] + srcdata[1] + srcdata[2];
414 alpha /= 255.0f + 255.0f + 255.0f;
415
416 float mult = (highlight?1.5f:1.0f);
417 float src0 = float(srcdata[0]) * mult; if (src0 > 255.0f) src0 = 255.0f;
418 float src1 = float(srcdata[1]) * mult; if (src1 > 255.0f) src1 = 255.0f;
419 float src2 = float(srcdata[2]) * mult; if (src2 > 255.0f) src2 = 255.0f;
420 destdata[0] = (unsigned char) ((1.0f - alpha) * float(backdropdata[0]) + alpha * float(src0));
421 destdata[1] = (unsigned char) ((1.0f - alpha) * float(backdropdata[1]) + alpha * float(src1));
422 destdata[2] = (unsigned char) ((1.0f - alpha) * float(backdropdata[2]) + alpha * float(src2));
423
424 srcdata += 3;
425 destdata += 3;
426 backdropdata += 3;
427 }
428 backdropdata = backdropstart + 3 * backdropImage_.GetWidth();
429
430 }
431
432 destBitamp = wxBitmap(dest, -1);
433 }
434
onPaint(wxPaintEvent & event)435 void MainFrame::onPaint(wxPaintEvent& event)
436 {
437 wxBufferedPaintDC dc(this);
438
439 dc.DrawBitmap(backdropBitmap_, 0, 0, false);
440
441 // So its easy to display an alpha blended bitmap in wxWindows then!!!
442 std::list<ImageData *>::iterator itor;
443 for (itor = images_.begin();
444 itor != images_.end();
445 ++itor)
446 {
447 ImageData *imageData = (*itor);
448
449 if (!imageData->cachedBitmap1.Ok())
450 {
451 generateCachedImage(imageData->x, imageData->y,
452 imageData->loadedImage, imageData->cachedBitmap1, false);
453 generateCachedImage(imageData->x, imageData->y,
454 imageData->loadedImage, imageData->cachedBitmap2, true);
455 generateCachedImage(imageData->x + 135, imageData->y + 2,
456 imageData->descriptionImage, imageData->cachedDescription, true);
457 }
458
459 if (mouseX_ > imageData->x &&
460 mouseY_ > imageData->y &&
461 mouseX_ < imageData->x + imageData->cachedBitmap1.GetWidth() &&
462 mouseY_ < imageData->y + imageData->cachedBitmap1.GetHeight())
463 {
464 dc.DrawBitmap(imageData->cachedBitmap2, imageData->x, imageData->y, false);
465 dc.DrawBitmap(imageData->cachedDescription, imageData->x + 135, imageData->y + 2, false);
466 }
467 else
468 {
469 dc.DrawBitmap(imageData->cachedBitmap1, imageData->x, imageData->y, false);
470 }
471 }
472 }
473
onEraseBackground(wxEraseEvent & event)474 void MainFrame::onEraseBackground(wxEraseEvent &event)
475 {
476 }
477
onTimer(wxTimerEvent & event)478 void MainFrame::onTimer(wxTimerEvent &event)
479 {
480 std::string newString;
481 SDL_LockMutex(messageMutex_);
482 if (!messageString_.empty())
483 {
484 newString = messageString_;
485 messageString_ = "";
486 }
487 SDL_UnlockMutex(messageMutex_);
488
489 if (!newString.empty())
490 {
491 if (exitCode_ != 64)
492 {
493 newString.append("\n"
494 "Would you like to load the failsafe "
495 "scorched3d settings?\n"
496 "This gives the best chance of working but "
497 "at the cost of graphical detail.\n"
498 "You can adjust this later in the Scorched3D "
499 "display settings dialog.\n"
500 "Note: Most problems can be fixed by using "
501 "the very latest drivers\n"
502 "for your graphics card.");
503 int answer = ::wxMessageBox(
504 wxString(newString.c_str(), wxConvUTF8),
505 wxT("Scorched3D Abnormal Termination"),
506 wxYES_NO | wxICON_ERROR);
507 if (answer == wxYES)
508 {
509 OptionsDisplay::instance()->loadSafeValues();
510 OptionsDisplay::instance()->writeOptionsToFile(ScorchedParams::instance()->getWriteFullOptions());
511 }
512 }
513 else
514 {
515 ::wxMessageBox(
516 wxString(newString.c_str(), wxConvUTF8),
517 wxT("Scorched3D Termination"),
518 wxICON_ERROR);
519 }
520 }
521 }
522
onDonateClick()523 void MainFrame::onDonateClick()
524 {
525 const char *exec =
526 "\"https://www.paypal.com/xclick/business=donations%40"
527 "scorched3d.co.uk&item_name=Scorched3D&no_note=1&tax=0¤cy_code=GBP\"";
528 S3D::showURL(exec);
529 }
530
onDisplayButton()531 void MainFrame::onDisplayButton()
532 {
533 showDisplayDialog();
534 }
535
onSingleButton()536 void MainFrame::onSingleButton()
537 {
538 runScorched3D("", false);
539 }
540
onServerButton()541 void MainFrame::onServerButton()
542 {
543 showServerSDialog();
544 }
545
onQuitButton()546 void MainFrame::onQuitButton()
547 {
548 wxWindowExit = true;
549 Close();
550 }
551
onHelpButton()552 void MainFrame::onHelpButton()
553 {
554 S3D::showURL("http://www.scorched3d.co.uk/wiki");
555 }
556
557 extern bool newVersion;
showMainDialog()558 void showMainDialog()
559 {
560 mainDialog = new MainFrame;
561 mainDialog->Show(TRUE);
562
563 if (newVersion)
564 {
565 wxBitmap bitmap;
566 if (bitmap.LoadFile(convertString(S3D::getDataFile("data/images/splash.gif")),
567 wxBITMAP_TYPE_GIF))
568 {
569 new wxSplashScreen(bitmap,
570 wxSPLASH_CENTRE_ON_SCREEN | wxSPLASH_TIMEOUT,
571 9000, NULL, -1, wxDefaultPosition, wxDefaultSize,
572 wxSIMPLE_BORDER | wxSTAY_ON_TOP);
573 }
574 }
575 }
576
getMainDialog()577 wxFrame *getMainDialog()
578 {
579 return mainDialog;
580 }
581