game.cpp

main 1674 lines · 45 KB Raw
Alex Alex Commit Client Upload Part 2 19/10/2026 00:06
1/*
2 * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "game.h"
24#include "localplayer.h"
25#include "map.h"
26#include "tile.h"
27#include "creature.h"
28#include "container.h"
29#include "statictext.h"
30#include <framework/core/eventdispatcher.h>
31#include <framework/ui/uimanager.h>
32#include <framework/core/application.h>
33#include "luavaluecasts_client.h"
34#include "protocolgame.h"
35#include "protocolcodes.h"
36
37#include <framework/util/extras.h>
38#include <framework/graphics/graph.h>
39#include <framework/net/packet_player.h>
40#include <framework/net/packet_recorder.h>
41
42Game g_game;
43
44Game::Game()
45{
46 m_protocolVersion = 0;
47 m_clientCustomOs = -1;
48 m_clientVersion = 0;
49 m_online = false;
50 m_denyBotCall = false;
51 m_dead = false;
52 m_serverBeat = 50;
53 m_seq = 0;
54 m_ping = -1;
55 m_pingDelay = 1000;
56 m_newPingDelay = 250;
57 m_canReportBugs = false;
58 m_fightMode = Otc::FightBalanced;
59 m_chaseMode = Otc::DontChase;
60 m_pvpMode = Otc::WhiteDove;
61 m_safeFight = true;
62}
63
64void Game::init()
65{
66 resetGameStates();
67}
68
69void Game::terminate()
70{
71 resetGameStates();
72 m_protocolGame = nullptr;
73}
74
75void Game::resetGameStates()
76{
77 m_online = false;
78 m_denyBotCall = false;
79 m_dead = false;
80 m_serverBeat = 50;
81 m_seq = 0;
82 m_ping = -1;
83 m_canReportBugs = false;
84 m_fightMode = Otc::FightBalanced;
85 m_chaseMode = Otc::DontChase;
86 m_pvpMode = Otc::WhiteDove;
87 m_safeFight = true;
88 m_followingCreature = nullptr;
89 m_attackingCreature = nullptr;
90 m_localPlayer = nullptr;
91 m_pingSent = 0;
92 m_pingReceived = 0;
93 m_walkId = 0;
94 m_walkPrediction = 0;
95 m_coins = 0;
96 m_transferableCoins = 0;
97 m_newPingIds.clear();
98 m_unjustifiedPoints = UnjustifiedPoints();
99
100 for(auto& it : m_containers) {
101 const ContainerPtr& container = it.second;
102 if(container)
103 container->onClose();
104 }
105
106 if(m_pingEvent) {
107 m_pingEvent->cancel();
108 m_pingEvent = nullptr;
109 }
110
111 if (m_newPingEvent) {
112 m_newPingEvent->cancel();
113 m_newPingEvent = nullptr;
114 }
115
116 if(m_checkConnectionEvent) {
117 m_checkConnectionEvent->cancel();
118 m_checkConnectionEvent = nullptr;
119 }
120
121 m_containers.clear();
122 m_vips.clear();
123 m_gmActions.clear();
124 g_map.resetAwareRange();
125}
126
127void Game::processConnectionError(const boost::system::error_code& ec)
128{
129 // connection errors only have meaning if we still have a protocol
130 if(m_protocolGame) {
131 g_lua.callGlobalField("g_game", "onConnectionError", ec.message(), ec.value());
132 processDisconnect();
133 }
134}
135
136void Game::processDisconnect()
137{
138 if(isOnline())
139 processGameEnd();
140
141 if(m_protocolGame) {
142 m_protocolGame->disconnect();
143 m_protocolGame = nullptr;
144 }
145}
146
147void Game::processUpdateNeeded(const std::string& signature)
148{
149 g_lua.callGlobalField("g_game", "onUpdateNeeded", signature);
150}
151
152void Game::processLoginError(const std::string& error)
153{
154 g_lua.callGlobalField("g_game", "onLoginError", error);
155}
156
157void Game::processLoginAdvice(const std::string& message)
158{
159 g_lua.callGlobalField("g_game", "onLoginAdvice", message);
160}
161
162void Game::processLoginWait(const std::string& message, int time)
163{
164 g_lua.callGlobalField("g_game", "onLoginWait", message, time);
165}
166
167void Game::processLoginToken(bool unknown)
168{
169 g_lua.callGlobalField("g_game", "onLoginToken", unknown);
170}
171
172void Game::processLogin()
173{
174 g_lua.callGlobalField("g_game", "onLogin");
175}
176
177void Game::processPendingGame()
178{
179 m_localPlayer->setPendingGame(true);
180 g_lua.callGlobalField("g_game", "onPendingGame");
181 m_protocolGame->sendEnterGame();
182}
183
184void Game::processEnterGame()
185{
186 m_localPlayer->setPendingGame(false);
187 g_lua.callGlobalField("g_game", "onEnterGame");
188}
189
190void Game::processGameStart()
191{
192 m_online = true;
193
194 // synchronize fight modes with the server
195 m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight, m_pvpMode);
196
197 // NOTE: the entire map description and local player information is not known yet (bot call is allowed here)
198 enableBotCall();
199 g_lua.callGlobalField("g_game", "onGameStart");
200 disableBotCall();
201
202 if (g_game.getFeature(Otc::GameExtendedClientPing)) {
203 m_newPingEvent = g_dispatcher.scheduleEvent([] {
204 g_game.newPing();
205 }, m_newPingDelay);
206 }
207 if(g_game.getFeature(Otc::GameClientPing)) {
208 m_pingEvent = g_dispatcher.scheduleEvent([] {
209 g_game.ping();
210 }, m_pingDelay);
211 }
212
213 m_checkConnectionEvent = g_dispatcher.cycleEvent([this] {
214 if(!g_game.isConnectionOk() && !m_connectionFailWarned) {
215 g_lua.callGlobalField("g_game", "onConnectionFailing", true);
216 m_connectionFailWarned = true;
217 } else if(g_game.isConnectionOk() && m_connectionFailWarned) {
218 g_lua.callGlobalField("g_game", "onConnectionFailing", false);
219 m_connectionFailWarned = false;
220 }
221 }, 1000);
222}
223
224void Game::processGameEnd()
225{
226 m_online = false;
227 g_lua.callGlobalField("g_game", "onGameEnd");
228
229 if(m_connectionFailWarned) {
230 g_lua.callGlobalField("g_game", "onConnectionFailing", false);
231 m_connectionFailWarned = false;
232 }
233
234 // reset game state
235 resetGameStates();
236
237 m_worldName = "";
238 m_characterName = "";
239
240 // clean map creatures
241 g_map.cleanDynamicThings();
242}
243
244void Game::processDeath(int deathType, int penality)
245{
246 m_dead = true;
247 m_localPlayer->stopWalk();
248
249 g_lua.callGlobalField("g_game", "onDeath", deathType, penality);
250}
251
252void Game::processGMActions(const std::vector<uint8>& actions)
253{
254 m_gmActions = actions;
255 g_lua.callGlobalField("g_game", "onGMActions", actions);
256}
257
258void Game::processPlayerHelpers(int helpers)
259{
260 g_lua.callGlobalField("g_game", "onPlayerHelpersUpdate", helpers);
261}
262
263void Game::processPlayerModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeMode, Otc::PVPModes pvpMode)
264{
265 m_fightMode = fightMode;
266 m_chaseMode = chaseMode;
267 m_safeFight = safeMode;
268 m_pvpMode = pvpMode;
269
270 g_lua.callGlobalField("g_game", "onFightModeChange", fightMode);
271 g_lua.callGlobalField("g_game", "onChaseModeChange", chaseMode);
272 g_lua.callGlobalField("g_game", "onSafeFightChange", safeMode);
273 g_lua.callGlobalField("g_game", "onPVPModeChange", pvpMode);
274}
275
276void Game::processPing()
277{
278 g_lua.callGlobalField("g_game", "onPing");
279 enableBotCall();
280 m_protocolGame->sendPingBack();
281 disableBotCall();
282}
283
284void Game::processPingBack()
285{
286 m_pingReceived++;
287
288 if (!g_game.getFeature(Otc::GameExtendedClientPing)) {
289 if (m_pingReceived == m_pingSent) {
290 m_ping = m_pingTimer.elapsed_millis();
291 g_graphs[GRAPH_LATENCY].addValue(m_ping);
292 }
293
294 g_lua.callGlobalField("g_game", "onPingBack", m_ping);
295 }
296
297 m_pingEvent = g_dispatcher.scheduleEvent([] {
298 g_game.ping();
299 }, m_pingDelay);
300}
301
302void Game::processNewPing(uint32_t pingId)
303{
304 auto it = m_newPingIds.find(pingId);
305
306 if (it == m_newPingIds.end())
307 return;
308
309 m_ping = it->second.elapsed_millis();
310 g_graphs[GRAPH_LATENCY].addValue(m_ping);
311 g_lua.callGlobalField("g_game", "onPingBack", m_ping);
312}
313
314void Game::processTextMessage(Otc::MessageMode mode, const std::string& text)
315{
316 g_lua.callGlobalField("g_game", "onTextMessage", mode, text);
317}
318
319void Game::processTalk(const std::string& name, int level, Otc::MessageMode mode, const std::string& text, int channelId, const Position& pos)
320{
321 g_lua.callGlobalField("g_game", "onTalk", name, level, mode, text, channelId, pos);
322}
323
324void Game::processOpenContainer(int containerId, const ItemPtr& containerItem, const std::string& name, int capacity, bool hasParent, const std::vector<ItemPtr>& items, bool isUnlocked, bool hasPages, int containerSize, int firstIndex)
325{
326 ContainerPtr previousContainer = getContainer(containerId);
327 ContainerPtr container = ContainerPtr(new Container(containerId, capacity, name, containerItem, hasParent, isUnlocked, hasPages, containerSize, firstIndex));
328 m_containers[containerId] = container;
329 container->onAddItems(items);
330
331 // we might want to close a container here
332 enableBotCall();
333 container->onOpen(previousContainer);
334 disableBotCall();
335
336 if(previousContainer)
337 previousContainer->onClose();
338}
339
340void Game::processCloseContainer(int containerId)
341{
342 ContainerPtr container = getContainer(containerId);
343 if(!container) {
344 return;
345 }
346
347 m_containers[containerId] = nullptr;
348 container->onClose();
349}
350
351void Game::processContainerAddItem(int containerId, const ItemPtr& item, int slot)
352{
353 ContainerPtr container = getContainer(containerId);
354 if(!container) {
355 return;
356 }
357
358 container->onAddItem(item, slot);
359}
360
361void Game::processContainerUpdateItem(int containerId, int slot, const ItemPtr& item)
362{
363 ContainerPtr container = getContainer(containerId);
364 if(!container) {
365 return;
366 }
367
368 container->onUpdateItem(slot, item);
369}
370
371void Game::processContainerRemoveItem(int containerId, int slot, const ItemPtr& lastItem)
372{
373 ContainerPtr container = getContainer(containerId);
374 if(!container) {
375 return;
376 }
377
378 container->onRemoveItem(slot, lastItem);
379}
380
381void Game::processInventoryChange(int slot, const ItemPtr& item)
382{
383 if(item)
384 item->setPosition(Position(65535, slot, 0));
385
386 m_localPlayer->setInventoryItem((Otc::InventorySlot)slot, item);
387}
388
389void Game::processChannelList(const std::vector<std::tuple<int, std::string> >& channelList)
390{
391 g_lua.callGlobalField("g_game", "onChannelList", channelList);
392}
393
394void Game::processOpenChannel(int channelId, const std::string& name)
395{
396 g_lua.callGlobalField("g_game", "onOpenChannel", channelId, name);
397}
398
399void Game::processOpenPrivateChannel(const std::string& name)
400{
401 g_lua.callGlobalField("g_game", "onOpenPrivateChannel", name);
402}
403
404void Game::processOpenOwnPrivateChannel(int channelId, const std::string& name)
405{
406 g_lua.callGlobalField("g_game", "onOpenOwnPrivateChannel", channelId, name);
407}
408
409void Game::processCloseChannel(int channelId)
410{
411 g_lua.callGlobalField("g_game", "onCloseChannel", channelId);
412}
413
414void Game::processRuleViolationChannel(int channelId)
415{
416 g_lua.callGlobalField("g_game", "onRuleViolationChannel", channelId);
417}
418
419void Game::processRuleViolationRemove(const std::string& name)
420{
421 g_lua.callGlobalField("g_game", "onRuleViolationRemove", name);
422}
423
424void Game::processRuleViolationCancel(const std::string& name)
425{
426 g_lua.callGlobalField("g_game", "onRuleViolationCancel", name);
427}
428
429void Game::processRuleViolationLock()
430{
431 g_lua.callGlobalField("g_game", "onRuleViolationLock");
432}
433
434void Game::processVipAdd(uint id, const std::string& name, uint status, const std::string& description, int iconId, bool notifyLogin)
435{
436 m_vips[id] = Vip(name, status, description, iconId, notifyLogin);
437 g_lua.callGlobalField("g_game", "onAddVip", id, name, status, description, iconId, notifyLogin);
438}
439
440void Game::processVipStateChange(uint id, uint status)
441{
442 if (m_vips.find(id) == m_vips.end()) return;
443 std::get<1>(m_vips[id]) = status;
444 g_lua.callGlobalField("g_game", "onVipStateChange", id, status);
445}
446
447void Game::processTutorialHint(int id)
448{
449 g_lua.callGlobalField("g_game", "onTutorialHint", id);
450}
451
452void Game::processAddAutomapFlag(const Position& pos, int icon, const std::string& message)
453{
454 g_lua.callGlobalField("g_game", "onAddAutomapFlag", pos, icon, message);
455}
456
457void Game::processRemoveAutomapFlag(const Position& pos, int icon, const std::string& message)
458{
459 g_lua.callGlobalField("g_game", "onRemoveAutomapFlag", pos, icon, message);
460}
461
462void Game::processOpenOutfitWindow(const Outfit& currentOutfit, const std::vector<std::tuple<int, std::string, int>>& outfitList,
463 const std::vector<std::tuple<int, std::string>>& mountList,
464 const std::vector<std::tuple<int, std::string>>& wingList,
465 const std::vector<std::tuple<int, std::string>>& auraList,
466 const std::vector<std::tuple<int, std::string>>& shaderList,
467 const std::vector<std::tuple<int, std::string>>& healthBarList,
468 const std::vector<std::tuple<int, std::string>>& manaBarList)
469{
470 g_lua.callGlobalField("g_game", "onOpenOutfitWindow", currentOutfit, outfitList, mountList, wingList, auraList, shaderList, healthBarList, manaBarList);
471}
472
473void Game::processOpenNpcTrade(const std::vector<std::tuple<ItemPtr, std::string, int, int64_t, int64_t> >& items)
474{
475 g_lua.callGlobalField("g_game", "onOpenNpcTrade", items);
476}
477
478void Game::processPlayerGoods(uint64_t money, const std::vector<std::tuple<ItemPtr, int> >& goods)
479{
480 g_lua.callGlobalField("g_game", "onPlayerGoods", money, goods);
481}
482
483void Game::processCloseNpcTrade()
484{
485 g_lua.callGlobalField("g_game", "onCloseNpcTrade");
486}
487
488void Game::processOwnTrade(const std::string& name, const std::vector<ItemPtr>& items)
489{
490 g_lua.callGlobalField("g_game", "onOwnTrade", name, items);
491}
492
493void Game::processCounterTrade(const std::string& name, const std::vector<ItemPtr>& items)
494{
495 g_lua.callGlobalField("g_game", "onCounterTrade", name, items);
496}
497
498void Game::processCloseTrade()
499{
500 g_lua.callGlobalField("g_game", "onCloseTrade");
501}
502
503void Game::processEditText(uint id, int itemId, int maxLength, const std::string& text, const std::string& writer, const std::string& date)
504{
505 g_lua.callGlobalField("g_game", "onEditText", id, itemId, maxLength, text, writer, date);
506}
507
508void Game::processEditList(uint id, int doorId, const std::string& text)
509{
510 g_lua.callGlobalField("g_game", "onEditList", id, doorId, text);
511}
512
513void Game::processQuestLog(const std::vector<std::tuple<int, std::string, bool> >& questList)
514{
515 g_lua.callGlobalField("g_game", "onQuestLog", questList);
516}
517
518void Game::processQuestLine(int questId, const std::vector<std::tuple<std::string, std::string, int> >& questMissions)
519{
520 g_lua.callGlobalField("g_game", "onQuestLine", questId, questMissions);
521}
522
523void Game::processModalDialog(uint32 id, std::string title, std::string message, std::vector<std::tuple<int, std::string> > buttonList, int enterButton, int escapeButton, std::vector<std::tuple<int, std::string> > choiceList, bool priority)
524{
525 g_lua.callGlobalField("g_game", "onModalDialog", id, title, message, buttonList, enterButton, escapeButton, choiceList, priority);
526}
527
528void Game::processAttackCancel(uint seq)
529{
530 if(seq == 0 || m_seq == seq)
531 cancelAttack();
532}
533
534void Game::processWalkCancel(Otc::Direction direction)
535{
536 m_localPlayer->cancelWalk(direction);
537}
538
539void Game::processNewWalkCancel(Otc::Direction dir)
540{
541 m_walkId += 1;
542 m_localPlayer->cancelNewWalk(dir);
543}
544
545void Game::processPredictiveWalkCancel(const Position& pos, Otc::Direction dir)
546{
547 m_walkPrediction += 1;
548 if (m_localPlayer->predictiveCancelWalk(pos, m_walkPrediction, dir)) {
549 m_walkId += 1;
550 }
551}
552
553void Game::processWalkId(uint32_t walkId)
554{
555 m_walkId = std::max(m_walkId, walkId); // fixes desync
556}
557
558void Game::loginWorld(const std::string& account, const std::string& password, const std::string& worldName, const std::string& worldHost, int worldPort, const std::string& characterName, const std::string& authenticatorToken, const std::string& sessionKey, const std::string& recordTo)
559{
560 if(m_protocolGame || isOnline())
561 stdext::throw_exception("Unable to login into a world while already online or logging.");
562
563 if(m_protocolVersion == 0)
564 stdext::throw_exception("Must set a valid game protocol version before logging.");
565
566 // reset the new game state
567 resetGameStates();
568
569 m_localPlayer = LocalPlayerPtr(new LocalPlayer);
570 m_localPlayer->setName(characterName);
571
572 m_protocolGame = ProtocolGamePtr(new ProtocolGame);
573 if (!recordTo.empty()) {
574 m_protocolGame->setRecorder(PacketRecorderPtr(new PacketRecorder(recordTo)));
575 }
576 m_protocolGame->login(account, password, worldHost, (uint16)worldPort, characterName, authenticatorToken, sessionKey, worldName);
577 m_characterName = characterName;
578 m_worldName = worldName;
579}
580
581void Game::playRecord(const std::string& file)
582{
583 if (m_protocolGame || isOnline())
584 stdext::throw_exception("Unable to login into a world while already online or logging.");
585
586 if (m_protocolVersion == 0)
587 stdext::throw_exception("Must set a valid game protocol version before logging.");
588
589 auto packetPlayer = PacketPlayerPtr(new PacketPlayer(file));
590 if (!packetPlayer)
591 stdext::throw_exception("Invalid record file.");
592
593 // reset the new game state
594 resetGameStates();
595
596 m_localPlayer = LocalPlayerPtr(new LocalPlayer);
597 m_localPlayer->setName("Player");
598
599 m_protocolGame = ProtocolGamePtr(new ProtocolGame);
600 m_protocolGame->playRecord(packetPlayer);
601 m_characterName = "Player";
602 m_worldName = "Record";
603}
604
605void Game::cancelLogin()
606{
607 // send logout even if the game has not started yet, to make sure that the player doesn't stay logged there
608 if(m_protocolGame)
609 m_protocolGame->sendLogout();
610
611 g_lua.callGlobalField("g_game", "onLogout");
612 processDisconnect();
613}
614
615void Game::forceLogout()
616{
617 if(!isOnline())
618 return;
619
620 g_lua.callGlobalField("g_game", "onLogout");
621 m_protocolGame->sendLogout();
622 processDisconnect();
623}
624
625void Game::safeLogout()
626{
627 if(!isOnline())
628 return;
629
630 g_lua.callGlobalField("g_game", "onLogout");
631 m_protocolGame->sendLogout();
632}
633
634void Game::autoWalk(const std::vector<Otc::Direction>& dirs, Position startPos)
635{
636 if(!canPerformGameAction())
637 return;
638
639 if (dirs.size() == 0)
640 return;
641
642 // protocol limits walk path
643 if((!g_game.getFeature(Otc::GameNewWalking) || dirs.size() > 4097) && dirs.size() > 127) {
644 g_logger.error("Auto walk path too great");
645 return;
646 }
647
648 if (g_extras.debugWalking) {
649 g_logger.info(stdext::format("[%i] Game::autoWalk", (int)g_clock.millis()));
650 }
651
652 // must cancel follow before any new walk
653 if (isFollowing()) {
654 cancelFollow();
655 }
656
657 auto it = dirs.begin();
658 Otc::Direction direction = *it;
659
660 uint8_t flags = 0x04; // auto walk flag
661
662 TilePtr toTile = g_map.getTile(startPos.translatedToDirection(direction));
663 if(startPos == m_localPlayer->getPrewalkingPosition() && toTile && toTile->isWalkable() && !m_localPlayer->isWalking() && m_localPlayer->canWalk(direction, true)) {
664 m_localPlayer->preWalk(direction);
665 m_localPlayer->startServerWalking();
666 flags |= 0x01; // prewalk flag
667 }
668
669 g_lua.callGlobalField("g_game", "onAutoWalk", dirs);
670
671 if (g_game.getFeature(Otc::GameNewWalking))
672 m_protocolGame->sendNewWalk(m_walkId, m_walkPrediction, startPos, flags, dirs);
673 else
674 m_protocolGame->sendAutoWalk(dirs);
675}
676
677void Game::walk(Otc::Direction direction, bool withPreWalk)
678{
679 m_denyBotCall = false;
680 if (!canPerformGameAction()) {
681 m_denyBotCall = true;
682 return;
683 }
684 if (g_extras.debugWalking) {
685 g_logger.info(stdext::format("[%i] Game::walk", (int)g_clock.millis()));
686 }
687
688 g_lua.callGlobalField("g_game", "onWalk", direction, withPreWalk);
689
690 if (g_game.getFeature(Otc::GameNewWalking)) {
691 Position pos = m_localPlayer->getPrewalkingPosition();
692 uint8_t flags = 0;
693 if (withPreWalk) {
694 flags |= 0x01;
695 }
696 m_protocolGame->sendNewWalk(m_walkId, m_walkPrediction, pos, flags, { direction });
697 m_denyBotCall = true;
698 return;
699 }
700
701 switch(direction) {
702 case Otc::North:
703 m_protocolGame->sendWalkNorth();
704 break;
705 case Otc::East:
706 m_protocolGame->sendWalkEast();
707 break;
708 case Otc::South:
709 m_protocolGame->sendWalkSouth();
710 break;
711 case Otc::West:
712 m_protocolGame->sendWalkWest();
713 break;
714 case Otc::NorthEast:
715 m_protocolGame->sendWalkNorthEast();
716 break;
717 case Otc::SouthEast:
718 m_protocolGame->sendWalkSouthEast();
719 break;
720 case Otc::SouthWest:
721 m_protocolGame->sendWalkSouthWest();
722 break;
723 case Otc::NorthWest:
724 m_protocolGame->sendWalkNorthWest();
725 break;
726 default:
727 break;
728 }
729 m_denyBotCall = true;
730}
731
732void Game::turn(Otc::Direction direction)
733{
734 m_denyBotCall = false;
735 if (!canPerformGameAction()) {
736 m_denyBotCall = true;
737 return;
738 }
739
740 if (g_game.getFeature(Otc::GameNewWalking)) {
741 m_localPlayer->setDirection(direction);
742 }
743
744 switch(direction) {
745 case Otc::North:
746 m_protocolGame->sendTurnNorth();
747 break;
748 case Otc::East:
749 m_protocolGame->sendTurnEast();
750 break;
751 case Otc::South:
752 m_protocolGame->sendTurnSouth();
753 break;
754 case Otc::West:
755 m_protocolGame->sendTurnWest();
756 break;
757 default:
758 break;
759 }
760 m_denyBotCall = true;
761}
762
763void Game::stop()
764{
765 if (g_extras.debugWalking) {
766 g_logger.info(stdext::format("[%i] Game::stop", (int)g_clock.millis()));
767 }
768
769 m_denyBotCall = false;
770 if (!canPerformGameAction()) {
771 m_denyBotCall = true;
772 return;
773 }
774
775 if(isFollowing())
776 cancelFollow(); // can change m_denyBotCall
777 m_denyBotCall = false;
778
779 m_protocolGame->sendStop();
780 m_denyBotCall = true;
781}
782
783void Game::look(const ThingPtr& thing, bool isBattleList)
784{
785 if(!canPerformGameAction() || !thing)
786 return;
787
788 if(thing->isCreature() && isBattleList && m_protocolVersion >= 961)
789 m_protocolGame->sendLookCreature(thing->getId());
790 else
791 m_protocolGame->sendLook(thing->getPosition(), thing->getId(), thing->getStackPos());
792}
793
794void Game::move(const ThingPtr& thing, const Position& toPos, int count)
795{
796 if (count <= 0)
797 count = 1;
798
799 if (!canPerformGameAction() || !thing || thing->getPosition() == toPos)
800 return;
801
802 uint id = thing->getId();
803 if (thing->isCreature()) {
804 CreaturePtr creature = thing->static_self_cast<Creature>();
805 id = Proto::Creature;
806 }
807
808 m_protocolGame->sendMove(thing->getPosition(), id, thing->getStackPos(), toPos, count);
809}
810
811void Game::moveRaw(const Position& pos, int id, int stackpos, const Position& toPos, int count)
812{
813 if (!canPerformGameAction())
814 return;
815
816 m_protocolGame->sendMove(pos, id, stackpos, toPos, count);
817}
818
819void Game::moveToParentContainer(const ThingPtr& thing, int count)
820{
821 if(!canPerformGameAction() || !thing || count <= 0)
822 return;
823
824 Position position = thing->getPosition();
825 move(thing, Position(position.x, position.y, 254), count);
826}
827
828void Game::rotate(const ThingPtr& thing)
829{
830 if(!canPerformGameAction() || !thing)
831 return;
832
833 m_protocolGame->sendRotateItem(thing->getPosition(), thing->getId(), thing->getStackPos());
834}
835
836void Game::wrap(const ThingPtr& thing)
837{
838 if (!canPerformGameAction() || !thing)
839 return;
840
841 m_protocolGame->sendWrapableItem(thing->getPosition(), thing->getId(), thing->getStackPos());
842}
843
844void Game::use(const ThingPtr& thing)
845{
846 if(!canPerformGameAction() || !thing)
847 return;
848
849 Position pos = thing->getPosition();
850 if(!pos.isValid()) // virtual item
851 pos = Position(0xFFFF, 0, 0); // inventory item
852
853 // some items, e.g. parcel, are not set as containers but they are.
854 // always try to use these items in free container slots.
855 m_protocolGame->sendUseItem(pos, thing->getId(), thing->getStackPos(), findEmptyContainerId());
856
857 g_lua.callGlobalField("g_game", "onUse", pos, thing->getId(), thing->getStackPos(), 0);
858}
859
860void Game::useInventoryItem(int itemId, int subType)
861{
862 if(!canPerformGameAction() || !g_things.isValidDatId(itemId, ThingCategoryItem))
863 return;
864
865 Position pos = Position(0xFFFF, 0, 0); // means that is a item in inventory
866
867 m_protocolGame->sendUseItem(pos, itemId, 0, subType);
868
869 g_lua.callGlobalField("g_game", "onUse", pos, itemId, 0, subType);
870}
871
872void Game::useWith(const ItemPtr& item, const ThingPtr& toThing, int subType)
873{
874 if(!canPerformGameAction() || !item || !toThing)
875 return;
876
877 Position pos = item->getPosition();
878 if(!pos.isValid()) // virtual item
879 pos = Position(0xFFFF, 0, 0); // means that is an item in inventory
880
881 if(toThing->isCreature() && (g_game.getProtocolVersion() >= 780 || g_game.getFeature(Otc::GameForceAllowItemHotkeys)))
882 m_protocolGame->sendUseOnCreature(pos, item->getId(), subType ? subType : item->getStackPos(), toThing->getId());
883 else
884 m_protocolGame->sendUseItemWith(pos, item->getId(), subType ? subType : item->getStackPos(), toThing->getPosition(), toThing->getId(), toThing->getStackPos());
885
886 g_lua.callGlobalField("g_game", "onUseWith", pos, item->getId(), toThing, subType);
887}
888
889void Game::useInventoryItemWith(int itemId, const ThingPtr& toThing, int subType)
890{
891 if(!canPerformGameAction() || !toThing)
892 return;
893
894 Position pos = Position(0xFFFF, 0, 0); // means that is a item in inventory
895
896 if(toThing->isCreature())
897 m_protocolGame->sendUseOnCreature(pos, itemId, subType, toThing->getId());
898 else
899 m_protocolGame->sendUseItemWith(pos, itemId, subType, toThing->getPosition(), toThing->getId(), toThing->getStackPos());
900
901 g_lua.callGlobalField("g_game", "onUseWith", pos, itemId, toThing, subType);
902}
903
904ItemPtr Game::findItemInContainers(uint itemId, int subType)
905{
906 for(auto& it : m_containers) {
907 const ContainerPtr& container = it.second;
908
909 if(container) {
910 ItemPtr item = container->findItemById(itemId, subType);
911 if(item != nullptr)
912 return item;
913 }
914 }
915 return nullptr;
916}
917
918int Game::open(const ItemPtr& item, const ContainerPtr& previousContainer)
919{
920 if(!canPerformGameAction() || !item)
921 return -1;
922
923 int id = 0;
924 if(!previousContainer)
925 id = findEmptyContainerId();
926 else
927 id = previousContainer->getId();
928
929 m_protocolGame->sendUseItem(item->getPosition(), item->getId(), item->getStackPos(), id);
930 return id;
931}
932
933void Game::openParent(const ContainerPtr& container)
934{
935 if(!canPerformGameAction() || !container)
936 return;
937
938 m_protocolGame->sendUpContainer(container->getId());
939}
940
941void Game::close(const ContainerPtr& container)
942{
943 if(!canPerformGameAction() || !container)
944 return;
945
946 m_protocolGame->sendCloseContainer(container->getId());
947}
948
949void Game::refreshContainer(const ContainerPtr& container)
950{
951 if(!canPerformGameAction())
952 return;
953 m_protocolGame->sendRefreshContainer(container->getId());
954}
955
956void Game::attack(CreaturePtr creature, bool cancel)
957{
958 if(!canPerformGameAction() || creature == m_localPlayer)
959 return;
960
961 // cancel when attacking again
962 if(creature && creature == m_attackingCreature)
963 creature = nullptr;
964
965 if(creature && isFollowing())
966 cancelFollow();
967
968 setAttackingCreature(creature);
969 m_localPlayer->stopAutoWalk();
970
971 if(m_protocolVersion >= 963) {
972 if(creature)
973 m_seq = creature->getId();
974 } else
975 m_seq++;
976
977 if(!cancel)
978 m_protocolGame->sendAttack(creature ? creature->getId() : 0, m_seq);
979}
980
981void Game::follow(CreaturePtr creature)
982{
983 m_denyBotCall = false;
984 if (!canPerformGameAction() || creature == m_localPlayer) {
985 m_denyBotCall = true;
986 return;
987 }
988
989 // cancel when following again
990 if(creature && creature == m_followingCreature)
991 creature = nullptr;
992
993 if(creature && isAttacking())
994 cancelAttack();
995
996 setFollowingCreature(creature);
997 m_localPlayer->stopAutoWalk();
998
999 if(m_protocolVersion >= 963) {
1000 if(creature)
1001 m_seq = creature->getId();
1002 } else
1003 m_seq++;
1004
1005 m_protocolGame->sendFollow(creature ? creature->getId() : 0, m_seq);
1006 m_denyBotCall = true;
1007}
1008
1009void Game::cancelAttackAndFollow()
1010{
1011 if(!canPerformGameAction())
1012 return;
1013
1014 if(isFollowing())
1015 setFollowingCreature(nullptr);
1016 if(isAttacking())
1017 setAttackingCreature(nullptr);
1018
1019 m_localPlayer->stopAutoWalk();
1020
1021 m_protocolGame->sendCancelAttackAndFollow();
1022
1023 g_lua.callGlobalField("g_game", "onCancelAttackAndFollow");
1024}
1025
1026void Game::talk(const std::string& message)
1027{
1028 if(!canPerformGameAction() || message.empty())
1029 return;
1030
1031 talkChannel(Otc::MessageSay, 0, message);
1032}
1033
1034void Game::talkChannel(Otc::MessageMode mode, int channelId, const std::string& message)
1035{
1036 if(!canPerformGameAction() || message.empty())
1037 return;
1038
1039 m_protocolGame->sendTalk(mode, channelId, "", message, m_localPlayer->getPosition(), m_localPlayer->getDirection());
1040}
1041
1042void Game::talkPrivate(Otc::MessageMode mode, const std::string& receiver, const std::string& message)
1043{
1044 if(!canPerformGameAction() || receiver.empty() || message.empty())
1045 return;
1046 m_protocolGame->sendTalk(mode, 0, receiver, message, m_localPlayer->getPosition(), m_localPlayer->getDirection());
1047}
1048
1049void Game::openPrivateChannel(const std::string& receiver)
1050{
1051 if(!canPerformGameAction() || receiver.empty())
1052 return;
1053 m_protocolGame->sendOpenPrivateChannel(receiver);
1054}
1055
1056void Game::requestChannels()
1057{
1058 if(!canPerformGameAction())
1059 return;
1060 m_protocolGame->sendRequestChannels();
1061}
1062
1063void Game::joinChannel(int channelId)
1064{
1065 if(!canPerformGameAction())
1066 return;
1067 m_protocolGame->sendJoinChannel(channelId);
1068}
1069
1070void Game::leaveChannel(int channelId)
1071{
1072 if(!canPerformGameAction())
1073 return;
1074 m_protocolGame->sendLeaveChannel(channelId);
1075}
1076
1077void Game::closeNpcChannel()
1078{
1079 if(!canPerformGameAction())
1080 return;
1081 m_protocolGame->sendCloseNpcChannel();
1082}
1083
1084void Game::openOwnChannel()
1085{
1086 if(!canPerformGameAction())
1087 return;
1088 m_protocolGame->sendOpenOwnChannel();
1089}
1090
1091void Game::inviteToOwnChannel(const std::string& name)
1092{
1093 if(!canPerformGameAction() || name.empty())
1094 return;
1095 m_protocolGame->sendInviteToOwnChannel(name);
1096}
1097
1098void Game::excludeFromOwnChannel(const std::string& name)
1099{
1100 if(!canPerformGameAction() || name.empty())
1101 return;
1102 m_protocolGame->sendExcludeFromOwnChannel(name);
1103}
1104
1105void Game::partyInvite(int creatureId)
1106{
1107 if(!canPerformGameAction())
1108 return;
1109 m_protocolGame->sendInviteToParty(creatureId);
1110}
1111
1112void Game::partyJoin(int creatureId)
1113{
1114 if(!canPerformGameAction())
1115 return;
1116 m_protocolGame->sendJoinParty(creatureId);
1117}
1118
1119void Game::partyRevokeInvitation(int creatureId)
1120{
1121 if(!canPerformGameAction())
1122 return;
1123 m_protocolGame->sendRevokeInvitation(creatureId);
1124}
1125
1126void Game::partyPassLeadership(int creatureId)
1127{
1128 if(!canPerformGameAction())
1129 return;
1130 m_protocolGame->sendPassLeadership(creatureId);
1131}
1132
1133void Game::partyLeave()
1134{
1135 if(!canPerformGameAction())
1136 return;
1137 m_protocolGame->sendLeaveParty();
1138}
1139
1140void Game::partyShareExperience(bool active)
1141{
1142 if(!canPerformGameAction())
1143 return;
1144 m_protocolGame->sendShareExperience(active);
1145}
1146
1147void Game::requestOutfit()
1148{
1149 if(!canPerformGameAction())
1150 return;
1151 m_protocolGame->sendRequestOutfit();
1152}
1153
1154void Game::changeOutfit(const Outfit& outfit)
1155{
1156 if(!canPerformGameAction())
1157 return;
1158 m_protocolGame->sendChangeOutfit(outfit);
1159}
1160
1161void Game::addVip(const std::string& name)
1162{
1163 if(!canPerformGameAction() || name.empty())
1164 return;
1165 m_protocolGame->sendAddVip(name);
1166}
1167
1168void Game::removeVip(int playerId)
1169{
1170 if(!canPerformGameAction())
1171 return;
1172
1173 auto it = m_vips.find(playerId);
1174 if(it == m_vips.end())
1175 return;
1176 m_vips.erase(it);
1177 m_protocolGame->sendRemoveVip(playerId);
1178}
1179
1180void Game::editVip(int playerId, const std::string& description, int iconId, bool notifyLogin)
1181{
1182 if(!canPerformGameAction())
1183 return;
1184
1185 auto it = m_vips.find(playerId);
1186 if(it == m_vips.end())
1187 return;
1188
1189 std::get<2>(m_vips[playerId]) = description;
1190 std::get<3>(m_vips[playerId]) = iconId;
1191 std::get<4>(m_vips[playerId]) = notifyLogin;
1192
1193 if(getFeature(Otc::GameAdditionalVipInfo))
1194 m_protocolGame->sendEditVip(playerId, description, iconId, notifyLogin);
1195}
1196
1197void Game::setChaseMode(Otc::ChaseModes chaseMode)
1198{
1199 if(!canPerformGameAction())
1200 return;
1201 if(m_chaseMode == chaseMode)
1202 return;
1203 m_chaseMode = chaseMode;
1204 m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight, m_pvpMode);
1205 g_lua.callGlobalField("g_game", "onChaseModeChange", chaseMode);
1206}
1207
1208void Game::setFightMode(Otc::FightModes fightMode)
1209{
1210 if(!canPerformGameAction())
1211 return;
1212 if(m_fightMode == fightMode)
1213 return;
1214 m_fightMode = fightMode;
1215 m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight, m_pvpMode);
1216 g_lua.callGlobalField("g_game", "onFightModeChange", fightMode);
1217}
1218
1219void Game::setSafeFight(bool on)
1220{
1221 if(!canPerformGameAction())
1222 return;
1223 if(m_safeFight == on)
1224 return;
1225 m_safeFight = on;
1226 m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight, m_pvpMode);
1227 g_lua.callGlobalField("g_game", "onSafeFightChange", on);
1228}
1229
1230void Game::setPVPMode(Otc::PVPModes pvpMode)
1231{
1232 if(!canPerformGameAction())
1233 return;
1234 if(!getFeature(Otc::GamePVPMode))
1235 return;
1236 if(m_pvpMode == pvpMode)
1237 return;
1238 m_pvpMode = pvpMode;
1239 m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight, m_pvpMode);
1240 g_lua.callGlobalField("g_game", "onPVPModeChange", pvpMode);
1241}
1242
1243void Game::setUnjustifiedPoints(UnjustifiedPoints unjustifiedPoints)
1244{
1245 if(!canPerformGameAction())
1246 return;
1247 if(!getFeature(Otc::GameUnjustifiedPoints))
1248 return;
1249 if(m_unjustifiedPoints == unjustifiedPoints)
1250 return;
1251
1252 m_unjustifiedPoints = unjustifiedPoints;
1253 g_lua.callGlobalField("g_game", "onUnjustifiedPointsChange", unjustifiedPoints);
1254}
1255
1256void Game::setOpenPvpSituations(int openPvpSituations)
1257{
1258 if(!canPerformGameAction())
1259 return;
1260 if(m_openPvpSituations == openPvpSituations)
1261 return;
1262
1263 m_openPvpSituations = openPvpSituations;
1264 g_lua.callGlobalField("g_game", "onOpenPvpSituationsChange", openPvpSituations);
1265}
1266
1267
1268void Game::inspectNpcTrade(const ItemPtr& item)
1269{
1270 if(!canPerformGameAction() || !item)
1271 return;
1272 m_protocolGame->sendInspectNpcTrade(item->getId(), item->getCount());
1273}
1274
1275void Game::buyItem(const ItemPtr& item, int amount, bool ignoreCapacity, bool buyWithBackpack)
1276{
1277 if(!canPerformGameAction() || !item)
1278 return;
1279 m_protocolGame->sendBuyItem(item->getId(), item->getCountOrSubType(), amount, ignoreCapacity, buyWithBackpack);
1280}
1281
1282void Game::sellItem(const ItemPtr& item, int amount, bool ignoreEquipped)
1283{
1284 if(!canPerformGameAction() || !item)
1285 return;
1286 m_protocolGame->sendSellItem(item->getId(), item->getCountOrSubType(), amount, ignoreEquipped);
1287}
1288
1289void Game::closeNpcTrade()
1290{
1291 if(!canPerformGameAction())
1292 return;
1293 m_protocolGame->sendCloseNpcTrade();
1294}
1295
1296void Game::requestTrade(const ItemPtr& item, const CreaturePtr& creature)
1297{
1298 if(!canPerformGameAction() || !item || !creature)
1299 return;
1300 m_protocolGame->sendRequestTrade(item->getPosition(), item->getId(), item->getStackPos(), creature->getId());
1301}
1302
1303void Game::inspectTrade(bool counterOffer, int index)
1304{
1305 if(!canPerformGameAction())
1306 return;
1307 m_protocolGame->sendInspectTrade(counterOffer, index);
1308}
1309
1310void Game::acceptTrade()
1311{
1312 if(!canPerformGameAction())
1313 return;
1314 m_protocolGame->sendAcceptTrade();
1315}
1316
1317void Game::rejectTrade()
1318{
1319 if(!canPerformGameAction())
1320 return;
1321 m_protocolGame->sendRejectTrade();
1322}
1323
1324void Game::editText(uint id, const std::string& text)
1325{
1326 if(!canPerformGameAction())
1327 return;
1328 m_protocolGame->sendEditText(id, text);
1329}
1330
1331void Game::editList(uint id, int doorId, const std::string& text)
1332{
1333 if(!canPerformGameAction())
1334 return;
1335 m_protocolGame->sendEditList(id, doorId, text);
1336}
1337
1338void Game::openRuleViolation(const std::string& reporter)
1339{
1340 if(!canPerformGameAction())
1341 return;
1342 m_protocolGame->sendOpenRuleViolation(reporter);
1343}
1344
1345void Game::closeRuleViolation(const std::string& reporter)
1346{
1347 if(!canPerformGameAction())
1348 return;
1349 m_protocolGame->sendCloseRuleViolation(reporter);
1350}
1351
1352void Game::cancelRuleViolation()
1353{
1354 if(!canPerformGameAction())
1355 return;
1356 m_protocolGame->sendCancelRuleViolation();
1357}
1358
1359void Game::reportBug(const std::string& comment)
1360{
1361 if(!canPerformGameAction())
1362 return;
1363 m_protocolGame->sendBugReport(comment);
1364}
1365
1366void Game::reportRuleViolation(const std::string& target, int reason, int action, const std::string& comment, const std::string& statement, int statementId, bool ipBanishment)
1367{
1368 if(!canPerformGameAction())
1369 return;
1370 m_protocolGame->sendRuleViolation(target, reason, action, comment, statement, statementId, ipBanishment);
1371}
1372
1373void Game::debugReport(const std::string& a, const std::string& b, const std::string& c, const std::string& d)
1374{
1375 m_protocolGame->sendDebugReport(a, b, c, d);
1376}
1377
1378void Game::requestQuestLog()
1379{
1380 if(!canPerformGameAction())
1381 return;
1382 m_protocolGame->sendRequestQuestLog();
1383}
1384
1385void Game::requestQuestLine(int questId)
1386{
1387 if(!canPerformGameAction())
1388 return;
1389 m_protocolGame->sendRequestQuestLine(questId);
1390}
1391
1392void Game::equipItem(const ItemPtr& item)
1393{
1394 if (!item || !canPerformGameAction())
1395 return;
1396 m_protocolGame->sendEquipItem(item->getId(), item->getCountOrSubType());
1397}
1398
1399void Game::equipItemId(int itemId, int subType)
1400{
1401 if (!canPerformGameAction())
1402 return;
1403 m_protocolGame->sendEquipItem(itemId, subType);
1404}
1405
1406void Game::mount(bool mount)
1407{
1408 if(!canPerformGameAction())
1409 return;
1410 m_protocolGame->sendOutfitExtensionStatus(mount ? 1 : 0);
1411}
1412
1413void Game::setOutfitExtensions(int mount, int wings, int aura, int shader, int healthBar, int manaBar)
1414{
1415 if (!canPerformGameAction())
1416 return;
1417 m_protocolGame->sendOutfitExtensionStatus(mount, wings, aura, shader, healthBar, manaBar);
1418}
1419
1420void Game::requestItemInfo(const ItemPtr& item, int index)
1421{
1422 if(!canPerformGameAction())
1423 return;
1424 m_protocolGame->sendRequestItemInfo(item->getId(), item->getSubType(), index);
1425}
1426
1427void Game::answerModalDialog(uint32 dialog, int button, int choice)
1428{
1429 if(!canPerformGameAction())
1430 return;
1431 m_protocolGame->sendAnswerModalDialog(dialog, button, choice);
1432}
1433
1434void Game::browseField(const Position& position)
1435{
1436 if(!canPerformGameAction())
1437 return;
1438 m_protocolGame->sendBrowseField(position);
1439}
1440
1441void Game::seekInContainer(int cid, int index)
1442{
1443 if(!canPerformGameAction())
1444 return;
1445 m_protocolGame->sendSeekInContainer(cid, index);
1446}
1447
1448void Game::buyStoreOffer(int offerId, int productType, const std::string& name)
1449{
1450 if(!canPerformGameAction())
1451 return;
1452 m_protocolGame->sendBuyStoreOffer(offerId, productType, name);
1453}
1454
1455void Game::requestTransactionHistory(int page, int entriesPerPage)
1456{
1457 if(!canPerformGameAction())
1458 return;
1459 m_protocolGame->sendRequestTransactionHistory(page, entriesPerPage);
1460}
1461
1462void Game::requestStoreOffers(const std::string& categoryName, int serviceType)
1463{
1464 if(!canPerformGameAction())
1465 return;
1466 m_protocolGame->sendRequestStoreOffers(categoryName, serviceType);
1467}
1468
1469void Game::openStore(int serviceType)
1470{
1471 if(!canPerformGameAction())
1472 return;
1473 m_protocolGame->sendOpenStore(serviceType);
1474}
1475
1476void Game::transferCoins(const std::string& recipient, int amount)
1477{
1478 if(!canPerformGameAction())
1479 return;
1480 m_protocolGame->sendTransferCoins(recipient, amount);
1481}
1482
1483void Game::openTransactionHistory(int entriesPerPage)
1484{
1485 if(!canPerformGameAction())
1486 return;
1487 m_protocolGame->sendOpenTransactionHistory(entriesPerPage);
1488}
1489
1490void Game::preyAction(int slot, int actionType, int index)
1491{
1492 if (!canPerformGameAction())
1493 return;
1494 m_protocolGame->sendPreyAction(slot, actionType, index);
1495}
1496
1497void Game::preyRequest()
1498{
1499 if (!canPerformGameAction())
1500 return;
1501 m_protocolGame->sendPreyRequest();
1502}
1503
1504
1505void Game::applyImbuement(uint8_t slot, uint32_t imbuementId, bool protectionCharm)
1506{
1507 if (!canPerformGameAction())
1508 return;
1509 m_protocolGame->sendApplyImbuement(slot, imbuementId, protectionCharm);
1510}
1511
1512void Game::clearImbuement(uint8_t slot)
1513{
1514 if (!canPerformGameAction())
1515 return;
1516 m_protocolGame->sendClearImbuement(slot);
1517}
1518
1519void Game::closeImbuingWindow()
1520{
1521 if (!canPerformGameAction())
1522 return;
1523 m_protocolGame->sendCloseImbuingWindow();
1524}
1525
1526void Game::ping()
1527{
1528 if(!m_protocolGame || !m_protocolGame->isConnected())
1529 return;
1530
1531 if(m_pingReceived != m_pingSent)
1532 return;
1533
1534 m_denyBotCall = false;
1535 m_protocolGame->sendPing();
1536 m_denyBotCall = true;
1537 m_pingSent++;
1538 m_pingTimer.restart();
1539}
1540
1541void Game::newPing()
1542{
1543 if(!m_protocolGame || !m_protocolGame->isConnected())
1544 return;
1545
1546 static uint32_t pingId = 1;
1547 pingId += 1;
1548 m_newPingIds[pingId] = stdext::timer();
1549
1550 m_protocolGame->sendNewPing(pingId, (int16_t)m_ping, (int16_t)g_app.getFps());
1551 m_newPingEvent = g_dispatcher.scheduleEvent([] {
1552 g_game.newPing();
1553 }, m_newPingDelay);
1554}
1555
1556void Game::changeMapAwareRange(int xrange, int yrange)
1557{
1558 if(!canPerformGameAction())
1559 return;
1560 m_protocolGame->sendChangeMapAwareRange(xrange, yrange);
1561}
1562
1563bool Game::checkBotProtection()
1564{
1565 if (getFeature(Otc::GameBotProtection)) {
1566 // accepts calls comming from a stacktrace containing only C++ functions,
1567 // if the stacktrace contains a lua function, then only accept if the engine is processing an input event
1568 if (m_denyBotCall && g_lua.isInCppCallback() && !g_app.isOnInputEvent() && !g_dispatcher.isBotSafe()) {
1569 g_logger.error(g_lua.traceback("caught a lua call to a bot protected game function, the call was cancelled"));
1570 return false;
1571 }
1572 }
1573
1574 return true;
1575}
1576
1577bool Game::canPerformGameAction()
1578{
1579 // we can only perform game actions if we meet these conditions:
1580 // - the game is online
1581 // - the local player exists
1582 // - the local player is not dead
1583 // - we have a game protocol
1584 // - the game protocol is connected
1585 // - its not a bot action
1586 return m_online && m_localPlayer && !m_localPlayer->isDead() && !m_dead && m_protocolGame && m_protocolGame->isConnected() && checkBotProtection();
1587}
1588
1589void Game::setProtocolVersion(int version)
1590{
1591 if(m_protocolVersion == version)
1592 return;
1593
1594 if(isOnline())
1595 stdext::throw_exception("Unable to change protocol version while online");
1596
1597 m_protocolVersion = version;
1598
1599 Proto::buildMessageModesMap(version);
1600
1601 g_lua.callGlobalField("g_game", "onProtocolVersionChange", version);
1602}
1603
1604void Game::setClientVersion(int version)
1605{
1606 if(isOnline())
1607 stdext::throw_exception("Unable to change client version while online");
1608
1609 m_clientVersion = version;
1610 g_lua.callGlobalField("g_game", "onClientVersionChange", version);
1611}
1612
1613void Game::setAttackingCreature(const CreaturePtr& creature)
1614{
1615 if(creature != m_attackingCreature) {
1616 CreaturePtr oldCreature = m_attackingCreature;
1617 m_attackingCreature = creature;
1618
1619 g_lua.callGlobalField("g_game", "onAttackingCreatureChange", creature, oldCreature);
1620 }
1621}
1622
1623void Game::setFollowingCreature(const CreaturePtr& creature)
1624{
1625 CreaturePtr oldCreature = m_followingCreature;
1626 m_followingCreature = creature;
1627
1628 g_lua.callGlobalField("g_game", "onFollowingCreatureChange", creature, oldCreature);
1629}
1630
1631std::string Game::formatCreatureName(const std::string& name)
1632{
1633 std::string formatedName = name;
1634 if(getFeature(Otc::GameFormatCreatureName) && name.length() > 0) {
1635 bool upnext = true;
1636 for(uint i=0;i<formatedName.length();++i) {
1637 char ch = formatedName[i];
1638 if(upnext) {
1639 formatedName[i] = stdext::upchar(ch);
1640 upnext = false;
1641 }
1642 if(ch == ' ')
1643 upnext = true;
1644 }
1645 }
1646 return formatedName;
1647}
1648
1649int Game::findEmptyContainerId()
1650{
1651 int id = 0;
1652 while(m_containers[id] != nullptr)
1653 id++;
1654 return id;
1655}
1656
1657int Game::getOs()
1658{
1659 if(m_clientCustomOs >= 0)
1660 return m_clientCustomOs;
1661
1662 if(g_app.getOs() == "windows")
1663 return 20;
1664 if(g_app.getOs() == "mac")
1665 return 22;
1666 if (g_app.getOs() == "android")
1667 return 23;
1668 if (g_app.getOs() == "ios")
1669 return 24;
1670 if (g_app.getOs() == "web")
1671 return 25;
1672 return 21; // linux
1673}
1674
Top