1 |
|
|
/* |
2 |
|
|
* The ManaPlus Client |
3 |
|
|
* Copyright (C) 2004-2009 The Mana World Development Team |
4 |
|
|
* Copyright (C) 2009-2010 The Mana Developers |
5 |
|
|
* Copyright (C) 2011-2019 The ManaPlus Developers |
6 |
|
|
* Copyright (C) 2019-2021 Andrei Karas |
7 |
|
|
* |
8 |
|
|
* This file is part of The ManaPlus Client. |
9 |
|
|
* |
10 |
|
|
* This program is free software; you can redistribute it and/or modify |
11 |
|
|
* it under the terms of the GNU General Public License as published by |
12 |
|
|
* the Free Software Foundation; either version 2 of the License, or |
13 |
|
|
* any later version. |
14 |
|
|
* |
15 |
|
|
* This program is distributed in the hope that it will be useful, |
16 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 |
|
|
* GNU General Public License for more details. |
19 |
|
|
* |
20 |
|
|
* You should have received a copy of the GNU General Public License |
21 |
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
22 |
|
|
*/ |
23 |
|
|
|
24 |
|
|
#include "game.h" |
25 |
|
|
|
26 |
|
|
#include "actormanager.h" |
27 |
|
|
#include "client.h" |
28 |
|
|
#include "configuration.h" |
29 |
|
|
#include "effectmanager.h" |
30 |
|
|
#include "eventsmanager.h" |
31 |
|
|
#include "gamemodifiers.h" |
32 |
|
|
#include "soundmanager.h" |
33 |
|
|
#include "settings.h" |
34 |
|
|
|
35 |
|
|
#include "being/crazymoves.h" |
36 |
|
|
#include "being/localplayer.h" |
37 |
|
|
#include "being/playerinfo.h" |
38 |
|
|
|
39 |
|
|
#include "const/itemshortcut.h" |
40 |
|
|
#include "const/spells.h" |
41 |
|
|
|
42 |
|
|
#include "const/gui/chat.h" |
43 |
|
|
|
44 |
|
|
#include "enums/being/beingdirection.h" |
45 |
|
|
|
46 |
|
|
#include "fs/mkdir.h" |
47 |
|
|
|
48 |
|
|
#include "fs/virtfs/fs.h" |
49 |
|
|
|
50 |
|
|
#include "gui/dialogsmanager.h" |
51 |
|
|
#include "gui/gui.h" |
52 |
|
|
#include "gui/popupmanager.h" |
53 |
|
|
#include "gui/viewport.h" |
54 |
|
|
#include "gui/windowmanager.h" |
55 |
|
|
#include "gui/windowmenu.h" |
56 |
|
|
|
57 |
|
|
#include "gui/fonts/font.h" |
58 |
|
|
|
59 |
|
|
#include "gui/shortcut/dropshortcut.h" |
60 |
|
|
#include "gui/shortcut/emoteshortcut.h" |
61 |
|
|
|
62 |
|
|
#include "gui/popups/popupmenu.h" |
63 |
|
|
|
64 |
|
|
#include "gui/windows/bankwindow.h" |
65 |
|
|
#include "gui/windows/clanwindow.h" |
66 |
|
|
#include "gui/windows/cutinwindow.h" |
67 |
|
|
#include "gui/windows/mailwindow.h" |
68 |
|
|
#include "gui/windows/chatwindow.h" |
69 |
|
|
#include "gui/windows/debugwindow.h" |
70 |
|
|
#include "gui/windows/didyouknowwindow.h" |
71 |
|
|
#include "gui/windows/emotewindow.h" |
72 |
|
|
#include "gui/windows/equipmentwindow.h" |
73 |
|
|
#include "gui/windows/inventorywindow.h" |
74 |
|
|
#include "gui/windows/killstats.h" |
75 |
|
|
#include "gui/windows/minimap.h" |
76 |
|
|
#include "gui/windows/ministatuswindow.h" |
77 |
|
|
#include "gui/windows/npcdialog.h" |
78 |
|
|
#include "gui/windows/outfitwindow.h" |
79 |
|
|
#include "gui/windows/setupwindow.h" |
80 |
|
|
#include "gui/windows/shopwindow.h" |
81 |
|
|
#include "gui/windows/shortcutwindow.h" |
82 |
|
|
#include "gui/windows/skilldialog.h" |
83 |
|
|
#include "gui/windows/socialwindow.h" |
84 |
|
|
#include "gui/windows/statuswindow.h" |
85 |
|
|
#include "gui/windows/tradewindow.h" |
86 |
|
|
#include "gui/windows/questswindow.h" |
87 |
|
|
#include "gui/windows/whoisonline.h" |
88 |
|
|
|
89 |
|
|
#include "gui/widgets/tabs/chat/battletab.h" |
90 |
|
|
|
91 |
|
|
#include "gui/widgets/createwidget.h" |
92 |
|
|
#include "gui/widgets/emoteshortcutcontainer.h" |
93 |
|
|
#include "gui/widgets/itemshortcutcontainer.h" |
94 |
|
|
#include "gui/widgets/spellshortcutcontainer.h" |
95 |
|
|
#include "gui/widgets/virtshortcutcontainer.h" |
96 |
|
|
|
97 |
|
|
#include "gui/widgets/tabs/chat/gmtab.h" |
98 |
|
|
#include "gui/widgets/tabs/chat/langtab.h" |
99 |
|
|
#include "gui/widgets/tabs/chat/tradetab.h" |
100 |
|
|
|
101 |
|
|
#include "input/inputmanager.h" |
102 |
|
|
#include "input/joystick.h" |
103 |
|
|
#include "input/keyboardconfig.h" |
104 |
|
|
|
105 |
|
|
#include "input/touch/touchmanager.h" |
106 |
|
|
|
107 |
|
|
#include "net/generalhandler.h" |
108 |
|
|
#include "net/gamehandler.h" |
109 |
|
|
#include "net/net.h" |
110 |
|
|
#include "net/packetcounters.h" |
111 |
|
|
|
112 |
|
|
#include "particle/particleengine.h" |
113 |
|
|
|
114 |
|
|
#include "resources/delayedmanager.h" |
115 |
|
|
#include "resources/mapreader.h" |
116 |
|
|
#include "resources/screenshothelper.h" |
117 |
|
|
|
118 |
|
|
#include "resources/db/mapdb.h" |
119 |
|
|
|
120 |
|
|
#include "resources/map/map.h" |
121 |
|
|
|
122 |
|
|
#include "resources/resourcemanager/resourcemanager.h" |
123 |
|
|
|
124 |
|
|
#include "resources/sprite/animatedsprite.h" |
125 |
|
|
|
126 |
|
|
#include "utils/delete2.h" |
127 |
|
|
#include "utils/foreach.h" |
128 |
|
|
#include "utils/gettext.h" |
129 |
|
|
#include "utils/perfstat.h" |
130 |
|
|
#include "utils/pnglib.h" |
131 |
|
|
#include "utils/sdlcheckutils.h" |
132 |
|
|
#include "utils/timer.h" |
133 |
|
|
|
134 |
|
|
#ifdef __native_client__ |
135 |
|
|
#include "utils/naclmessages.h" |
136 |
|
|
#endif // __native_client__ |
137 |
|
|
|
138 |
|
|
#include "listeners/assertlistener.h" |
139 |
|
|
#include "listeners/errorlistener.h" |
140 |
|
|
|
141 |
|
|
#ifdef TMWA_SUPPORT |
142 |
|
|
#include "net/tmwa/guildmanager.h" |
143 |
|
|
#endif // TMWA_SUPPORT |
144 |
|
|
|
145 |
|
|
#ifdef USE_MUMBLE |
146 |
|
|
#include "mumblemanager.h" |
147 |
|
|
#endif // USE_MUMBLE |
148 |
|
|
|
149 |
|
|
#ifdef WIN32 |
150 |
|
|
#include <sys/time.h> |
151 |
|
|
#undef ERROR |
152 |
|
|
#endif // WIN32 |
153 |
|
|
|
154 |
|
|
#include <fstream> |
155 |
|
|
|
156 |
|
|
#include "debug.h" |
157 |
|
|
|
158 |
|
|
QuitDialog *quitDialog = nullptr; |
159 |
|
|
Window *disconnectedDialog = nullptr; |
160 |
|
|
|
161 |
|
|
bool mStatsReUpdated = false; |
162 |
|
|
const time_t adjustDelay = 10; |
163 |
|
|
|
164 |
|
|
/** |
165 |
|
|
* Initialize every game sub-engines in the right order |
166 |
|
|
*/ |
167 |
|
|
static void initEngines() |
168 |
|
|
{ |
169 |
|
|
actorManager = new ActorManager; |
170 |
|
|
effectManager = new EffectManager; |
171 |
|
|
#ifdef TMWA_SUPPORT |
172 |
|
|
GuildManager::init(); |
173 |
|
|
#endif // TMWA_SUPPORT |
174 |
|
|
|
175 |
|
|
crazyMoves = new CrazyMoves; |
176 |
|
|
|
177 |
|
|
particleEngine = new ParticleEngine; |
178 |
|
|
particleEngine->setMap(nullptr); |
179 |
|
|
ParticleEngine::setupEngine(); |
180 |
|
|
BeingInfo::init(); |
181 |
|
|
|
182 |
|
|
if (gameHandler != nullptr) |
183 |
|
|
gameHandler->initEngines(); |
184 |
|
|
|
185 |
|
|
keyboard.update(); |
186 |
|
|
if (joystick != nullptr) |
187 |
|
|
joystick->update(); |
188 |
|
|
|
189 |
|
|
UpdateStatusListener::distributeEvent(); |
190 |
|
|
} |
191 |
|
|
|
192 |
|
|
/** |
193 |
|
|
* Create all the various globally accessible gui windows |
194 |
|
|
*/ |
195 |
|
|
static void createGuiWindows() |
196 |
|
|
{ |
197 |
|
|
if (setupWindow != nullptr) |
198 |
|
|
setupWindow->clearWindowsForReset(); |
199 |
|
|
|
200 |
|
|
if (emoteShortcut != nullptr) |
201 |
|
|
emoteShortcut->load(); |
202 |
|
|
|
203 |
|
|
GameModifiers::init(); |
204 |
|
|
|
205 |
|
|
// Create dialogs |
206 |
|
|
CREATEWIDGETV0(emoteWindow, EmoteWindow); |
207 |
|
|
delete2(debugChatTab) |
208 |
|
|
if (chatWindow != nullptr) |
209 |
|
|
{ |
210 |
|
|
chatWindow->scheduleDelete(); |
211 |
|
|
chatWindow = nullptr; |
212 |
|
|
} |
213 |
|
|
CREATEWIDGETV(chatWindow, ChatWindow, |
214 |
|
|
"Chat"); |
215 |
|
|
CREATEWIDGETV0(tradeWindow, TradeWindow); |
216 |
|
|
CREATEWIDGETV(equipmentWindow, EquipmentWindow, |
217 |
|
|
PlayerInfo::getEquipment(), |
218 |
|
|
localPlayer, |
219 |
|
|
false); |
220 |
|
|
CREATEWIDGETV(beingEquipmentWindow, EquipmentWindow, |
221 |
|
|
nullptr, |
222 |
|
|
nullptr, |
223 |
|
|
true); |
224 |
|
|
beingEquipmentWindow->setVisible(Visible_false); |
225 |
|
|
CREATEWIDGETV0(statusWindow, StatusWindow); |
226 |
|
|
CREATEWIDGETV0(miniStatusWindow, MiniStatusWindow); |
227 |
|
|
CREATEWIDGETV(inventoryWindow, InventoryWindow, |
228 |
|
|
PlayerInfo::getInventory()); |
229 |
|
|
if (Net::getNetworkType() != ServerType::TMWATHENA) |
230 |
|
|
{ |
231 |
|
|
CREATEWIDGETV(cartWindow, InventoryWindow, |
232 |
|
|
PlayerInfo::getCartInventory()); |
233 |
|
|
} |
234 |
|
|
CREATEWIDGETV0(cutInWindow, CutInWindow); |
235 |
|
|
CREATEWIDGETV0(shopWindow, ShopWindow); |
236 |
|
|
CREATEWIDGETV0(skillDialog, SkillDialog); |
237 |
|
|
CREATEWIDGETV0(minimap, Minimap); |
238 |
|
|
if (debugWindow != nullptr) |
239 |
|
|
{ |
240 |
|
|
debugWindow->scheduleDelete(); |
241 |
|
|
debugWindow = nullptr; |
242 |
|
|
} |
243 |
|
|
CREATEWIDGETV(debugWindow, DebugWindow, |
244 |
|
|
"Debug"); |
245 |
|
|
CREATEWIDGETV(itemShortcutWindow, ShortcutWindow, |
246 |
|
|
"ItemShortcut", "items.xml", 83, 460); |
247 |
|
|
|
248 |
|
|
for (unsigned f = 0; f < SHORTCUT_TABS - 1; f ++) |
249 |
|
|
{ |
250 |
|
|
itemShortcutWindow->addTab(toString(f + 1), |
251 |
|
|
new ItemShortcutContainer(nullptr, f)); |
252 |
|
|
} |
253 |
|
|
if (Net::getNetworkType() != ServerType::TMWATHENA) |
254 |
|
|
{ |
255 |
|
|
itemShortcutWindow->addTab("A", |
256 |
|
|
new ItemShortcutContainer(nullptr, SHORTCUT_TABS - 1)); |
257 |
|
|
} |
258 |
|
|
if (config.getBoolValue("showDidYouKnow")) |
259 |
|
|
{ |
260 |
|
|
didYouKnowWindow->setVisible(Visible_true); |
261 |
|
|
didYouKnowWindow->loadData(0); |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
CREATEWIDGETV(emoteShortcutWindow, ShortcutWindow, |
265 |
|
|
"EmoteShortcut", |
266 |
|
|
new EmoteShortcutContainer(nullptr), |
267 |
|
|
"emotes.xml", |
268 |
|
|
130, 480); |
269 |
|
|
CREATEWIDGETV0(outfitWindow, OutfitWindow); |
270 |
|
|
CREATEWIDGETV(dropShortcutWindow, ShortcutWindow, |
271 |
|
|
"DropShortcut", |
272 |
|
|
new VirtShortcutContainer(nullptr, dropShortcut), |
273 |
|
|
"drops.xml", |
274 |
|
|
0, 0); |
275 |
|
|
CREATEWIDGETV(spellShortcutWindow, ShortcutWindow, |
276 |
|
|
"SpellShortcut", |
277 |
|
|
"spells.xml", |
278 |
|
|
265, 328); |
279 |
|
|
for (unsigned f = 0; f < SPELL_SHORTCUT_TABS; f ++) |
280 |
|
|
{ |
281 |
|
|
spellShortcutWindow->addTab(toString(f + 1), |
282 |
|
|
new SpellShortcutContainer(nullptr, f)); |
283 |
|
|
} |
284 |
|
|
|
285 |
|
|
CREATEWIDGETV0(bankWindow, BankWindow); |
286 |
|
|
CREATEWIDGETV0(mailWindow, MailWindow); |
287 |
|
|
CREATEWIDGETV0(whoIsOnline, WhoIsOnline); |
288 |
|
|
CREATEWIDGETV0(killStats, KillStats); |
289 |
|
|
CREATEWIDGETV0(socialWindow, SocialWindow); |
290 |
|
|
CREATEWIDGETV0(questsWindow, QuestsWindow); |
291 |
|
|
CREATEWIDGETV0(clanWindow, ClanWindow); |
292 |
|
|
|
293 |
|
|
// TRANSLATORS: chat tab header |
294 |
|
|
localChatTab = new ChatTab(chatWindow, _("General"), |
295 |
|
|
GENERAL_CHANNEL, "#General", ChatTabType::INPUT); |
296 |
|
|
localChatTab->setAllowHighlight(false); |
297 |
|
|
if (config.getBoolValue("showChatHistory")) |
298 |
|
|
localChatTab->loadFromLogFile("#General"); |
299 |
|
|
|
300 |
|
|
// TRANSLATORS: chat tab header |
301 |
|
|
debugChatTab = new ChatTab(chatWindow, _("Debug"), "", |
302 |
|
|
"#Debug", ChatTabType::DEBUG); |
303 |
|
|
debugChatTab->setAllowHighlight(false); |
304 |
|
|
|
305 |
|
|
if (assertListener != nullptr) |
306 |
|
|
{ |
307 |
|
|
const StringVect &messages = assertListener->getMessages(); |
308 |
|
|
FOR_EACH (StringVectCIter, it, messages) |
309 |
|
|
{ |
310 |
|
|
debugChatTab->chatLog(*it, |
311 |
|
|
ChatMsgType::BY_SERVER, |
312 |
|
|
IgnoreRecord_false, |
313 |
|
|
TryRemoveColors_true); |
314 |
|
|
} |
315 |
|
|
delete2(assertListener) |
316 |
|
|
} |
317 |
|
|
if (config.getBoolValue("enableTradeTab")) |
318 |
|
|
chatWindow->addSpecialChannelTab(TRADE_CHANNEL, false); |
319 |
|
|
else |
320 |
|
|
tradeChatTab = nullptr; |
321 |
|
|
|
322 |
|
|
if (config.getBoolValue("enableBattleTab")) |
323 |
|
|
{ |
324 |
|
|
battleChatTab = new BattleTab(chatWindow); |
325 |
|
|
battleChatTab->setAllowHighlight(false); |
326 |
|
|
} |
327 |
|
|
else |
328 |
|
|
{ |
329 |
|
|
battleChatTab = nullptr; |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
chatWindow->showGMTab(); |
333 |
|
|
if (!isSafeMode) |
334 |
|
|
chatWindow->loadState(); |
335 |
|
|
|
336 |
|
|
if (setupWindow != nullptr) |
337 |
|
|
setupWindow->externalUpdate(); |
338 |
|
|
|
339 |
|
|
if (localPlayer != nullptr) |
340 |
|
|
localPlayer->updateStatus(); |
341 |
|
|
|
342 |
|
|
if (generalHandler != nullptr) |
343 |
|
|
generalHandler->gameStarted(); |
344 |
|
|
} |
345 |
|
|
|
346 |
|
|
/** |
347 |
|
|
* Destroy all the globally accessible gui windows |
348 |
|
|
*/ |
349 |
|
|
static void destroyGuiWindows() |
350 |
|
|
{ |
351 |
|
|
if (generalHandler != nullptr) |
352 |
|
|
generalHandler->gameEnded(); |
353 |
|
|
|
354 |
|
|
if (whoIsOnline != nullptr) |
355 |
|
|
whoIsOnline->setAllowUpdate(false); |
356 |
|
|
|
357 |
|
|
#ifdef TMWA_SUPPORT |
358 |
|
|
GuildManager::clear(); |
359 |
|
|
#endif // TMWA_SUPPORT |
360 |
|
|
|
361 |
|
|
delete2(windowMenu) |
362 |
|
|
delete2(localChatTab) // Need to do this first, so it can remove itself |
363 |
|
|
delete2(debugChatTab) |
364 |
|
|
delete2(tradeChatTab) |
365 |
|
|
delete2(battleChatTab) |
366 |
|
|
delete2(langChatTab) |
367 |
|
|
delete2(gmChatTab) |
368 |
|
|
#ifdef TMWA_SUPPORT |
369 |
|
|
if (guildManager != nullptr && GuildManager::getEnableGuildBot()) |
370 |
|
|
guildManager->reload(); |
371 |
|
|
#endif // TMWA_SUPPORT |
372 |
|
|
|
373 |
|
|
logger->log("start deleting"); |
374 |
|
|
delete2(emoteWindow) |
375 |
|
|
delete2(chatWindow) |
376 |
|
|
logger->log("end deleting"); |
377 |
|
|
delete2(statusWindow) |
378 |
|
|
delete2(miniStatusWindow) |
379 |
|
|
delete2(inventoryWindow) |
380 |
|
|
delete2(cartWindow) |
381 |
|
|
delete2(shopWindow) |
382 |
|
|
delete2(skillDialog) |
383 |
|
|
delete2(minimap) |
384 |
|
|
delete2(equipmentWindow) |
385 |
|
|
delete2(beingEquipmentWindow) |
386 |
|
|
delete2(tradeWindow) |
387 |
|
|
delete2(debugWindow) |
388 |
|
|
delete2(itemShortcutWindow) |
389 |
|
|
delete2(emoteShortcutWindow) |
390 |
|
|
delete2(outfitWindow) |
391 |
|
|
delete2(socialWindow) |
392 |
|
|
delete2(dropShortcutWindow) |
393 |
|
|
delete2(spellShortcutWindow) |
394 |
|
|
delete2(bankWindow) |
395 |
|
|
delete2(cutInWindow) |
396 |
|
|
delete2(mailWindow) |
397 |
|
|
delete2(questsWindow) |
398 |
|
|
delete2(whoIsOnline) |
399 |
|
|
delete2(killStats) |
400 |
|
|
delete2(clanWindow) |
401 |
|
|
} |
402 |
|
|
|
403 |
|
|
Game *Game::mInstance = nullptr; |
404 |
|
|
|
405 |
|
|
Game::Game() : |
406 |
|
|
mCurrentMap(nullptr), |
407 |
|
|
mMapName(""), |
408 |
|
|
mValidSpeed(true), |
409 |
|
|
mNextAdjustTime(cur_time + adjustDelay), |
410 |
|
|
mAdjustLevel(0), |
411 |
|
|
mAdjustPerfomance(config.getBoolValue("adjustPerfomance")), |
412 |
|
|
mLowerCounter(0), |
413 |
|
|
mPing(0), |
414 |
|
|
mTime(cur_time + 1), |
415 |
|
|
mTime2(cur_time + 10) |
416 |
|
|
{ |
417 |
|
|
touchManager.setInGame(true); |
418 |
|
|
|
419 |
|
|
// assert(!mInstance); |
420 |
|
|
if (mInstance != nullptr) |
421 |
|
|
logger->log("error: double game creation"); |
422 |
|
|
mInstance = this; |
423 |
|
|
|
424 |
|
|
config.incValue("gamecount"); |
425 |
|
|
|
426 |
|
|
disconnectedDialog = nullptr; |
427 |
|
|
|
428 |
|
|
// Create the viewport |
429 |
|
|
viewport = new Viewport; |
430 |
|
|
viewport->setSize(mainGraphics->mWidth, mainGraphics->mHeight); |
431 |
|
|
PlayerInfo::clear(); |
432 |
|
|
|
433 |
|
|
emptyBeingSlot = new BeingSlot; |
434 |
|
|
|
435 |
|
|
BasicContainer2 *const top = static_cast<BasicContainer2*>(gui->getTop()); |
436 |
|
|
if (top != nullptr) |
437 |
|
|
top->add(viewport); |
438 |
|
|
viewport->requestMoveToBottom(); |
439 |
|
|
|
440 |
|
|
AnimatedSprite::setEnableCache( |
441 |
|
|
mainGraphics->getOpenGL() != RENDER_SOFTWARE && |
442 |
|
|
config.getBoolValue("enableDelayedAnimations")); |
443 |
|
|
|
444 |
|
|
CompoundSprite::setEnableDelay( |
445 |
|
|
config.getBoolValue("enableCompoundSpriteDelay")); |
446 |
|
|
|
447 |
|
|
createGuiWindows(); |
448 |
|
|
windowMenu = new WindowMenu(nullptr); |
449 |
|
|
|
450 |
|
|
if (windowContainer != nullptr) |
451 |
|
|
windowContainer->add(windowMenu); |
452 |
|
|
|
453 |
|
|
#ifdef USE_OPENGL |
454 |
|
|
MapReader::loadEmptyAtlas(); |
455 |
|
|
#endif // USE_OPENGL |
456 |
|
|
|
457 |
|
|
initEngines(); |
458 |
|
|
|
459 |
|
|
chatWindow->postConnection(); |
460 |
|
|
mailWindow->postConnection(); |
461 |
|
|
|
462 |
|
|
// Initialize beings |
463 |
|
|
if (actorManager != nullptr) |
464 |
|
|
actorManager->setPlayer(localPlayer); |
465 |
|
|
|
466 |
|
|
gameHandler->ping(tick_time); |
467 |
|
|
|
468 |
|
|
if (setupWindow != nullptr) |
469 |
|
|
setupWindow->setInGame(true); |
470 |
|
|
clearKeysArray(); |
471 |
|
|
|
472 |
|
|
#ifdef TMWA_SUPPORT |
473 |
|
|
if (guildManager != nullptr && GuildManager::getEnableGuildBot()) |
474 |
|
|
guildManager->requestGuildInfo(); |
475 |
|
|
#endif // TMWA_SUPPORT |
476 |
|
|
|
477 |
|
|
settings.disableLoggingInGame = config.getBoolValue( |
478 |
|
|
"disableLoggingInGame"); |
479 |
|
|
} |
480 |
|
|
|
481 |
|
|
Game::~Game() |
482 |
|
|
{ |
483 |
|
|
#ifdef USE_OPENGL |
484 |
|
|
MapReader::unloadEmptyAtlas(); |
485 |
|
|
#endif // USE_OPENGL |
486 |
|
|
|
487 |
|
|
settings.disableLoggingInGame = false; |
488 |
|
|
touchManager.setInGame(false); |
489 |
|
|
config.write(); |
490 |
|
|
serverConfig.write(); |
491 |
|
|
resetAdjustLevel(); |
492 |
|
|
destroyGuiWindows(); |
493 |
|
|
|
494 |
|
|
AnimatedSprite::setEnableCache(false); |
495 |
|
|
|
496 |
|
|
delete2(actorManager) |
497 |
|
|
if (client->getState() != State::CHANGE_MAP) |
498 |
|
|
delete2(localPlayer) |
499 |
|
|
if (effectManager != nullptr) |
500 |
|
|
effectManager->clear(); |
501 |
|
|
delete2(effectManager) |
502 |
|
|
delete2(particleEngine) |
503 |
|
|
delete2(viewport) |
504 |
|
|
delete2(mCurrentMap) |
505 |
|
|
#ifdef TMWA_SUPPORT |
506 |
|
|
delete2(guildManager) |
507 |
|
|
#endif // TMWA_SUPPORT |
508 |
|
|
#ifdef USE_MUMBLE |
509 |
|
|
delete2(mumbleManager) |
510 |
|
|
#endif // USE_MUMBLE |
511 |
|
|
|
512 |
|
|
delete2(crazyMoves) |
513 |
|
|
delete2(emptyBeingSlot) |
514 |
|
|
|
515 |
|
|
Being::clearCache(); |
516 |
|
|
mInstance = nullptr; |
517 |
|
|
PlayerInfo::gameDestroyed(); |
518 |
|
|
} |
519 |
|
|
|
520 |
|
|
void Game::addWatermark() |
521 |
|
|
{ |
522 |
|
|
if ((boldFont == nullptr) || !config.getBoolValue("addwatermark")) |
523 |
|
|
return; |
524 |
|
|
|
525 |
|
|
const Color &color1 = theme->getColor(ThemeColorId::TEXT, 255); |
526 |
|
|
const Color &color2 = theme->getColor(ThemeColorId::TEXT_OUTLINE, 255); |
527 |
|
|
|
528 |
|
|
boldFont->drawString(mainGraphics, |
529 |
|
|
color1, |
530 |
|
|
color2, |
531 |
|
|
settings.serverName, |
532 |
|
|
100, 50); |
533 |
|
|
} |
534 |
|
|
|
535 |
|
|
bool Game::createScreenshot(const std::string &prefix) |
536 |
|
|
{ |
537 |
|
|
if ((mainGraphics == nullptr) || (screenshortHelper == nullptr)) |
538 |
|
|
return false; |
539 |
|
|
|
540 |
|
|
SDL_Surface *screenshot = nullptr; |
541 |
|
|
|
542 |
|
|
if (!config.getBoolValue("showip") && (gui != nullptr)) |
543 |
|
|
{ |
544 |
|
|
mainGraphics->setSecure(true); |
545 |
|
|
screenshortHelper->prepare(); |
546 |
|
|
gui->draw(); |
547 |
|
|
addWatermark(); |
548 |
|
|
screenshot = screenshortHelper->getScreenshot(); |
549 |
|
|
mainGraphics->setSecure(false); |
550 |
|
|
} |
551 |
|
|
else |
552 |
|
|
{ |
553 |
|
|
addWatermark(); |
554 |
|
|
screenshot = screenshortHelper->getScreenshot(); |
555 |
|
|
} |
556 |
|
|
|
557 |
|
|
if (screenshot == nullptr) |
558 |
|
|
return false; |
559 |
|
|
|
560 |
|
|
return saveScreenshot(screenshot, prefix); |
561 |
|
|
} |
562 |
|
|
|
563 |
|
|
bool Game::saveScreenshot(SDL_Surface *const screenshot, |
564 |
|
|
const std::string &prefix) |
565 |
|
|
{ |
566 |
|
|
std::string screenshotDirectory = settings.screenshotDir; |
567 |
|
|
if (mkdir_r(screenshotDirectory.c_str()) != 0) |
568 |
|
|
{ |
569 |
|
|
logger->log("Directory %s doesn't exist and can't be created! " |
570 |
|
|
"Setting screenshot directory to home.", |
571 |
|
|
screenshotDirectory.c_str()); |
572 |
|
|
screenshotDirectory = std::string(VirtFs::getUserDir()); |
573 |
|
|
} |
574 |
|
|
|
575 |
|
|
// Search for an unused screenshot name |
576 |
|
|
std::stringstream filename; |
577 |
|
|
std::fstream testExists; |
578 |
|
|
|
579 |
|
|
time_t rawtime; |
580 |
|
|
char buffer [100]; |
581 |
|
|
time(&rawtime); |
582 |
|
|
tm *const timeinfo = localtime(&rawtime); |
583 |
|
|
strftime(buffer, 99, "%Y-%m-%d_%H-%M-%S", timeinfo); |
584 |
|
|
|
585 |
|
|
const std::string serverName = settings.serverName; |
586 |
|
|
std::string screenShortStr; |
587 |
|
|
if (prefix.empty()) |
588 |
|
|
{ |
589 |
|
|
if (serverName.empty()) |
590 |
|
|
{ |
591 |
|
|
screenShortStr = strprintf("%s_Screenshot_%s_", |
592 |
|
|
branding.getValue("appName", "ManaPlus").c_str(), |
593 |
|
|
buffer); |
594 |
|
|
} |
595 |
|
|
else |
596 |
|
|
{ |
597 |
|
|
screenShortStr = strprintf("%s_Screenshot_%s_%s_", |
598 |
|
|
branding.getValue("appName", "ManaPlus").c_str(), |
599 |
|
|
serverName.c_str(), buffer); |
600 |
|
|
} |
601 |
|
|
|
602 |
|
|
bool found = false; |
603 |
|
|
static unsigned int screenshotCount = 0; |
604 |
|
|
do |
605 |
|
|
{ |
606 |
|
|
screenshotCount++; |
607 |
|
|
filename.str(""); |
608 |
|
|
filename << screenshotDirectory << "/"; |
609 |
|
|
filename << screenShortStr << screenshotCount << ".png"; |
610 |
|
|
testExists.open(filename.str().c_str(), std::ios::in); |
611 |
|
|
found = !testExists.is_open(); |
612 |
|
|
testExists.close(); |
613 |
|
|
} |
614 |
|
|
while (!found); |
615 |
|
|
} |
616 |
|
|
else |
617 |
|
|
{ |
618 |
|
|
screenShortStr = prefix; |
619 |
|
|
filename.str(""); |
620 |
|
|
filename << screenshotDirectory << "/"; |
621 |
|
|
filename << screenShortStr; |
622 |
|
|
} |
623 |
|
|
|
624 |
|
|
const std::string fileNameStr = filename.str(); |
625 |
|
|
const bool success = PngLib::writePNG(screenshot, fileNameStr); |
626 |
|
|
#ifdef __native_client__ |
627 |
|
|
std::string nacScreenshotlDir = fileNameStr; |
628 |
|
|
cutFirst(nacScreenshotlDir, "/persistent"); |
629 |
|
|
naclPostMessage("copy-from-persistent", nacScreenshotlDir); |
630 |
|
|
logger->log("nacl screenshot path: " + nacScreenshotlDir); |
631 |
|
|
#endif // __native_client__ |
632 |
|
|
|
633 |
|
|
if (success) |
634 |
|
|
{ |
635 |
|
|
if (localChatTab != nullptr) |
636 |
|
|
{ |
637 |
|
|
// TRANSLATORS: save file message |
638 |
|
|
std::string str = strprintf(_("Screenshot saved as %s"), |
639 |
|
|
fileNameStr.c_str()); |
640 |
|
|
localChatTab->chatLog(str, |
641 |
|
|
ChatMsgType::BY_SERVER, |
642 |
|
|
IgnoreRecord_false, |
643 |
|
|
TryRemoveColors_true); |
644 |
|
|
} |
645 |
|
|
} |
646 |
|
|
else |
647 |
|
|
{ |
648 |
|
|
if (localChatTab != nullptr) |
649 |
|
|
{ |
650 |
|
|
// TRANSLATORS: save file message |
651 |
|
|
localChatTab->chatLog(_("Saving screenshot failed!"), |
652 |
|
|
ChatMsgType::BY_SERVER, |
653 |
|
|
IgnoreRecord_false, |
654 |
|
|
TryRemoveColors_true); |
655 |
|
|
} |
656 |
|
|
logger->log1("Error: could not save screenshot."); |
657 |
|
|
} |
658 |
|
|
|
659 |
|
|
MSDL_FreeSurface(screenshot); |
660 |
|
|
return success; |
661 |
|
|
} |
662 |
|
|
|
663 |
|
|
void Game::logic() |
664 |
|
|
{ |
665 |
|
|
BLOCK_START("Game::logic") |
666 |
|
|
handleInput(); |
667 |
|
|
|
668 |
|
|
// Handle all necessary game logic |
669 |
|
|
if (actorManager != nullptr) |
670 |
|
|
actorManager->logic(); |
671 |
|
|
if (particleEngine != nullptr) |
672 |
|
|
particleEngine->update(); |
673 |
|
|
if (mCurrentMap != nullptr) |
674 |
|
|
mCurrentMap->update(1); |
675 |
|
|
|
676 |
|
|
BLOCK_END("Game::logic") |
677 |
|
|
} |
678 |
|
|
|
679 |
|
|
void Game::slowLogic() |
680 |
|
|
{ |
681 |
|
|
BLOCK_START("Game::slowLogic") |
682 |
|
|
if (localPlayer != nullptr) |
683 |
|
|
localPlayer->slowLogic(); |
684 |
|
|
const time_t time = cur_time; |
685 |
|
|
if (mTime != time) |
686 |
|
|
{ |
687 |
|
|
if (valTest(Updated)) |
688 |
|
|
mValidSpeed = false; |
689 |
|
|
|
690 |
|
|
mTime = time + 1; |
691 |
|
|
if (debugWindow != nullptr) |
692 |
|
|
debugWindow->slowLogic(); |
693 |
|
|
if (killStats != nullptr) |
694 |
|
|
killStats->update(); |
695 |
|
|
if (socialWindow != nullptr) |
696 |
|
|
socialWindow->slowLogic(); |
697 |
|
|
if (whoIsOnline != nullptr) |
698 |
|
|
whoIsOnline->slowLogic(); |
699 |
|
|
Being::reReadConfig(); |
700 |
|
|
if (killStats != nullptr) |
701 |
|
|
killStats->recalcStats(); |
702 |
|
|
|
703 |
|
|
if (time > mTime2 || mTime2 - time > 10) |
704 |
|
|
{ |
705 |
|
|
mTime2 = time + 10; |
706 |
|
|
config.writeUpdated(); |
707 |
|
|
serverConfig.writeUpdated(); |
708 |
|
|
} |
709 |
|
|
if (effectManager != nullptr) |
710 |
|
|
effectManager->logic(); |
711 |
|
|
} |
712 |
|
|
|
713 |
|
|
if (mainGraphics->getOpenGL() != RENDER_SOFTWARE) |
714 |
|
|
DelayedManager::delayedLoad(); |
715 |
|
|
|
716 |
|
|
#ifdef TMWA_SUPPORT |
717 |
|
|
if (shopWindow != nullptr) |
718 |
|
|
shopWindow->updateTimes(); |
719 |
|
|
if (guildManager != nullptr) |
720 |
|
|
guildManager->slowLogic(); |
721 |
|
|
#endif // TMWA_SUPPORT |
722 |
|
|
|
723 |
|
|
if (skillDialog != nullptr) |
724 |
|
|
skillDialog->slowLogic(); |
725 |
|
|
|
726 |
|
|
PacketCounters::update(); |
727 |
|
|
|
728 |
|
|
// Handle network stuff |
729 |
|
|
if (!gameHandler->isConnected()) |
730 |
|
|
{ |
731 |
|
|
if (client->getState() == State::CHANGE_MAP) |
732 |
|
|
return; // Not a problem here |
733 |
|
|
|
734 |
|
|
if (client->getState() != State::ERROR) |
735 |
|
|
{ |
736 |
|
|
if (disconnectedDialog == nullptr) |
737 |
|
|
{ |
738 |
|
|
// TRANSLATORS: error message text |
739 |
|
|
errorMessage = _("The connection to the server was lost."); |
740 |
|
|
disconnectedDialog = DialogsManager::openErrorDialog( |
741 |
|
|
// TRANSLATORS: error message header |
742 |
|
|
_("Network Error"), |
743 |
|
|
errorMessage, |
744 |
|
|
Modal_false); |
745 |
|
|
disconnectedDialog->addActionListener(&errorListener); |
746 |
|
|
disconnectedDialog->requestMoveToTop(); |
747 |
|
|
} |
748 |
|
|
} |
749 |
|
|
|
750 |
|
|
if ((viewport != nullptr) && !errorMessage.empty()) |
751 |
|
|
{ |
752 |
|
|
const Map *const map = viewport->getMap(); |
753 |
|
|
if (map != nullptr) |
754 |
|
|
map->saveExtraLayer(); |
755 |
|
|
} |
756 |
|
|
DialogsManager::closeDialogs(); |
757 |
|
|
WindowManager::setFramerate(config.getIntValue("fpslimit")); |
758 |
|
|
mNextAdjustTime = cur_time + adjustDelay; |
759 |
|
|
if (client->getState() != State::ERROR) |
760 |
|
|
errorMessage.clear(); |
761 |
|
|
} |
762 |
|
|
else |
763 |
|
|
{ |
764 |
|
|
if (gameHandler->mustPing() |
765 |
|
|
&& get_elapsed_time1(mPing) > 3000) |
766 |
|
|
{ |
767 |
|
|
mPing = tick_time; |
768 |
|
|
gameHandler->ping(tick_time); |
769 |
|
|
} |
770 |
|
|
|
771 |
|
|
if (mAdjustPerfomance) |
772 |
|
|
adjustPerfomance(); |
773 |
|
|
if (disconnectedDialog != nullptr) |
774 |
|
|
{ |
775 |
|
|
disconnectedDialog->scheduleDelete(); |
776 |
|
|
disconnectedDialog = nullptr; |
777 |
|
|
} |
778 |
|
|
} |
779 |
|
|
BLOCK_END("Game::slowLogic") |
780 |
|
|
} |
781 |
|
|
|
782 |
|
|
void Game::adjustPerfomance() |
783 |
|
|
{ |
784 |
|
|
FUNC_BLOCK("Game::adjustPerfomance", 1) |
785 |
|
|
const time_t time = cur_time; |
786 |
|
|
if (mNextAdjustTime <= adjustDelay) |
787 |
|
|
{ |
788 |
|
|
mNextAdjustTime = time + adjustDelay; |
789 |
|
|
} |
790 |
|
|
else if (mNextAdjustTime < time) |
791 |
|
|
{ |
792 |
|
|
mNextAdjustTime = time + adjustDelay; |
793 |
|
|
|
794 |
|
|
if (mAdjustLevel > 3 || |
795 |
|
|
localPlayer == nullptr || |
796 |
|
|
localPlayer->getHalfAway() || |
797 |
|
|
settings.awayMode) |
798 |
|
|
{ |
799 |
|
|
return; |
800 |
|
|
} |
801 |
|
|
|
802 |
|
|
int maxFps = WindowManager::getFramerate(); |
803 |
|
|
if (maxFps != config.getIntValue("fpslimit")) |
804 |
|
|
return; |
805 |
|
|
|
806 |
|
|
if (maxFps == 0) |
807 |
|
|
maxFps = 30; |
808 |
|
|
else if (maxFps < 10) |
809 |
|
|
return; |
810 |
|
|
|
811 |
|
|
if (fps < maxFps - 10) |
812 |
|
|
{ |
813 |
|
|
if (mLowerCounter < 2) |
814 |
|
|
{ |
815 |
|
|
mLowerCounter ++; |
816 |
|
|
mNextAdjustTime = cur_time + 1; |
817 |
|
|
return; |
818 |
|
|
} |
819 |
|
|
mLowerCounter = 0; |
820 |
|
|
mAdjustLevel ++; |
821 |
|
|
switch (mAdjustLevel) |
822 |
|
|
{ |
823 |
|
|
case 1: |
824 |
|
|
{ |
825 |
|
|
if (config.getBoolValue("beingopacity")) |
826 |
|
|
{ |
827 |
|
|
config.setValue("beingopacity", false); |
828 |
|
|
config.setSilent("beingopacity", true); |
829 |
|
|
if (localChatTab != nullptr) |
830 |
|
|
{ |
831 |
|
|
localChatTab->chatLog( |
832 |
|
|
// TRANSLATORS: auto adjust settings message |
833 |
|
|
_("Auto disable Show beings transparency"), |
834 |
|
|
ChatMsgType::BY_SERVER, |
835 |
|
|
IgnoreRecord_false, |
836 |
|
|
TryRemoveColors_true); |
837 |
|
|
} |
838 |
|
|
} |
839 |
|
|
else |
840 |
|
|
{ |
841 |
|
|
mNextAdjustTime = time + 1; |
842 |
|
|
mLowerCounter = 2; |
843 |
|
|
} |
844 |
|
|
break; |
845 |
|
|
} |
846 |
|
|
case 2: |
847 |
|
|
if (ParticleEngine::emitterSkip < 4) |
848 |
|
|
{ |
849 |
|
|
ParticleEngine::emitterSkip = 4; |
850 |
|
|
if (localChatTab != nullptr) |
851 |
|
|
{ |
852 |
|
|
localChatTab->chatLog( |
853 |
|
|
// TRANSLATORS: auto adjust settings message |
854 |
|
|
_("Auto lower Particle effects"), |
855 |
|
|
ChatMsgType::BY_SERVER, |
856 |
|
|
IgnoreRecord_false, |
857 |
|
|
TryRemoveColors_true); |
858 |
|
|
} |
859 |
|
|
} |
860 |
|
|
else |
861 |
|
|
{ |
862 |
|
|
mNextAdjustTime = time + 1; |
863 |
|
|
mLowerCounter = 2; |
864 |
|
|
} |
865 |
|
|
break; |
866 |
|
|
case 3: |
867 |
|
|
if (!config.getBoolValue("alphaCache")) |
868 |
|
|
{ |
869 |
|
|
config.setValue("alphaCache", true); |
870 |
|
|
config.setSilent("alphaCache", false); |
871 |
|
|
if (localChatTab != nullptr) |
872 |
|
|
{ |
873 |
|
|
localChatTab->chatLog( |
874 |
|
|
// TRANSLATORS: auto adjust settings message |
875 |
|
|
_("Auto enable opacity cache"), |
876 |
|
|
ChatMsgType::BY_SERVER, |
877 |
|
|
IgnoreRecord_false, |
878 |
|
|
TryRemoveColors_true); |
879 |
|
|
} |
880 |
|
|
} |
881 |
|
|
break; |
882 |
|
|
default: |
883 |
|
|
break; |
884 |
|
|
} |
885 |
|
|
} |
886 |
|
|
} |
887 |
|
|
} |
888 |
|
|
|
889 |
|
|
void Game::resetAdjustLevel() |
890 |
|
|
{ |
891 |
|
|
if (!mAdjustPerfomance) |
892 |
|
|
return; |
893 |
|
|
|
894 |
|
|
mNextAdjustTime = cur_time + adjustDelay; |
895 |
|
|
switch (mAdjustLevel) |
896 |
|
|
{ |
897 |
|
|
case 1: |
898 |
|
|
config.setValue("beingopacity", |
899 |
|
|
config.getBoolValue("beingopacity")); |
900 |
|
|
break; |
901 |
|
|
case 2: |
902 |
|
|
config.setValue("beingopacity", |
903 |
|
|
config.getBoolValue("beingopacity")); |
904 |
|
|
ParticleEngine::emitterSkip = config.getIntValue( |
905 |
|
|
"particleEmitterSkip") + 1; |
906 |
|
|
break; |
907 |
|
|
default: |
908 |
|
|
case 3: |
909 |
|
|
config.setValue("beingopacity", |
910 |
|
|
config.getBoolValue("beingopacity")); |
911 |
|
|
ParticleEngine::emitterSkip = config.getIntValue( |
912 |
|
|
"particleEmitterSkip") + 1; |
913 |
|
|
config.setValue("alphaCache", |
914 |
|
|
config.getBoolValue("alphaCache")); |
915 |
|
|
break; |
916 |
|
|
} |
917 |
|
|
mAdjustLevel = 0; |
918 |
|
|
} |
919 |
|
|
|
920 |
|
|
void Game::handleMove() |
921 |
|
|
{ |
922 |
|
|
BLOCK_START("Game::handleMove") |
923 |
|
|
if (localPlayer == nullptr) |
924 |
|
|
{ |
925 |
|
|
BLOCK_END("Game::handleMove") |
926 |
|
|
return; |
927 |
|
|
} |
928 |
|
|
|
929 |
|
|
// Moving player around |
930 |
|
|
if ((chatWindow != nullptr) && |
931 |
|
|
(quitDialog == nullptr) && |
932 |
|
|
localPlayer->canMove() && |
933 |
|
|
!chatWindow->isInputFocused() && |
934 |
|
|
!InventoryWindow::isAnyInputFocused() && |
935 |
|
|
!popupMenu->isPopupVisible()) |
936 |
|
|
{ |
937 |
|
|
NpcDialog *const dialog = NpcDialog::getActive(); |
938 |
|
|
if (dialog != nullptr) |
939 |
|
|
{ |
940 |
|
|
BLOCK_END("Game::handleMove") |
941 |
|
|
return; |
942 |
|
|
} |
943 |
|
|
|
944 |
|
|
// Ignore input if either "ignore" key is pressed |
945 |
|
|
// Stops the character moving about if the user's window manager |
946 |
|
|
// uses "ignore+arrow key" to switch virtual desktops. |
947 |
|
|
if (inputManager.isActionActive(InputAction::IGNORE_INPUT_1) || |
948 |
|
|
inputManager.isActionActive(InputAction::IGNORE_INPUT_2)) |
949 |
|
|
{ |
950 |
|
|
BLOCK_END("Game::handleMove") |
951 |
|
|
return; |
952 |
|
|
} |
953 |
|
|
|
954 |
|
|
unsigned char direction = 0; |
955 |
|
|
|
956 |
|
|
// Translate pressed keys to movement and direction |
957 |
|
|
if (inputManager.isActionActive(InputAction::MOVE_UP) || |
958 |
|
|
((joystick != nullptr) && joystick->isUp())) |
959 |
|
|
{ |
960 |
|
|
direction |= BeingDirection::UP; |
961 |
|
|
setValidSpeed(); |
962 |
|
|
localPlayer->cancelFollow(); |
963 |
|
|
} |
964 |
|
|
else if (inputManager.isActionActive(InputAction::MOVE_DOWN) || |
965 |
|
|
((joystick != nullptr) && joystick->isDown())) |
966 |
|
|
{ |
967 |
|
|
direction |= BeingDirection::DOWN; |
968 |
|
|
setValidSpeed(); |
969 |
|
|
localPlayer->cancelFollow(); |
970 |
|
|
} |
971 |
|
|
|
972 |
|
|
if (inputManager.isActionActive(InputAction::MOVE_LEFT) || |
973 |
|
|
((joystick != nullptr) && joystick->isLeft())) |
974 |
|
|
{ |
975 |
|
|
direction |= BeingDirection::LEFT; |
976 |
|
|
setValidSpeed(); |
977 |
|
|
localPlayer->cancelFollow(); |
978 |
|
|
} |
979 |
|
|
else if (inputManager.isActionActive(InputAction::MOVE_RIGHT) || |
980 |
|
|
((joystick != nullptr) && joystick->isRight())) |
981 |
|
|
{ |
982 |
|
|
direction |= BeingDirection::RIGHT; |
983 |
|
|
setValidSpeed(); |
984 |
|
|
localPlayer->cancelFollow(); |
985 |
|
|
} |
986 |
|
|
else if (inputManager.isActionActive(InputAction::MOVE_FORWARD)) |
987 |
|
|
{ |
988 |
|
|
direction = localPlayer->getDirection(); |
989 |
|
|
setValidSpeed(); |
990 |
|
|
localPlayer->cancelFollow(); |
991 |
|
|
} |
992 |
|
|
|
993 |
|
|
if ((!inputManager.isActionActive(InputAction::EMOTE) |
994 |
|
|
&& !inputManager.isActionActive(InputAction::PET_EMOTE) |
995 |
|
|
&& !inputManager.isActionActive(InputAction::HOMUN_EMOTE) |
996 |
|
|
&& !inputManager.isActionActive(InputAction::STOP_ATTACK)) |
997 |
|
|
|| direction == 0) |
998 |
|
|
{ |
999 |
|
|
moveInDirection(direction); |
1000 |
|
|
} |
1001 |
|
|
} |
1002 |
|
|
BLOCK_END("Game::handleMove") |
1003 |
|
|
} |
1004 |
|
|
|
1005 |
|
|
void Game::moveInDirection(const unsigned char direction) |
1006 |
|
|
{ |
1007 |
|
|
if (viewport == nullptr) |
1008 |
|
|
return; |
1009 |
|
|
|
1010 |
|
|
if (settings.cameraMode == 0U) |
1011 |
|
|
{ |
1012 |
|
|
if (localPlayer != nullptr) |
1013 |
|
|
localPlayer->specialMove(direction); |
1014 |
|
|
} |
1015 |
|
|
else |
1016 |
|
|
{ |
1017 |
|
|
int dx = 0; |
1018 |
|
|
int dy = 0; |
1019 |
|
|
if ((direction & BeingDirection::LEFT) != 0) |
1020 |
|
|
dx = -5; |
1021 |
|
|
else if ((direction & BeingDirection::RIGHT) != 0) |
1022 |
|
|
dx = 5; |
1023 |
|
|
|
1024 |
|
|
if ((direction & BeingDirection::UP) != 0) |
1025 |
|
|
dy = -5; |
1026 |
|
|
else if ((direction & BeingDirection::DOWN) != 0) |
1027 |
|
|
dy = 5; |
1028 |
|
|
viewport->moveCamera(dx, dy); |
1029 |
|
|
} |
1030 |
|
|
} |
1031 |
|
|
|
1032 |
|
|
void Game::updateFrameRate(int fpsLimit) |
1033 |
|
|
{ |
1034 |
|
|
if (fpsLimit == 0) |
1035 |
|
|
{ |
1036 |
|
|
if (settings.awayMode) |
1037 |
|
|
{ |
1038 |
|
|
if (settings.inputFocused != KeyboardFocus::Unfocused || |
1039 |
|
|
settings.mouseFocused) |
1040 |
|
|
{ |
1041 |
|
|
fpsLimit = config.getIntValue("fpslimit"); |
1042 |
|
|
} |
1043 |
|
|
else |
1044 |
|
|
{ |
1045 |
|
|
fpsLimit = config.getIntValue("altfpslimit"); |
1046 |
|
|
} |
1047 |
|
|
} |
1048 |
|
|
else |
1049 |
|
|
{ |
1050 |
|
|
fpsLimit = config.getIntValue("fpslimit"); |
1051 |
|
|
} |
1052 |
|
|
} |
1053 |
|
|
WindowManager::setFramerate(fpsLimit); |
1054 |
|
|
mNextAdjustTime = cur_time + adjustDelay; |
1055 |
|
|
} |
1056 |
|
|
|
1057 |
|
|
/** |
1058 |
|
|
* The huge input handling method. |
1059 |
|
|
*/ |
1060 |
|
|
void Game::handleInput() |
1061 |
|
|
{ |
1062 |
|
|
BLOCK_START("Game::handleInput 1") |
1063 |
|
|
if (joystick != nullptr) |
1064 |
|
|
joystick->logic(); |
1065 |
|
|
|
1066 |
|
|
eventsManager.handleGameEvents(); |
1067 |
|
|
|
1068 |
|
|
// If the user is configuring the keys then don't respond. |
1069 |
|
|
if (!keyboard.isEnabled() || settings.awayMode) |
1070 |
|
|
{ |
1071 |
|
|
BLOCK_END("Game::handleInput 1") |
1072 |
|
|
return; |
1073 |
|
|
} |
1074 |
|
|
|
1075 |
|
|
// If pressed outfits keys, stop processing keys. |
1076 |
|
|
if (inputManager.isActionActive(InputAction::WEAR_OUTFIT) |
1077 |
|
|
|| inputManager.isActionActive(InputAction::COPY_OUTFIT) |
1078 |
|
|
|| ((setupWindow != nullptr) && setupWindow->isWindowVisible())) |
1079 |
|
|
{ |
1080 |
|
|
BLOCK_END("Game::handleInput 1") |
1081 |
|
|
return; |
1082 |
|
|
} |
1083 |
|
|
|
1084 |
|
|
handleMove(); |
1085 |
|
|
InputManager::handleRepeat(); |
1086 |
|
|
BLOCK_END("Game::handleInput 1") |
1087 |
|
|
} |
1088 |
|
|
|
1089 |
|
|
/** |
1090 |
|
|
* Changes the currently active map. Should only be called while the game is |
1091 |
|
|
* running. |
1092 |
|
|
*/ |
1093 |
|
|
void Game::changeMap(const std::string &mapPath) |
1094 |
|
|
{ |
1095 |
|
|
BLOCK_START("Game::changeMap") |
1096 |
|
|
|
1097 |
|
|
skipPerfFrames = 3; |
1098 |
|
|
resetAdjustLevel(); |
1099 |
|
|
ResourceManager::cleanProtected(); |
1100 |
|
|
|
1101 |
|
|
PopupManager::clearPopup(); |
1102 |
|
|
PopupManager::closePopupMenu(); |
1103 |
|
|
|
1104 |
|
|
// Clean up floor items, beings and particles |
1105 |
|
|
if (actorManager != nullptr) |
1106 |
|
|
actorManager->clear(); |
1107 |
|
|
|
1108 |
|
|
// Close the popup menu on map change so that invalid options can't be |
1109 |
|
|
// executed. |
1110 |
|
|
if (viewport != nullptr) |
1111 |
|
|
viewport->cleanHoverItems(); |
1112 |
|
|
|
1113 |
|
|
// Unset the map of the player so that its particles are cleared before |
1114 |
|
|
// being deleted in the next step |
1115 |
|
|
if (localPlayer != nullptr) |
1116 |
|
|
localPlayer->setMap(nullptr); |
1117 |
|
|
|
1118 |
|
|
if (particleEngine != nullptr) |
1119 |
|
|
particleEngine->clear(); |
1120 |
|
|
|
1121 |
|
|
mMapName = mapPath; |
1122 |
|
|
|
1123 |
|
|
std::string fullMap = pathJoin(paths.getValue("maps", "maps/"), |
1124 |
|
|
mMapName).append(".tmx"); |
1125 |
|
|
std::string realFullMap = pathJoin(paths.getValue("maps", "maps/"), |
1126 |
|
|
MapDB::getMapName(mMapName)).append(".tmx"); |
1127 |
|
|
|
1128 |
|
|
if (!VirtFs::exists(realFullMap)) |
1129 |
|
|
realFullMap.append(".gz"); |
1130 |
|
|
|
1131 |
|
|
// Attempt to load the new map |
1132 |
|
|
Map *const newMap = MapReader::readMap(fullMap, realFullMap); |
1133 |
|
|
|
1134 |
|
|
if (mCurrentMap != nullptr) |
1135 |
|
|
mCurrentMap->saveExtraLayer(); |
1136 |
|
|
|
1137 |
|
|
if (newMap != nullptr) |
1138 |
|
|
newMap->addExtraLayer(); |
1139 |
|
|
|
1140 |
|
|
if (socialWindow != nullptr) |
1141 |
|
|
socialWindow->setMap(newMap); |
1142 |
|
|
|
1143 |
|
|
// Notify the minimap and actorManager about the map change |
1144 |
|
|
if (minimap != nullptr) |
1145 |
|
|
minimap->setMap(newMap); |
1146 |
|
|
if (actorManager != nullptr) |
1147 |
|
|
actorManager->setMap(newMap); |
1148 |
|
|
if (particleEngine != nullptr) |
1149 |
|
|
particleEngine->setMap(newMap); |
1150 |
|
|
if (viewport != nullptr) |
1151 |
|
|
viewport->setMap(newMap); |
1152 |
|
|
|
1153 |
|
|
// Initialize map-based particle effects |
1154 |
|
|
if (newMap != nullptr) |
1155 |
|
|
newMap->initializeParticleEffects(); |
1156 |
|
|
|
1157 |
|
|
// Start playing new music file when necessary |
1158 |
|
|
const std::string oldMusic = mCurrentMap != nullptr |
1159 |
|
|
? mCurrentMap->getMusicFile() : ""; |
1160 |
|
|
const std::string newMusic = newMap != nullptr ? |
1161 |
|
|
newMap->getMusicFile() : ""; |
1162 |
|
|
if (newMusic != oldMusic) |
1163 |
|
|
{ |
1164 |
|
|
if (newMusic.empty()) |
1165 |
|
|
soundManager.fadeOutMusic(1000); |
1166 |
|
|
else |
1167 |
|
|
soundManager.fadeOutAndPlayMusic(newMusic, 1000); |
1168 |
|
|
} |
1169 |
|
|
|
1170 |
|
|
if (mCurrentMap != nullptr) |
1171 |
|
|
mCurrentMap->saveExtraLayer(); |
1172 |
|
|
|
1173 |
|
|
delete mCurrentMap; |
1174 |
|
|
mCurrentMap = newMap; |
1175 |
|
|
|
1176 |
|
|
if (questsWindow != nullptr) |
1177 |
|
|
questsWindow->setMap(mCurrentMap); |
1178 |
|
|
|
1179 |
|
|
#ifdef USE_MUMBLE |
1180 |
|
|
if (mumbleManager) |
1181 |
|
|
mumbleManager->setMap(mapPath); |
1182 |
|
|
#endif // USE_MUMBLE |
1183 |
|
|
|
1184 |
|
|
if (localPlayer != nullptr) |
1185 |
|
|
localPlayer->recreateItemParticles(); |
1186 |
|
|
|
1187 |
|
|
gameHandler->mapLoadedEvent(); |
1188 |
|
|
Perf::init(); |
1189 |
|
|
BLOCK_END("Game::changeMap") |
1190 |
|
|
} |
1191 |
|
|
|
1192 |
|
|
void Game::updateHistory(const SDL_Event &event) |
1193 |
|
|
{ |
1194 |
|
|
if ((localPlayer == nullptr) || (settings.attackType == 0U)) |
1195 |
|
|
return; |
1196 |
|
|
|
1197 |
|
|
if (CAST_S32(event.key.keysym.sym) != -1) |
1198 |
|
|
{ |
1199 |
|
|
bool old = false; |
1200 |
|
|
|
1201 |
|
|
const InputActionT key = KeyboardConfig::getKeyIndex(event, 1); |
1202 |
|
|
const time_t time = cur_time; |
1203 |
|
|
int idx = -1; |
1204 |
|
|
for (int f = 0; f < MAX_LASTKEYS; f ++) |
1205 |
|
|
{ |
1206 |
|
|
LastKey &lastKey = mLastKeys[f]; |
1207 |
|
|
if (lastKey.key == key) |
1208 |
|
|
{ |
1209 |
|
|
idx = f; |
1210 |
|
|
old = true; |
1211 |
|
|
break; |
1212 |
|
|
} |
1213 |
|
|
else if (idx >= 0 && lastKey.time < mLastKeys[idx].time) |
1214 |
|
|
{ |
1215 |
|
|
idx = f; |
1216 |
|
|
} |
1217 |
|
|
} |
1218 |
|
|
if (idx < 0) |
1219 |
|
|
{ |
1220 |
|
|
idx = 0; |
1221 |
|
|
for (int f = 0; f < MAX_LASTKEYS; f ++) |
1222 |
|
|
{ |
1223 |
|
|
LastKey &lastKey = mLastKeys[f]; |
1224 |
|
|
if (lastKey.key == InputAction::NO_VALUE || |
1225 |
|
|
lastKey.time < mLastKeys[idx].time) |
1226 |
|
|
{ |
1227 |
|
|
idx = f; |
1228 |
|
|
} |
1229 |
|
|
} |
1230 |
|
|
} |
1231 |
|
|
|
1232 |
|
|
if (idx < 0) |
1233 |
|
|
idx = 0; |
1234 |
|
|
|
1235 |
|
|
LastKey &keyIdx = mLastKeys[idx]; |
1236 |
|
|
if (!old) |
1237 |
|
|
{ |
1238 |
|
|
keyIdx.time = time; |
1239 |
|
|
keyIdx.key = key; |
1240 |
|
|
keyIdx.cnt = 0; |
1241 |
|
|
} |
1242 |
|
|
else |
1243 |
|
|
{ |
1244 |
|
|
keyIdx.cnt++; |
1245 |
|
|
} |
1246 |
|
|
} |
1247 |
|
|
} |
1248 |
|
|
|
1249 |
|
|
void Game::checkKeys() |
1250 |
|
|
{ |
1251 |
|
|
const int timeRange = 120; |
1252 |
|
|
const int cntInTime = 130; |
1253 |
|
|
|
1254 |
|
|
if ((localPlayer == nullptr) || (settings.attackType == 0U)) |
1255 |
|
|
return; |
1256 |
|
|
|
1257 |
|
|
const time_t time = cur_time; |
1258 |
|
|
for (int f = 0; f < MAX_LASTKEYS; f ++) |
1259 |
|
|
{ |
1260 |
|
|
LastKey &lastKey = mLastKeys[f]; |
1261 |
|
|
if (lastKey.key != InputAction::NO_VALUE) |
1262 |
|
|
{ |
1263 |
|
|
if (lastKey.time + timeRange < time) |
1264 |
|
|
{ |
1265 |
|
|
if (lastKey.cnt > cntInTime) |
1266 |
|
|
mValidSpeed = false; |
1267 |
|
|
lastKey.key = InputAction::NO_VALUE; |
1268 |
|
|
} |
1269 |
|
|
} |
1270 |
|
|
} |
1271 |
|
|
} |
1272 |
|
|
|
1273 |
|
|
void Game::setValidSpeed() |
1274 |
|
|
{ |
1275 |
|
|
clearKeysArray(); |
1276 |
|
|
mValidSpeed = true; |
1277 |
|
|
} |
1278 |
|
|
|
1279 |
|
|
void Game::clearKeysArray() |
1280 |
|
|
{ |
1281 |
|
|
for (int f = 0; f < MAX_LASTKEYS; f ++) |
1282 |
|
|
{ |
1283 |
|
|
mLastKeys[f].time = 0; |
1284 |
|
|
mLastKeys[f].key = InputAction::NO_VALUE; |
1285 |
|
|
mLastKeys[f].cnt = 0; |
1286 |
|
|
} |
1287 |
|
|
} |
1288 |
|
|
|
1289 |
|
|
void Game::videoResized(const int width, const int height) |
1290 |
|
|
{ |
1291 |
|
|
if (viewport != nullptr) |
1292 |
|
|
viewport->setSize(width, height); |
1293 |
|
|
if (windowMenu != nullptr) |
1294 |
|
|
windowMenu->setPosition(width - windowMenu->getWidth(), 0); |
1295 |
✓✗✓✗
|
3 |
} |