1 #include "GameController.h"
2 
3 #include "GameView.h"
4 #include "GameModel.h"
5 
6 #include "RenderPreset.h"
7 #include "Menu.h"
8 #include "Tool.h"
9 #include "Brush.h"
10 #include "QuickOptions.h"
11 #include "GameModelException.h"
12 
13 #include "Config.h"
14 #include "Format.h"
15 #include "Platform.h"
16 #include "Controller.h"
17 #include "Notification.h"
18 
19 #include "client/GameSave.h"
20 #include "client/Client.h"
21 
22 #include "gui/search/SearchController.h"
23 #include "gui/render/RenderController.h"
24 #include "gui/login/LoginController.h"
25 #include "gui/preview/PreviewController.h"
26 #include "gui/tags/TagsController.h"
27 #include "gui/console/ConsoleController.h"
28 #include "gui/localbrowser/LocalBrowserController.h"
29 #include "gui/options/OptionsController.h"
30 
31 #include "gui/dialogues/ErrorMessage.h"
32 #include "gui/dialogues/InformationMessage.h"
33 #include "gui/dialogues/ConfirmPrompt.h"
34 
35 #include "gui/elementsearch/ElementSearchActivity.h"
36 #include "gui/profile/ProfileActivity.h"
37 #include "gui/colourpicker/ColourPickerActivity.h"
38 #include "gui/update/UpdateActivity.h"
39 #include "gui/filebrowser/FileBrowserActivity.h"
40 #include "gui/save/LocalSaveActivity.h"
41 #include "gui/save/ServerSaveActivity.h"
42 
43 #include "gui/tags/TagsView.h"
44 #include "gui/search/SearchView.h"
45 #include "gui/render/RenderView.h"
46 #include "gui/preview/PreviewView.h"
47 #include "gui/options/OptionsView.h"
48 #include "gui/login/LoginView.h"
49 #include "gui/localbrowser/LocalBrowserView.h"
50 #include "gui/console/ConsoleView.h"
51 
52 #include "gui/interface/Keys.h"
53 #include "gui/interface/Mouse.h"
54 #include "gui/interface/Engine.h"
55 
56 #include "debug/DebugInfo.h"
57 #include "debug/DebugParts.h"
58 #include "debug/ElementPopulation.h"
59 #include "debug/DebugLines.h"
60 #include "debug/ParticleDebug.h"
61 
62 #ifdef LUACONSOLE
63 #include "lua/LuaScriptInterface.h"
64 #else
65 #include "lua/TPTScriptInterface.h"
66 #endif
67 #include "lua/LuaEvents.h"
68 
69 #include "graphics/Renderer.h"
70 
71 #include "simulation/Simulation.h"
72 #include "simulation/SimulationData.h"
73 #include "simulation/Air.h"
74 #include "simulation/Snapshot.h"
75 #include "simulation/ElementClasses.h"
76 
77 #ifdef GetUserName
78 # undef GetUserName // dammit windows
79 #endif
80 
GameController()81 GameController::GameController():
82 	firstTick(true),
83 	foundSignID(-1),
84 	activePreview(NULL),
85 	search(NULL),
86 	renderOptions(NULL),
87 	loginWindow(NULL),
88 	console(NULL),
89 	tagsWindow(NULL),
90 	localBrowser(NULL),
91 	options(NULL),
92 	debugFlags(0),
93 	HasDone(false)
94 {
95 	gameView = new GameView();
96 	gameModel = new GameModel();
97 	gameModel->BuildQuickOptionMenu(this);
98 
99 	gameView->AttachController(this);
100 	gameModel->AddObserver(gameView);
101 
102 	gameView->SetDebugHUD(Client::Ref().GetPrefBool("Renderer.DebugMode", false));
103 
104 #ifdef LUACONSOLE
105 	commandInterface = new LuaScriptInterface(this, gameModel);
106 #else
107 	commandInterface = new TPTScriptInterface(this, gameModel);
108 #endif
109 
110 	Client::Ref().AddListener(this);
111 
112 	debugInfo.push_back(new DebugParts(0x1, gameModel->GetSimulation()));
113 	debugInfo.push_back(new ElementPopulationDebug(0x2, gameModel->GetSimulation()));
114 	debugInfo.push_back(new DebugLines(0x4, gameView, this));
115 	debugInfo.push_back(new ParticleDebug(0x8, gameModel->GetSimulation(), gameModel));
116 }
117 
~GameController()118 GameController::~GameController()
119 {
120 	if(search)
121 	{
122 		delete search;
123 	}
124 	if(renderOptions)
125 	{
126 		delete renderOptions;
127 	}
128 	if(loginWindow)
129 	{
130 		delete loginWindow;
131 	}
132 	if(tagsWindow)
133 	{
134 		delete tagsWindow;
135 	}
136 	if(console)
137 	{
138 		delete console;
139 	}
140 	if(activePreview)
141 	{
142 		delete activePreview;
143 	}
144 	if(localBrowser)
145 	{
146 		delete localBrowser;
147 	}
148 	if (options)
149 	{
150 		delete options;
151 	}
152 	for(std::vector<DebugInfo*>::iterator iter = debugInfo.begin(), end = debugInfo.end(); iter != end; iter++)
153 	{
154 		delete *iter;
155 	}
156 	//deleted here because it refuses to be deleted when deleted from gameModel even with the same code
157 	std::deque<Snapshot*> history = gameModel->GetHistory();
158 	for(std::deque<Snapshot*>::iterator iter = history.begin(), end = history.end(); iter != end; ++iter)
159 	{
160 		delete *iter;
161 	}
162 	std::vector<QuickOption*> quickOptions = gameModel->GetQuickOptions();
163 	for(std::vector<QuickOption*>::iterator iter = quickOptions.begin(), end = quickOptions.end(); iter != end; ++iter)
164 	{
165 		delete *iter;
166 	}
167 	std::vector<Notification*> notifications = gameModel->GetNotifications();
168 	for(std::vector<Notification*>::iterator iter = notifications.begin(); iter != notifications.end(); ++iter)
169 	{
170 		delete *iter;
171 	}
172 	delete gameModel;
173 	if (gameView->CloseActiveWindow())
174 	{
175 		delete gameView;
176 	}
177 }
178 
HistoryRestore()179 void GameController::HistoryRestore()
180 {
181 	std::deque<Snapshot*> history = gameModel->GetHistory();
182 	if (!history.size())
183 		return;
184 	unsigned int historyPosition = gameModel->GetHistoryPosition();
185 	unsigned int newHistoryPosition = std::max((int)historyPosition-1, 0);
186 	// When undoing, save the current state as a final redo
187 	// This way ctrl+y will always bring you back to the point right before your last ctrl+z
188 	if (historyPosition == history.size())
189 	{
190 		Snapshot * newSnap = gameModel->GetSimulation()->CreateSnapshot();
191 		if (newSnap)
192 			newSnap->Authors = Client::Ref().GetAuthorInfo();
193 		delete gameModel->GetRedoHistory();
194 		gameModel->SetRedoHistory(newSnap);
195 	}
196 	Snapshot * snap = history[newHistoryPosition];
197 	gameModel->GetSimulation()->Restore(*snap);
198 	Client::Ref().OverwriteAuthorInfo(snap->Authors);
199 	gameModel->SetHistory(history);
200 	gameModel->SetHistoryPosition(newHistoryPosition);
201 }
202 
HistorySnapshot()203 void GameController::HistorySnapshot()
204 {
205 	std::deque<Snapshot*> history = gameModel->GetHistory();
206 	unsigned int historyPosition = gameModel->GetHistoryPosition();
207 	Snapshot * newSnap = gameModel->GetSimulation()->CreateSnapshot();
208 	if (newSnap)
209 	{
210 		newSnap->Authors = Client::Ref().GetAuthorInfo();
211 		while (historyPosition < history.size())
212 		{
213 			Snapshot * snap = history.back();
214 			history.pop_back();
215 			delete snap;
216 		}
217 		if (history.size() >= gameModel->GetUndoHistoryLimit())
218 		{
219 			Snapshot * snap = history.front();
220 			history.pop_front();
221 			delete snap;
222 			if (historyPosition > history.size())
223 				historyPosition--;
224 		}
225 		history.push_back(newSnap);
226 		gameModel->SetHistory(history);
227 		gameModel->SetHistoryPosition(std::min((size_t)historyPosition+1, history.size()));
228 		delete gameModel->GetRedoHistory();
229 		gameModel->SetRedoHistory(NULL);
230 	}
231 }
232 
HistoryForward()233 void GameController::HistoryForward()
234 {
235 	std::deque<Snapshot*> history = gameModel->GetHistory();
236 	if (!history.size())
237 		return;
238 	unsigned int historyPosition = gameModel->GetHistoryPosition();
239 	unsigned int newHistoryPosition = std::min((size_t)historyPosition+1, history.size());
240 	Snapshot *snap;
241 	if (newHistoryPosition == history.size())
242 		snap = gameModel->GetRedoHistory();
243 	else
244 		snap = history[newHistoryPosition];
245 	if (!snap)
246 		return;
247 	gameModel->GetSimulation()->Restore(*snap);
248 	Client::Ref().OverwriteAuthorInfo(snap->Authors);
249 	gameModel->SetHistoryPosition(newHistoryPosition);
250 }
251 
GetView()252 GameView * GameController::GetView()
253 {
254 	return gameView;
255 }
256 
GetSignAt(int x,int y)257 int GameController::GetSignAt(int x, int y)
258 {
259 	Simulation * sim = gameModel->GetSimulation();
260 	for (int i = sim->signs.size()-1; i >= 0; i--)
261 	{
262 		int signx, signy, signw, signh;
263 		sim->signs[i].getDisplayText(sim, signx, signy, signw, signh);
264 		if (x>=signx && x<=signx+signw && y>=signy && y<=signy+signh)
265 			return i;
266 	}
267 	return -1;
268 }
269 
270 // assumed to already be a valid sign
GetSignText(int signID)271 String GameController::GetSignText(int signID)
272 {
273 	return gameModel->GetSimulation()->signs[signID].text;
274 }
275 
GetSignSplit(int signID)276 std::pair<int, sign::Type> GameController::GetSignSplit(int signID)
277 {
278 	return gameModel->GetSimulation()->signs[signID].split();
279 }
280 
PlaceSave(ui::Point position)281 void GameController::PlaceSave(ui::Point position)
282 {
283 	GameSave *placeSave = gameModel->GetPlaceSave();
284 	if (placeSave)
285 	{
286 		HistorySnapshot();
287 		if (!gameModel->GetSimulation()->Load(placeSave, !gameView->ShiftBehaviour(), position.X, position.Y))
288 		{
289 			gameModel->SetPaused(placeSave->paused | gameModel->GetPaused());
290 			Client::Ref().MergeStampAuthorInfo(placeSave->authors);
291 		}
292 	}
293 }
294 
Install()295 void GameController::Install()
296 {
297 #if defined(MACOSX)
298 	new InformationMessage("No installation necessary", "You don't need to install The Powder Toy on OS X", false);
299 #elif defined(WIN) || defined(LIN)
300 	new ConfirmPrompt("Install The Powder Toy", "Do you wish to install The Powder Toy on this computer?\nThis allows you to open save files and saves directly from the website.", { [] {
301 		if (Client::Ref().DoInstallation())
302 		{
303 			new InformationMessage("Success", "Installation completed", false);
304 		}
305 		else
306 		{
307 			new ErrorMessage("Could not install", "The installation did not complete due to an error");
308 		}
309 	} });
310 #else
311 	new ErrorMessage("Cannot install", "You cannot install The Powder Toy on this platform");
312 #endif
313 }
314 
AdjustGridSize(int direction)315 void GameController::AdjustGridSize(int direction)
316 {
317 	if(direction > 0)
318 		gameModel->GetRenderer()->SetGridSize((gameModel->GetRenderer()->GetGridSize()+1)%10);
319 	else
320 		gameModel->GetRenderer()->SetGridSize((gameModel->GetRenderer()->GetGridSize()+9)%10);
321 }
322 
InvertAirSim()323 void GameController::InvertAirSim()
324 {
325 	gameModel->GetSimulation()->air->Invert();
326 }
327 
328 
AdjustBrushSize(int delta,bool logarithmic,bool xAxis,bool yAxis)329 void GameController::AdjustBrushSize(int delta, bool logarithmic, bool xAxis, bool yAxis)
330 {
331 	if(xAxis && yAxis)
332 		return;
333 
334 	ui::Point newSize(0, 0);
335 	ui::Point oldSize = gameModel->GetBrush()->GetRadius();
336 	if(logarithmic)
337 		newSize = gameModel->GetBrush()->GetRadius() + ui::Point(delta * std::max(gameModel->GetBrush()->GetRadius().X / 5, 1), delta * std::max(gameModel->GetBrush()->GetRadius().Y / 5, 1));
338 	else
339 		newSize = gameModel->GetBrush()->GetRadius() + ui::Point(delta, delta);
340 	if(newSize.X < 0)
341 		newSize.X = 0;
342 	if(newSize.Y < 0)
343 		newSize.Y = 0;
344 	if(newSize.X > 200)
345 		newSize.X = 200;
346 	if(newSize.Y > 200)
347 		newSize.Y = 200;
348 
349 	if(xAxis)
350 		SetBrushSize(ui::Point(newSize.X, oldSize.Y));
351 	else if(yAxis)
352 		SetBrushSize(ui::Point(oldSize.X, newSize.Y));
353 	else
354 		SetBrushSize(newSize);
355 }
356 
SetBrushSize(ui::Point newSize)357 void GameController::SetBrushSize(ui::Point newSize)
358 {
359 	gameModel->GetBrush()->SetRadius(newSize);
360 }
361 
AdjustZoomSize(int delta,bool logarithmic)362 void GameController::AdjustZoomSize(int delta, bool logarithmic)
363 {
364 	int newSize;
365 	if(logarithmic)
366 		newSize = gameModel->GetZoomSize() + std::max(gameModel->GetZoomSize() / 10, 1) * delta;
367 	else
368 		newSize = gameModel->GetZoomSize() + delta;
369 	if(newSize<5)
370 			newSize = 5;
371 	if(newSize>64)
372 			newSize = 64;
373 	gameModel->SetZoomSize(newSize);
374 
375 	int newZoomFactor = 256/newSize;
376 	if(newZoomFactor<3)
377 		newZoomFactor = 3;
378 	gameModel->SetZoomFactor(newZoomFactor);
379 }
380 
MouseInZoom(ui::Point position)381 bool GameController::MouseInZoom(ui::Point position)
382 {
383 	if(position.X >= XRES)
384 		position.X = XRES-1;
385 	if(position.Y >= YRES)
386 		position.Y = YRES-1;
387 	if(position.Y < 0)
388 		position.Y = 0;
389 	if(position.X < 0)
390 		position.X = 0;
391 
392 	return gameModel->MouseInZoom(position);
393 }
394 
PointTranslate(ui::Point point)395 ui::Point GameController::PointTranslate(ui::Point point)
396 {
397 	if(point.X >= XRES)
398 		point.X = XRES-1;
399 	if(point.Y >= YRES)
400 		point.Y = YRES-1;
401 	if(point.Y < 0)
402 		point.Y = 0;
403 	if(point.X < 0)
404 		point.X = 0;
405 
406 	return gameModel->AdjustZoomCoords(point);
407 }
408 
NormaliseBlockCoord(ui::Point point)409 ui::Point GameController::NormaliseBlockCoord(ui::Point point)
410 {
411 	return (point/CELL)*CELL;
412 }
413 
DrawRect(int toolSelection,ui::Point point1,ui::Point point2)414 void GameController::DrawRect(int toolSelection, ui::Point point1, ui::Point point2)
415 {
416 	Simulation * sim = gameModel->GetSimulation();
417 	Tool * activeTool = gameModel->GetActiveTool(toolSelection);
418 	gameModel->SetLastTool(activeTool);
419 	Brush * cBrush = gameModel->GetBrush();
420 	if(!activeTool || !cBrush)
421 		return;
422 	activeTool->SetStrength(1.0f);
423 	activeTool->DrawRect(sim, cBrush, point1, point2);
424 }
425 
DrawLine(int toolSelection,ui::Point point1,ui::Point point2)426 void GameController::DrawLine(int toolSelection, ui::Point point1, ui::Point point2)
427 {
428 	Simulation * sim = gameModel->GetSimulation();
429 	Tool * activeTool = gameModel->GetActiveTool(toolSelection);
430 	gameModel->SetLastTool(activeTool);
431 	Brush * cBrush = gameModel->GetBrush();
432 	if(!activeTool || !cBrush)
433 		return;
434 	activeTool->SetStrength(1.0f);
435 	activeTool->DrawLine(sim, cBrush, point1, point2);
436 }
437 
DrawFill(int toolSelection,ui::Point point)438 void GameController::DrawFill(int toolSelection, ui::Point point)
439 {
440 	Simulation * sim = gameModel->GetSimulation();
441 	Tool * activeTool = gameModel->GetActiveTool(toolSelection);
442 	gameModel->SetLastTool(activeTool);
443 	Brush * cBrush = gameModel->GetBrush();
444 	if(!activeTool || !cBrush)
445 		return;
446 	activeTool->SetStrength(1.0f);
447 	activeTool->DrawFill(sim, cBrush, point);
448 }
449 
DrawPoints(int toolSelection,ui::Point oldPos,ui::Point newPos,bool held)450 void GameController::DrawPoints(int toolSelection, ui::Point oldPos, ui::Point newPos, bool held)
451 {
452 	Simulation * sim = gameModel->GetSimulation();
453 	Tool * activeTool = gameModel->GetActiveTool(toolSelection);
454 	gameModel->SetLastTool(activeTool);
455 	Brush * cBrush = gameModel->GetBrush();
456 	if (!activeTool || !cBrush)
457 	{
458 		return;
459 	}
460 
461 	activeTool->SetStrength(gameModel->GetToolStrength());
462 	if (!held)
463 		activeTool->Draw(sim, cBrush, newPos);
464 	else
465 		activeTool->DrawLine(sim, cBrush, oldPos, newPos, true);
466 }
467 
LoadClipboard()468 bool GameController::LoadClipboard()
469 {
470 	GameSave *clip = gameModel->GetClipboard();
471 	if (!clip)
472 		return false;
473 	gameModel->SetPlaceSave(clip);
474 	if (gameModel->GetPlaceSave() && gameModel->GetPlaceSave()->Collapsed())
475 		gameModel->GetPlaceSave()->Expand();
476 	return true;
477 }
478 
LoadStamp(GameSave * stamp)479 void GameController::LoadStamp(GameSave *stamp)
480 {
481 	gameModel->SetPlaceSave(stamp);
482 	if(gameModel->GetPlaceSave() && gameModel->GetPlaceSave()->Collapsed())
483 		gameModel->GetPlaceSave()->Expand();
484 }
485 
TranslateSave(ui::Point point)486 void GameController::TranslateSave(ui::Point point)
487 {
488 	vector2d translate = v2d_new(point.X, point.Y);
489 	vector2d translated = gameModel->GetPlaceSave()->Translate(translate);
490 	ui::Point currentPlaceSaveOffset = gameView->GetPlaceSaveOffset();
491 	// resets placeSaveOffset to 0, which is why we back it up first
492 	gameModel->SetPlaceSave(gameModel->GetPlaceSave());
493 	gameView->SetPlaceSaveOffset(ui::Point(translated.x, translated.y) + currentPlaceSaveOffset);
494 }
495 
TransformSave(matrix2d transform)496 void GameController::TransformSave(matrix2d transform)
497 {
498 	vector2d translate = v2d_zero;
499 	gameModel->GetPlaceSave()->Transform(transform, translate);
500 	gameModel->SetPlaceSave(gameModel->GetPlaceSave());
501 }
502 
ToolClick(int toolSelection,ui::Point point)503 void GameController::ToolClick(int toolSelection, ui::Point point)
504 {
505 	Simulation * sim = gameModel->GetSimulation();
506 	Tool * activeTool = gameModel->GetActiveTool(toolSelection);
507 	Brush * cBrush = gameModel->GetBrush();
508 	if(!activeTool || !cBrush)
509 		return;
510 	activeTool->Click(sim, cBrush, point);
511 }
512 
StampRegion(ui::Point point1,ui::Point point2)513 ByteString GameController::StampRegion(ui::Point point1, ui::Point point2)
514 {
515 	GameSave * newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), point1.X, point1.Y, point2.X, point2.Y);
516 	if(newSave)
517 	{
518 		newSave->paused = gameModel->GetPaused();
519 		ByteString stampName = Client::Ref().AddStamp(newSave);
520 		delete newSave;
521 		if (stampName.length() == 0)
522 			new ErrorMessage("Could not create stamp", "Error serializing save file");
523 		return stampName;
524 	}
525 	else
526 	{
527 		new ErrorMessage("Could not create stamp", "Error generating save file");
528 		return "";
529 	}
530 }
531 
CopyRegion(ui::Point point1,ui::Point point2)532 void GameController::CopyRegion(ui::Point point1, ui::Point point2)
533 {
534 	GameSave * newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), point1.X, point1.Y, point2.X, point2.Y);
535 	if(newSave)
536 	{
537 		Json::Value clipboardInfo;
538 		clipboardInfo["type"] = "clipboard";
539 		clipboardInfo["username"] = Client::Ref().GetAuthUser().Username;
540 		clipboardInfo["date"] = (Json::Value::UInt64)time(NULL);
541 		Client::Ref().SaveAuthorInfo(&clipboardInfo);
542 		newSave->authors = clipboardInfo;
543 
544 		newSave->paused = gameModel->GetPaused();
545 		gameModel->SetClipboard(newSave);
546 	}
547 }
548 
CutRegion(ui::Point point1,ui::Point point2)549 void GameController::CutRegion(ui::Point point1, ui::Point point2)
550 {
551 	CopyRegion(point1, point2);
552 	gameModel->GetSimulation()->clear_area(point1.X, point1.Y, point2.X-point1.X, point2.Y-point1.Y);
553 }
554 
MouseMove(int x,int y,int dx,int dy)555 bool GameController::MouseMove(int x, int y, int dx, int dy)
556 {
557 	MouseMoveEvent ev(x, y, dx, dy);
558 	return commandInterface->HandleEvent(LuaEvents::mousemove, &ev);
559 }
560 
MouseDown(int x,int y,unsigned button)561 bool GameController::MouseDown(int x, int y, unsigned button)
562 {
563 	MouseDownEvent ev(x, y, button);
564 	bool ret = commandInterface->HandleEvent(LuaEvents::mousedown, &ev);
565 	if (ret && y<YRES && x<XRES && !gameView->GetPlacingSave() && !gameView->GetPlacingZoom())
566 	{
567 		ui::Point point = gameModel->AdjustZoomCoords(ui::Point(x, y));
568 		x = point.X;
569 		y = point.Y;
570 		if (!gameModel->GetActiveTool(0) || gameModel->GetActiveTool(0)->GetIdentifier() != "DEFAULT_UI_SIGN" || button != SDL_BUTTON_LEFT) //If it's not a sign tool or you are right/middle clicking
571 		{
572 			foundSignID = GetSignAt(x, y);
573 			if (foundSignID != -1)
574 			{
575 				if (gameModel->GetSimulation()->signs[foundSignID].split().first)
576 				{
577 					return false;
578 				}
579 			}
580 		}
581 	}
582 	return ret;
583 }
584 
MouseUp(int x,int y,unsigned button,char type)585 bool GameController::MouseUp(int x, int y, unsigned button, char type)
586 {
587 	MouseUpEvent ev(x, y, button, type);
588 	bool ret = commandInterface->HandleEvent(LuaEvents::mouseup, &ev);
589 	if (type)
590 		return ret;
591 	if (ret && foundSignID != -1 && y<YRES && x<XRES && !gameView->GetPlacingSave())
592 	{
593 		ui::Point point = gameModel->AdjustZoomCoords(ui::Point(x, y));
594 		x = point.X;
595 		y = point.Y;
596 		if (!gameModel->GetActiveTool(0) || gameModel->GetActiveTool(0)->GetIdentifier() != "DEFAULT_UI_SIGN" || button != SDL_BUTTON_LEFT) //If it's not a sign tool or you are right/middle clicking
597 		{
598 			int foundSignID = GetSignAt(x, y);
599 			if (foundSignID != -1)
600 			{
601 				sign &foundSign = gameModel->GetSimulation()->signs[foundSignID];
602 				String str = foundSign.text;
603 				auto si = gameModel->GetSimulation()->signs[foundSignID].split();
604 				if (si.first)
605 				{
606 					ret = false;
607 					switch (si.second)
608 					{
609 					case sign::Type::Save:
610 						{
611 							int saveID = str.Substr(3, si.first - 3).ToNumber<int>(true);
612 							if (saveID)
613 								OpenSavePreview(saveID, 0, false);
614 						}
615 						break;
616 					case sign::Type::Thread:
617 						Platform::OpenURI(ByteString::Build(SCHEME "powdertoy.co.uk/Discussions/Thread/View.html?Thread=", str.Substr(3, si.first - 3).ToUtf8()));
618 						break;
619 					case sign::Type::Search:
620 						OpenSearch(str.Substr(3, si.first - 3));
621 						break;
622 					case sign::Type::Button:
623 						gameModel->GetSimulation()->create_part(-1, foundSign.x, foundSign.y, PT_SPRK);
624 						break;
625 					default: break;
626 					}
627 				}
628 			}
629 		}
630 	}
631 	foundSignID = -1;
632 	return ret;
633 }
634 
MouseWheel(int x,int y,int d)635 bool GameController::MouseWheel(int x, int y, int d)
636 {
637 	MouseWheelEvent ev(x, y, d);
638 	return commandInterface->HandleEvent(LuaEvents::mousewheel, &ev);
639 }
640 
TextInput(String text)641 bool GameController::TextInput(String text)
642 {
643 	TextInputEvent ev(text);
644 	return commandInterface->HandleEvent(LuaEvents::textinput, &ev);
645 }
646 
KeyPress(int key,int scan,bool repeat,bool shift,bool ctrl,bool alt)647 bool GameController::KeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
648 {
649 	KeyEvent ev(key, scan, repeat, shift, ctrl, alt);
650 	bool ret = commandInterface->HandleEvent(LuaEvents::keypress, &ev);
651 	if (repeat)
652 		return ret;
653 	if (ret)
654 	{
655 		Simulation * sim = gameModel->GetSimulation();
656 		if (!gameView->GetPlacingSave())
657 		{
658 			// Go right command
659 			if (key == SDLK_RIGHT)
660 			{
661 				sim->player.comm = (int)(sim->player.comm)|0x02;
662 			}
663 			// Go left command
664 			else if (key == SDLK_LEFT)
665 			{
666 				sim->player.comm = (int)(sim->player.comm)|0x01;
667 			}
668 			// Use element command
669 			else if (key == SDLK_DOWN && ((int)(sim->player.comm)&0x08)!=0x08)
670 			{
671 				sim->player.comm = (int)(sim->player.comm)|0x08;
672 			}
673 			// Jump command
674 			else if (key == SDLK_UP && ((int)(sim->player.comm)&0x04)!=0x04)
675 			{
676 				sim->player.comm = (int)(sim->player.comm)|0x04;
677 			}
678 		}
679 
680 		// Go right command
681 		if (scan == SDL_SCANCODE_D)
682 		{
683 			sim->player2.comm = (int)(sim->player2.comm)|0x02;
684 		}
685 		// Go left command
686 		else if (scan == SDL_SCANCODE_A)
687 		{
688 			sim->player2.comm = (int)(sim->player2.comm)|0x01;
689 		}
690 		// Use element command
691 		else if (scan == SDL_SCANCODE_S && ((int)(sim->player2.comm)&0x08)!=0x08)
692 		{
693 			sim->player2.comm = (int)(sim->player2.comm)|0x08;
694 		}
695 		// Jump command
696 		else if (scan == SDL_SCANCODE_W && ((int)(sim->player2.comm)&0x04)!=0x04)
697 		{
698 			sim->player2.comm = (int)(sim->player2.comm)|0x04;
699 		}
700 
701 		if (!sim->elementCount[PT_STKM2] || ctrl)
702 		{
703 			switch(scan)
704 			{
705 			case SDL_SCANCODE_W:
706 				SwitchGravity();
707 				break;
708 			case SDL_SCANCODE_D:
709 				gameView->SetDebugHUD(!gameView->GetDebugHUD());
710 				break;
711 			case SDL_SCANCODE_S:
712 				gameView->BeginStampSelection();
713 				break;
714 			}
715 		}
716 
717 		for(std::vector<DebugInfo*>::iterator iter = debugInfo.begin(), end = debugInfo.end(); iter != end; iter++)
718 		{
719 			if ((*iter)->debugID & debugFlags)
720 				if (!(*iter)->KeyPress(key, scan, shift, ctrl, alt, gameView->GetMousePosition()))
721 					ret = false;
722 		}
723 	}
724 	return ret;
725 }
726 
KeyRelease(int key,int scan,bool repeat,bool shift,bool ctrl,bool alt)727 bool GameController::KeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt)
728 {
729 	KeyEvent ev(key, scan, repeat, shift, ctrl, alt);
730 	bool ret = commandInterface->HandleEvent(LuaEvents::keyrelease, &ev);
731 	if (repeat)
732 		return ret;
733 	if (ret)
734 	{
735 		Simulation * sim = gameModel->GetSimulation();
736 		if (key == SDLK_RIGHT || key == SDLK_LEFT)
737 		{
738 			sim->player.pcomm = sim->player.comm;  //Saving last movement
739 			sim->player.comm = (int)(sim->player.comm)&12;  //Stop command
740 		}
741 		else if (key == SDLK_UP)
742 		{
743 			sim->player.comm = (int)(sim->player.comm)&11;
744 		}
745 		else if (key == SDLK_DOWN)
746 		{
747 			sim->player.comm = (int)(sim->player.comm)&7;
748 		}
749 
750 		if (scan == SDL_SCANCODE_D || scan == SDL_SCANCODE_A)
751 		{
752 			sim->player2.pcomm = sim->player2.comm;  //Saving last movement
753 			sim->player2.comm = (int)(sim->player2.comm)&12;  //Stop command
754 		}
755 		else if (scan == SDL_SCANCODE_W)
756 		{
757 			sim->player2.comm = (int)(sim->player2.comm)&11;
758 		}
759 		else if (scan == SDL_SCANCODE_S)
760 		{
761 			sim->player2.comm = (int)(sim->player2.comm)&7;
762 		}
763 	}
764 	return ret;
765 }
766 
Tick()767 void GameController::Tick()
768 {
769 	if(firstTick)
770 	{
771 #ifdef LUACONSOLE
772 		((LuaScriptInterface*)commandInterface)->Init();
773 #endif
774 #if !defined(MACOSX) && !defined(NO_INSTALL_CHECK)
775 		if (Client::Ref().IsFirstRun())
776 		{
777 			Install();
778 		}
779 #endif
780 		firstTick = false;
781 	}
782 	for(std::vector<DebugInfo*>::iterator iter = debugInfo.begin(), end = debugInfo.end(); iter != end; iter++)
783 	{
784 		if ((*iter)->debugID & debugFlags)
785 			(*iter)->Draw();
786 	}
787 	commandInterface->OnTick();
788 }
789 
Blur()790 void GameController::Blur()
791 {
792 	// Tell lua that mouse is up (even if it really isn't)
793 	MouseUp(0, 0, 0, 1);
794 	BlurEvent ev;
795 	commandInterface->HandleEvent(LuaEvents::blur, &ev);
796 }
797 
Exit()798 void GameController::Exit()
799 {
800 	CloseEvent ev;
801 	commandInterface->HandleEvent(LuaEvents::close, &ev);
802 	gameView->CloseActiveWindow();
803 	HasDone = true;
804 }
805 
ResetAir()806 void GameController::ResetAir()
807 {
808 	Simulation * sim = gameModel->GetSimulation();
809 	sim->air->Clear();
810 	for (int i = 0; i < NPART; i++)
811 	{
812 		if (sim->parts[i].type == PT_QRTZ || sim->parts[i].type == PT_GLAS || sim->parts[i].type == PT_TUNG)
813 		{
814 			sim->parts[i].pavg[0] = sim->parts[i].pavg[1] = 0;
815 		}
816 	}
817 }
818 
ResetSpark()819 void GameController::ResetSpark()
820 {
821 	Simulation * sim = gameModel->GetSimulation();
822 	for (int i = 0; i < NPART; i++)
823 		if (sim->parts[i].type == PT_SPRK)
824 		{
825 			if (sim->parts[i].ctype >= 0 && sim->parts[i].ctype < PT_NUM && sim->elements[sim->parts[i].ctype].Enabled)
826 			{
827 				sim->parts[i].type = sim->parts[i].ctype;
828 				sim->parts[i].ctype = sim->parts[i].life = 0;
829 			}
830 			else
831 				sim->kill_part(i);
832 		}
833 	memset(sim->wireless, 0, sizeof(sim->wireless));
834 }
835 
SwitchGravity()836 void GameController::SwitchGravity()
837 {
838 	gameModel->GetSimulation()->gravityMode = (gameModel->GetSimulation()->gravityMode+1)%3;
839 
840 	switch (gameModel->GetSimulation()->gravityMode)
841 	{
842 	case 0:
843 		gameModel->SetInfoTip("Gravity: Vertical");
844 		break;
845 	case 1:
846 		gameModel->SetInfoTip("Gravity: Off");
847 		break;
848 	case 2:
849 		gameModel->SetInfoTip("Gravity: Radial");
850 		break;
851 	}
852 }
853 
SwitchAir()854 void GameController::SwitchAir()
855 {
856 	gameModel->GetSimulation()->air->airMode = (gameModel->GetSimulation()->air->airMode+1)%5;
857 
858 	switch (gameModel->GetSimulation()->air->airMode)
859 	{
860 	case 0:
861 		gameModel->SetInfoTip("Air: On");
862 		break;
863 	case 1:
864 		gameModel->SetInfoTip("Air: Pressure Off");
865 		break;
866 	case 2:
867 		gameModel->SetInfoTip("Air: Velocity Off");
868 		break;
869 	case 3:
870 		gameModel->SetInfoTip("Air: Off");
871 		break;
872 	case 4:
873 		gameModel->SetInfoTip("Air: No Update");
874 		break;
875 	}
876 }
877 
ToggleAHeat()878 void GameController::ToggleAHeat()
879 {
880 	gameModel->SetAHeatEnable(!gameModel->GetAHeatEnable());
881 }
882 
GetAHeatEnable()883 bool GameController::GetAHeatEnable()
884 {
885 	return gameModel->GetAHeatEnable();
886 }
887 
ToggleNewtonianGravity()888 void GameController::ToggleNewtonianGravity()
889 {
890 	gameModel->SetNewtonianGravity(!gameModel->GetNewtonianGrvity());
891 }
892 
LoadRenderPreset(int presetNum)893 void GameController::LoadRenderPreset(int presetNum)
894 {
895 	Renderer * renderer = gameModel->GetRenderer();
896 	RenderPreset preset = renderer->renderModePresets[presetNum];
897 	gameModel->SetInfoTip(preset.Name);
898 	renderer->SetRenderMode(preset.RenderModes);
899 	renderer->SetDisplayMode(preset.DisplayModes);
900 	renderer->SetColourMode(preset.ColourMode);
901 }
902 
Update()903 void GameController::Update()
904 {
905 	ui::Point pos = gameView->GetMousePosition();
906 	gameModel->GetRenderer()->mousePos = PointTranslate(pos);
907 	if (pos.X < XRES && pos.Y < YRES)
908 		gameView->SetSample(gameModel->GetSimulation()->GetSample(PointTranslate(pos).X, PointTranslate(pos).Y));
909 	else
910 		gameView->SetSample(gameModel->GetSimulation()->GetSample(pos.X, pos.Y));
911 
912 	Simulation * sim = gameModel->GetSimulation();
913 	sim->BeforeSim();
914 	if (!sim->sys_pause || sim->framerender)
915 	{
916 		sim->UpdateParticles(0, NPART);
917 		sim->AfterSim();
918 	}
919 
920 	//if either STKM or STK2 isn't out, reset it's selected element. Defaults to PT_DUST unless right selected is something else
921 	//This won't run if the stickmen dies in a frame, since it respawns instantly
922 	if (!sim->player.spwn || !sim->player2.spwn)
923 	{
924 		int rightSelected = PT_DUST;
925 		Tool * activeTool = gameModel->GetActiveTool(1);
926 		if (activeTool->GetIdentifier().BeginsWith("DEFAULT_PT_"))
927 		{
928 			int sr = activeTool->GetToolID();
929 			if (sr && sim->IsValidElement(sr))
930 				rightSelected = sr;
931 		}
932 
933 		void Element_STKM_set_element(Simulation *sim, playerst *playerp, int element);
934 		if (!sim->player.spwn)
935 			Element_STKM_set_element(sim, &sim->player, rightSelected);
936 		if (!sim->player2.spwn)
937 			Element_STKM_set_element(sim, &sim->player2, rightSelected);
938 	}
939 	if(renderOptions && renderOptions->HasExited)
940 	{
941 		delete renderOptions;
942 		renderOptions = NULL;
943 	}
944 
945 	if(search && search->HasExited)
946 	{
947 		delete search;
948 		search = NULL;
949 	}
950 
951 	if(activePreview && activePreview->HasExited)
952 	{
953 		delete activePreview;
954 		activePreview = NULL;
955 	}
956 
957 	if(loginWindow && loginWindow->HasExited)
958 	{
959 		delete loginWindow;
960 		loginWindow = NULL;
961 	}
962 
963 	if(localBrowser && localBrowser->HasDone)
964 	{
965 		delete localBrowser;
966 		localBrowser = NULL;
967 	}
968 }
969 
SetZoomEnabled(bool zoomEnabled)970 void GameController::SetZoomEnabled(bool zoomEnabled)
971 {
972 	gameModel->SetZoomEnabled(zoomEnabled);
973 }
974 
SetToolStrength(float value)975 void GameController::SetToolStrength(float value)
976 {
977 	gameModel->SetToolStrength(value);
978 }
979 
SetZoomPosition(ui::Point position)980 void GameController::SetZoomPosition(ui::Point position)
981 {
982 	ui::Point zoomPosition = position-(gameModel->GetZoomSize()/2);
983 	if(zoomPosition.X < 0)
984 			zoomPosition.X = 0;
985 	if(zoomPosition.Y < 0)
986 			zoomPosition.Y = 0;
987 	if(zoomPosition.X >= XRES-gameModel->GetZoomSize())
988 			zoomPosition.X = XRES-gameModel->GetZoomSize();
989 	if(zoomPosition.Y >= YRES-gameModel->GetZoomSize())
990 			zoomPosition.Y = YRES-gameModel->GetZoomSize();
991 
992 	ui::Point zoomWindowPosition = ui::Point(0, 0);
993 	if(position.X < XRES/2)
994 		zoomWindowPosition.X = XRES-(gameModel->GetZoomSize()*gameModel->GetZoomFactor());
995 
996 	gameModel->SetZoomPosition(zoomPosition);
997 	gameModel->SetZoomWindowPosition(zoomWindowPosition);
998 }
999 
SetPaused(bool pauseState)1000 void GameController::SetPaused(bool pauseState)
1001 {
1002 	gameModel->SetPaused(pauseState);
1003 }
1004 
SetPaused()1005 void GameController::SetPaused()
1006 {
1007 	gameModel->SetPaused(!gameModel->GetPaused());
1008 }
1009 
SetDecoration(bool decorationState)1010 void GameController::SetDecoration(bool decorationState)
1011 {
1012 	gameModel->SetDecoration(decorationState);
1013 }
1014 
SetDecoration()1015 void GameController::SetDecoration()
1016 {
1017 	gameModel->SetDecoration(!gameModel->GetDecoration());
1018 }
1019 
ShowGravityGrid()1020 void GameController::ShowGravityGrid()
1021 {
1022 	gameModel->ShowGravityGrid(!gameModel->GetGravityGrid());
1023 	gameModel->UpdateQuickOptions();
1024 }
1025 
SetHudEnable(bool hudState)1026 void GameController::SetHudEnable(bool hudState)
1027 {
1028 	gameView->SetHudEnable(hudState);
1029 }
1030 
GetHudEnable()1031 bool GameController::GetHudEnable()
1032 {
1033 	return gameView->GetHudEnable();
1034 }
1035 
SetDebugHUD(bool hudState)1036 void GameController::SetDebugHUD(bool hudState)
1037 {
1038 	gameView->SetDebugHUD(hudState);
1039 }
1040 
GetDebugHUD()1041 bool GameController::GetDebugHUD()
1042 {
1043 	return gameView->GetDebugHUD();
1044 }
1045 
SetActiveColourPreset(int preset)1046 void GameController::SetActiveColourPreset(int preset)
1047 {
1048 	gameModel->SetActiveColourPreset(preset);
1049 }
1050 
SetColour(ui::Colour colour)1051 void GameController::SetColour(ui::Colour colour)
1052 {
1053 	gameModel->SetColourSelectorColour(colour);
1054 	gameModel->SetPresetColour(colour);
1055 }
1056 
SetActiveMenu(int menuID)1057 void GameController::SetActiveMenu(int menuID)
1058 {
1059 	gameModel->SetActiveMenu(menuID);
1060 	if(menuID == SC_DECO)
1061 		gameModel->SetColourSelectorVisibility(true);
1062 	else
1063 		gameModel->SetColourSelectorVisibility(false);
1064 }
1065 
GetMenuList()1066 std::vector<Menu*> GameController::GetMenuList()
1067 {
1068 	return gameModel->GetMenuList();
1069 }
1070 
GetNumMenus(bool onlyEnabled)1071 int GameController::GetNumMenus(bool onlyEnabled)
1072 {
1073 	int count = 0;
1074 	if (onlyEnabled)
1075 	{
1076 		std::vector<Menu*> menuList = gameModel->GetMenuList();
1077 		for (std::vector<Menu*>::iterator it = menuList.begin(), end = menuList.end(); it != end; ++it)
1078 			if ((*it)->GetVisible())
1079 				count++;
1080 	}
1081 	else
1082 		count = gameModel->GetMenuList().size();
1083 	return count;
1084 }
1085 
RebuildFavoritesMenu()1086 void GameController::RebuildFavoritesMenu()
1087 {
1088 	gameModel->BuildFavoritesMenu();
1089 }
1090 
GetActiveTool(int selection)1091 Tool * GameController::GetActiveTool(int selection)
1092 {
1093 	return gameModel->GetActiveTool(selection);
1094 }
1095 
SetActiveTool(int toolSelection,Tool * tool)1096 void GameController::SetActiveTool(int toolSelection, Tool * tool)
1097 {
1098 	if (gameModel->GetActiveMenu() == SC_DECO && toolSelection == 2)
1099 		toolSelection = 0;
1100 	gameModel->SetActiveTool(toolSelection, tool);
1101 	gameModel->GetRenderer()->gravityZonesEnabled = false;
1102 	if (toolSelection == 3)
1103 		gameModel->GetSimulation()->replaceModeSelected = tool->GetToolID();
1104 	gameModel->SetLastTool(tool);
1105 	for(int i = 0; i < 3; i++)
1106 	{
1107 		if(gameModel->GetActiveTool(i) == gameModel->GetMenuList().at(SC_WALL)->GetToolList().at(WL_GRAV))
1108 			gameModel->GetRenderer()->gravityZonesEnabled = true;
1109 	}
1110 	if(tool->GetIdentifier() == "DEFAULT_UI_PROPERTY")
1111 		((PropertyTool *)tool)->OpenWindow(gameModel->GetSimulation());
1112 }
1113 
SetActiveTool(int toolSelection,ByteString identifier)1114 void GameController::SetActiveTool(int toolSelection, ByteString identifier)
1115 {
1116 	Tool *tool = gameModel->GetToolFromIdentifier(identifier);
1117 	if (!tool)
1118 		return;
1119 	SetActiveTool(toolSelection, tool);
1120 }
1121 
SetLastTool(Tool * tool)1122 void GameController::SetLastTool(Tool * tool)
1123 {
1124 	gameModel->SetLastTool(tool);
1125 }
1126 
GetReplaceModeFlags()1127 int GameController::GetReplaceModeFlags()
1128 {
1129 	return gameModel->GetSimulation()->replaceModeFlags;
1130 }
1131 
SetReplaceModeFlags(int flags)1132 void GameController::SetReplaceModeFlags(int flags)
1133 {
1134 	int old_flags = gameModel->GetSimulation()->replaceModeFlags;
1135 	if (!(old_flags & REPLACE_MODE) && (flags & REPLACE_MODE))
1136 	{
1137 		// if replace mode has just been enabled, disable specific delete
1138 		flags &= ~SPECIFIC_DELETE;
1139 	}
1140 	if (!(old_flags & SPECIFIC_DELETE) && (flags & SPECIFIC_DELETE))
1141 	{
1142 		// if specific delete has just been enabled, disable replace mode
1143 		flags &= ~REPLACE_MODE;
1144 	}
1145 	if ((flags & SPECIFIC_DELETE) && (flags & REPLACE_MODE))
1146 	{
1147 		// if both have just been enabled, arbitrarily disable one of them
1148 		flags &= ~SPECIFIC_DELETE;
1149 	}
1150 	gameModel->GetSimulation()->replaceModeFlags = flags;
1151 }
1152 
OpenSearch(String searchText)1153 void GameController::OpenSearch(String searchText)
1154 {
1155 	if(!search)
1156 		search = new SearchController([this] {
1157 			if (search->GetLoadedSave())
1158 			{
1159 				try
1160 				{
1161 					HistorySnapshot();
1162 					gameModel->SetSave(search->GetLoadedSave(), gameView->ShiftBehaviour());
1163 					search->ReleaseLoadedSave();
1164 				}
1165 				catch(GameModelException & ex)
1166 				{
1167 					new ErrorMessage("Cannot open save", ByteString(ex.what()).FromUtf8());
1168 				}
1169 			}
1170 		});
1171 	if (searchText.length())
1172 		search->DoSearch2(searchText);
1173 	ui::Engine::Ref().ShowWindow(search->GetView());
1174 }
1175 
OpenLocalSaveWindow(bool asCurrent)1176 void GameController::OpenLocalSaveWindow(bool asCurrent)
1177 {
1178 	Simulation * sim = gameModel->GetSimulation();
1179 	GameSave * gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour());
1180 	if(!gameSave)
1181 	{
1182 		new ErrorMessage("Error", "Unable to build save.");
1183 	}
1184 	else
1185 	{
1186 		gameSave->paused = gameModel->GetPaused();
1187 
1188 		SaveFile tempSave("");
1189 		if (gameModel->GetSaveFile())
1190 		{
1191 			tempSave.SetFileName(gameModel->GetSaveFile()->GetName());
1192 			tempSave.SetDisplayName(gameModel->GetSaveFile()->GetDisplayName());
1193 		}
1194 		tempSave.SetGameSave(gameSave);
1195 
1196 		if (!asCurrent || !gameModel->GetSaveFile())
1197 		{
1198 			new LocalSaveActivity(tempSave, [this](SaveFile *file) {
1199 				gameModel->SetSaveFile(file, gameView->ShiftBehaviour());
1200 			});
1201 		}
1202 		else if (gameModel->GetSaveFile())
1203 		{
1204 			Json::Value localSaveInfo;
1205 			localSaveInfo["type"] = "localsave";
1206 			localSaveInfo["username"] = Client::Ref().GetAuthUser().Username;
1207 			localSaveInfo["title"] = gameModel->GetSaveFile()->GetName();
1208 			localSaveInfo["date"] = (Json::Value::UInt64)time(NULL);
1209 			Client::Ref().SaveAuthorInfo(&localSaveInfo);
1210 			gameSave->authors = localSaveInfo;
1211 
1212 			gameModel->SetSaveFile(&tempSave, gameView->ShiftBehaviour());
1213 			Client::Ref().MakeDirectory(LOCAL_SAVE_DIR);
1214 			std::vector<char> saveData = gameSave->Serialise();
1215 			if (saveData.size() == 0)
1216 				new ErrorMessage("Error", "Unable to serialize game data.");
1217 			else if (Client::Ref().WriteFile(saveData, gameModel->GetSaveFile()->GetName()))
1218 				new ErrorMessage("Error", "Unable to write save file.");
1219 			else
1220 				gameModel->SetInfoTip("Saved Successfully");
1221 		}
1222 	}
1223 }
1224 
LoadSaveFile(SaveFile * file)1225 void GameController::LoadSaveFile(SaveFile * file)
1226 {
1227 	gameModel->SetSaveFile(file, gameView->ShiftBehaviour());
1228 }
1229 
1230 
LoadSave(SaveInfo * save)1231 void GameController::LoadSave(SaveInfo * save)
1232 {
1233 	gameModel->SetSave(save, gameView->ShiftBehaviour());
1234 }
1235 
OpenSaveDone()1236 void GameController::OpenSaveDone()
1237 {
1238 	if (activePreview->GetDoOpen() && activePreview->GetSaveInfo())
1239 	{
1240 		try
1241 		{
1242 			HistorySnapshot();
1243 			LoadSave(activePreview->GetSaveInfo());
1244 		}
1245 		catch(GameModelException & ex)
1246 		{
1247 			new ErrorMessage("Cannot open save", ByteString(ex.what()).FromUtf8());
1248 		}
1249 	}
1250 }
1251 
OpenSavePreview(int saveID,int saveDate,bool instant)1252 void GameController::OpenSavePreview(int saveID, int saveDate, bool instant)
1253 {
1254 	activePreview = new PreviewController(saveID, saveDate, instant, [this] { OpenSaveDone(); });
1255 	ui::Engine::Ref().ShowWindow(activePreview->GetView());
1256 }
1257 
OpenSavePreview()1258 void GameController::OpenSavePreview()
1259 {
1260 	if(gameModel->GetSave())
1261 	{
1262 		activePreview = new PreviewController(gameModel->GetSave()->GetID(), 0, false, [this] { OpenSaveDone(); });
1263 		ui::Engine::Ref().ShowWindow(activePreview->GetView());
1264 	}
1265 }
1266 
OpenLocalBrowse()1267 void GameController::OpenLocalBrowse()
1268 {
1269 	new FileBrowserActivity(LOCAL_SAVE_DIR PATH_SEP, [this](std::unique_ptr<SaveFile> file) {
1270 		HistorySnapshot();
1271 		LoadSaveFile(file.get());
1272 	});
1273 }
1274 
OpenLogin()1275 void GameController::OpenLogin()
1276 {
1277 	loginWindow = new LoginController();
1278 	ui::Engine::Ref().ShowWindow(loginWindow->GetView());
1279 }
1280 
OpenProfile()1281 void GameController::OpenProfile()
1282 {
1283 	if(Client::Ref().GetAuthUser().UserID)
1284 	{
1285 		new ProfileActivity(Client::Ref().GetAuthUser().Username);
1286 	}
1287 	else
1288 	{
1289 		loginWindow = new LoginController();
1290 		ui::Engine::Ref().ShowWindow(loginWindow->GetView());
1291 	}
1292 }
1293 
OpenElementSearch()1294 void GameController::OpenElementSearch()
1295 {
1296 	std::vector<Tool*> toolList;
1297 	std::vector<Menu*> menuList = gameModel->GetMenuList();
1298 	for(auto *mm : menuList)
1299 	{
1300 		if(!mm)
1301 			continue;
1302 		std::vector<Tool*> menuToolList = mm->GetToolList();
1303 		if(!menuToolList.size())
1304 			continue;
1305 		toolList.insert(toolList.end(), menuToolList.begin(), menuToolList.end());
1306 	}
1307 	std::vector<Tool*> hiddenTools = gameModel->GetUnlistedTools();
1308 	toolList.insert(toolList.end(), hiddenTools.begin(), hiddenTools.end());
1309 	new ElementSearchActivity(this, toolList);
1310 }
1311 
OpenColourPicker()1312 void GameController::OpenColourPicker()
1313 {
1314 	new ColourPickerActivity(gameModel->GetColourSelectorColour(), [this](ui::Colour colour) {
1315 		SetColour(colour);
1316 	});
1317 }
1318 
OpenTags()1319 void GameController::OpenTags()
1320 {
1321 	if(gameModel->GetSave() && gameModel->GetSave()->GetID())
1322 	{
1323 		delete tagsWindow;
1324 		tagsWindow = new TagsController([this] { gameView->NotifySaveChanged(gameModel); }, gameModel->GetSave());
1325 		ui::Engine::Ref().ShowWindow(tagsWindow->GetView());
1326 	}
1327 	else
1328 	{
1329 		new ErrorMessage("Error", "No save open");
1330 	}
1331 }
1332 
OpenStamps()1333 void GameController::OpenStamps()
1334 {
1335 	localBrowser = new LocalBrowserController([this] {
1336 		SaveFile *file = localBrowser->GetSave();
1337 		if (file)
1338 		{
1339 			if (file->GetError().length())
1340 				new ErrorMessage("Error loading stamp", file->GetError());
1341 			else if (localBrowser->GetMoveToFront())
1342 				Client::Ref().MoveStampToFront(file->GetDisplayName().ToUtf8());
1343 			LoadStamp(file->GetGameSave());
1344 		}
1345 	});
1346 	ui::Engine::Ref().ShowWindow(localBrowser->GetView());
1347 }
1348 
OpenOptions()1349 void GameController::OpenOptions()
1350 {
1351 	options = new OptionsController(gameModel, [this] {
1352 		gameModel->UpdateQuickOptions();
1353 		Client::Ref().WritePrefs();
1354 	});
1355 	ui::Engine::Ref().ShowWindow(options->GetView());
1356 
1357 }
1358 
ShowConsole()1359 void GameController::ShowConsole()
1360 {
1361 	if (!console)
1362 		console = new ConsoleController(NULL, commandInterface);
1363 	if (console->GetView() != ui::Engine::Ref().GetWindow())
1364 		ui::Engine::Ref().ShowWindow(console->GetView());
1365 }
1366 
HideConsole()1367 void GameController::HideConsole()
1368 {
1369 	if (!console)
1370 		return;
1371 	console->GetView()->CloseActiveWindow();
1372 }
1373 
OpenRenderOptions()1374 void GameController::OpenRenderOptions()
1375 {
1376 	renderOptions = new RenderController(gameModel->GetRenderer(), NULL);
1377 	ui::Engine::Ref().ShowWindow(renderOptions->GetView());
1378 }
1379 
OpenSaveWindow()1380 void GameController::OpenSaveWindow()
1381 {
1382 	if(gameModel->GetUser().UserID)
1383 	{
1384 		Simulation * sim = gameModel->GetSimulation();
1385 		GameSave * gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour());
1386 		if(!gameSave)
1387 		{
1388 			new ErrorMessage("Error", "Unable to build save.");
1389 		}
1390 		else
1391 		{
1392 			gameSave->paused = gameModel->GetPaused();
1393 
1394 			if(gameModel->GetSave())
1395 			{
1396 				SaveInfo tempSave(*gameModel->GetSave());
1397 				tempSave.SetGameSave(gameSave);
1398 				new ServerSaveActivity(tempSave, [this](SaveInfo &save) {
1399 					save.SetVote(1);
1400 					save.SetVotesUp(1);
1401 					LoadSave(&save);
1402 				});
1403 			}
1404 			else
1405 			{
1406 				SaveInfo tempSave(0, 0, 0, 0, 0, gameModel->GetUser().Username, "");
1407 				tempSave.SetGameSave(gameSave);
1408 				new ServerSaveActivity(tempSave, [this](SaveInfo &save) {
1409 					save.SetVote(1);
1410 					save.SetVotesUp(1);
1411 					LoadSave(&save);
1412 				});
1413 			}
1414 		}
1415 	}
1416 	else
1417 	{
1418 		new ErrorMessage("Error", "You need to login to upload saves.");
1419 	}
1420 }
1421 
SaveAsCurrent()1422 void GameController::SaveAsCurrent()
1423 {
1424 	if(gameModel->GetSave() && gameModel->GetUser().UserID && gameModel->GetUser().Username == gameModel->GetSave()->GetUserName())
1425 	{
1426 		Simulation * sim = gameModel->GetSimulation();
1427 		GameSave * gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour());
1428 		if(!gameSave)
1429 		{
1430 			new ErrorMessage("Error", "Unable to build save.");
1431 		}
1432 		else
1433 		{
1434 			gameSave->paused = gameModel->GetPaused();
1435 
1436 			if(gameModel->GetSave())
1437 			{
1438 				SaveInfo tempSave(*gameModel->GetSave());
1439 				tempSave.SetGameSave(gameSave);
1440 				new ServerSaveActivity(tempSave, true, [this](SaveInfo &save) { LoadSave(&save); });
1441 			}
1442 			else
1443 			{
1444 				SaveInfo tempSave(0, 0, 0, 0, 0, gameModel->GetUser().Username, "");
1445 				tempSave.SetGameSave(gameSave);
1446 				new ServerSaveActivity(tempSave, true, [this](SaveInfo &save) { LoadSave(&save); });
1447 			}
1448 		}
1449 	}
1450 	else if(gameModel->GetUser().UserID)
1451 	{
1452 		OpenSaveWindow();
1453 	}
1454 	else
1455 	{
1456 		new ErrorMessage("Error", "You need to login to upload saves.");
1457 	}
1458 }
1459 
FrameStep()1460 void GameController::FrameStep()
1461 {
1462 	gameModel->FrameStep(1);
1463 	gameModel->SetPaused(true);
1464 }
1465 
Vote(int direction)1466 void GameController::Vote(int direction)
1467 {
1468 	if(gameModel->GetSave() && gameModel->GetUser().UserID && gameModel->GetSave()->GetID() && gameModel->GetSave()->GetVote()==0)
1469 	{
1470 		try
1471 		{
1472 			gameModel->SetVote(direction);
1473 		}
1474 		catch(GameModelException & ex)
1475 		{
1476 			new ErrorMessage("Error while voting", ByteString(ex.what()).FromUtf8());
1477 		}
1478 	}
1479 }
1480 
ChangeBrush()1481 void GameController::ChangeBrush()
1482 {
1483 	auto prev_size = gameModel->GetBrush()->GetRadius();
1484 	gameModel->SetBrushID(gameModel->GetBrushID()+1);
1485 	gameModel->GetBrush()->SetRadius(prev_size);
1486 }
1487 
ClearSim()1488 void GameController::ClearSim()
1489 {
1490 	HistorySnapshot();
1491 	gameModel->SetSave(NULL, false);
1492 	gameModel->ClearSimulation();
1493 }
1494 
ElementResolve(int type,int ctype)1495 String GameController::ElementResolve(int type, int ctype)
1496 {
1497 	if (gameModel && gameModel->GetSimulation())
1498 	{
1499 		return gameModel->GetSimulation()->ElementResolve(type, ctype);
1500 	}
1501 	return "";
1502 }
1503 
BasicParticleInfo(Particle const & sample_part)1504 String GameController::BasicParticleInfo(Particle const &sample_part)
1505 {
1506 	if (gameModel && gameModel->GetSimulation())
1507 	{
1508 		return gameModel->GetSimulation()->BasicParticleInfo(sample_part);
1509 	}
1510 	return "";
1511 }
1512 
ReloadSim()1513 void GameController::ReloadSim()
1514 {
1515 	if(gameModel->GetSave() && gameModel->GetSave()->GetGameSave())
1516 	{
1517 		HistorySnapshot();
1518 		gameModel->SetSave(gameModel->GetSave(), gameView->ShiftBehaviour());
1519 	}
1520 	else if(gameModel->GetSaveFile() && gameModel->GetSaveFile()->GetGameSave())
1521 	{
1522 		HistorySnapshot();
1523 		gameModel->SetSaveFile(gameModel->GetSaveFile(), gameView->ShiftBehaviour());
1524 	}
1525 }
1526 
IsValidElement(int type)1527 bool GameController::IsValidElement(int type)
1528 {
1529 	if(gameModel && gameModel->GetSimulation())
1530 	{
1531 		return (type && gameModel->GetSimulation()->IsValidElement(type));
1532 	}
1533 	else
1534 		return false;
1535 }
1536 
WallName(int type)1537 String GameController::WallName(int type)
1538 {
1539 	if(gameModel && gameModel->GetSimulation() && type >= 0 && type < UI_WALLCOUNT)
1540 		return gameModel->GetSimulation()->wtypes[type].name;
1541 	else
1542 		return String();
1543 }
1544 
Record(bool record)1545 int GameController::Record(bool record)
1546 {
1547 	return gameView->Record(record);
1548 }
1549 
NotifyAuthUserChanged(Client * sender)1550 void GameController::NotifyAuthUserChanged(Client * sender)
1551 {
1552 	User newUser = sender->GetAuthUser();
1553 	gameModel->SetUser(newUser);
1554 }
1555 
NotifyNewNotification(Client * sender,std::pair<String,ByteString> notification)1556 void GameController::NotifyNewNotification(Client * sender, std::pair<String, ByteString> notification)
1557 {
1558 	class LinkNotification : public Notification
1559 	{
1560 		ByteString link;
1561 	public:
1562 		LinkNotification(ByteString link_, String message) : Notification(message), link(link_) {}
1563 		virtual ~LinkNotification() {}
1564 
1565 		void Action() override
1566 		{
1567 			Platform::OpenURI(link);
1568 		}
1569 	};
1570 	gameModel->AddNotification(new LinkNotification(notification.second, notification.first));
1571 }
1572 
NotifyUpdateAvailable(Client * sender)1573 void GameController::NotifyUpdateAvailable(Client * sender)
1574 {
1575 	class UpdateNotification : public Notification
1576 	{
1577 		GameController * c;
1578 	public:
1579 		UpdateNotification(GameController * c, String message) : Notification(message), c(c) {}
1580 		virtual ~UpdateNotification() {}
1581 
1582 		void Action() override
1583 		{
1584 			UpdateInfo info = Client::Ref().GetUpdateInfo();
1585 			StringBuilder updateMessage;
1586 #ifndef MACOSX
1587 			updateMessage << "Are you sure you want to run the updater? Please save any changes before updating.\n\nCurrent version:\n ";
1588 #else
1589 			updateMessage << "Click \"Continue\" to download the latest version from our website.\n\nCurrent version:\n ";
1590 #endif
1591 
1592 #ifdef SNAPSHOT
1593 			updateMessage << "Snapshot " << SNAPSHOT_ID;
1594 #elif MOD_ID > 0
1595 			updateMessage << "Mod version " << SNAPSHOT_ID;
1596 #elif defined(BETA)
1597 			updateMessage << SAVE_VERSION << "." << MINOR_VERSION << " Beta, Build " << BUILD_NUM;
1598 #else
1599 			updateMessage << SAVE_VERSION << "." << MINOR_VERSION << " Stable, Build " << BUILD_NUM;
1600 #endif
1601 
1602 			updateMessage << "\nNew version:\n ";
1603 			if (info.Type == UpdateInfo::Beta)
1604 				updateMessage << info.Major << "." << info.Minor << " Beta, Build " << info.Build;
1605 			else if (info.Type == UpdateInfo::Snapshot)
1606 #if MOD_ID > 0
1607 				updateMessage << "Mod version " << info.Time;
1608 #else
1609 				updateMessage << "Snapshot " << info.Time;
1610 #endif
1611 			else if(info.Type == UpdateInfo::Stable)
1612 				updateMessage << info.Major << "." << info.Minor << " Stable, Build " << info.Build;
1613 
1614 			if (info.Changelog.length())
1615 				updateMessage << "\n\nChangelog:\n" << info.Changelog;
1616 
1617 			new ConfirmPrompt("Run Updater", updateMessage.Build(), { [this] { c->RunUpdater(); } });
1618 		}
1619 	};
1620 
1621 	switch(sender->GetUpdateInfo().Type)
1622 	{
1623 		case UpdateInfo::Snapshot:
1624 #if MOD_ID > 0
1625 			gameModel->AddNotification(new UpdateNotification(this, "A new mod update is available - click here to update"));
1626 #else
1627 			gameModel->AddNotification(new UpdateNotification(this, "A new snapshot is available - click here to update"));
1628 #endif
1629 			break;
1630 		case UpdateInfo::Stable:
1631 			gameModel->AddNotification(new UpdateNotification(this, "A new version is available - click here to update"));
1632 			break;
1633 		case UpdateInfo::Beta:
1634 			gameModel->AddNotification(new UpdateNotification(this, "A new beta is available - click here to update"));
1635 			break;
1636 	}
1637 }
1638 
RemoveNotification(Notification * notification)1639 void GameController::RemoveNotification(Notification * notification)
1640 {
1641 	gameModel->RemoveNotification(notification);
1642 }
1643 
RunUpdater()1644 void GameController::RunUpdater()
1645 {
1646 #ifndef MACOSX
1647 	Exit();
1648 	new UpdateActivity();
1649 #else
1650 
1651 #ifdef UPDATESERVER
1652 	ByteString file = ByteString::Build(SCHEME, UPDATESERVER, Client::Ref().GetUpdateInfo().File);
1653 #else
1654 	ByteString file = ByteString::Build(SCHEME, SERVER, Client::Ref().GetUpdateInfo().File);
1655 #endif
1656 
1657 	Platform::OpenURI(file);
1658 #endif // MACOSX
1659 }
1660 
GetMouseClickRequired()1661 bool GameController::GetMouseClickRequired()
1662 {
1663 	return gameModel->GetMouseClickRequired();
1664 }
1665