1 //       _________ __                 __
2 //      /   _____//  |_____________ _/  |______     ____  __ __  ______
3 //      \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
4 //      /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
5 //     /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
6 //             \/                  \/          \//_____/            \/
7 //  ______________________                           ______________________
8 //                        T H E   W A R   B E G I N S
9 //         Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name mainloop.cpp - The main game loop. */
12 //
13 //      (c) Copyright 1998-2019 by Lutz Sammer, Jimmy Salmon and Andrettin
14 //
15 //      This program is free software; you can redistribute it and/or modify
16 //      it under the terms of the GNU General Public License as published by
17 //      the Free Software Foundation; only version 2 of the License.
18 //
19 //      This program is distributed in the hope that it will be useful,
20 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //      GNU General Public License for more details.
23 //
24 //      You should have received a copy of the GNU General Public License
25 //      along with this program; if not, write to the Free Software
26 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 //      02111-1307, USA.
28 //
29 
30 //@{
31 
32 //----------------------------------------------------------------------------
33 //  Includes
34 //----------------------------------------------------------------------------
35 
36 #include "stratagus.h"
37 
38 #include "actions.h"
39 #include "character.h"
40 #include "civilization.h"
41 #include "commands.h"
42 #include "dialogue.h"
43 #include "editor.h"
44 //Wyrmgus start
45 #include "font.h"
46 //Wyrmgus end
47 #include "game.h"
48 //Wyrmgus start
49 #include "grand_strategy.h"
50 #include "luacallback.h"
51 //Wyrmgus end
52 #include "map/map.h"
53 #include "map/map_layer.h"
54 #include "map/terrain_type.h"
55 #include "map/tileset.h" //for tile animation
56 #include "missile.h"
57 #include "network.h"
58 #include "particle.h"
59 #include "plane.h"
60 //Wyrmgus start
61 #include "quest.h"
62 //Wyrmgus end
63 #include "replay.h"
64 #include "results.h"
65 //Wyrmgus start
66 #include "settings.h"
67 //Wyrmgus end
68 #include "sound.h"
69 #include "sound_server.h"
70 #include "time/calendar.h"
71 #include "time/time_of_day.h"
72 #include "translate.h"
73 #include "trigger.h"
74 #include "ui/interface.h"
75 #include "ui/ui.h"
76 #include "unit/unit.h"
77 //Wyrmgus start
78 #include "unit/unit_manager.h"
79 #include "upgrade/upgrade.h"
80 //Wyrmgus end
81 #include "video.h"
82 #include "world.h"
83 
84 #include <guichan.h>
85 
86 #ifdef USE_OAML
87 #include <oaml.h>
88 
89 extern oamlApi *oaml;
90 extern bool enableOAML;
91 #endif
92 
93 void DrawGuichanWidgets();
94 
95 //----------------------------------------------------------------------------
96 // Variables
97 //----------------------------------------------------------------------------
98 
99 /// variable set when we are scrolling via keyboard
100 int KeyScrollState = ScrollNone;
101 
102 /// variable set when we are scrolling via mouse
103 int MouseScrollState = ScrollNone;
104 
105 EventCallback GameCallbacks;   /// Game callbacks
106 EventCallback EditorCallbacks; /// Editor callbacks
107 
108 //----------------------------------------------------------------------------
109 // Functions
110 //----------------------------------------------------------------------------
111 
112 /**
113 **  Handle scrolling area.
114 **
115 **  @param state  Scroll direction/state.
116 **  @param fast   Flag scroll faster.
117 **
118 **  @todo  Support dynamic acceleration of scroll speed.
119 **  @todo  If the scroll key is longer pressed the area is scrolled faster.
120 */
DoScrollArea(int state,bool fast,bool isKeyboard)121 void DoScrollArea(int state, bool fast, bool isKeyboard)
122 {
123 	CViewport *vp;
124 	int stepx;
125 	int stepy;
126 	static int remx = 0; // FIXME: docu
127 	static int remy = 0; // FIXME: docu
128 
129 	int speed = isKeyboard ? UI.KeyScrollSpeed : UI.MouseScrollSpeed;
130 
131 	if (state == ScrollNone) {
132 		return;
133 	}
134 
135 	vp = UI.SelectedViewport;
136 
137 	if (fast) {
138 		//Wyrmgus start
139 //		stepx = (int)(speed * vp->MapWidth / 2 * Map.GetCurrentPixelTileSize().x * FRAMES_PER_SECOND / 4);
140 //		stepy = (int)(speed * vp->MapHeight / 2 * Map.GetCurrentPixelTileSize().y * FRAMES_PER_SECOND / 4);
141 		stepx = (int)(speed * Map.GetCurrentPixelTileSize().x * FRAMES_PER_SECOND / 4 * 4);
142 		stepy = (int)(speed * Map.GetCurrentPixelTileSize().y * FRAMES_PER_SECOND / 4 * 4);
143 		//Wyrmgus end
144 	} else {// dynamic: let these variables increase up to fast..
145 		// FIXME: pixels per second should be configurable
146 		stepx = (int)(speed * Map.GetCurrentPixelTileSize().x * FRAMES_PER_SECOND / 4);
147 		stepy = (int)(speed * Map.GetCurrentPixelTileSize().y * FRAMES_PER_SECOND / 4);
148 	}
149 	if ((state & (ScrollLeft | ScrollRight)) && (state & (ScrollLeft | ScrollRight)) != (ScrollLeft | ScrollRight)) {
150 		stepx = stepx * 100 * 100 / VideoSyncSpeed / FRAMES_PER_SECOND / (SkipFrames + 1);
151 		remx += stepx - (stepx / 100) * 100;
152 		stepx /= 100;
153 		if (remx > 100) {
154 			++stepx;
155 			remx -= 100;
156 		}
157 	} else {
158 		stepx = 0;
159 	}
160 	if ((state & (ScrollUp | ScrollDown)) && (state & (ScrollUp | ScrollDown)) != (ScrollUp | ScrollDown)) {
161 		stepy = stepy * 100 * 100 / VideoSyncSpeed / FRAMES_PER_SECOND / (SkipFrames + 1);
162 		remy += stepy - (stepy / 100) * 100;
163 		stepy /= 100;
164 		if (remy > 100) {
165 			++stepy;
166 			remy -= 100;
167 		}
168 	} else {
169 		stepy = 0;
170 	}
171 
172 	if (state & ScrollUp) {
173 		stepy = -stepy;
174 	}
175 	if (state & ScrollLeft) {
176 		stepx = -stepx;
177 	}
178 	const PixelDiff offset(stepx, stepy);
179 
180 	vp->Set(vp->MapPos, vp->Offset + offset);
181 
182 	// This recalulates some values
183 	HandleMouseMove(CursorScreenPos);
184 }
185 
186 /**
187 **  Draw map area
188 */
DrawMapArea()189 void DrawMapArea()
190 {
191 	// Draw all of the viewports
192 	for (CViewport *vp = UI.Viewports; vp < UI.Viewports + UI.NumViewports; ++vp) {
193 		// Center viewport on tracked unit
194 		if (vp->Unit) {
195 			if (vp->Unit->Destroyed || vp->Unit->CurrentAction() == UnitActionDie) {
196 				vp->Unit = nullptr;
197 			} else {
198 				if (UI.CurrentMapLayer != vp->Unit->MapLayer) {
199 					ChangeCurrentMapLayer(vp->Unit->MapLayer->ID);
200 				}
201 				vp->Center(vp->Unit->GetMapPixelPosCenter());
202 			}
203 		}
204 		vp->Draw();
205 	}
206 }
207 
208 /**
209 **  Display update.
210 **
211 **  This functions updates everything on screen. The map, the gui, the
212 **  cursors.
213 */
UpdateDisplay()214 void UpdateDisplay()
215 {
216 	if (GameRunning || Editor.Running == EditorEditing) {
217 		// to prevent empty spaces in the UI
218 #if defined(USE_OPENGL) || defined(USE_GLES)
219 		Video.FillRectangleClip(ColorBlack, 0, 0, Video.ViewportWidth, Video.ViewportHeight);
220 #else
221 		Video.FillRectangleClip(ColorBlack, 0, 0, Video.Width, Video.Height);
222 #endif
223 		DrawMapArea();
224 		DrawMessages();
225 
226 		if (CursorState == CursorStateRectangle) {
227 			DrawCursor();
228 		}
229 
230 		//Wyrmgus start
231 		if (CursorBuilding && CursorOn == CursorOnMap) {
232 			DrawBuildingCursor();
233 		}
234 		//Wyrmgus end
235 
236 		if ((Preference.BigScreen && !BigMapMode) || (!Preference.BigScreen && BigMapMode)) {
237 			UiToggleBigMap();
238 		}
239 
240 		if (!BigMapMode) {
241 			for (size_t i = 0; i < UI.Fillers.size(); ++i) {
242 				UI.Fillers[i].G->DrawSubClip(0, 0,
243 											 UI.Fillers[i].G->Width,
244 											 UI.Fillers[i].G->Height,
245 											 UI.Fillers[i].X, UI.Fillers[i].Y);
246 			}
247 			DrawMenuButtonArea();
248 			DrawUserDefinedButtons();
249 
250 			UI.Minimap.Draw();
251 			UI.Minimap.DrawViewportArea(*UI.SelectedViewport);
252 
253 			UI.InfoPanel.Draw();
254 			DrawResources();
255 			DrawTime();
256 			DrawAge();
257 			DrawMapLayerButtons();
258 			UI.StatusLine.Draw();
259 			UI.StatusLine.DrawCosts();
260 			UI.ButtonPanel.Draw();
261 		}
262 
263 		DrawTimer();
264 
265 		//Wyrmgus start
266 		//draw worker icon if there are idle workers
267 		if (UI.IdleWorkerButton && !ThisPlayer->FreeWorkers.empty()) {
268 			const PixelPos pos(UI.IdleWorkerButton->X, UI.IdleWorkerButton->Y);
269 			const int flag = (ButtonAreaUnderCursor == ButtonAreaIdleWorker && ButtonUnderCursor == 0) ? (IconActive | (MouseButtons & LeftButton)) : 0;
270 
271 			ThisPlayer->FreeWorkers[0]->GetIcon().Icon->DrawUnitIcon(*UI.IdleWorkerButton->Style, flag, pos, ".", ThisPlayer->Index);
272 		}
273 
274 		//draw icon if there are units with available level up upgrades
275 		if (UI.LevelUpUnitButton && !ThisPlayer->LevelUpUnits.empty()) {
276 			const PixelPos pos(UI.LevelUpUnitButton->X, UI.LevelUpUnitButton->Y);
277 			const int flag = (ButtonAreaUnderCursor == ButtonAreaLevelUpUnit && ButtonUnderCursor == 0) ? (IconActive | (MouseButtons & LeftButton)) : 0;
278 
279 			ThisPlayer->LevelUpUnits[0]->GetIcon().Icon->DrawUnitIcon(*UI.LevelUpUnitButton->Style, flag, pos, "", ThisPlayer->Index);
280 		}
281 
282 		//draw icon if the player has a hero
283 		for (size_t i = 0; i < UI.HeroUnitButtons.size() && i < ThisPlayer->Heroes.size(); ++i) {
284 			const PixelPos pos(UI.HeroUnitButtons[i].X, UI.HeroUnitButtons[i].Y);
285 			const int flag = (ButtonAreaUnderCursor == ButtonAreaHeroUnit && ButtonUnderCursor == i) ? (IconActive | (MouseButtons & LeftButton)) : 0;
286 
287 			ThisPlayer->Heroes[i]->GetIcon().Icon->DrawUnitIcon(*UI.HeroUnitButtons[i].Style, flag, pos, "", ThisPlayer->Index);
288 		}
289 
290 		DrawPopups();
291 		//Wyrmgus end
292 	}
293 
294 	DrawPieMenu(); // draw pie menu only if needed
295 
296 	DrawGuichanWidgets();
297 
298 	if (CursorState != CursorStateRectangle) {
299 		DrawCursor();
300 	}
301 
302 	//
303 	// Update changes to display.
304 	//
305 	Invalidate();
306 }
307 
InitGameCallbacks()308 static void InitGameCallbacks()
309 {
310 	GameCallbacks.ButtonPressed = HandleButtonDown;
311 	GameCallbacks.ButtonReleased = HandleButtonUp;
312 	GameCallbacks.MouseMoved = HandleMouseMove;
313 	GameCallbacks.MouseExit = HandleMouseExit;
314 	GameCallbacks.KeyPressed = HandleKeyDown;
315 	GameCallbacks.KeyReleased = HandleKeyUp;
316 	GameCallbacks.KeyRepeated = HandleKeyRepeat;
317 	GameCallbacks.NetworkEvent = NetworkEvent;
318 }
319 
GameLogicLoop()320 static void GameLogicLoop()
321 {
322 	// Can't find a better place.
323 	// FIXME: We need to find a better place!
324 	SaveGameLoading = false;
325 
326 #ifdef USE_OAML
327 	if (enableOAML && oaml && UI.CurrentMapLayer->GetTimeOfDay()) {
328 		// Time of day can change our main music loop, if the current playing track is set for this
329 		SetMusicCondition(OAML_CONDID_MAIN_LOOP, UI.CurrentMapLayer->GetTimeOfDay()->ID);
330 	}
331 #endif
332 
333 	//
334 	// Game logic part
335 	//
336 	if (!GamePaused && NetworkInSync && !SkipGameCycle) {
337 		SinglePlayerReplayEachCycle();
338 		++GameCycle;
339 		MultiPlayerReplayEachCycle();
340 		NetworkCommands(); // Get network commands
341 		TriggersEachCycle();// handle triggers
342 		UnitActions();      // handle units
343 		MissileActions();   // handle missiles
344 		PlayersEachCycle(); // handle players
345 		UpdateTimer();      // update game timer
346 
347 		for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
348 			CMapLayer *map_layer = Map.MapLayers[z];
349 			map_layer->DoPerCycleLoop();
350 		}
351 
352 		//
353 		// Work todo each second.
354 		// Split into different frames, to reduce cpu time.
355 		// Increment mana of magic units.
356 		// Update mini-map.
357 		// Update map fog of war.
358 		// Call AI.
359 		// Check game goals.
360 		// Check rescue of units.
361 		//
362 		switch (GameCycle % CYCLES_PER_SECOND) {
363 			case 0: // At cycle 0, start all ai players...
364 				if (GameCycle == 0) {
365 					for (int player = 0; player < NumPlayers; ++player) {
366 						PlayersEachSecond(player);
367 					}
368 				}
369 				break;
370 			case 1:
371 				break;
372 			case 2:
373 				break;
374 			case 3: // minimap update
375 				UI.Minimap.UpdateCache = true;
376 				break;
377 			case 4:
378 				break;
379 			case 5: // forest grow
380 				Map.RegenerateForest();
381 				break;
382 			case 6: // overtaking units
383 				RescueUnits();
384 				break;
385 			//Wyrmgus start
386 			/*
387 			default: {
388 				// FIXME: assume that NumPlayers < (CYCLES_PER_SECOND - 7)
389 				int player = (GameCycle % CYCLES_PER_SECOND) - 7;
390 				Assert(player >= 0);
391 				if (player < NumPlayers) {
392 					PlayersEachSecond(player);
393 				}
394 			}
395 			*/
396 			//Wyrmgus end
397 		}
398 
399 		//Wyrmgus start
400 		int player = (GameCycle - 1) % CYCLES_PER_SECOND;
401 		Assert(player >= 0);
402 		if (player < NumPlayers) {
403 			PlayersEachSecond(player);
404 			if ((player + CYCLES_PER_SECOND) < NumPlayers) {
405 				PlayersEachSecond(player + CYCLES_PER_SECOND);
406 			}
407 		}
408 
409 		player = (GameCycle - 1) % (CYCLES_PER_MINUTE / 2);
410 		Assert(player >= 0);
411 		if (player < NumPlayers) {
412 			PlayersEachHalfMinute(player);
413 		}
414 
415 		player = (GameCycle - 1) % CYCLES_PER_MINUTE;
416 		Assert(player >= 0);
417 		if (player < NumPlayers) {
418 			PlayersEachMinute(player);
419 		}
420 		//Wyrmgus end
421 
422 		if (GameCycle > 0) {
423 			if (GameCycle % CYCLES_PER_IN_GAME_HOUR == 0) {
424 				CDate::CurrentTotalHours++;
425 
426 				for (size_t i = 0; i < CCalendar::Calendars.size(); ++i) {
427 					CCalendar *calendar = CCalendar::Calendars[i];
428 
429 					calendar->CurrentDate.AddHours(calendar, 1, DEFAULT_DAY_MULTIPLIER_PER_YEAR);
430 
431 					if (calendar->CurrentDayOfTheWeek != -1 && CDate::CurrentTotalHours % calendar->HoursPerDay == 0) { //day passed in the calendar
432 						calendar->CurrentDayOfTheWeek++;
433 						calendar->CurrentDayOfTheWeek %= calendar->DaysOfTheWeek.size();
434 					}
435 				}
436 
437 				for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
438 					CMapLayer *map_layer = Map.MapLayers[z];
439 					map_layer->DoPerHourLoop();
440 				}
441 			}
442 		}
443 
444 		if (Preference.AutosaveMinutes != 0 && !IsNetworkGame() && GameCycle > 0 && (GameCycle % (CYCLES_PER_MINUTE * Preference.AutosaveMinutes)) == 0) { // autosave every X minutes, if the option is enabled
445 			UI.StatusLine.Set(_("Autosave"));
446 			//Wyrmgus start
447 //			SaveGame("autosave.sav");
448 			CclCommand("if (RunSaveGame ~= nil) then RunSaveGame(\"autosave.sav\") end;");
449 			//Wyrmgus end
450 		}
451 	}
452 
453 	UpdateMessages();     // update messages
454 	ParticleManager.update(); // handle particles
455 	CheckMusicFinished(); // Check for next song
456 
457 	if (FastForwardCycle <= GameCycle || !(GameCycle & 0x3f)) {
458 		WaitEventsOneFrame();
459 	}
460 
461 	if (!NetworkInSync) {
462 		NetworkRecover(); // recover network
463 	}
464 }
465 
466 //#define REALVIDEO
467 #ifdef REALVIDEO
468 static	int RealVideoSyncSpeed;
469 #endif
470 
DisplayLoop()471 static void DisplayLoop()
472 {
473 #if defined(USE_OPENGL) || defined(USE_GLES)
474 	if (UseOpenGL) {
475 		/* update only if screen changed */
476 		ValidateOpenGLScreen();
477 	}
478 #endif
479 
480 	/* update only if viewmode changed */
481 	CheckViewportMode();
482 
483 	/*
484 	 *	update only if Update flag is set
485 	 *	FIXME: still not secure
486 	 */
487 	if (UI.Minimap.UpdateCache) {
488 		UI.Minimap.Update();
489 		UI.Minimap.UpdateCache = false;
490 	}
491 
492 	//
493 	// Map scrolling
494 
495 	//
496 	DoScrollArea(MouseScrollState | KeyScrollState, (KeyModifiers & ModifierControl) != 0, MouseScrollState == 0 && KeyScrollState > 0);
497 
498 	ColorCycle();
499 
500 #ifdef REALVIDEO
501 	if (FastForwardCycle > GameCycle && RealVideoSyncSpeed != VideoSyncSpeed) {
502 		RealVideoSyncSpeed = VideoSyncSpeed;
503 		VideoSyncSpeed = 3000;
504 	}
505 #endif
506 	if (FastForwardCycle <= GameCycle || GameCycle <= 10 || !(GameCycle & 0x3f)) {
507 		//FIXME: this might be better placed somewhere at front of the
508 		// program, as we now still have a game on the background and
509 		// need to go through the game-menu or supply a map file
510 		UpdateDisplay();
511 
512 		//
513 		// If double-buffered mode, we will display the contains of
514 		// VideoMemory. If direct mode this does nothing. In X11 it does
515 		// XFlush
516 		//
517 		RealizeVideoMemory();
518 	}
519 #ifdef REALVIDEO
520 	if (FastForwardCycle == GameCycle) {
521 		VideoSyncSpeed = RealVideoSyncSpeed;
522 	}
523 #endif
524 }
525 
SingleGameLoop()526 static void SingleGameLoop()
527 {
528 	while (GameRunning) {
529 		DisplayLoop();
530 		GameLogicLoop();
531 	}
532 }
533 
534 /**
535 **  Game main loop.
536 **
537 **  Unit actions.
538 **  Missile actions.
539 **  Players (AI).
540 **  Cyclic events (color cycle,...)
541 **  Display update.
542 **  Input/Network/Sound.
543 */
GameMainLoop()544 void GameMainLoop()
545 {
546 	const EventCallback *old_callbacks;
547 
548 	InitGameCallbacks();
549 
550 	old_callbacks = GetCallbacks();
551 	SetCallbacks(&GameCallbacks);
552 
553 	SetVideoSync();
554 	GameCursor = UI.Point.Cursor;
555 	//Wyrmgus start
556 	GameEstablishing = false;
557 	//Wyrmgus end
558 	GameRunning = true;
559 
560 	CParticleManager::init();
561 
562 #ifdef REALVIDEO
563 	RealVideoSyncSpeed = VideoSyncSpeed;
564 #endif
565 
566 	CclCommand("if (GameStarting ~= nil) then GameStarting() end");
567 
568 	MultiPlayerReplayEachCycle();
569 
570 	//Wyrmgus start
571 	if (GameCycle == 0) { // so that these don't trigger when loading a saved game
572 		if (CurrentCampaign != nullptr) {
573 			for (int i = 0; i < NumPlayers; ++i) {
574 				if (Players[i].Type != PlayerNobody && Players[i].Race != 0 && Players[i].Faction != -1) {
575 					if (CurrentCampaign->StartDate.Year) {
576 						CCivilization *civilization = CCivilization::Civilizations[Players[i].Race];
577 						CFaction *faction = PlayerRaces.Factions[Players[i].Faction];
578 
579 						for (std::map<std::string, std::map<CDate, bool>>::iterator iterator = civilization->HistoricalUpgrades.begin(); iterator != civilization->HistoricalUpgrades.end(); ++iterator) {
580 							int upgrade_id = UpgradeIdByIdent(iterator->first);
581 							if (upgrade_id == -1) {
582 								fprintf(stderr, "Upgrade \"%s\" doesn't exist.\n", iterator->first.c_str());
583 								continue;
584 							}
585 							for (std::map<CDate, bool>::reverse_iterator second_iterator = iterator->second.rbegin(); second_iterator != iterator->second.rend(); ++second_iterator) {
586 								if (second_iterator->first.Year == 0 || CurrentCampaign->StartDate.ContainsDate(second_iterator->first)) {
587 									if (second_iterator->second && UpgradeIdentAllowed(Players[i], iterator->first.c_str()) != 'R') {
588 										UpgradeAcquire(Players[i], AllUpgrades[upgrade_id]);
589 									} else if (!second_iterator->second) {
590 										break;
591 									}
592 								}
593 							}
594 						}
595 
596 						for (std::map<std::string, std::map<CDate, bool>>::iterator iterator = faction->HistoricalUpgrades.begin(); iterator != faction->HistoricalUpgrades.end(); ++iterator) {
597 							int upgrade_id = UpgradeIdByIdent(iterator->first);
598 							if (upgrade_id == -1) {
599 								fprintf(stderr, "Upgrade \"%s\" doesn't exist.\n", iterator->first.c_str());
600 								continue;
601 							}
602 							for (std::map<CDate, bool>::reverse_iterator second_iterator = iterator->second.rbegin(); second_iterator != iterator->second.rend(); ++second_iterator) {
603 								if (second_iterator->first.Year == 0 || CurrentCampaign->StartDate.ContainsDate(second_iterator->first)) {
604 									if (second_iterator->second && UpgradeIdentAllowed(Players[i], iterator->first.c_str()) != 'R') {
605 										UpgradeAcquire(Players[i], AllUpgrades[upgrade_id]);
606 									} else if (!second_iterator->second) {
607 										break;
608 									}
609 								}
610 							}
611 						}
612 
613 						for (std::map<std::pair<CDate, CFaction *>, int>::iterator iterator = faction->HistoricalDiplomacyStates.begin(); iterator != faction->HistoricalDiplomacyStates.end(); ++iterator) { //set the appropriate historical diplomacy states to other factions
614 							if (iterator->first.first.Year == 0 || CurrentCampaign->StartDate.ContainsDate(iterator->first.first)) {
615 								CPlayer *diplomacy_state_player = GetFactionPlayer(iterator->first.second);
616 								if (diplomacy_state_player) {
617 									CommandDiplomacy(i, iterator->second, diplomacy_state_player->Index);
618 									CommandDiplomacy(diplomacy_state_player->Index, iterator->second, i);
619 									if (iterator->second == DiplomacyAllied) {
620 										CommandSharedVision(i, true, diplomacy_state_player->Index);
621 										CommandSharedVision(diplomacy_state_player->Index, true, i);
622 									}
623 								}
624 							}
625 						}
626 
627 						for (std::map<std::pair<CDate, int>, int>::iterator iterator = faction->HistoricalResources.begin(); iterator != faction->HistoricalResources.end(); ++iterator) { //set the appropriate historical resource quantities
628 							if (iterator->first.first.Year == 0 || CurrentCampaign->StartDate.ContainsDate(iterator->first.first)) {
629 								Players[i].SetResource(iterator->first.second, iterator->second);
630 							}
631 						}
632 					}
633 				}
634 			}
635 
636 			if (CurrentCampaign->StartEffects) {
637 				CurrentCampaign->StartEffects->pushPreamble();
638 				CurrentCampaign->StartEffects->run();
639 			}
640 		}
641 
642 		//if the person player has no faction, bring up the faction choice interface
643 		if (ThisPlayer && ThisPlayer->Faction == -1) {
644 			char buf[256];
645 			snprintf(buf, sizeof(buf), "if (ChooseFaction ~= nil) then ChooseFaction(\"%s\", \"%s\") end", ThisPlayer->Race != -1 ? PlayerRaces.Name[ThisPlayer->Race].c_str() : "", "");
646 			CclCommand(buf);
647 		}
648 
649 		if (!IsNetworkGame() && ThisPlayer && CurrentCustomHero != nullptr) {
650 			Vec2i resPos;
651 			FindNearestDrop(*CurrentCustomHero->Type, ThisPlayer->StartPos, resPos, LookingW, ThisPlayer->StartMapLayer);
652 			CUnit *custom_hero = MakeUnitAndPlace(resPos, *CurrentCustomHero->Type, ThisPlayer, ThisPlayer->StartMapLayer);
653 			custom_hero->SetCharacter(CurrentCustomHero->Ident, true);
654 		}
655 
656 		//update the sold units of all units before starting, to make sure they fit the current conditions
657 		for (CUnitManager::Iterator it = UnitManager.begin(); it != UnitManager.end(); ++it) {
658 			CUnit *unit = *it;
659 			if (unit && unit->IsAlive()) {
660 				unit->UpdateSoldUnits();
661 			}
662 		}
663 
664 		if (CurrentQuest != nullptr && CurrentQuest->IntroductionDialogue != nullptr) {
665 			CurrentQuest->IntroductionDialogue->Call(ThisPlayer->Index);
666 		}
667 	}
668 	//Wyrmgus end
669 
670 	SingleGameLoop();
671 
672 	//
673 	// Game over
674 	//
675 	if (GameResult == GameExit) {
676 		Exit(0);
677 		return;
678 	}
679 
680 #ifdef REALVIDEO
681 	if (FastForwardCycle > GameCycle) {
682 		VideoSyncSpeed = RealVideoSyncSpeed;
683 	}
684 #endif
685 	NetworkQuitGame();
686 	EndReplayLog();
687 
688 	GameCycle = 0;//????
689 	CDate::CurrentTotalHours = 0;
690 	CParticleManager::exit();
691 	FlagRevealMap = 0;
692 	ReplayRevealMap = 0;
693 	GamePaused = false;
694 	GodMode = false;
695 
696 	SetCallbacks(old_callbacks);
697 }
698 
699 //@}
700