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 28KB

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