No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gameinterface.lua 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. WALK_STEPS_RETRY = 10
  2. gameRootPanel = nil
  3. gameMapPanel = nil
  4. gameRightPanel = nil
  5. gameLeftPanel = nil
  6. gameBottomPanel = nil
  7. showTopMenuButton = nil
  8. logoutButton = nil
  9. mouseGrabberWidget = nil
  10. countWindow = nil
  11. logoutWindow = nil
  12. exitWindow = nil
  13. bottomSplitter = nil
  14. limitedZoom = false
  15. currentViewMode = 0
  16. smartWalkDirs = {}
  17. smartWalkDir = nil
  18. walkFunction = nil
  19. hookedMenuOptions = {}
  20. lastDirTime = g_clock.millis()
  21. function init()
  22. g_ui.importStyle('styles/countwindow')
  23. connect(g_game, {
  24. onGameStart = onGameStart,
  25. onGameEnd = onGameEnd,
  26. onLoginAdvice = onLoginAdvice,
  27. }, true)
  28. -- Call load AFTER game window has been created and
  29. -- resized to a stable state, otherwise the saved
  30. -- settings can get overridden by false onGeometryChange
  31. -- events
  32. connect(g_app, {
  33. onRun = load,
  34. onExit = save
  35. })
  36. gameRootPanel = g_ui.displayUI('gameinterface')
  37. gameRootPanel:hide()
  38. gameRootPanel:lower()
  39. gameRootPanel.onGeometryChange = updateStretchShrink
  40. gameRootPanel.onFocusChange = stopSmartWalk
  41. mouseGrabberWidget = gameRootPanel:getChildById('mouseGrabber')
  42. mouseGrabberWidget.onMouseRelease = onMouseGrabberRelease
  43. bottomSplitter = gameRootPanel:getChildById('bottomSplitter')
  44. gameMapPanel = gameRootPanel:getChildById('gameMapPanel')
  45. gameRightPanel = gameRootPanel:getChildById('gameRightPanel')
  46. gameLeftPanel = gameRootPanel:getChildById('gameLeftPanel')
  47. gameBottomPanel = gameRootPanel:getChildById('gameBottomPanel')
  48. connect(gameLeftPanel, { onVisibilityChange = onLeftPanelVisibilityChange })
  49. logoutButton = modules.client_topmenu.addLeftButton('logoutButton', tr('Exit'),
  50. '/images/topbuttons/logout', tryLogout, true)
  51. showTopMenuButton = gameMapPanel:getChildById('showTopMenuButton')
  52. showTopMenuButton.onClick = function()
  53. modules.client_topmenu.toggle()
  54. end
  55. setupViewMode(0)
  56. bindKeys()
  57. if g_game.isOnline() then
  58. show()
  59. end
  60. end
  61. function bindKeys()
  62. gameRootPanel:setAutoRepeatDelay(200)
  63. bindWalkKey('Up', North)
  64. bindWalkKey('Right', East)
  65. bindWalkKey('Down', South)
  66. bindWalkKey('Left', West)
  67. bindWalkKey('Numpad8', North)
  68. bindWalkKey('Numpad9', NorthEast)
  69. bindWalkKey('Numpad6', East)
  70. bindWalkKey('Numpad3', SouthEast)
  71. bindWalkKey('Numpad2', South)
  72. bindWalkKey('Numpad1', SouthWest)
  73. bindWalkKey('Numpad4', West)
  74. bindWalkKey('Numpad7', NorthWest)
  75. bindTurnKey('Ctrl+Up', North)
  76. bindTurnKey('Ctrl+Right', East)
  77. bindTurnKey('Ctrl+Down', South)
  78. bindTurnKey('Ctrl+Left', West)
  79. bindTurnKey('Ctrl+Numpad8', North)
  80. bindTurnKey('Ctrl+Numpad6', East)
  81. bindTurnKey('Ctrl+Numpad2', South)
  82. bindTurnKey('Ctrl+Numpad4', West)
  83. g_keyboard.bindKeyPress('Escape', function() g_game.cancelAttackAndFollow() end, gameRootPanel)
  84. g_keyboard.bindKeyPress('Ctrl+=', function() gameMapPanel:zoomIn() end, gameRootPanel)
  85. g_keyboard.bindKeyPress('Ctrl+-', function() gameMapPanel:zoomOut() end, gameRootPanel)
  86. g_keyboard.bindKeyDown('Ctrl+Q', function() tryLogout(false) end, gameRootPanel)
  87. g_keyboard.bindKeyDown('Ctrl+L', function() tryLogout(false) end, gameRootPanel)
  88. g_keyboard.bindKeyDown('Ctrl+W', function() g_map.cleanTexts() modules.game_textmessage.clearMessages() end, gameRootPanel)
  89. g_keyboard.bindKeyDown('Ctrl+.', nextViewMode, gameRootPanel)
  90. end
  91. function bindWalkKey(key, dir)
  92. g_keyboard.bindKeyDown(key, function() changeWalkDir(dir) end, gameRootPanel, true)
  93. g_keyboard.bindKeyUp(key, function() changeWalkDir(dir, true) end, gameRootPanel, true)
  94. g_keyboard.bindKeyPress(key, function() smartWalk(dir) end, gameRootPanel)
  95. end
  96. function unbindWalkKey(key)
  97. g_keyboard.unbindKeyDown(key, gameRootPanel)
  98. g_keyboard.unbindKeyUp(key, gameRootPanel)
  99. g_keyboard.unbindKeyPress(key, gameRootPanel)
  100. end
  101. function bindTurnKey(key, dir)
  102. local function callback(widget, code, repeatTicks)
  103. if g_clock.millis() - lastDirTime >= modules.client_options.getOption('turnDelay') then
  104. g_game.turn(dir)
  105. changeWalkDir(dir)
  106. lastDirTime = g_clock.millis()
  107. end
  108. end
  109. g_keyboard.bindKeyPress(key, callback, gameRootPanel)
  110. end
  111. function terminate()
  112. hide()
  113. hookedMenuOptions = {}
  114. stopSmartWalk()
  115. disconnect(g_game, {
  116. onGameStart = onGameStart,
  117. onGameEnd = onGameEnd,
  118. onLoginAdvice = onLoginAdvice
  119. })
  120. disconnect(gameLeftPanel, { onVisibilityChange = onLeftPanelVisibilityChange })
  121. logoutButton:destroy()
  122. gameRootPanel:destroy()
  123. end
  124. function onGameStart()
  125. show()
  126. -- open tibia has delay in auto walking
  127. if not g_game.isOfficialTibia() then
  128. g_game.enableFeature(GameForceFirstAutoWalkStep)
  129. else
  130. g_game.disableFeature(GameForceFirstAutoWalkStep)
  131. end
  132. end
  133. function onGameEnd()
  134. setupViewMode(0)
  135. hide()
  136. end
  137. function show()
  138. connect(g_app, { onClose = tryExit })
  139. modules.client_background.hide()
  140. gameRootPanel:show()
  141. gameRootPanel:focus()
  142. gameMapPanel:followCreature(g_game.getLocalPlayer())
  143. setupViewMode(0)
  144. updateStretchShrink()
  145. logoutButton:setTooltip(tr('Logout'))
  146. addEvent(function()
  147. if not limitedZoom or g_game.isGM() then
  148. gameMapPanel:setMaxZoomOut(513)
  149. gameMapPanel:setLimitVisibleRange(false)
  150. else
  151. gameMapPanel:setMaxZoomOut(11)
  152. gameMapPanel:setLimitVisibleRange(true)
  153. end
  154. end)
  155. end
  156. function hide()
  157. disconnect(g_app, { onClose = tryExit })
  158. logoutButton:setTooltip(tr('Exit'))
  159. if logoutWindow then
  160. logoutWindow:destroy()
  161. logoutWindow = nil
  162. end
  163. if exitWindow then
  164. exitWindow:destroy()
  165. exitWindow = nil
  166. end
  167. if countWindow then
  168. countWindow:destroy()
  169. countWindow = nil
  170. end
  171. gameRootPanel:hide()
  172. modules.client_background.show()
  173. end
  174. function save()
  175. local settings = {}
  176. settings.splitterMarginBottom = bottomSplitter:getMarginBottom()
  177. g_settings.setNode('game_interface', settings)
  178. end
  179. function load()
  180. local settings = g_settings.getNode('game_interface')
  181. if settings then
  182. if settings.splitterMarginBottom then
  183. bottomSplitter:setMarginBottom(settings.splitterMarginBottom)
  184. end
  185. end
  186. end
  187. function onLoginAdvice(message)
  188. displayInfoBox(tr("For Your Information"), message)
  189. end
  190. function forceExit()
  191. g_game.cancelLogin()
  192. scheduleEvent(exit, 10)
  193. return true
  194. end
  195. function tryExit()
  196. if exitWindow then
  197. return true
  198. end
  199. local exitFunc = function() g_game.safeLogout() forceExit() end
  200. local logoutFunc = function() g_game.safeLogout() exitWindow:destroy() exitWindow = nil end
  201. local cancelFunc = function() exitWindow:destroy() exitWindow = nil end
  202. exitWindow = displayGeneralBox(tr('Exit'), tr("If you shut down the program, your character might stay in the game.\nClick on 'Logout' to ensure that you character leaves the game properly.\nClick on 'Exit' if you want to exit the program without logging out your character."),
  203. { { text=tr('Force Exit'), callback=exitFunc },
  204. { text=tr('Logout'), callback=logoutFunc },
  205. { text=tr('Cancel'), callback=cancelFunc },
  206. anchor=AnchorHorizontalCenter }, logoutFunc, cancelFunc)
  207. return true
  208. end
  209. function tryLogout(prompt)
  210. if type(prompt) ~= "boolean" then
  211. prompt = true
  212. end
  213. if not g_game.isOnline() then
  214. exit()
  215. return
  216. end
  217. if logoutWindow then
  218. return
  219. end
  220. local msg, yesCallback
  221. if not g_game.isConnectionOk() then
  222. msg = 'Your connection is failing, if you logout now your character will be still online, do you want to force logout?'
  223. yesCallback = function()
  224. g_game.forceLogout()
  225. if logoutWindow then
  226. logoutWindow:destroy()
  227. logoutWindow=nil
  228. end
  229. end
  230. else
  231. msg = 'Are you sure you want to logout?'
  232. yesCallback = function()
  233. g_game.safeLogout()
  234. if logoutWindow then
  235. logoutWindow:destroy()
  236. logoutWindow=nil
  237. end
  238. end
  239. end
  240. local noCallback = function()
  241. logoutWindow:destroy()
  242. logoutWindow=nil
  243. end
  244. if prompt then
  245. logoutWindow = displayGeneralBox(tr('Logout'), tr(msg), {
  246. { text=tr('Yes'), callback=yesCallback },
  247. { text=tr('No'), callback=noCallback },
  248. anchor=AnchorHorizontalCenter}, yesCallback, noCallback)
  249. else
  250. yesCallback()
  251. end
  252. end
  253. function stopSmartWalk()
  254. smartWalkDirs = {}
  255. smartWalkDir = nil
  256. end
  257. function changeWalkDir(dir, pop)
  258. while table.removevalue(smartWalkDirs, dir) do end
  259. if pop then
  260. if #smartWalkDirs == 0 then
  261. stopSmartWalk()
  262. return
  263. end
  264. else
  265. table.insert(smartWalkDirs, 1, dir)
  266. end
  267. smartWalkDir = smartWalkDirs[1]
  268. if modules.client_options.getOption('smartWalk') and #smartWalkDirs > 1 then
  269. for _,d in pairs(smartWalkDirs) do
  270. if (smartWalkDir == North and d == West) or (smartWalkDir == West and d == North) then
  271. smartWalkDir = NorthWest
  272. break
  273. elseif (smartWalkDir == North and d == East) or (smartWalkDir == East and d == North) then
  274. smartWalkDir = NorthEast
  275. break
  276. elseif (smartWalkDir == South and d == West) or (smartWalkDir == West and d == South) then
  277. smartWalkDir = SouthWest
  278. break
  279. elseif (smartWalkDir == South and d == East) or (smartWalkDir == East and d == South) then
  280. smartWalkDir = SouthEast
  281. break
  282. end
  283. end
  284. end
  285. end
  286. function smartWalk(dir)
  287. if g_keyboard.getModifiers() == KeyboardNoModifier then
  288. local func = walkFunction
  289. if not func then
  290. if modules.client_options.getOption('dashWalk') then
  291. func = g_game.dashWalk
  292. else
  293. func = g_game.walk
  294. end
  295. end
  296. local dire = smartWalkDir or dir
  297. func(dire)
  298. return true
  299. end
  300. return false
  301. end
  302. function updateStretchShrink()
  303. if modules.client_options.getOption('dontStretchShrink') and not alternativeView then
  304. gameMapPanel:setVisibleDimension({ width = 15, height = 11 })
  305. -- Set gameMapPanel size to height = 11 * 32 + 2
  306. bottomSplitter:setMarginBottom(bottomSplitter:getMarginBottom() + (gameMapPanel:getHeight() - 32 * 11) - 10)
  307. end
  308. end
  309. function onMouseGrabberRelease(self, mousePosition, mouseButton)
  310. if selectedThing == nil then return false end
  311. if mouseButton == MouseLeftButton then
  312. local clickedWidget = gameRootPanel:recursiveGetChildByPos(mousePosition, false)
  313. if clickedWidget then
  314. if selectedType == 'use' then
  315. onUseWith(clickedWidget, mousePosition)
  316. elseif selectedType == 'trade' then
  317. onTradeWith(clickedWidget, mousePosition)
  318. end
  319. end
  320. end
  321. selectedThing = nil
  322. g_mouse.popCursor('target')
  323. self:ungrabMouse()
  324. return true
  325. end
  326. function onUseWith(clickedWidget, mousePosition)
  327. if clickedWidget:getClassName() == 'UIGameMap' then
  328. local tile = clickedWidget:getTile(mousePosition)
  329. if tile then
  330. if selectedThing:isFluidContainer() or selectedThing:isMultiUse() then
  331. g_game.useWith(selectedThing, tile:getTopMultiUseThing())
  332. else
  333. g_game.useWith(selectedThing, tile:getTopUseThing())
  334. end
  335. end
  336. elseif clickedWidget:getClassName() == 'UIItem' and not clickedWidget:isVirtual() then
  337. g_game.useWith(selectedThing, clickedWidget:getItem())
  338. elseif clickedWidget:getClassName() == 'UICreatureButton' then
  339. local creature = clickedWidget:getCreature()
  340. if creature then
  341. g_game.useWith(selectedThing, creature)
  342. end
  343. end
  344. end
  345. function onTradeWith(clickedWidget, mousePosition)
  346. if clickedWidget:getClassName() == 'UIGameMap' then
  347. local tile = clickedWidget:getTile(mousePosition)
  348. if tile then
  349. g_game.requestTrade(selectedThing, tile:getTopCreature())
  350. end
  351. elseif clickedWidget:getClassName() == 'UICreatureButton' then
  352. local creature = clickedWidget:getCreature()
  353. if creature then
  354. g_game.requestTrade(selectedThing, creature)
  355. end
  356. end
  357. end
  358. function startUseWith(thing)
  359. if not thing then return end
  360. if g_ui.isMouseGrabbed() then
  361. if selectedThing then
  362. selectedThing = thing
  363. selectedType = 'use'
  364. end
  365. return
  366. end
  367. selectedType = 'use'
  368. selectedThing = thing
  369. mouseGrabberWidget:grabMouse()
  370. g_mouse.pushCursor('target')
  371. end
  372. function startTradeWith(thing)
  373. if not thing then return end
  374. if g_ui.isMouseGrabbed() then
  375. if selectedThing then
  376. selectedThing = thing
  377. selectedType = 'trade'
  378. end
  379. return
  380. end
  381. selectedType = 'trade'
  382. selectedThing = thing
  383. mouseGrabberWidget:grabMouse()
  384. g_mouse.pushCursor('target')
  385. end
  386. function isMenuHookCategoryEmpty(category)
  387. if category then
  388. for _,opt in pairs(category) do
  389. if opt then return false end
  390. end
  391. end
  392. return true
  393. end
  394. function addMenuHook(category, name, callback, condition, shortcut)
  395. if not hookedMenuOptions[category] then
  396. hookedMenuOptions[category] = {}
  397. end
  398. hookedMenuOptions[category][name] = {
  399. callback = callback,
  400. condition = condition,
  401. shortcut = shortcut
  402. }
  403. end
  404. function removeMenuHook(category, name)
  405. if not name then
  406. hookedMenuOptions[category] = {}
  407. else
  408. hookedMenuOptions[category][name] = nil
  409. end
  410. end
  411. function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
  412. if not g_game.isOnline() then return end
  413. local menu = g_ui.createWidget('PopupMenu')
  414. menu:setGameMenu(true)
  415. local classic = modules.client_options.getOption('classicControl')
  416. local shortcut = nil
  417. if not classic then shortcut = '(Shift)' else shortcut = nil end
  418. if lookThing then
  419. menu:addOption(tr('Look'), function() g_game.look(lookThing) end, shortcut)
  420. end
  421. if not classic then shortcut = '(Ctrl)' else shortcut = nil end
  422. if useThing then
  423. if useThing:isContainer() then
  424. if useThing:getParentContainer() then
  425. menu:addOption(tr('Open'), function() g_game.open(useThing, useThing:getParentContainer()) end, shortcut)
  426. menu:addOption(tr('Open in new window'), function() g_game.open(useThing) end)
  427. else
  428. menu:addOption(tr('Open'), function() g_game.open(useThing) end, shortcut)
  429. end
  430. else
  431. if useThing:isMultiUse() then
  432. menu:addOption(tr('Use with ...'), function() startUseWith(useThing) end, shortcut)
  433. else
  434. menu:addOption(tr('Use'), function() g_game.use(useThing) end, shortcut)
  435. end
  436. end
  437. if useThing:isRotateable() then
  438. menu:addOption(tr('Rotate'), function() g_game.rotate(useThing) end)
  439. end
  440. if g_game.getFeature(GameBrowseField) and useThing:getPosition().x ~= 0xffff then
  441. menu:addOption(tr('Browse Field'), function() g_game.browseField(useThing:getPosition()) end)
  442. end
  443. end
  444. if lookThing and not lookThing:isCreature() and not lookThing:isNotMoveable() and lookThing:isPickupable() then
  445. menu:addSeparator()
  446. menu:addOption(tr('Trade with ...'), function() startTradeWith(lookThing) end)
  447. end
  448. if lookThing then
  449. local parentContainer = lookThing:getParentContainer()
  450. if parentContainer and parentContainer:hasParent() then
  451. menu:addOption(tr('Move up'), function() g_game.moveToParentContainer(lookThing, lookThing:getCount()) end)
  452. end
  453. end
  454. if creatureThing then
  455. local localPlayer = g_game.getLocalPlayer()
  456. menu:addSeparator()
  457. if creatureThing:isLocalPlayer() then
  458. menu:addOption(tr('Set Outfit'), function() g_game.requestOutfit() end)
  459. if g_game.getFeature(GamePlayerMounts) then
  460. if not localPlayer:isMounted() then
  461. menu:addOption(tr('Mount'), function() localPlayer:mount() end)
  462. else
  463. menu:addOption(tr('Dismount'), function() localPlayer:dismount() end)
  464. end
  465. end
  466. if creatureThing:isPartyMember() then
  467. if creatureThing:isPartyLeader() then
  468. if creatureThing:isPartySharedExperienceActive() then
  469. menu:addOption(tr('Disable Shared Experience'), function() g_game.partyShareExperience(false) end)
  470. else
  471. menu:addOption(tr('Enable Shared Experience'), function() g_game.partyShareExperience(true) end)
  472. end
  473. end
  474. menu:addOption(tr('Leave Party'), function() g_game.partyLeave() end)
  475. end
  476. else
  477. local localPosition = localPlayer:getPosition()
  478. if not classic then shortcut = '(Alt)' else shortcut = nil end
  479. if creatureThing:getPosition().z == localPosition.z then
  480. if g_game.getAttackingCreature() ~= creatureThing then
  481. menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end, shortcut)
  482. else
  483. menu:addOption(tr('Stop Attack'), function() g_game.cancelAttack() end, shortcut)
  484. end
  485. if g_game.getFollowingCreature() ~= creatureThing then
  486. menu:addOption(tr('Follow'), function() g_game.follow(creatureThing) end)
  487. else
  488. menu:addOption(tr('Stop Follow'), function() g_game.cancelFollow() end)
  489. end
  490. end
  491. if creatureThing:isPlayer() then
  492. menu:addSeparator()
  493. local creatureName = creatureThing:getName()
  494. menu:addOption(tr('Message to %s', creatureName), function() g_game.openPrivateChannel(creatureName) end)
  495. if modules.game_console.getOwnPrivateTab() then
  496. menu:addOption(tr('Invite to private chat'), function() g_game.inviteToOwnChannel(creatureName) end)
  497. menu:addOption(tr('Exclude from private chat'), function() g_game.excludeFromOwnChannel(creatureName) end) -- [TODO] must be removed after message's popup labels been implemented
  498. end
  499. if not localPlayer:hasVip(creatureName) then
  500. menu:addOption(tr('Add to VIP list'), function() g_game.addVip(creatureName) end)
  501. end
  502. if modules.game_console.isIgnored(creatureName) then
  503. menu:addOption(tr('Unignore') .. ' ' .. creatureName, function() modules.game_console.removeIgnoredPlayer(creatureName) end)
  504. else
  505. menu:addOption(tr('Ignore') .. ' ' .. creatureName, function() modules.game_console.addIgnoredPlayer(creatureName) end)
  506. end
  507. local localPlayerShield = localPlayer:getShield()
  508. local creatureShield = creatureThing:getShield()
  509. if localPlayerShield == ShieldNone or localPlayerShield == ShieldWhiteBlue then
  510. if creatureShield == ShieldWhiteYellow then
  511. menu:addOption(tr('Join %s\'s Party', creatureThing:getName()), function() g_game.partyJoin(creatureThing:getId()) end)
  512. else
  513. menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end)
  514. end
  515. elseif localPlayerShield == ShieldWhiteYellow then
  516. if creatureShield == ShieldWhiteBlue then
  517. menu:addOption(tr('Revoke %s\'s Invitation', creatureThing:getName()), function() g_game.partyRevokeInvitation(creatureThing:getId()) end)
  518. end
  519. elseif localPlayerShield == ShieldYellow or localPlayerShield == ShieldYellowSharedExp or localPlayerShield == ShieldYellowNoSharedExpBlink or localPlayerShield == ShieldYellowNoSharedExp then
  520. if creatureShield == ShieldWhiteBlue then
  521. menu:addOption(tr('Revoke %s\'s Invitation', creatureThing:getName()), function() g_game.partyRevokeInvitation(creatureThing:getId()) end)
  522. elseif creatureShield == ShieldBlue or creatureShield == ShieldBlueSharedExp or creatureShield == ShieldBlueNoSharedExpBlink or creatureShield == ShieldBlueNoSharedExp then
  523. menu:addOption(tr('Pass Leadership to %s', creatureThing:getName()), function() g_game.partyPassLeadership(creatureThing:getId()) end)
  524. else
  525. menu:addOption(tr('Invite to Party'), function() g_game.partyInvite(creatureThing:getId()) end)
  526. end
  527. end
  528. end
  529. end
  530. if modules.game_ruleviolation.hasWindowAccess() and creatureThing:isPlayer() then
  531. menu:addSeparator()
  532. menu:addOption(tr('Rule Violation'), function() modules.game_ruleviolation.show(creatureThing:getName()) end)
  533. end
  534. menu:addSeparator()
  535. menu:addOption(tr('Copy Name'), function() g_window.setClipboardText(creatureThing:getName()) end)
  536. end
  537. -- hooked menu options
  538. for _,category in pairs(hookedMenuOptions) do
  539. if not isMenuHookCategoryEmpty(category) then
  540. menu:addSeparator()
  541. for name,opt in pairs(category) do
  542. if opt and opt.condition(menuPosition, lookThing, useThing, creatureThing) then
  543. menu:addOption(name, function() opt.callback(menuPosition,
  544. lookThing, useThing, creatureThing) end, opt.shortcut)
  545. end
  546. end
  547. end
  548. end
  549. menu:display(menuPosition)
  550. end
  551. function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing, attackCreature)
  552. local keyboardModifiers = g_keyboard.getModifiers()
  553. if not modules.client_options.getOption('classicControl') then
  554. if keyboardModifiers == KeyboardNoModifier and mouseButton == MouseRightButton then
  555. createThingMenu(menuPosition, lookThing, useThing, creatureThing)
  556. return true
  557. elseif lookThing and keyboardModifiers == KeyboardShiftModifier and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
  558. g_game.look(lookThing)
  559. return true
  560. elseif useThing and keyboardModifiers == KeyboardCtrlModifier and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
  561. if useThing:isContainer() then
  562. if useThing:getParentContainer() then
  563. g_game.open(useThing, useThing:getParentContainer())
  564. else
  565. g_game.open(useThing)
  566. end
  567. return true
  568. elseif useThing:isMultiUse() then
  569. startUseWith(useThing)
  570. return true
  571. else
  572. g_game.use(useThing)
  573. return true
  574. end
  575. return true
  576. elseif attackCreature and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
  577. g_game.attack(attackCreature)
  578. return true
  579. elseif creatureThing and creatureThing:getPosition().z == autoWalkPos.z and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
  580. g_game.attack(creatureThing)
  581. return true
  582. end
  583. -- classic control
  584. else
  585. if useThing and keyboardModifiers == KeyboardNoModifier and mouseButton == MouseRightButton and not g_mouse.isPressed(MouseLeftButton) then
  586. local player = g_game.getLocalPlayer()
  587. if attackCreature and attackCreature ~= player then
  588. g_game.attack(attackCreature)
  589. return true
  590. elseif creatureThing and creatureThing ~= player and creatureThing:getPosition().z == autoWalkPos.z then
  591. g_game.attack(creatureThing)
  592. return true
  593. elseif useThing:isContainer() then
  594. if useThing:getParentContainer() then
  595. g_game.open(useThing, useThing:getParentContainer())
  596. return true
  597. else
  598. g_game.open(useThing)
  599. return true
  600. end
  601. elseif useThing:isMultiUse() then
  602. startUseWith(useThing)
  603. return true
  604. else
  605. g_game.use(useThing)
  606. return true
  607. end
  608. return true
  609. elseif lookThing and keyboardModifiers == KeyboardShiftModifier and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
  610. g_game.look(lookThing)
  611. return true
  612. elseif lookThing and ((g_mouse.isPressed(MouseLeftButton) and mouseButton == MouseRightButton) or (g_mouse.isPressed(MouseRightButton) and mouseButton == MouseLeftButton)) then
  613. g_game.look(lookThing)
  614. return true
  615. elseif useThing and keyboardModifiers == KeyboardCtrlModifier and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
  616. createThingMenu(menuPosition, lookThing, useThing, creatureThing)
  617. return true
  618. elseif attackCreature and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
  619. g_game.attack(attackCreature)
  620. return true
  621. elseif creatureThing and creatureThing:getPosition().z == autoWalkPos.z and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
  622. g_game.attack(creatureThing)
  623. return true
  624. end
  625. end
  626. local player = g_game.getLocalPlayer()
  627. player:stopAutoWalk()
  628. if autoWalkPos and keyboardModifiers == KeyboardNoModifier and mouseButton == MouseLeftButton then
  629. player:autoWalk(autoWalkPos)
  630. return true
  631. end
  632. return false
  633. end
  634. function moveStackableItem(item, toPos)
  635. if countWindow then
  636. return
  637. end
  638. if g_keyboard.isCtrlPressed() then
  639. g_game.move(item, toPos, item:getCount())
  640. return
  641. elseif g_keyboard.isShiftPressed() then
  642. g_game.move(item, toPos, 1)
  643. return
  644. end
  645. local count = item:getCount()
  646. countWindow = g_ui.createWidget('CountWindow', rootWidget)
  647. local itembox = countWindow:getChildById('item')
  648. local scrollbar = countWindow:getChildById('countScrollBar')
  649. itembox:setItemId(item:getId())
  650. itembox:setItemCount(count)
  651. scrollbar:setMaximum(count)
  652. scrollbar:setMinimum(1)
  653. scrollbar:setValue(count)
  654. local spinbox = countWindow:getChildById('spinBox')
  655. spinbox:setMaximum(count)
  656. spinbox:setMinimum(0)
  657. spinbox:setValue(0)
  658. spinbox:hideButtons()
  659. spinbox:focus()
  660. spinbox.firstEdit = true
  661. local spinBoxValueChange = function(self, value)
  662. spinbox.firstEdit = false
  663. scrollbar:setValue(value)
  664. end
  665. spinbox.onValueChange = spinBoxValueChange
  666. local check = function()
  667. if spinbox.firstEdit then
  668. spinbox:setValue(spinbox:getMaximum())
  669. spinbox.firstEdit = false
  670. end
  671. end
  672. g_keyboard.bindKeyPress("Up", function() check() spinbox:up() end, spinbox)
  673. g_keyboard.bindKeyPress("Down", function() check() spinbox:down() end, spinbox)
  674. g_keyboard.bindKeyPress("Right", function() check() spinbox:up() end, spinbox)
  675. g_keyboard.bindKeyPress("Left", function() check() spinbox:down() end, spinbox)
  676. g_keyboard.bindKeyPress("PageUp", function() check() spinbox:setValue(spinbox:getValue()+10) end, spinbox)
  677. g_keyboard.bindKeyPress("PageDown", function() check() spinbox:setValue(spinbox:getValue()-10) end, spinbox)
  678. scrollbar.onValueChange = function(self, value)
  679. itembox:setItemCount(value)
  680. spinbox.onValueChange = nil
  681. spinbox:setValue(value)
  682. spinbox.onValueChange = spinBoxValueChange
  683. end
  684. local okButton = countWindow:getChildById('buttonOk')
  685. local moveFunc = function()
  686. g_game.move(item, toPos, itembox:getItemCount())
  687. okButton:getParent():destroy()
  688. countWindow = nil
  689. end
  690. local cancelButton = countWindow:getChildById('buttonCancel')
  691. local cancelFunc = function()
  692. cancelButton:getParent():destroy()
  693. countWindow = nil
  694. end
  695. countWindow.onEnter = moveFunc
  696. countWindow.onEscape = cancelFunc
  697. okButton.onClick = moveFunc
  698. cancelButton.onClick = cancelFunc
  699. end
  700. function getRootPanel()
  701. return gameRootPanel
  702. end
  703. function getMapPanel()
  704. return gameMapPanel
  705. end
  706. function getRightPanel()
  707. return gameRightPanel
  708. end
  709. function getLeftPanel()
  710. return gameLeftPanel
  711. end
  712. function getBottomPanel()
  713. return gameBottomPanel
  714. end
  715. function getShowTopMenuButton()
  716. return showTopMenuButton
  717. end
  718. function onLeftPanelVisibilityChange(leftPanel, visible)
  719. if not visible and g_game.isOnline() then
  720. local children = leftPanel:getChildren()
  721. for i=1,#children do
  722. children[i]:setParent(gameRightPanel)
  723. end
  724. end
  725. end
  726. function nextViewMode()
  727. setupViewMode((currentViewMode + 1) % 3)
  728. end
  729. function setupViewMode(mode)
  730. if mode == currentViewMode then return end
  731. if currentViewMode == 2 then
  732. gameMapPanel:addAnchor(AnchorLeft, 'gameLeftPanel', AnchorRight)
  733. gameMapPanel:addAnchor(AnchorRight, 'gameRightPanel', AnchorLeft)
  734. gameMapPanel:addAnchor(AnchorBottom, 'gameBottomPanel', AnchorTop)
  735. gameRootPanel:addAnchor(AnchorTop, 'topMenu', AnchorBottom)
  736. gameLeftPanel:setOn(modules.client_options.getOption('showLeftPanel'))
  737. gameLeftPanel:setImageColor('white')
  738. gameRightPanel:setImageColor('white')
  739. gameLeftPanel:setMarginTop(0)
  740. gameRightPanel:setMarginTop(0)
  741. gameBottomPanel:setImageColor('white')
  742. modules.client_topmenu.getTopMenu():setImageColor('white')
  743. g_game.changeMapAwareRange(18, 14)
  744. end
  745. if mode == 0 then
  746. gameMapPanel:setKeepAspectRatio(true)
  747. gameMapPanel:setLimitVisibleRange(false)
  748. gameMapPanel:setZoom(11)
  749. gameMapPanel:setVisibleDimension({ width = 15, height = 11 })
  750. elseif mode == 1 then
  751. gameMapPanel:setKeepAspectRatio(false)
  752. gameMapPanel:setLimitVisibleRange(true)
  753. gameMapPanel:setZoom(11)
  754. gameMapPanel:setVisibleDimension({ width = 15, height = 11 })
  755. elseif mode == 2 then
  756. local limit = limitedZoom and not g_game.isGM()
  757. gameMapPanel:setLimitVisibleRange(limit)
  758. gameMapPanel:setZoom(11)
  759. gameMapPanel:setVisibleDimension({ width = 15, height = 11 })
  760. gameMapPanel:fill('parent')
  761. gameRootPanel:fill('parent')
  762. gameLeftPanel:setImageColor('alpha')
  763. gameRightPanel:setImageColor('alpha')
  764. gameLeftPanel:setMarginTop(modules.client_topmenu.getTopMenu()
  765. :getHeight() - gameLeftPanel:getPaddingTop())
  766. gameRightPanel:setMarginTop(modules.client_topmenu.getTopMenu()
  767. :getHeight() - gameRightPanel:getPaddingTop())
  768. gameLeftPanel:setOn(true)
  769. gameLeftPanel:setVisible(true)
  770. gameRightPanel:setOn(true)
  771. gameMapPanel:setOn(true)
  772. gameBottomPanel:setImageColor('#ffffff88')
  773. modules.client_topmenu.getTopMenu():setImageColor('#ffffff66')
  774. if not limit then
  775. g_game.changeMapAwareRange(24, 20)
  776. end
  777. end
  778. currentViewMode = mode
  779. end
  780. function limitZoom()
  781. limitedZoom = true
  782. end