OTCv8 3.0 rev 2

This commit is contained in:
OTCv8
2021-04-07 21:31:27 +00:00
parent e93bcdc9cf
commit d77991f60f
85 changed files with 8780 additions and 8200 deletions

View File

@@ -1,6 +1,6 @@
Font
name: cipsoftFont
texture: cipsoftFont
height: 8
glyph-size: 8 8
space-width: 2
Font
name: cipsoftFont
texture: cipsoftFont
height: 8
glyph-size: 8 8
space-width: 2

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@@ -0,0 +1,11 @@
varying vec2 v_TexCoord;
uniform vec4 u_Color;
uniform sampler2D u_Tex0;
void main()
{
gl_FragColor = texture2D(u_Tex0, v_TexCoord) * u_Color;
if(gl_FragColor.a < 0.01)
discard;
}

View File

@@ -0,0 +1,14 @@
attribute vec2 a_Vertex;
attribute vec2 a_TexCoord;
uniform mat3 u_TransformMatrix;
uniform mat3 u_ProjectionMatrix;
uniform mat3 u_TextureMatrix;
varying vec2 v_TexCoord;
void main()
{
gl_Position = vec4((u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy, 1.0)).xy, 1.0, 1.0);
v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;
}

View File

@@ -0,0 +1,15 @@
varying vec2 v_TexCoord;
varying vec2 v_TexCoord2;
uniform vec4 u_Color;
uniform sampler2D u_Tex0;
uniform sampler2D u_Tex1;
void main()
{
gl_FragColor = texture2D(u_Tex0, v_TexCoord) * u_Color;
gl_FragColor += texture2D(u_Tex1, v_TexCoord2);
if(gl_FragColor.a < 0.01)
discard;
}

View File

@@ -0,0 +1,33 @@
attribute vec2 a_TexCoord;
attribute vec2 a_Vertex;
varying vec2 v_TexCoord;
varying vec2 v_TexCoord2;
uniform mat3 u_TextureMatrix;
uniform mat3 u_TransformMatrix;
uniform mat3 u_ProjectionMatrix;
uniform vec2 u_Offset;
uniform vec2 u_Center;
uniform float u_Time;
vec2 effectTextureSize = vec2(466.0, 342.0);
vec2 direction = vec2(1.0,0.2);
float speed = 200.0;
vec2 rotate(vec2 v, float a) {
float s = sin(a);
float c = cos(a);
mat2 m = mat2(c, -s, s, c);
return m * v;
}
void main()
{
gl_Position = vec4((u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy, 1.0)).xy, 1.0, 1.0);
v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;
v_TexCoord2 = ((a_Vertex + direction * u_Time * speed) / effectTextureSize);
}

View File

@@ -0,0 +1,16 @@
uniform mat4 u_Color;
varying vec2 v_TexCoord;
varying vec2 v_TexCoord2;
varying vec2 v_TexCoord3;
uniform sampler2D u_Tex0;
uniform sampler2D u_Tex1;
void main()
{
gl_FragColor = texture2D(u_Tex0, v_TexCoord);
vec4 texcolor = texture2D(u_Tex0, v_TexCoord2);
vec4 effectColor = texture2D(u_Tex1, v_TexCoord3);
if(texcolor.a > 0.1) {
gl_FragColor *= effectColor;
}
if(gl_FragColor.a < 0.01) discard;
}

View File

@@ -0,0 +1,47 @@
attribute vec2 a_TexCoord;
uniform mat3 u_TextureMatrix;
varying vec2 v_TexCoord;
varying vec2 v_TexCoord2;
varying vec2 v_TexCoord3;
attribute vec2 a_Vertex;
uniform mat3 u_TransformMatrix;
uniform mat3 u_ProjectionMatrix;
uniform vec2 u_Offset;
uniform vec2 u_Center;
uniform float u_Time;
vec2 effectTextureSize = vec2(466.0, 342.0);
vec2 direction = vec2(1.0,0.2);
float speed = 200.0;
vec2 rotate(vec2 v, float a) {
float s = sin(a);
float c = cos(a);
mat2 m = mat2(c, -s, s, c);
return m * v;
}
void main()
{
vec2 offset = direction * speed * u_Time;
gl_Position = vec4((u_ProjectionMatrix * u_TransformMatrix * vec3(a_Vertex.xy, 1.0)).xy, 1.0, 1.0);
v_TexCoord = (u_TextureMatrix * vec3(a_TexCoord,1.0)).xy;
v_TexCoord2 = (u_TextureMatrix * vec3(a_TexCoord + u_Offset,1.0)).xy;
vec2 vertex = a_Vertex;
if(vertex.x < u_Center.x) {
vertex.x = effectTextureSize.x / 10.0;
}
if(vertex.x > u_Center.x) {
vertex.x = effectTextureSize.x - effectTextureSize.x / 10.0;
}
if(vertex.y < u_Center.y) {
vertex.y = effectTextureSize.y / 10.0;
}
if(vertex.y > u_Center.y) {
vertex.y = effectTextureSize.y - effectTextureSize.y / 10.0;
}
v_TexCoord3 = ((vertex + direction * u_Time * speed) / effectTextureSize);
}

View File

@@ -1,60 +1,60 @@
SmallScrollBar < UIScrollBar
orientation: vertical
margin-bottom: 1
step: 20
width: 8
image-source: /images/ui/scrollbar
image-clip: 39 0 13 65
image-border: 1
pixels-scroll: true
UIButton
id: decrementButton
anchors.top: parent.top
anchors.left: parent.left
image-source: /images/ui/scrollbar
image-clip: 0 0 13 13
image-color: #ffffffff
size: 8 8
$hover:
image-clip: 13 0 13 13
$pressed:
image-clip: 26 0 13 13
$disabled:
image-color: #ffffff66
UIButton
id: incrementButton
anchors.bottom: parent.bottom
anchors.right: parent.right
size: 8 8
image-source: /images/ui/scrollbar
image-clip: 0 13 13 13
image-color: #ffffffff
$hover:
image-clip: 13 13 13 13
$pressed:
image-clip: 26 13 13 13
$disabled:
image-color: #ffffff66
UIButton
id: sliderButton
anchors.centerIn: parent
size: 8 11
image-source: /images/ui/scrollbar
image-clip: 0 26 13 13
image-border: 2
image-color: #ffffffff
$hover:
image-clip: 13 26 13 13
$pressed:
image-clip: 26 26 13 13
$disabled:
image-color: #ffffff66
Label
id: valueLabel
anchors.fill: parent
color: white
SmallScrollBar < UIScrollBar
orientation: vertical
margin-bottom: 1
step: 20
width: 8
image-source: /images/ui/scrollbar
image-clip: 39 0 13 65
image-border: 1
pixels-scroll: true
UIButton
id: decrementButton
anchors.top: parent.top
anchors.left: parent.left
image-source: /images/ui/scrollbar
image-clip: 0 0 13 13
image-color: #ffffffff
size: 8 8
$hover:
image-clip: 13 0 13 13
$pressed:
image-clip: 26 0 13 13
$disabled:
image-color: #ffffff66
UIButton
id: incrementButton
anchors.bottom: parent.bottom
anchors.right: parent.right
size: 8 8
image-source: /images/ui/scrollbar
image-clip: 0 13 13 13
image-color: #ffffffff
$hover:
image-clip: 13 13 13 13
$pressed:
image-clip: 26 13 13 13
$disabled:
image-color: #ffffff66
UIButton
id: sliderButton
anchors.centerIn: parent
size: 8 11
image-source: /images/ui/scrollbar
image-clip: 0 26 13 13
image-border: 2
image-color: #ffffffff
$hover:
image-clip: 13 26 13 13
$pressed:
image-clip: 26 26 13 13
$disabled:
image-color: #ffffff66
Label
id: valueLabel
anchors.fill: parent
color: white
text-align: center

View File

@@ -1,186 +1,186 @@
ConsoleLabel < UITextEdit
font: verdana-11px-antialised
height: 14
color: yellow
margin-left: 2
text-wrap: true
text-auto-resize: true
selection-color: #111416
selection-background-color: #999999
change-cursor-image: false
cursor-visible: false
editable: false
draggable: true
selectable: false
focusable: false
ConsolePhantomLabel < UILabel
font: verdana-11px-antialised
height: 14
color: yellow
text-wrap: true
text-auto-resize: true
selection-color: #111416
selection-background-color: #999999
ConsoleTabBar < MoveableTabBar
height: 28
ConsoleTabBarPanel < MoveableTabBarPanel
id: consoleTab
ScrollablePanel
id: consoleBuffer
anchors.fill: parent
margin-right: 12
vertical-scrollbar: consoleScrollBar
layout:
type: verticalBox
align-bottom: true
border-width: 1
border-color: #202327
background: #00000066
inverted-scroll: true
padding: 1
VerticalScrollBar
id: consoleScrollBar
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
step: 14
pixels-scroll: true
ConsoleTabBarButton < MoveableTabBarButton
height: 28
padding: 15
ConsolePanel < Panel
image-source: /images/ui/panel_bottom
image-border: 4
$first:
anchors.fill: parent
$!first:
anchors.top: prev.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
CheckBox
id: toggleChat
!tooltip: tr('Disable chat mode, allow to walk using ASDW')
anchors.left: parent.left
anchors.top: parent.top
margin-left: 13
margin-top: 8
@onCheckChange: toggleChat()
TabButton
id: prevChannelButton
icon: /images/game/console/leftarrow
anchors.left: toggleChat.right
anchors.top: parent.top
margin-left: 3
margin-top: 6
ConsoleTabBar
id: consoleTabBar
anchors.left: prev.right
anchors.top: parent.top
anchors.right: next.left
margin-left: 5
margin-top: 3
margin-right: 5
tab-spacing: 2
movable: true
TabButton
id: nextChannelButton
icon: /images/game/console/rightarrow
anchors.right: next.left
anchors.top: parent.top
margin-right: 5
margin-top: 6
TabButton
id: closeChannelButton
!tooltip: tr('Close this channel') .. ' (Ctrl+E)'
icon: /images/game/console/closechannel
anchors.right: next.left
anchors.top: parent.top
enabled: false
margin-right: 5
margin-top: 6
@onClick: removeCurrentTab()
TabButton
id: clearChannelButton
!tooltip: tr('Clear current message window')
icon: /images/game/console/clearchannel
anchors.right: next.left
anchors.top: parent.top
margin-right: 5
margin-top: 6
@onClick: |
local consoleTabBar = self:getParent():getChildById('consoleTabBar')
clearChannel(consoleTabBar)
TabButton
id: channelsButton
!tooltip: tr('Open new channel') .. ' (Ctrl+O)'
icon: /images/game/console/channels
anchors.right: next.left
anchors.top: parent.top
margin-right: 5
margin-top: 6
@onClick: g_game.requestChannels()
TabButton
id: ignoreButton
!tooltip: tr('Ignore players')
icon: /images/game/console/ignore
anchors.right: parent.right
anchors.top: parent.top
margin-right: 5
margin-top: 6
@onClick: onClickIgnoreButton()
Panel
id: consoleContentPanel
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: consoleTextEdit.top
margin-left: 6
margin-right: 6
margin-bottom: 4
margin-top: 4
padding: 1
focusable: false
phantom: true
TabButton
id: sayModeButton
icon: /images/game/console/say
!tooltip: tr('Adjust volume')
&sayMode: 2
size: 20 20
anchors.left: parent.left
anchors.bottom: parent.bottom
margin-left: 6
margin-bottom: 6
@onClick: sayModeChange()
TextEdit
id: consoleTextEdit
anchors.left: sayModeButton.right
anchors.right: parent.right
anchors.bottom: parent.bottom
margin-right: 6
margin-left: 6
margin-bottom: 6
shift-navigation: true
max-length: 255
text-auto-submit: true
ConsoleLabel < UITextEdit
font: verdana-11px-antialised
height: 14
color: yellow
margin-left: 2
text-wrap: true
text-auto-resize: true
selection-color: #111416
selection-background-color: #999999
change-cursor-image: false
cursor-visible: false
editable: false
draggable: true
selectable: false
focusable: false
ConsolePhantomLabel < UILabel
font: verdana-11px-antialised
height: 14
color: yellow
text-wrap: true
text-auto-resize: true
selection-color: #111416
selection-background-color: #999999
ConsoleTabBar < MoveableTabBar
height: 28
ConsoleTabBarPanel < MoveableTabBarPanel
id: consoleTab
ScrollablePanel
id: consoleBuffer
anchors.fill: parent
margin-right: 12
vertical-scrollbar: consoleScrollBar
layout:
type: verticalBox
align-bottom: true
border-width: 1
border-color: #202327
background: #00000066
inverted-scroll: true
padding: 1
VerticalScrollBar
id: consoleScrollBar
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
step: 14
pixels-scroll: true
ConsoleTabBarButton < MoveableTabBarButton
height: 28
padding: 15
ConsolePanel < Panel
image-source: /images/ui/panel_bottom
image-border: 4
$first:
anchors.fill: parent
$!first:
anchors.top: prev.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
CheckBox
id: toggleChat
!tooltip: tr('Disable chat mode, allow to walk using ASDW')
anchors.left: parent.left
anchors.top: parent.top
margin-left: 13
margin-top: 8
@onCheckChange: toggleChat()
TabButton
id: prevChannelButton
icon: /images/game/console/leftarrow
anchors.left: toggleChat.right
anchors.top: parent.top
margin-left: 3
margin-top: 6
ConsoleTabBar
id: consoleTabBar
anchors.left: prev.right
anchors.top: parent.top
anchors.right: next.left
margin-left: 5
margin-top: 3
margin-right: 5
tab-spacing: 2
movable: true
TabButton
id: nextChannelButton
icon: /images/game/console/rightarrow
anchors.right: next.left
anchors.top: parent.top
margin-right: 5
margin-top: 6
TabButton
id: closeChannelButton
!tooltip: tr('Close this channel') .. ' (Ctrl+E)'
icon: /images/game/console/closechannel
anchors.right: next.left
anchors.top: parent.top
enabled: false
margin-right: 5
margin-top: 6
@onClick: removeCurrentTab()
TabButton
id: clearChannelButton
!tooltip: tr('Clear current message window')
icon: /images/game/console/clearchannel
anchors.right: next.left
anchors.top: parent.top
margin-right: 5
margin-top: 6
@onClick: |
local consoleTabBar = self:getParent():getChildById('consoleTabBar')
clearChannel(consoleTabBar)
TabButton
id: channelsButton
!tooltip: tr('Open new channel') .. ' (Ctrl+O)'
icon: /images/game/console/channels
anchors.right: next.left
anchors.top: parent.top
margin-right: 5
margin-top: 6
@onClick: g_game.requestChannels()
TabButton
id: ignoreButton
!tooltip: tr('Ignore players')
icon: /images/game/console/ignore
anchors.right: parent.right
anchors.top: parent.top
margin-right: 5
margin-top: 6
@onClick: onClickIgnoreButton()
Panel
id: consoleContentPanel
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: consoleTextEdit.top
margin-left: 6
margin-right: 6
margin-bottom: 4
margin-top: 4
padding: 1
focusable: false
phantom: true
TabButton
id: sayModeButton
icon: /images/game/console/say
!tooltip: tr('Adjust volume')
&sayMode: 2
size: 20 20
anchors.left: parent.left
anchors.bottom: parent.bottom
margin-left: 6
margin-bottom: 6
@onClick: sayModeChange()
TextEdit
id: consoleTextEdit
anchors.left: sayModeButton.right
anchors.right: parent.right
anchors.bottom: parent.bottom
margin-right: 6
margin-left: 6
margin-bottom: 6
shift-navigation: true
max-length: 255
text-auto-submit: true

View File

@@ -1,74 +1,74 @@
PageButton < Button
size: 30 18
margin: 1
ContainerWindow < MiniWindow
height: 150
&save: true
&containerWindow: true
UIItem
id: containerItemWidget
virtual: true
size: 16 16
anchors.top: parent.top
anchors.left: parent.left
margin-top: 1
margin-left: 3
UIButton
id: upButton
anchors.top: lockButton.top
anchors.right: lockButton.left
margin-right: 3
size: 14 14
image-source: /images/ui/miniwindow_buttons
image-clip: 42 0 14 14
$hover:
image-clip: 42 14 14 14
$pressed:
image-clip: 42 28 14 14
Panel
id: pagePanel
anchors.left: parent.left
anchors.right: parent.right
anchors.top: miniwindowTopBar.bottom
margin: 1 3 0 3
background: #00000066
height: 20
$on:
visible: true
$!on:
visible: false
Label
id: pageLabel
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 2
text-auto-resize: true
PageButton
id: prevPageButton
text: <
anchors.top: parent.top
anchors.left: parent.left
PageButton
id: nextPageButton
text: >
anchors.top: parent.top
anchors.right: parent.right
MiniWindowContents
padding-right: 0
layout:
type: grid
cell-size: 34 34
flow: true
PageButton < Button
size: 30 18
margin: 1
ContainerWindow < MiniWindow
height: 150
&save: true
&containerWindow: true
UIItem
id: containerItemWidget
virtual: true
size: 16 16
anchors.top: parent.top
anchors.left: parent.left
margin-top: 1
margin-left: 3
UIButton
id: upButton
anchors.top: lockButton.top
anchors.right: lockButton.left
margin-right: 3
size: 14 14
image-source: /images/ui/miniwindow_buttons
image-clip: 42 0 14 14
$hover:
image-clip: 42 14 14 14
$pressed:
image-clip: 42 28 14 14
Panel
id: pagePanel
anchors.left: parent.left
anchors.right: parent.right
anchors.top: miniwindowTopBar.bottom
margin: 1 3 0 3
background: #00000066
height: 20
$on:
visible: true
$!on:
visible: false
Label
id: pageLabel
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 2
text-auto-resize: true
PageButton
id: prevPageButton
text: <
anchors.top: parent.top
anchors.left: parent.left
PageButton
id: nextPageButton
text: >
anchors.top: parent.top
anchors.right: parent.right
MiniWindowContents
padding-right: 0
layout:
type: grid
cell-size: 34 34
flow: true

View File

@@ -1,3 +1,3 @@
EnterGameWindow < StaticMainWindow
!text: tr('Enter Game')
EnterGameWindow < StaticMainWindow
!text: tr('Enter Game')
size: 260 354

View File

@@ -1,2 +1,2 @@
GameButtonsWindow < MiniWindow
height: 26
GameButtonsWindow < MiniWindow
height: 26

View File

@@ -1,147 +1,147 @@
ExperienceBar < ProgressBar
id: experienceBar
background-color: #B6E866
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin: 1
margin-top: 3
SoulLabel < GameLabel
id: soulLabel
text-align: right
color: white
font: verdana-11px-rounded
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.horizontalCenter
margin-top: 5
margin-right: 3
on: true
$!on:
visible: false
margin-top: 0
height: 0
CapLabel < GameLabel
id: capLabel
color: white
font: verdana-11px-rounded
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.horizontalCenter
margin-top: 5
margin-left: 3
on: true
$!on:
visible: false
margin-top: 0
height: 0
ConditionWidget < UIWidget
size: 18 18
$!first:
margin-left: 2
HealthOverlay < UIWidget
id: healthOverlay
anchors.fill: parent
phantom: true
HealthBar
id: topHealthBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.horizontalCenter
phantom: true
ManaBar
id: topManaBar
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.horizontalCenter
phantom: true
UIProgressBar
id: healthCircle
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/left_empty
margin-right: 169
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: healthCircleFront
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/left_full
margin-right: 169
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: manaCircle
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/right_empty
image-auto-resize: true
margin-left: 130
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: manaCircleFront
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/right_full
margin-left: 130
margin-bottom: 16
opacity: 0.4
image-color: #0000FFFF
phantom: true
HealthInfoWindow < MiniWindow
icon: /images/topbuttons/healthinfo
!text: tr('Health Info')
height: 123
MiniWindowContents
HealthBar
id: healthBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
margin: 2
margin-top: 1
ManaBar
id: manaBar
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin: 2
ExperienceBar
Panel
id: conditionPanel
layout:
type: horizontalBox
height: 22
margin-top: 4
padding: 2
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
border-width: 1
border-color: #00000077
background-color: #ffffff11
SoulLabel
CapLabel
ExperienceBar < ProgressBar
id: experienceBar
background-color: #B6E866
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin: 1
margin-top: 3
SoulLabel < GameLabel
id: soulLabel
text-align: right
color: white
font: verdana-11px-rounded
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.horizontalCenter
margin-top: 5
margin-right: 3
on: true
$!on:
visible: false
margin-top: 0
height: 0
CapLabel < GameLabel
id: capLabel
color: white
font: verdana-11px-rounded
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.horizontalCenter
margin-top: 5
margin-left: 3
on: true
$!on:
visible: false
margin-top: 0
height: 0
ConditionWidget < UIWidget
size: 18 18
$!first:
margin-left: 2
HealthOverlay < UIWidget
id: healthOverlay
anchors.fill: parent
phantom: true
HealthBar
id: topHealthBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.horizontalCenter
phantom: true
ManaBar
id: topManaBar
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.horizontalCenter
phantom: true
UIProgressBar
id: healthCircle
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/left_empty
margin-right: 169
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: healthCircleFront
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/left_full
margin-right: 169
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: manaCircle
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/right_empty
image-auto-resize: true
margin-left: 130
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: manaCircleFront
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/right_full
margin-left: 130
margin-bottom: 16
opacity: 0.4
image-color: #0000FFFF
phantom: true
HealthInfoWindow < MiniWindow
icon: /images/topbuttons/healthinfo
!text: tr('Health Info')
height: 123
MiniWindowContents
HealthBar
id: healthBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
margin: 2
margin-top: 1
ManaBar
id: manaBar
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin: 2
ExperienceBar
Panel
id: conditionPanel
layout:
type: horizontalBox
height: 22
margin-top: 4
padding: 2
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
border-width: 1
border-color: #00000077
background-color: #ffffff11
SoulLabel
CapLabel

View File

@@ -1,299 +1,299 @@
InventoryItem < Item
$on:
image-source: /images/ui/item-blessed
HeadSlot < InventoryItem
id: slot1
image-source: /images/game/slots/head
&position: {x=65535, y=1, z=0}
$on:
image-source: /images/game/slots/head-blessed
BodySlot < InventoryItem
id: slot4
image-source: /images/game/slots/body
&position: {x=65535, y=4, z=0}
$on:
image-source: /images/game/slots/body-blessed
LegSlot < InventoryItem
id: slot7
image-source: /images/game/slots/legs
&position: {x=65535, y=7, z=0}
$on:
image-source: /images/game/slots/legs-blessed
FeetSlot < InventoryItem
id: slot8
image-source: /images/game/slots/feet
&position: {x=65535, y=8, z=0}
$on:
image-source: /images/game/slots/feet-blessed
NeckSlot < InventoryItem
id: slot2
image-source: /images/game/slots/neck
&position: {x=65535, y=2, z=0}
$on:
image-source: /images/game/slots/neck-blessed
LeftSlot < InventoryItem
id: slot6
image-source: /images/game/slots/left-hand
&position: {x=65535, y=6, z=0}
$on:
image-source: /images/game/slots/left-hand-blessed
FingerSlot < InventoryItem
id: slot9
image-source: /images/game/slots/finger
&position: {x=65535, y=9, z=0}
$on:
image-source: /images/game/slots/finger-blessed
BackSlot < InventoryItem
id: slot3
image-source: /images/game/slots/back
&position: {x=65535, y=3, z=0}
$on:
image-source: /images/game/slots/back-blessed
RightSlot < InventoryItem
id: slot5
image-source: /images/game/slots/right-hand
&position: {x=65535, y=5, z=0}
$on:
image-source: /images/game/slots/right-hand-blessed
AmmoSlot < InventoryItem
id: slot10
image-source: /images/game/slots/ammo
&position: {x=65535, y=10, z=0}
$on:
image-source: /images/game/slots/ammo-blessed
PurseButton < UIButton
id: purseButton
size: 34 12
!tooltip: tr('Open purse')
icon-source: /images/game/slots/purse
icon-clip: 0 0 34 12
$on:
icon-clip: 0 12 34 12
$pressed:
icon-clip: 0 12 34 12
CombatBox < UICheckBox
size: 20 20
image-clip: 0 0 20 20
margin-left: 4
$checked:
image-clip: 0 20 20 20
InventoryButton < Button
font: verdana-11px-antialised
height: 18
margin-top: 2
text-align: center
SoulCapLabel < GameLabel
text-align: center
color: #FFFFFF
font: cipsoftFont
margin-top: 4
text-offset: 0 3
width: 36
height: 20
icon-source: /images/game/slots/soulcap
FightOffensiveBox < CombatBox
image-source: /images/game/combatmodes/fightoffensive
FightBalancedBox < CombatBox
image-source: /images/game/combatmodes/fightbalanced
FightDefensiveBox < CombatBox
image-source: /images/game/combatmodes/fightdefensive
ChaseModeBox < CombatBox
image-source: /images/game/combatmodes/chasemode
SafeFightBox < CombatBox
image-source: /images/game/combatmodes/safefight
MountButton < CombatBox
image-source: /images/game/combatmodes/mount
InventoryWindow < MiniWindow
icon: /images/topbuttons/inventory
height: 200
id: inventoryWindow
@onClose: modules.game_inventory.onMiniWindowClose()
&save: true
&autoOpen: 3
MiniWindowContents
anchors.left: parent.left
Panel
id: inventoryPanel
margin-right: 63
margin-top: 2
anchors.fill: parent
HeadSlot
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 3
BodySlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
LegSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
FeetSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
NeckSlot
anchors.top: slot1.top
anchors.right: slot1.left
margin-top: 13
margin-right: 5
LeftSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
FingerSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
BackSlot
anchors.top: slot1.top
anchors.left: slot1.right
margin-top: 13
margin-left: 5
RightSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
AmmoSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
SoulCapLabel
id: soulLabel
anchors.top: slot10.bottom
anchors.horizontalCenter: slot10.horizontalCenter
SoulCapLabel
id: capLabel
anchors.top: slot9.bottom
anchors.horizontalCenter: slot9.horizontalCenter
PurseButton
anchors.left: slot3.left
anchors.bottom: slot3.top
margin-bottom: 3
Panel
id: conditionPanel
layout:
type: horizontalBox
height: 22
padding: 2
anchors.top: slot8.bottom
anchors.left: slot6.left
anchors.right: slot5.right
margin-top: 4
border-width: 1
border-color: #00000077
background-color: #ffffff22
Panel
margin-top: 5
anchors.fill: parent
anchors.left: prev.right
FightOffensiveBox
id: fightOffensiveBox
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
ChaseModeBox
id: chaseModeBox
anchors.left: prev.right
anchors.top: parent.top
FightBalancedBox
id: fightBalancedBox
margin-top: 22
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
SafeFightBox
id: safeFightBox
margin-top: 22
anchors.left: prev.right
anchors.top: parent.top
FightDefensiveBox
id: fightDefensiveBox
margin-top: 44
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
MountButton
id: mountButton
margin-top: 44
anchors.left: prev.right
anchors.top: parent.top
Panel
id: buttonsPanel
margin-top: 4
margin-right: 5
anchors.fill: parent
anchors.top: prev.bottom
layout:
type: verticalBox
UIButton
id: buttonPvp
height: 20
icon: /images/game/combatmodes/pvp
icon-clip: 0 0 42 20
$on:
icon-clip: 0 20 42 20
InventoryButton
!text: tr('Stop')
@onClick: g_game.stop(); g_game.cancelAttackAndFollow()
InventoryButton
!text: tr('Options')
@onClick: modules.client_options.toggle()
InventoryButton
!text: tr('Hotkeys')
@onClick: modules.game_hotkeys.toggle()
InventoryButton
!text: tr('Logout')
@onClick: modules.game_interface.tryLogout()
InventoryItem < Item
$on:
image-source: /images/ui/item-blessed
HeadSlot < InventoryItem
id: slot1
image-source: /images/game/slots/head
&position: {x=65535, y=1, z=0}
$on:
image-source: /images/game/slots/head-blessed
BodySlot < InventoryItem
id: slot4
image-source: /images/game/slots/body
&position: {x=65535, y=4, z=0}
$on:
image-source: /images/game/slots/body-blessed
LegSlot < InventoryItem
id: slot7
image-source: /images/game/slots/legs
&position: {x=65535, y=7, z=0}
$on:
image-source: /images/game/slots/legs-blessed
FeetSlot < InventoryItem
id: slot8
image-source: /images/game/slots/feet
&position: {x=65535, y=8, z=0}
$on:
image-source: /images/game/slots/feet-blessed
NeckSlot < InventoryItem
id: slot2
image-source: /images/game/slots/neck
&position: {x=65535, y=2, z=0}
$on:
image-source: /images/game/slots/neck-blessed
LeftSlot < InventoryItem
id: slot6
image-source: /images/game/slots/left-hand
&position: {x=65535, y=6, z=0}
$on:
image-source: /images/game/slots/left-hand-blessed
FingerSlot < InventoryItem
id: slot9
image-source: /images/game/slots/finger
&position: {x=65535, y=9, z=0}
$on:
image-source: /images/game/slots/finger-blessed
BackSlot < InventoryItem
id: slot3
image-source: /images/game/slots/back
&position: {x=65535, y=3, z=0}
$on:
image-source: /images/game/slots/back-blessed
RightSlot < InventoryItem
id: slot5
image-source: /images/game/slots/right-hand
&position: {x=65535, y=5, z=0}
$on:
image-source: /images/game/slots/right-hand-blessed
AmmoSlot < InventoryItem
id: slot10
image-source: /images/game/slots/ammo
&position: {x=65535, y=10, z=0}
$on:
image-source: /images/game/slots/ammo-blessed
PurseButton < UIButton
id: purseButton
size: 34 12
!tooltip: tr('Open purse')
icon-source: /images/game/slots/purse
icon-clip: 0 0 34 12
$on:
icon-clip: 0 12 34 12
$pressed:
icon-clip: 0 12 34 12
CombatBox < UICheckBox
size: 20 20
image-clip: 0 0 20 20
margin-left: 4
$checked:
image-clip: 0 20 20 20
InventoryButton < Button
font: verdana-11px-antialised
height: 18
margin-top: 2
text-align: center
SoulCapLabel < GameLabel
text-align: center
color: #FFFFFF
font: cipsoftFont
margin-top: 4
text-offset: 0 3
width: 36
height: 20
icon-source: /images/game/slots/soulcap
FightOffensiveBox < CombatBox
image-source: /images/game/combatmodes/fightoffensive
FightBalancedBox < CombatBox
image-source: /images/game/combatmodes/fightbalanced
FightDefensiveBox < CombatBox
image-source: /images/game/combatmodes/fightdefensive
ChaseModeBox < CombatBox
image-source: /images/game/combatmodes/chasemode
SafeFightBox < CombatBox
image-source: /images/game/combatmodes/safefight
MountButton < CombatBox
image-source: /images/game/combatmodes/mount
InventoryWindow < MiniWindow
icon: /images/topbuttons/inventory
height: 200
id: inventoryWindow
@onClose: modules.game_inventory.onMiniWindowClose()
&save: true
&autoOpen: 3
MiniWindowContents
anchors.left: parent.left
Panel
id: inventoryPanel
margin-right: 63
margin-top: 2
anchors.fill: parent
HeadSlot
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 3
BodySlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
LegSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
FeetSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
NeckSlot
anchors.top: slot1.top
anchors.right: slot1.left
margin-top: 13
margin-right: 5
LeftSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
FingerSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
BackSlot
anchors.top: slot1.top
anchors.left: slot1.right
margin-top: 13
margin-left: 5
RightSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
AmmoSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
SoulCapLabel
id: soulLabel
anchors.top: slot10.bottom
anchors.horizontalCenter: slot10.horizontalCenter
SoulCapLabel
id: capLabel
anchors.top: slot9.bottom
anchors.horizontalCenter: slot9.horizontalCenter
PurseButton
anchors.left: slot3.left
anchors.bottom: slot3.top
margin-bottom: 3
Panel
id: conditionPanel
layout:
type: horizontalBox
height: 22
padding: 2
anchors.top: slot8.bottom
anchors.left: slot6.left
anchors.right: slot5.right
margin-top: 4
border-width: 1
border-color: #00000077
background-color: #ffffff22
Panel
margin-top: 5
anchors.fill: parent
anchors.left: prev.right
FightOffensiveBox
id: fightOffensiveBox
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
ChaseModeBox
id: chaseModeBox
anchors.left: prev.right
anchors.top: parent.top
FightBalancedBox
id: fightBalancedBox
margin-top: 22
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
SafeFightBox
id: safeFightBox
margin-top: 22
anchors.left: prev.right
anchors.top: parent.top
FightDefensiveBox
id: fightDefensiveBox
margin-top: 44
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
MountButton
id: mountButton
margin-top: 44
anchors.left: prev.right
anchors.top: parent.top
Panel
id: buttonsPanel
margin-top: 4
margin-right: 5
anchors.fill: parent
anchors.top: prev.bottom
layout:
type: verticalBox
UIButton
id: buttonPvp
height: 20
icon: /images/game/combatmodes/pvp
icon-clip: 0 0 42 20
$on:
icon-clip: 0 20 42 20
InventoryButton
!text: tr('Stop')
@onClick: g_game.stop(); g_game.cancelAttackAndFollow()
InventoryButton
!text: tr('Options')
@onClick: modules.client_options.toggle()
InventoryButton
!text: tr('Hotkeys')
@onClick: modules.game_hotkeys.toggle()
InventoryButton
!text: tr('Logout')
@onClick: modules.game_interface.tryLogout()

View File

@@ -1,6 +1,6 @@
-- CONFIG
APP_NAME = "otclientv8" -- important, change it, it's name for config dir and files in appdata
APP_VERSION = 1343 -- client version for updater and login to identify outdated client
APP_VERSION = 1341 -- client version for updater and login to identify outdated client
DEFAULT_LAYOUT = "retro" -- on android it's forced to "mobile", check code bellow
-- If you don't use updater or other service, set it to updater = ""
@@ -16,11 +16,16 @@ Services = {
-- Servers accept http login url, websocket login url or ip:port:version
Servers = {
--[[ OTClientV8 = "http://otclient.ovh/api/login.php",
OTClientV8c = "otclient.ovh:7171",
OTClientV8proxy = "http://otclient.ovh/api/login.php?proxy=1",
OTClientV8c = "otclient.ovh:7171:1099:25:30:80:90",
OTClientV8Test = "http://otclient.ovh/api/login2.php",
Evoulinia = "evolunia.net:7171:1098",
GarneraTest = "garnera-global.net:7171:1100",
LocalTestServ = "127.0.0.1:7171:1098:110:30:93" ]]
}
--Server = "ws://otclient.ovh:3000/"
--Server = "ws://127.0.0.1:88/"
--USE_NEW_ENERGAME = true -- uses entergamev2 based on websockets instead of entergame
ALLOW_CUSTOM_SERVERS = true -- if true it shows option ANOTHER on server list
@@ -84,5 +89,4 @@ if type(Services.updater) == 'string' and Services.updater:len() > 4
g_modules.ensureModuleLoaded("updater")
return Updater.init(loadModules)
end
loadModules()

View File

@@ -1,4 +1,4 @@
## Layouts overwrite files from `/data`
Foe example, if you have file `/data/images/background.png` and `/layouts/dragonball/images/background.png`, and dragonball layout is selected, then `/layouts/dragonball/images/background.png` will be loaded instead of `/data/images/background.png`.
## Dont make layout named `default`, this name is reserved
## Layouts overwrite files from `/data`
Foe example, if you have file `/data/images/background.png` and `/layouts/dragonball/images/background.png`, and dragonball layout is selected, then `/layouts/dragonball/images/background.png` will be loaded instead of `/data/images/background.png`.
## Dont make layout named `default`, this name is reserved

View File

@@ -1,219 +1,219 @@
ConsoleLabel < UITextEdit
font: verdana-11px-antialised
height: 14
color: yellow
margin-left: 1
text-wrap: true
text-auto-resize: true
selection-color: #111416
selection-background-color: #808080
change-cursor-image: false
cursor-visible: false
editable: false
draggable: true
selectable: false
focusable: false
ConsolePhantomLabel < UILabel
font: verdana-11px-antialised
height: 14
color: yellow
text-wrap: true
text-auto-resize: true
selection-color: #111416
selection-background-color: #999999
ConsoleTabBar < MoveableTabBar
height: 16
ConsoleTabBarPanel < MoveableTabBarPanel
id: consoleTab
ScrollablePanel
id: consoleBuffer
anchors.fill: parent
margin-right: 12
vertical-scrollbar: consoleScrollBar
layout:
type: verticalBox
align-bottom: true
border-width: 1
border-color: #202327
background: #00000066
inverted-scroll: true
padding: 1
VerticalScrollBar
id: consoleScrollBar
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
step: 14
pixels-scroll: true
ConsoleTabBarButton < MoveableTabBarButton
height: 16
padding: 15
ConsolePanel < Panel
image-source: /images/ui/panel_bottom
image-border: 7
image-border-top: 29
$first:
anchors.fill: parent
$!first:
anchors.top: prev.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
CheckBox
id: toggleChat
!tooltip: tr('Disable chat mode, allow to walk using ASDW')
anchors.left: parent.left
anchors.top: parent.top
margin-left: 6
margin-top: 3
@onCheckChange: toggleChat()
TabButton
id: prevChannelButton
icon: /images/game/console/leftarrow
anchors.left: toggleChat.right
anchors.top: parent.top
margin-top: 1
size: 16 16
ConsoleTabBar
id: consoleTabBar
anchors.left: prev.right
anchors.top: parent.top
anchors.right: next.left
margin-top: 0
tab-spacing: 2
movable: true
TabButton
id: nextChannelButton
icon: /images/game/console/rightarrow
anchors.right: next.left
anchors.top: parent.top
margin-top: 1
size: 16 16
margin-right: 5
TabButton
id: closeChannelButton
!tooltip: tr('Close this channel') .. ' (Ctrl+E)'
icon: /images/game/console/closechannel
anchors.right: next.left
anchors.top: parent.top
enabled: false
margin-right: 5
margin-top: 1
size: 16 16
icon-clip: 0 0 16 16
$pressed:
icon-clip: 0 16 16 16
@onClick: removeCurrentTab()
TabButton
id: clearChannelButton
!tooltip: tr('Clear current message window')
icon: /images/game/console/clearchannel
anchors.right: next.left
anchors.top: parent.top
margin-right: 5
margin-top: 1
size: 16 16
icon-clip: 0 0 16 16
$pressed:
icon-clip: 0 16 16 16
@onClick: |
local consoleTabBar = self:getParent():getChildById('consoleTabBar')
clearChannel(consoleTabBar)
TabButton
id: channelsButton
!tooltip: tr('Open new channel') .. ' (Ctrl+O)'
icon: /images/game/console/channels
anchors.right: next.left
anchors.top: parent.top
margin-right: 5
margin-top: 1
size: 16 16
icon-clip: 0 0 16 16
$pressed:
icon-clip: 0 16 16 16
@onClick: g_game.requestChannels()
TabButton
id: ignoreButton
!tooltip: tr('Ignore players')
icon: /images/game/console/ignore
anchors.right: parent.right
anchors.top: parent.top
margin-right: 5
margin-top: 1
size: 16 16
icon-clip: 0 0 16 16
$pressed:
icon-clip: 0 16 16 16
@onClick: onClickIgnoreButton()
Panel
id: consoleContentPanel
anchors.top: consoleTabBar.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: consoleTextEdit.top
margin-left: 6
margin-right: 6
margin-bottom: 2
margin-top: 6
padding: 1
focusable: false
phantom: true
TabButton
id: sayModeButton
icon: /images/game/console/say
!tooltip: tr('Adjust volume')
&sayMode: 2
size: 18 18
anchors.left: parent.left
anchors.bottom: parent.bottom
margin-left: 8
margin-bottom: 4
@onClick: sayModeChange()
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: prev.top
margin-bottom: 3
margin-left: 7
margin-right: 7
TextEdit
id: consoleTextEdit
anchors.left: sayModeButton.right
anchors.right: parent.right
anchors.bottom: parent.bottom
margin-right: 7
margin-left: 2
margin-bottom: 2
shift-navigation: true
max-length: 255
text-auto-submit: true
ConsoleLabel < UITextEdit
font: verdana-11px-antialised
height: 14
color: yellow
margin-left: 1
text-wrap: true
text-auto-resize: true
selection-color: #111416
selection-background-color: #808080
change-cursor-image: false
cursor-visible: false
editable: false
draggable: true
selectable: false
focusable: false
ConsolePhantomLabel < UILabel
font: verdana-11px-antialised
height: 14
color: yellow
text-wrap: true
text-auto-resize: true
selection-color: #111416
selection-background-color: #999999
ConsoleTabBar < MoveableTabBar
height: 16
ConsoleTabBarPanel < MoveableTabBarPanel
id: consoleTab
ScrollablePanel
id: consoleBuffer
anchors.fill: parent
margin-right: 12
vertical-scrollbar: consoleScrollBar
layout:
type: verticalBox
align-bottom: true
border-width: 1
border-color: #202327
background: #00000066
inverted-scroll: true
padding: 1
VerticalScrollBar
id: consoleScrollBar
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
step: 14
pixels-scroll: true
ConsoleTabBarButton < MoveableTabBarButton
height: 16
padding: 15
ConsolePanel < Panel
image-source: /images/ui/panel_bottom
image-border: 7
image-border-top: 29
$first:
anchors.fill: parent
$!first:
anchors.top: prev.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
CheckBox
id: toggleChat
!tooltip: tr('Disable chat mode, allow to walk using ASDW')
anchors.left: parent.left
anchors.top: parent.top
margin-left: 6
margin-top: 3
@onCheckChange: toggleChat()
TabButton
id: prevChannelButton
icon: /images/game/console/leftarrow
anchors.left: toggleChat.right
anchors.top: parent.top
margin-top: 1
size: 16 16
ConsoleTabBar
id: consoleTabBar
anchors.left: prev.right
anchors.top: parent.top
anchors.right: next.left
margin-top: 0
tab-spacing: 2
movable: true
TabButton
id: nextChannelButton
icon: /images/game/console/rightarrow
anchors.right: next.left
anchors.top: parent.top
margin-top: 1
size: 16 16
margin-right: 5
TabButton
id: closeChannelButton
!tooltip: tr('Close this channel') .. ' (Ctrl+E)'
icon: /images/game/console/closechannel
anchors.right: next.left
anchors.top: parent.top
enabled: false
margin-right: 5
margin-top: 1
size: 16 16
icon-clip: 0 0 16 16
$pressed:
icon-clip: 0 16 16 16
@onClick: removeCurrentTab()
TabButton
id: clearChannelButton
!tooltip: tr('Clear current message window')
icon: /images/game/console/clearchannel
anchors.right: next.left
anchors.top: parent.top
margin-right: 5
margin-top: 1
size: 16 16
icon-clip: 0 0 16 16
$pressed:
icon-clip: 0 16 16 16
@onClick: |
local consoleTabBar = self:getParent():getChildById('consoleTabBar')
clearChannel(consoleTabBar)
TabButton
id: channelsButton
!tooltip: tr('Open new channel') .. ' (Ctrl+O)'
icon: /images/game/console/channels
anchors.right: next.left
anchors.top: parent.top
margin-right: 5
margin-top: 1
size: 16 16
icon-clip: 0 0 16 16
$pressed:
icon-clip: 0 16 16 16
@onClick: g_game.requestChannels()
TabButton
id: ignoreButton
!tooltip: tr('Ignore players')
icon: /images/game/console/ignore
anchors.right: parent.right
anchors.top: parent.top
margin-right: 5
margin-top: 1
size: 16 16
icon-clip: 0 0 16 16
$pressed:
icon-clip: 0 16 16 16
@onClick: onClickIgnoreButton()
Panel
id: consoleContentPanel
anchors.top: consoleTabBar.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: consoleTextEdit.top
margin-left: 6
margin-right: 6
margin-bottom: 2
margin-top: 6
padding: 1
focusable: false
phantom: true
TabButton
id: sayModeButton
icon: /images/game/console/say
!tooltip: tr('Adjust volume')
&sayMode: 2
size: 18 18
anchors.left: parent.left
anchors.bottom: parent.bottom
margin-left: 8
margin-bottom: 4
@onClick: sayModeChange()
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: prev.top
margin-bottom: 3
margin-left: 7
margin-right: 7
TextEdit
id: consoleTextEdit
anchors.left: sayModeButton.right
anchors.right: parent.right
anchors.bottom: parent.bottom
margin-right: 7
margin-left: 2
margin-bottom: 2
shift-navigation: true
max-length: 255
text-auto-submit: true

View File

@@ -1,3 +1,3 @@
EnterGameWindow < StaticMainWindow
!text: tr('Enter Game')
EnterGameWindow < StaticMainWindow
!text: tr('Enter Game')
size: 260 340

View File

@@ -1,16 +1,16 @@
GameButtonsWindow < HeadlessMiniWindow
height: 26
&forceOpen: true
&autoOpen: 4
MiniWindowContents
margin-top: 2
Panel
id: buttons
anchors.fill: parent
layout:
type: grid
cell-spacing: 3
cell-size: 20 20
GameButtonsWindow < HeadlessMiniWindow
height: 26
&forceOpen: true
&autoOpen: 4
MiniWindowContents
margin-top: 2
Panel
id: buttons
anchors.fill: parent
layout:
type: grid
cell-spacing: 3
cell-size: 20 20
flow: true

View File

@@ -1,154 +1,154 @@
ExperienceBar < ProgressBar
id: experienceBar
background-color: #B6E866
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin: 1
margin-top: 3
SoulLabel < GameLabel
id: soulLabel
text-align: right
color: white
font: verdana-11px-rounded
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.horizontalCenter
margin-top: 5
margin-right: 3
on: true
$!on:
visible: false
margin-top: 0
height: 0
CapLabel < GameLabel
id: capLabel
color: white
font: verdana-11px-rounded
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.horizontalCenter
margin-top: 5
margin-left: 3
on: true
$!on:
visible: false
margin-top: 0
height: 0
ConditionWidget < UIWidget
size: 18 18
$!first:
margin-left: 2
HealthOverlay < UIWidget
id: healthOverlay
anchors.fill: parent
phantom: true
HealthBar
id: topHealthBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.horizontalCenter
phantom: true
ManaBar
id: topManaBar
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.horizontalCenter
phantom: true
UIProgressBar
id: healthCircle
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/left_empty
margin-right: 169
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: healthCircleFront
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/left_full
margin-right: 169
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: manaCircle
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/right_empty
margin-left: 130
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: manaCircleFront
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/right_full
margin-left: 130
margin-bottom: 16
opacity: 0.4
image-color: #0000FFFF
phantom: true
HealthInfoWindow < HeadlessMiniWindow
icon:
text:
height: 100
&forceOpen: true
icon: /images/topbuttons/healthinfo
!text: tr('Health Info')
MiniWindowContents
margin-top: 2
HealthBar
id: healthBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
margin: 2
margin-top: 0
phantom: true
ManaBar
id: manaBar
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin: 2
margin-bottom: 0
phantom: true
ExperienceBar
Panel
id: conditionPanel
layout:
type: horizontalBox
height: 22
margin-top: 4
padding: 2
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
border-width: 1
border-color: #00000077
background-color: #ffffff11
SoulLabel
CapLabel
ExperienceBar < ProgressBar
id: experienceBar
background-color: #B6E866
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin: 1
margin-top: 3
SoulLabel < GameLabel
id: soulLabel
text-align: right
color: white
font: verdana-11px-rounded
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.horizontalCenter
margin-top: 5
margin-right: 3
on: true
$!on:
visible: false
margin-top: 0
height: 0
CapLabel < GameLabel
id: capLabel
color: white
font: verdana-11px-rounded
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.horizontalCenter
margin-top: 5
margin-left: 3
on: true
$!on:
visible: false
margin-top: 0
height: 0
ConditionWidget < UIWidget
size: 18 18
$!first:
margin-left: 2
HealthOverlay < UIWidget
id: healthOverlay
anchors.fill: parent
phantom: true
HealthBar
id: topHealthBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.horizontalCenter
phantom: true
ManaBar
id: topManaBar
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.horizontalCenter
phantom: true
UIProgressBar
id: healthCircle
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/left_empty
margin-right: 169
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: healthCircleFront
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/left_full
margin-right: 169
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: manaCircle
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/right_empty
margin-left: 130
margin-bottom: 16
opacity: 0.5
phantom: true
UIProgressBar
id: manaCircleFront
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
image-source: /images/game/circle/right_full
margin-left: 130
margin-bottom: 16
opacity: 0.4
image-color: #0000FFFF
phantom: true
HealthInfoWindow < HeadlessMiniWindow
icon:
text:
height: 100
&forceOpen: true
icon: /images/topbuttons/healthinfo
!text: tr('Health Info')
MiniWindowContents
margin-top: 2
HealthBar
id: healthBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
margin: 2
margin-top: 0
phantom: true
ManaBar
id: manaBar
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin: 2
margin-bottom: 0
phantom: true
ExperienceBar
Panel
id: conditionPanel
layout:
type: horizontalBox
height: 22
margin-top: 4
padding: 2
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
border-width: 1
border-color: #00000077
background-color: #ffffff11
SoulLabel
CapLabel

View File

@@ -1,333 +1,333 @@
InventoryItem < Item
$on:
image-source: /images/ui/item-blessed
HeadSlot < InventoryItem
id: slot1
image-source: /images/game/slots/head
&position: {x=65535, y=1, z=0}
$on:
image-source: /images/game/slots/head-blessed
BodySlot < InventoryItem
id: slot4
image-source: /images/game/slots/body
&position: {x=65535, y=4, z=0}
$on:
image-source: /images/game/slots/body-blessed
LegSlot < InventoryItem
id: slot7
image-source: /images/game/slots/legs
&position: {x=65535, y=7, z=0}
$on:
image-source: /images/game/slots/legs-blessed
FeetSlot < InventoryItem
id: slot8
image-source: /images/game/slots/feet
&position: {x=65535, y=8, z=0}
$on:
image-source: /images/game/slots/feet-blessed
NeckSlot < InventoryItem
id: slot2
image-source: /images/game/slots/neck
&position: {x=65535, y=2, z=0}
$on:
image-source: /images/game/slots/neck-blessed
LeftSlot < InventoryItem
id: slot6
image-source: /images/game/slots/left-hand
&position: {x=65535, y=6, z=0}
$on:
image-source: /images/game/slots/left-hand-blessed
FingerSlot < InventoryItem
id: slot9
image-source: /images/game/slots/finger
&position: {x=65535, y=9, z=0}
$on:
image-source: /images/game/slots/finger-blessed
BackSlot < InventoryItem
id: slot3
image-source: /images/game/slots/back
&position: {x=65535, y=3, z=0}
$on:
image-source: /images/game/slots/back-blessed
RightSlot < InventoryItem
id: slot5
image-source: /images/game/slots/right-hand
&position: {x=65535, y=5, z=0}
$on:
image-source: /images/game/slots/right-hand-blessed
AmmoSlot < InventoryItem
id: slot10
image-source: /images/game/slots/ammo
&position: {x=65535, y=10, z=0}
$on:
image-source: /images/game/slots/ammo-blessed
PurseButton < UIButton
id: purseButton
size: 34 12
!tooltip: tr('Open purse')
icon-source: /images/game/slots/purse
icon-clip: 0 0 34 12
$on:
icon-clip: 0 12 34 12
$pressed:
icon-clip: 0 12 34 12
CombatBox < UICheckBox
size: 20 20
image-clip: 0 0 20 20
margin-left: 4
$checked:
image-clip: 0 20 20 20
InventoryButton < Button
height: 18
margin-top: 2
text-align: center
font: cipsoftFont
color: white
size: 45 20
text-offset: 2 2
SoulCapLabel < GameLabel
text-align: center
color: #FFFFFF
font: cipsoftFont
margin-top: 4
text-offset: 0 3
width: 36
height: 20
icon-source: /images/game/slots/soulcap
FightOffensiveBox < CombatBox
image-source: /images/game/combatmodes/fightoffensive
FightBalancedBox < CombatBox
image-source: /images/game/combatmodes/fightbalanced
FightDefensiveBox < CombatBox
image-source: /images/game/combatmodes/fightdefensive
ChaseModeBox < CombatBox
image-source: /images/game/combatmodes/chasemode
SafeFightBox < CombatBox
image-source: /images/game/combatmodes/safefight
MountButton < CombatBox
image-source: /images/game/combatmodes/mount
InventoryWindow < HeadlessMiniWindow
icon: /images/topbuttons/inventory
height: 178
id: inventoryWindow
@onClose: modules.game_inventory.onMiniWindowClose()
&save: true
&autoOpen: 3
&forceOpen: true
MiniWindowContents
anchors.left: parent.left
margin-top: 0
UIButton
id: minimizeButton
anchors.top: parent.top
anchors.left: parent.left
size: 14 14
image-source: /images/ui/miniwindow_buttons
image-clip: 0 0 14 14
margin-top: 3
margin-left: 4
$hover:
image-clip: 0 14 14 14
$pressed:
image-clip: 0 28 14 14
$on:
image-clip: 14 0 14 14
$on hover:
image-clip: 14 14 14 14
$on pressed:
image-clip: 14 28 14 14
@onClick: |
self:getParent():getParent().minimizeButton:onClick()
Panel
id: inventoryPanel
margin-right: 63
margin-top: 2
anchors.fill: parent
HeadSlot
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 3
BodySlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
LegSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
FeetSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
NeckSlot
anchors.top: slot1.top
anchors.right: slot1.left
margin-top: 13
margin-right: 5
LeftSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
FingerSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
BackSlot
anchors.top: slot1.top
anchors.left: slot1.right
margin-top: 13
margin-left: 5
RightSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
AmmoSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
SoulCapLabel
id: soulLabel
anchors.top: slot10.bottom
anchors.horizontalCenter: slot10.horizontalCenter
SoulCapLabel
id: capLabel
anchors.top: slot9.bottom
anchors.horizontalCenter: slot9.horizontalCenter
PurseButton
anchors.left: slot3.left
anchors.bottom: slot3.top
margin-bottom: 3
Panel
id: conditionPanel
layout:
type: horizontalBox
height: 22
padding: 2
anchors.top: slot8.bottom
anchors.left: slot6.left
anchors.right: slot5.right
margin-top: 4
border-width: 1
border-color: #00000077
background-color: #ffffff22
Panel
margin-top: 5
anchors.fill: parent
anchors.left: prev.right
FightOffensiveBox
id: fightOffensiveBox
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
ChaseModeBox
id: chaseModeBox
anchors.left: prev.right
anchors.top: parent.top
FightBalancedBox
id: fightBalancedBox
margin-top: 22
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
SafeFightBox
id: safeFightBox
margin-top: 22
anchors.left: prev.right
anchors.top: parent.top
FightDefensiveBox
id: fightDefensiveBox
margin-top: 44
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
MountButton
id: mountButton
margin-top: 44
anchors.left: prev.right
anchors.top: parent.top
Panel
id: buttonsPanel
margin-top: 4
margin-right: 5
anchors.fill: parent
anchors.top: prev.bottom
layout:
type: verticalBox
UIButton
id: buttonPvp
height: 20
icon: /images/game/combatmodes/pvp
icon-clip: 0 0 42 20
$on:
icon-clip: 0 20 42 20
InventoryButton
!text: tr('Stop')
@onClick: g_game.stop(); g_game.cancelAttackAndFollow()
InventoryButton
!text: tr('Options')
@onClick: modules.client_options.toggle()
InventoryButton
!text: tr('Hotkeys')
@onClick: modules.game_hotkeys.toggle()
InventoryButton
!text: tr('Logout')
@onClick: modules.game_interface.tryLogout()
InventoryItem < Item
$on:
image-source: /images/ui/item-blessed
HeadSlot < InventoryItem
id: slot1
image-source: /images/game/slots/head
&position: {x=65535, y=1, z=0}
$on:
image-source: /images/game/slots/head-blessed
BodySlot < InventoryItem
id: slot4
image-source: /images/game/slots/body
&position: {x=65535, y=4, z=0}
$on:
image-source: /images/game/slots/body-blessed
LegSlot < InventoryItem
id: slot7
image-source: /images/game/slots/legs
&position: {x=65535, y=7, z=0}
$on:
image-source: /images/game/slots/legs-blessed
FeetSlot < InventoryItem
id: slot8
image-source: /images/game/slots/feet
&position: {x=65535, y=8, z=0}
$on:
image-source: /images/game/slots/feet-blessed
NeckSlot < InventoryItem
id: slot2
image-source: /images/game/slots/neck
&position: {x=65535, y=2, z=0}
$on:
image-source: /images/game/slots/neck-blessed
LeftSlot < InventoryItem
id: slot6
image-source: /images/game/slots/left-hand
&position: {x=65535, y=6, z=0}
$on:
image-source: /images/game/slots/left-hand-blessed
FingerSlot < InventoryItem
id: slot9
image-source: /images/game/slots/finger
&position: {x=65535, y=9, z=0}
$on:
image-source: /images/game/slots/finger-blessed
BackSlot < InventoryItem
id: slot3
image-source: /images/game/slots/back
&position: {x=65535, y=3, z=0}
$on:
image-source: /images/game/slots/back-blessed
RightSlot < InventoryItem
id: slot5
image-source: /images/game/slots/right-hand
&position: {x=65535, y=5, z=0}
$on:
image-source: /images/game/slots/right-hand-blessed
AmmoSlot < InventoryItem
id: slot10
image-source: /images/game/slots/ammo
&position: {x=65535, y=10, z=0}
$on:
image-source: /images/game/slots/ammo-blessed
PurseButton < UIButton
id: purseButton
size: 34 12
!tooltip: tr('Open purse')
icon-source: /images/game/slots/purse
icon-clip: 0 0 34 12
$on:
icon-clip: 0 12 34 12
$pressed:
icon-clip: 0 12 34 12
CombatBox < UICheckBox
size: 20 20
image-clip: 0 0 20 20
margin-left: 4
$checked:
image-clip: 0 20 20 20
InventoryButton < Button
height: 18
margin-top: 2
text-align: center
font: cipsoftFont
color: white
size: 45 20
text-offset: 2 2
SoulCapLabel < GameLabel
text-align: center
color: #FFFFFF
font: cipsoftFont
margin-top: 4
text-offset: 0 3
width: 36
height: 20
icon-source: /images/game/slots/soulcap
FightOffensiveBox < CombatBox
image-source: /images/game/combatmodes/fightoffensive
FightBalancedBox < CombatBox
image-source: /images/game/combatmodes/fightbalanced
FightDefensiveBox < CombatBox
image-source: /images/game/combatmodes/fightdefensive
ChaseModeBox < CombatBox
image-source: /images/game/combatmodes/chasemode
SafeFightBox < CombatBox
image-source: /images/game/combatmodes/safefight
MountButton < CombatBox
image-source: /images/game/combatmodes/mount
InventoryWindow < HeadlessMiniWindow
icon: /images/topbuttons/inventory
height: 178
id: inventoryWindow
@onClose: modules.game_inventory.onMiniWindowClose()
&save: true
&autoOpen: 3
&forceOpen: true
MiniWindowContents
anchors.left: parent.left
margin-top: 0
UIButton
id: minimizeButton
anchors.top: parent.top
anchors.left: parent.left
size: 14 14
image-source: /images/ui/miniwindow_buttons
image-clip: 0 0 14 14
margin-top: 3
margin-left: 4
$hover:
image-clip: 0 14 14 14
$pressed:
image-clip: 0 28 14 14
$on:
image-clip: 14 0 14 14
$on hover:
image-clip: 14 14 14 14
$on pressed:
image-clip: 14 28 14 14
@onClick: |
self:getParent():getParent().minimizeButton:onClick()
Panel
id: inventoryPanel
margin-right: 63
margin-top: 2
anchors.fill: parent
HeadSlot
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 3
BodySlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
LegSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
FeetSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
NeckSlot
anchors.top: slot1.top
anchors.right: slot1.left
margin-top: 13
margin-right: 5
LeftSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
FingerSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
BackSlot
anchors.top: slot1.top
anchors.left: slot1.right
margin-top: 13
margin-left: 5
RightSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
AmmoSlot
anchors.top: prev.bottom
anchors.horizontalCenter: prev.horizontalCenter
margin-top: 3
SoulCapLabel
id: soulLabel
anchors.top: slot10.bottom
anchors.horizontalCenter: slot10.horizontalCenter
SoulCapLabel
id: capLabel
anchors.top: slot9.bottom
anchors.horizontalCenter: slot9.horizontalCenter
PurseButton
anchors.left: slot3.left
anchors.bottom: slot3.top
margin-bottom: 3
Panel
id: conditionPanel
layout:
type: horizontalBox
height: 22
padding: 2
anchors.top: slot8.bottom
anchors.left: slot6.left
anchors.right: slot5.right
margin-top: 4
border-width: 1
border-color: #00000077
background-color: #ffffff22
Panel
margin-top: 5
anchors.fill: parent
anchors.left: prev.right
FightOffensiveBox
id: fightOffensiveBox
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
ChaseModeBox
id: chaseModeBox
anchors.left: prev.right
anchors.top: parent.top
FightBalancedBox
id: fightBalancedBox
margin-top: 22
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
SafeFightBox
id: safeFightBox
margin-top: 22
anchors.left: prev.right
anchors.top: parent.top
FightDefensiveBox
id: fightDefensiveBox
margin-top: 44
anchors.left: parent.left
anchors.top: parent.top
margin-left: 8
MountButton
id: mountButton
margin-top: 44
anchors.left: prev.right
anchors.top: parent.top
Panel
id: buttonsPanel
margin-top: 4
margin-right: 5
anchors.fill: parent
anchors.top: prev.bottom
layout:
type: verticalBox
UIButton
id: buttonPvp
height: 20
icon: /images/game/combatmodes/pvp
icon-clip: 0 0 42 20
$on:
icon-clip: 0 20 42 20
InventoryButton
!text: tr('Stop')
@onClick: g_game.stop(); g_game.cancelAttackAndFollow()
InventoryButton
!text: tr('Options')
@onClick: modules.client_options.toggle()
InventoryButton
!text: tr('Hotkeys')
@onClick: modules.game_hotkeys.toggle()
InventoryButton
!text: tr('Logout')
@onClick: modules.game_interface.tryLogout()

View File

@@ -8,7 +8,7 @@ function init()
background:lower()
clientVersionLabel = background:getChildById('clientVersionLabel')
clientVersionLabel:setText('OTClientV8 ' .. g_app.getVersion() .. '\nMade by:\n' .. g_app.getAuthor() .. "\notclient@otclient.ovh")
clientVersionLabel:setText('OTClientV8 ' .. g_app.getVersion() .. '\nrev ' .. g_app.getBuildRevision() .. '\nMade by:\n' .. g_app.getAuthor() .. "")
if not g_game.isOnline() then
addEvent(function() g_effects.fadeIn(clientVersionLabel, 1500) end)

View File

@@ -15,7 +15,7 @@ local serverSelector
local clientVersionSelector
local serverHostTextEdit
local rememberPasswordBox
local protos = {"740", "760", "772", "792", "800", "810", "854", "860", "870", "910", "961", "1000", "1077", "1090", "1096", "1098", "1099", "1100", "1200", "1220", "1230", "1240", "1250", "1252"}
local protos = {"740", "760", "772", "792", "800", "810", "854", "860", "870", "910", "961", "1000", "1077", "1090", "1096", "1098", "1099", "1100", "1200", "1220"}
local checkedByUpdater = {}
local waitingForHttpResults = 0

View File

@@ -31,4 +31,4 @@ Module
dofile 'base64'
dofile 'json'
dofile 'http'
dofile 'test'

62
modules/corelib/test.lua Normal file
View File

@@ -0,0 +1,62 @@
Test = {
tests = {},
activeTest = 0,
screenShot = 1
}
Test.Test = function(name, func)
local testId = #Test.tests + 1
Test.tests[testId] = {
name = name,
actions = {},
delay = 0,
start = 0
}
local test = function(testFunc)
table.insert(Test.tests[testId].actions, {type = "test", value = testFunc})
end
local wait = function(millis)
Test.tests[testId].delay = Test.tests[testId].delay + millis
table.insert(Test.tests[testId].actions, {type = "wait", value = Test.tests[testId].delay})
end
local ss = function()
table.insert(Test.tests[testId].actions, {type = "screenshot"})
end
local fail = function(message)
g_logger.fatal("Test " .. name .. " failed: " .. message)
end
func(test, wait, ss, fail)
end
Test.run = function()
if Test.activeTest > #Test.tests then
g_logger.info("[TEST] Finished tests. Exiting...")
return g_app.exit()
end
local test = Test.tests[Test.activeTest]
if not test or #test.actions == 0 then
Test.activeTest = Test.activeTest + 1
local nextTest = Test.tests[Test.activeTest]
if nextTest then
nextTest.start = g_clock.millis()
g_logger.info("[TEST] Starting test: " .. nextTest.name)
end
return scheduleEvent(Test.run, 500)
end
local action = test.actions[1]
if action.type == "test" then
table.remove(test.actions, 1)
action.value()
elseif action.type == "screenshot" then
table.remove(test.actions, 1)
g_app.doScreenshot(Test.screenShot .. ".png")
Test.screenShot = Test.screenShot + 1
elseif action.type == "wait" then
if action.value + test.start < g_clock.millis() then
table.remove(test.actions, 1)
end
end
scheduleEvent(Test.run, 100)
end

View File

@@ -194,12 +194,20 @@ function UIMiniWindowContainer:order()
if not children[i].miniLoaded then return end
end
table.sort(children, function(a, b)
local indexA = a.miniIndex or a.autoOpen or 999
local indexB = b.miniIndex or b.autoOpen or 999
return indexA < indexB
end)
self:reorderChildren(children)
local ignoreIndex = 0
for i=1,#children do
if children[i].miniIndex then
self:swapInsert(children[i], children[i].miniIndex)
elseif children[i].autoOpen then
self:swapInsert(children[i], children[i].autoOpen)
end
if children[i].save then
children[i].miniIndex = i - ignoreIndex
else
ignoreIndex = ignoreIndex + 1
end
end
end

View File

@@ -1,455 +1,455 @@
actionPanel1 = nil
actionPanel2 = nil
local actionConfig
local hotkeyAssignWindow
local actionButtonsInPanel = 50
ActionTypes = {
USE = 0,
USE_SELF = 1,
USE_TARGET = 2,
USE_WITH = 3,
EQUIP = 4
}
ActionColors = {
empty = '#00000033',
text = '#00000033',
itemUse = '#8888FF88',
itemUseSelf = '#00FF0088',
itemUseTarget = '#FF000088',
itemUseWith = '#F5B32588',
itemEquip = '#FFFFFF88'
}
function init()
local bottomPanel = modules.game_interface.getActionPanel()
actionPanel1 = g_ui.loadUI('actionbar', bottomPanel)
bottomPanel:moveChildToIndex(actionPanel1, 1)
actionPanel2 = g_ui.loadUI('actionbar', bottomPanel)
bottomPanel:moveChildToIndex(actionPanel2, 1)
actionConfig = g_configs.create("/actionbar.otml")
connect(g_game, {
onGameStart = online,
onGameEnd = offline,
onSpellGroupCooldown = onSpellGroupCooldown,
onSpellCooldown = onSpellCooldown
})
if g_game.isOnline() then
online()
end
end
function terminate()
disconnect(g_game, {
onGameStart = online,
onGameEnd = offline,
onSpellGroupCooldown = onSpellGroupCooldown,
onSpellCooldown = onSpellCooldown
})
-- remove hotkeys, also saves config
if actionPanel1.tabBar:getChildCount() > 0 and actionPanel2.tabBar:getChildCount() > 0 then
offline()
end
actionPanel1:destroy()
actionPanel2:destroy()
end
function show()
if not g_game.isOnline() then return end
actionPanel1:setOn(g_settings.getBoolean("actionBar1", false))
actionPanel2:setOn(g_settings.getBoolean("actionBar2", false))
end
function hide()
actionPanel1:setOn(false)
actionPanel2:setOn(false)
end
function switchMode(newMode)
if newMode then
actionPanel1:setImageColor('#ffffff88')
actionPanel2:setImageColor('#ffffff88')
else
actionPanel1:setImageColor('white')
actionPanel2:setImageColor('white')
end
end
function online()
setupActionPanel(1, actionPanel1)
setupActionPanel(2, actionPanel2)
show()
end
function offline()
hide()
if hotkeyAssignWindow then
hotkeyAssignWindow:destroy()
hotkeyAssignWindow = nil
end
local gameRootPanel = modules.game_interface.getRootPanel()
for index, panel in ipairs({actionPanel1, actionPanel2}) do
local config = {}
for i, child in ipairs(panel.tabBar:getChildren()) do
if child.config then
table.insert(config, child.config)
if type(child.config.hotkey) == 'string' and child.config.hotkey:len() > 0 then
g_keyboard.unbindKeyPress(child.config.hotkey, child.callback, gameRootPanel)
end
else
table.insert(config, {})
end
if child.cooldownEvent then
removeEvent(child.cooldownEvent)
end
end
actionConfig:setNode('actions_' .. index, config)
panel.tabBar:destroyChildren()
end
actionConfig:save()
end
function setupActionPanel(index, panel)
local rawConfig = actionConfig:getNode('actions_' .. index) or {}
local config = {}
for i, buttonConfig in pairs(rawConfig) do -- sorting, because key in rawConfig is string
config[tonumber(i)] = buttonConfig
end
for i=1,actionButtonsInPanel do
local action = g_ui.createWidget('ActionButton', panel.tabBar)
action.config = config[i] or {}
setupAction(action)
end
panel.nextButton.onClick = function()
panel.tabBar:moveChildToIndex(panel.tabBar:getFirstChild(), panel.tabBar:getChildCount())
end
panel.prevButton.onClick = function()
panel.tabBar:moveChildToIndex(panel.tabBar:getLastChild(), 1)
end
end
function setupAction(action)
local config = action.config
action.item:setShowCount(false)
action.onMouseRelease = actionOnMouseRelease
action.onTouchRelease = actionOnMouseRelease
action.callback = function(k, c, ticks) executeAction(action, ticks) end
action.item.onItemChange = nil -- disable callbacks for setup
if config then
if type(config.text) == 'number' then
config.text = tostring(config.text)
end
if type(config.hotkey) == 'number' then
config.hotkey = tostring(config.hotkey)
end
if type(config.hotkey) == 'string' and config.hotkey:len() > 0 then
local gameRootPanel = modules.game_interface.getRootPanel()
g_keyboard.bindKeyPress(config.hotkey, action.callback, gameRootPanel)
action.hotkeyLabel:setText(config.hotkey)
else
action.hotkeyLabel:setText("")
end
action.text:setImageSource("")
action.cooldownTill = 0
action.cooldownStart = 0
if type(config.text) == 'string' and config.text:len() > 0 then
action.text:setText(config.text)
action.item:setBorderColor(ActionColors.text)
action.item:setOn(true) -- removes background
action.item:setItemId(0)
if Spells then
local spell, profile = Spells.getSpellByWords(config.text:lower())
action.spell = spell
if action.spell and action.spell.icon and profile then
action.text:setImageSource(SpelllistSettings[profile].iconFile)
action.text:setImageClip(Spells.getImageClip(SpellIcons[action.spell.icon][1], profile))
action.text:setText("")
end
end
else
action.text:setText("")
action.spell = nil
if type(config.item) == 'number' and config.item > 100 then
action.item:setOn(true)
action.item:setItemId(config.item)
action.item:setItemCount(config.count or 1)
setupActionType(action, config.actionType)
else
action.item:setItemId(0)
action.item:setOn(false)
action.item:setBorderColor(ActionColors.empty)
end
end
end
action.item.onItemChange = actionOnItemChange
end
function setupActionType(action, actionType)
local item = action.item:getItem()
if action.item:getItem():isMultiUse() then
if not actionType or actionType <= ActionTypes.USE then
actionType = ActionTypes.USE_WITH
end
elseif g_game.getClientVersion() >= 910 then
if actionType ~= ActionTypes.USE and actionType ~= ActionTypes.EQUIP then
actionType = ActionTypes.USE
end
else
actionType = ActionTypes.USE
end
action.config.actionType = actionType
if action.config.actionType == ActionTypes.USE then
action.item:setBorderColor(ActionColors.itemUse)
elseif action.config.actionType == ActionTypes.USE_SELF then
action.item:setBorderColor(ActionColors.itemUseSelf)
elseif action.config.actionType == ActionTypes.USE_TARGET then
action.item:setBorderColor(ActionColors.itemUseTarget)
elseif action.config.actionType == ActionTypes.USE_WITH then
action.item:setBorderColor(ActionColors.itemUseWith)
elseif action.config.actionType == ActionTypes.EQUIP then
action.item:setBorderColor(ActionColors.itemEquip)
end
end
function updateAction(action, newConfig)
local config = action.config
if type(config.hotkey) == 'string' and config.hotkey:len() > 0 then
local gameRootPanel = modules.game_interface.getRootPanel()
g_keyboard.unbindKeyPress(config.hotkey, action.callback, gameRootPanel)
end
for key, val in pairs(newConfig) do
action.config[key] = val
end
setupAction(action)
end
function actionOnMouseRelease(action, mousePosition, mouseButton)
if mouseButton == MouseTouch then return end
if mouseButton == MouseRightButton or not action.item:isOn() then
local menu = g_ui.createWidget('PopupMenu')
menu:setGameMenu(true)
if action.item:getItemId() > 0 then
if action.item:getItem():isMultiUse() then
menu:addOption(tr('Use on yourself'), function() return setupActionType(action, ActionTypes.USE_SELF) end)
menu:addOption(tr('Use on target'), function() return setupActionType(action, ActionTypes.USE_TARGET) end)
menu:addOption(tr('With crosshair'), function() return setupActionType(action, ActionTypes.USE_WITH) end)
end
if g_game.getClientVersion() >= 910 then
if not action.item:getItem():isMultiUse() then
menu:addOption(tr('Use'), function() return setupActionType(action, ActionTypes.USE) end)
end
menu:addOption(tr('Equip'), function() return setupActionType(action, ActionTypes.EQUIP) end)
end
else
menu:addOption(tr('Select item'), function() return modules.game_itemselector.show(action.item) end)
end
menu:addSeparator()
menu:addOption(tr('Set text'), function()
modules.client_textedit.singlelineEditor(action.config.text or "", function(newText)
updateAction(action, {text=newText, item=0})
end)
end)
menu:addOption(tr('Set hotkey'), function()
if hotkeyAssignWindow then
hotkeyAssignWindow:destroy()
end
local assignWindow = g_ui.createWidget('ActionAssignWindow', rootWidget)
assignWindow:grabKeyboard()
assignWindow.comboPreview.keyCombo = ''
assignWindow.onKeyDown = function(assignWindow, keyCode, keyboardModifiers)
local keyCombo = determineKeyComboDesc(keyCode, keyboardModifiers)
assignWindow.comboPreview:setText(tr('Current action hotkey: %s', keyCombo))
assignWindow.comboPreview.keyCombo = keyCombo
assignWindow.comboPreview:resizeToText()
return true
end
assignWindow.onDestroy = function(widget)
if widget == hotkeyAssignWindow then
hotkeyAssignWindow = nil
end
end
assignWindow.addButton.onClick = function()
updateAction(action, {hotkey=tostring(assignWindow.comboPreview.keyCombo)})
assignWindow:destroy()
end
hotkeyAssignWindow = assignWindow
end)
menu:addSeparator()
menu:addOption(tr('Clear'), function()
updateAction(action, {hotkey="", text="", item=0, count=1})
end)
menu:display(mousePosition)
return true
elseif mouseButton == MouseLeftButton or mouseButton == MouseTouch2 or mouseButton == MouseTouch3 then
action.callback()
return true
end
return false
end
function actionOnItemChange(widget)
updateAction(widget:getParent(), {text="", item=widget:getItemId(), count=widget:getItemCountOrSubType()})
end
function onSpellCooldown(iconId, duration)
for index, panel in ipairs({actionPanel1, actionPanel2}) do
for i, child in ipairs(panel.tabBar:getChildren()) do
if child.spell and child.spell.id == iconId then
startCooldown(child, duration)
end
end
end
end
function onSpellGroupCooldown(groupId, duration)
for index, panel in ipairs({actionPanel1, actionPanel2}) do
for i, child in ipairs(panel.tabBar:getChildren()) do
if child.spell and child.spell.group then
for group, duration in pairs(child.spell.group) do
if groupId == group then
startCooldown(child, duration)
end
end
end
end
end
end
function startCooldown(action, duration)
if type(action.cooldownTill) == 'number' and action.cooldownTill > g_clock.millis() + duration then
return -- already has cooldown with greater duration
end
action.cooldownStart = g_clock.millis()
action.cooldownTill = g_clock.millis() + duration
updateCooldown(action)
end
function updateCooldown(action)
if not action or not action.cooldownTill then return end
local timeleft = action.cooldownTill - g_clock.millis()
if timeleft <= 30 then
action.cooldown:setPercent(100)
action.cooldownEvent = nil
return
end
local duration = action.cooldownTill - action.cooldownStart
action.cooldown:setPercent(100 - math.floor(100 * timeleft / duration))
action.cooldownEvent = scheduleEvent(function() updateCooldown(action) end, 30)
end
function executeAction(action, ticks)
if not action.config then return end
if type(ticks) ~= 'number' then ticks = 0 end
local actionDelay = 100
if ticks == 0 then
actionDelay = 200 -- for first use
elseif action.actionDelayTo ~= nil and g_clock.millis() < action.actionDelayTo then
return
end
local actionType = action.config.actionType
if type(action.config.text) == 'string' and action.config.text:len() > 0 then
if g_app.isMobile() then -- turn to direction of targer
local target = g_game.getAttackingCreature()
if target then
local pos = g_game.getLocalPlayer():getPosition()
local tpos = target:getPosition()
if pos and tpos then
local offx = tpos.x - pos.x
local offy = tpos.y - pos.y
if offy < 0 and offx <= 0 and math.abs(offx) < math.abs(offy) then
g_game.turn(Directions.North)
elseif offy > 0 and offx >= 0 and math.abs(offx) < math.abs(offy) then
g_game.turn(Directions.South)
elseif offx < 0 and offy <= 0 and math.abs(offx) > math.abs(offy) then
g_game.turn(Directions.West)
elseif offx > 0 and offy >= 0 and math.abs(offx) > math.abs(offy) then
g_game.turn(Directions.East)
end
end
end
end
if modules.game_interface.isChatVisible() then
modules.game_console.sendMessage(action.config.text)
else
g_game.talk(action.config.text)
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif action.item:getItemId() > 0 then
if actionType == ActionTypes.USE then
if g_game.getClientVersion() < 780 then
local item = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if item then
g_game.use(item)
end
else
g_game.useInventoryItem(action.item:getItemId())
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif actionType == ActionTypes.USE_SELF then
if g_game.getClientVersion() < 780 then
local item = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if item then
g_game.useWith(item, g_game.getLocalPlayer())
end
else
g_game.useInventoryItemWith(action.item:getItemId(), g_game.getLocalPlayer(), action.item:getItemSubType() or -1)
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif actionType == ActionTypes.USE_TARGET then
local attackingCreature = g_game.getAttackingCreature()
if not attackingCreature then
local item = Item.create(action.item:getItemId())
if g_game.getClientVersion() < 780 then
local tmpItem = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if not tmpItem then return end
item = tmpItem
end
modules.game_interface.startUseWith(item, action.item:getItemSubType() or - 1)
return
end
if not attackingCreature:getTile() then return end
if g_game.getClientVersion() < 780 then
local item = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if item then
g_game.useWith(item, attackingCreature, action.item:getItemSubType() or -1)
end
else
g_game.useInventoryItemWith(action.item:getItemId(), attackingCreature, action.item:getItemSubType() or -1)
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif actionType == ActionTypes.USE_WITH then
local item = Item.create(action.item:getItemId())
if g_game.getClientVersion() < 780 then
local tmpItem = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if not tmpItem then return true end
item = tmpItem
end
modules.game_interface.startUseWith(item, action.item:getItemSubType() or - 1)
elseif actionType == ActionTypes.EQUIP then
if g_game.getClientVersion() >= 910 then
local item = Item.create(action.item:getItemId())
g_game.equipItem(item)
action.actionDelayTo = g_clock.millis() + actionDelay
end
end
end
actionPanel1 = nil
actionPanel2 = nil
local actionConfig
local hotkeyAssignWindow
local actionButtonsInPanel = 50
ActionTypes = {
USE = 0,
USE_SELF = 1,
USE_TARGET = 2,
USE_WITH = 3,
EQUIP = 4
}
ActionColors = {
empty = '#00000033',
text = '#00000033',
itemUse = '#8888FF88',
itemUseSelf = '#00FF0088',
itemUseTarget = '#FF000088',
itemUseWith = '#F5B32588',
itemEquip = '#FFFFFF88'
}
function init()
local bottomPanel = modules.game_interface.getActionPanel()
actionPanel1 = g_ui.loadUI('actionbar', bottomPanel)
bottomPanel:moveChildToIndex(actionPanel1, 1)
actionPanel2 = g_ui.loadUI('actionbar', bottomPanel)
bottomPanel:moveChildToIndex(actionPanel2, 1)
actionConfig = g_configs.create("/actionbar.otml")
connect(g_game, {
onGameStart = online,
onGameEnd = offline,
onSpellGroupCooldown = onSpellGroupCooldown,
onSpellCooldown = onSpellCooldown
})
if g_game.isOnline() then
online()
end
end
function terminate()
disconnect(g_game, {
onGameStart = online,
onGameEnd = offline,
onSpellGroupCooldown = onSpellGroupCooldown,
onSpellCooldown = onSpellCooldown
})
-- remove hotkeys, also saves config
if actionPanel1.tabBar:getChildCount() > 0 and actionPanel2.tabBar:getChildCount() > 0 then
offline()
end
actionPanel1:destroy()
actionPanel2:destroy()
end
function show()
if not g_game.isOnline() then return end
actionPanel1:setOn(g_settings.getBoolean("actionBar1", false))
actionPanel2:setOn(g_settings.getBoolean("actionBar2", false))
end
function hide()
actionPanel1:setOn(false)
actionPanel2:setOn(false)
end
function switchMode(newMode)
if newMode then
actionPanel1:setImageColor('#ffffff88')
actionPanel2:setImageColor('#ffffff88')
else
actionPanel1:setImageColor('white')
actionPanel2:setImageColor('white')
end
end
function online()
setupActionPanel(1, actionPanel1)
setupActionPanel(2, actionPanel2)
show()
end
function offline()
hide()
if hotkeyAssignWindow then
hotkeyAssignWindow:destroy()
hotkeyAssignWindow = nil
end
local gameRootPanel = modules.game_interface.getRootPanel()
for index, panel in ipairs({actionPanel1, actionPanel2}) do
local config = {}
for i, child in ipairs(panel.tabBar:getChildren()) do
if child.config then
table.insert(config, child.config)
if type(child.config.hotkey) == 'string' and child.config.hotkey:len() > 0 then
g_keyboard.unbindKeyPress(child.config.hotkey, child.callback, gameRootPanel)
end
else
table.insert(config, {})
end
if child.cooldownEvent then
removeEvent(child.cooldownEvent)
end
end
actionConfig:setNode('actions_' .. index, config)
panel.tabBar:destroyChildren()
end
actionConfig:save()
end
function setupActionPanel(index, panel)
local rawConfig = actionConfig:getNode('actions_' .. index) or {}
local config = {}
for i, buttonConfig in pairs(rawConfig) do -- sorting, because key in rawConfig is string
config[tonumber(i)] = buttonConfig
end
for i=1,actionButtonsInPanel do
local action = g_ui.createWidget('ActionButton', panel.tabBar)
action.config = config[i] or {}
setupAction(action)
end
panel.nextButton.onClick = function()
panel.tabBar:moveChildToIndex(panel.tabBar:getFirstChild(), panel.tabBar:getChildCount())
end
panel.prevButton.onClick = function()
panel.tabBar:moveChildToIndex(panel.tabBar:getLastChild(), 1)
end
end
function setupAction(action)
local config = action.config
action.item:setShowCount(false)
action.onMouseRelease = actionOnMouseRelease
action.onTouchRelease = actionOnMouseRelease
action.callback = function(k, c, ticks) executeAction(action, ticks) end
action.item.onItemChange = nil -- disable callbacks for setup
if config then
if type(config.text) == 'number' then
config.text = tostring(config.text)
end
if type(config.hotkey) == 'number' then
config.hotkey = tostring(config.hotkey)
end
if type(config.hotkey) == 'string' and config.hotkey:len() > 0 then
local gameRootPanel = modules.game_interface.getRootPanel()
g_keyboard.bindKeyPress(config.hotkey, action.callback, gameRootPanel)
action.hotkeyLabel:setText(config.hotkey)
else
action.hotkeyLabel:setText("")
end
action.text:setImageSource("")
action.cooldownTill = 0
action.cooldownStart = 0
if type(config.text) == 'string' and config.text:len() > 0 then
action.text:setText(config.text)
action.item:setBorderColor(ActionColors.text)
action.item:setOn(true) -- removes background
action.item:setItemId(0)
if Spells then
local spell, profile = Spells.getSpellByWords(config.text:lower())
action.spell = spell
if action.spell and action.spell.icon and profile then
action.text:setImageSource(SpelllistSettings[profile].iconFile)
action.text:setImageClip(Spells.getImageClip(SpellIcons[action.spell.icon][1], profile))
action.text:setText("")
end
end
else
action.text:setText("")
action.spell = nil
if type(config.item) == 'number' and config.item > 100 then
action.item:setOn(true)
action.item:setItemId(config.item)
action.item:setItemCount(config.count or 1)
setupActionType(action, config.actionType)
else
action.item:setItemId(0)
action.item:setOn(false)
action.item:setBorderColor(ActionColors.empty)
end
end
end
action.item.onItemChange = actionOnItemChange
end
function setupActionType(action, actionType)
local item = action.item:getItem()
if action.item:getItem():isMultiUse() then
if not actionType or actionType <= ActionTypes.USE then
actionType = ActionTypes.USE_WITH
end
elseif g_game.getClientVersion() >= 910 then
if actionType ~= ActionTypes.USE and actionType ~= ActionTypes.EQUIP then
actionType = ActionTypes.USE
end
else
actionType = ActionTypes.USE
end
action.config.actionType = actionType
if action.config.actionType == ActionTypes.USE then
action.item:setBorderColor(ActionColors.itemUse)
elseif action.config.actionType == ActionTypes.USE_SELF then
action.item:setBorderColor(ActionColors.itemUseSelf)
elseif action.config.actionType == ActionTypes.USE_TARGET then
action.item:setBorderColor(ActionColors.itemUseTarget)
elseif action.config.actionType == ActionTypes.USE_WITH then
action.item:setBorderColor(ActionColors.itemUseWith)
elseif action.config.actionType == ActionTypes.EQUIP then
action.item:setBorderColor(ActionColors.itemEquip)
end
end
function updateAction(action, newConfig)
local config = action.config
if type(config.hotkey) == 'string' and config.hotkey:len() > 0 then
local gameRootPanel = modules.game_interface.getRootPanel()
g_keyboard.unbindKeyPress(config.hotkey, action.callback, gameRootPanel)
end
for key, val in pairs(newConfig) do
action.config[key] = val
end
setupAction(action)
end
function actionOnMouseRelease(action, mousePosition, mouseButton)
if mouseButton == MouseTouch then return end
if mouseButton == MouseRightButton or not action.item:isOn() then
local menu = g_ui.createWidget('PopupMenu')
menu:setGameMenu(true)
if action.item:getItemId() > 0 then
if action.item:getItem():isMultiUse() then
menu:addOption(tr('Use on yourself'), function() return setupActionType(action, ActionTypes.USE_SELF) end)
menu:addOption(tr('Use on target'), function() return setupActionType(action, ActionTypes.USE_TARGET) end)
menu:addOption(tr('With crosshair'), function() return setupActionType(action, ActionTypes.USE_WITH) end)
end
if g_game.getClientVersion() >= 910 then
if not action.item:getItem():isMultiUse() then
menu:addOption(tr('Use'), function() return setupActionType(action, ActionTypes.USE) end)
end
menu:addOption(tr('Equip'), function() return setupActionType(action, ActionTypes.EQUIP) end)
end
else
menu:addOption(tr('Select item'), function() return modules.game_itemselector.show(action.item) end)
end
menu:addSeparator()
menu:addOption(tr('Set text'), function()
modules.client_textedit.singlelineEditor(action.config.text or "", function(newText)
updateAction(action, {text=newText, item=0})
end)
end)
menu:addOption(tr('Set hotkey'), function()
if hotkeyAssignWindow then
hotkeyAssignWindow:destroy()
end
local assignWindow = g_ui.createWidget('ActionAssignWindow', rootWidget)
assignWindow:grabKeyboard()
assignWindow.comboPreview.keyCombo = ''
assignWindow.onKeyDown = function(assignWindow, keyCode, keyboardModifiers)
local keyCombo = determineKeyComboDesc(keyCode, keyboardModifiers)
assignWindow.comboPreview:setText(tr('Current action hotkey: %s', keyCombo))
assignWindow.comboPreview.keyCombo = keyCombo
assignWindow.comboPreview:resizeToText()
return true
end
assignWindow.onDestroy = function(widget)
if widget == hotkeyAssignWindow then
hotkeyAssignWindow = nil
end
end
assignWindow.addButton.onClick = function()
updateAction(action, {hotkey=tostring(assignWindow.comboPreview.keyCombo)})
assignWindow:destroy()
end
hotkeyAssignWindow = assignWindow
end)
menu:addSeparator()
menu:addOption(tr('Clear'), function()
updateAction(action, {hotkey="", text="", item=0, count=1})
end)
menu:display(mousePosition)
return true
elseif mouseButton == MouseLeftButton or mouseButton == MouseTouch2 or mouseButton == MouseTouch3 then
action.callback()
return true
end
return false
end
function actionOnItemChange(widget)
updateAction(widget:getParent(), {text="", item=widget:getItemId(), count=widget:getItemCountOrSubType()})
end
function onSpellCooldown(iconId, duration)
for index, panel in ipairs({actionPanel1, actionPanel2}) do
for i, child in ipairs(panel.tabBar:getChildren()) do
if child.spell and child.spell.id == iconId then
startCooldown(child, duration)
end
end
end
end
function onSpellGroupCooldown(groupId, duration)
for index, panel in ipairs({actionPanel1, actionPanel2}) do
for i, child in ipairs(panel.tabBar:getChildren()) do
if child.spell and child.spell.group then
for group, duration in pairs(child.spell.group) do
if groupId == group then
startCooldown(child, duration)
end
end
end
end
end
end
function startCooldown(action, duration)
if type(action.cooldownTill) == 'number' and action.cooldownTill > g_clock.millis() + duration then
return -- already has cooldown with greater duration
end
action.cooldownStart = g_clock.millis()
action.cooldownTill = g_clock.millis() + duration
updateCooldown(action)
end
function updateCooldown(action)
if not action or not action.cooldownTill then return end
local timeleft = action.cooldownTill - g_clock.millis()
if timeleft <= 30 then
action.cooldown:setPercent(100)
action.cooldownEvent = nil
return
end
local duration = action.cooldownTill - action.cooldownStart
action.cooldown:setPercent(100 - math.floor(100 * timeleft / duration))
action.cooldownEvent = scheduleEvent(function() updateCooldown(action) end, 30)
end
function executeAction(action, ticks)
if not action.config then return end
if type(ticks) ~= 'number' then ticks = 0 end
local actionDelay = 100
if ticks == 0 then
actionDelay = 200 -- for first use
elseif action.actionDelayTo ~= nil and g_clock.millis() < action.actionDelayTo then
return
end
local actionType = action.config.actionType
if type(action.config.text) == 'string' and action.config.text:len() > 0 then
if g_app.isMobile() then -- turn to direction of targer
local target = g_game.getAttackingCreature()
if target then
local pos = g_game.getLocalPlayer():getPosition()
local tpos = target:getPosition()
if pos and tpos then
local offx = tpos.x - pos.x
local offy = tpos.y - pos.y
if offy < 0 and offx <= 0 and math.abs(offx) < math.abs(offy) then
g_game.turn(Directions.North)
elseif offy > 0 and offx >= 0 and math.abs(offx) < math.abs(offy) then
g_game.turn(Directions.South)
elseif offx < 0 and offy <= 0 and math.abs(offx) > math.abs(offy) then
g_game.turn(Directions.West)
elseif offx > 0 and offy >= 0 and math.abs(offx) > math.abs(offy) then
g_game.turn(Directions.East)
end
end
end
end
if modules.game_interface.isChatVisible() then
modules.game_console.sendMessage(action.config.text)
else
g_game.talk(action.config.text)
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif action.item:getItemId() > 0 then
if actionType == ActionTypes.USE then
if g_game.getClientVersion() < 780 then
local item = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if item then
g_game.use(item)
end
else
g_game.useInventoryItem(action.item:getItemId())
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif actionType == ActionTypes.USE_SELF then
if g_game.getClientVersion() < 780 then
local item = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if item then
g_game.useWith(item, g_game.getLocalPlayer())
end
else
g_game.useInventoryItemWith(action.item:getItemId(), g_game.getLocalPlayer(), action.item:getItemSubType() or -1)
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif actionType == ActionTypes.USE_TARGET then
local attackingCreature = g_game.getAttackingCreature()
if not attackingCreature then
local item = Item.create(action.item:getItemId())
if g_game.getClientVersion() < 780 then
local tmpItem = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if not tmpItem then return end
item = tmpItem
end
modules.game_interface.startUseWith(item, action.item:getItemSubType() or - 1)
return
end
if not attackingCreature:getTile() then return end
if g_game.getClientVersion() < 780 then
local item = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if item then
g_game.useWith(item, attackingCreature, action.item:getItemSubType() or -1)
end
else
g_game.useInventoryItemWith(action.item:getItemId(), attackingCreature, action.item:getItemSubType() or -1)
end
action.actionDelayTo = g_clock.millis() + actionDelay
elseif actionType == ActionTypes.USE_WITH then
local item = Item.create(action.item:getItemId())
if g_game.getClientVersion() < 780 then
local tmpItem = g_game.findPlayerItem(action.item:getItemId(), action.item:getItemSubType() or -1)
if not tmpItem then return true end
item = tmpItem
end
modules.game_interface.startUseWith(item, action.item:getItemSubType() or - 1)
elseif actionType == ActionTypes.EQUIP then
if g_game.getClientVersion() >= 910 then
local item = Item.create(action.item:getItemId())
g_game.equipItem(item)
action.actionDelayTo = g_clock.millis() + actionDelay
end
end
end
end

View File

@@ -1,145 +1,145 @@
ActionButton < Panel
font: cipsoftFont
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 40
padding: 1 1 1 1
margin-left: 1
$first:
anchors.left: parent.left
$!first:
anchors.left: prev.right
Item
id: item
anchors.fill: parent
&selectable: true
&editable: false
virtual: true
border-width: 1
border-color: #00000000
$!on:
image-source: /images/game/actionbarslot
Label
id: text
anchors.fill: parent
text-auto-resize: true
text-wrap: true
phantom: true
text-align: center
font: verdana-9px
Label
id: hotkeyLabel
anchors.top: parent.top
anchors.left: parent.left
margin: 2 3 3 3
text-auto-resize: true
text-wrap: false
phantom: true
font: small-9px
color: yellow
UIProgressRect
id: cooldown
background: #585858AA
percent: 100
focusable: false
phantom: true
anchors.fill: parent
margin: 1 1 1 1
Panel
id: actionBar
focusable: false
image-source: /images/ui/panel_side
image-border: 4
margin-top: -1
$on:
height: 40
visible: true
$!on:
height: 0
visible: false
TabButton
id: prevButton
icon: /images/game/console/leftarrow
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-left: 1
margin-top: 1
margin-bottom: 2
Panel
id: tabBar
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: prev.right
anchors.right: next.left
margin-right: 3
clipping: true
TabButton
id: nextButton
icon: /images/game/console/rightarrow
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 1
margin-top: 1
margin-bottom: 2
ActionAssignWindow < MainWindow
id: assignWindow
!text: tr('Button Assign')
size: 360 150
@onEscape: self:destroy()
Label
!text: tr('Please, press the key you wish to use for action')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-auto-resize: true
text-align: left
Label
id: comboPreview
!text: tr('Current action hotkey: %s', 'none')
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: prev.bottom
margin-top: 10
text-auto-resize: true
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: next.top
margin-bottom: 10
Button
id: addButton
!text: tr('Add')
width: 64
anchors.right: next.left
anchors.bottom: parent.bottom
margin-right: 10
Button
id: cancelButton
!text: tr('Cancel')
width: 64
anchors.right: parent.right
anchors.bottom: parent.bottom
ActionButton < Panel
font: cipsoftFont
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 40
padding: 1 1 1 1
margin-left: 1
$first:
anchors.left: parent.left
$!first:
anchors.left: prev.right
Item
id: item
anchors.fill: parent
&selectable: true
&editable: false
virtual: true
border-width: 1
border-color: #00000000
$!on:
image-source: /images/game/actionbarslot
Label
id: text
anchors.fill: parent
text-auto-resize: true
text-wrap: true
phantom: true
text-align: center
font: verdana-9px
Label
id: hotkeyLabel
anchors.top: parent.top
anchors.left: parent.left
margin: 2 3 3 3
text-auto-resize: true
text-wrap: false
phantom: true
font: small-9px
color: yellow
UIProgressRect
id: cooldown
background: #585858AA
percent: 100
focusable: false
phantom: true
anchors.fill: parent
margin: 1 1 1 1
Panel
id: actionBar
focusable: false
image-source: /images/ui/panel_side
image-border: 4
margin-top: -1
$on:
height: 40
visible: true
$!on:
height: 0
visible: false
TabButton
id: prevButton
icon: /images/game/console/leftarrow
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-left: 1
margin-top: 1
margin-bottom: 2
Panel
id: tabBar
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: prev.right
anchors.right: next.left
margin-right: 3
clipping: true
TabButton
id: nextButton
icon: /images/game/console/rightarrow
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
margin-right: 1
margin-top: 1
margin-bottom: 2
ActionAssignWindow < MainWindow
id: assignWindow
!text: tr('Button Assign')
size: 360 150
@onEscape: self:destroy()
Label
!text: tr('Please, press the key you wish to use for action')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-auto-resize: true
text-align: left
Label
id: comboPreview
!text: tr('Current action hotkey: %s', 'none')
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: prev.bottom
margin-top: 10
text-auto-resize: true
HorizontalSeparator
id: separator
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: next.top
margin-bottom: 10
Button
id: addButton
!text: tr('Add')
width: 64
anchors.right: next.left
anchors.bottom: parent.bottom
margin-right: 10
Button
id: cancelButton
!text: tr('Cancel')
width: 64
anchors.right: parent.right
anchors.bottom: parent.bottom
@onClick: self:getParent():destroy()

View File

@@ -283,7 +283,7 @@ function checkCreatures()
for i=#creatures + 1,maxCreatures do
if battleButtons[i]:isHidden() then break end
battleButtons[i]:hide()
battleButtons[i]:setOn(false)
battleButton:setOn(false)
end
battlePanel:getLayout():enableUpdates()

View File

@@ -474,7 +474,9 @@ function initCallbacks()
onChannelList = botChannelList,
onOpenChannel = botOpenChannel,
onCloseChannel = botCloseChannel,
onChannelEvent = botChannelEvent
onChannelEvent = botChannelEvent,
onImbuementWindow = botOnImbuementWindow,
onModalDialog = botOnModalDialog,
})
connect(Tile, {
@@ -527,7 +529,9 @@ function terminateCallbacks()
onChannelList = botChannelList,
onOpenChannel = botOpenChannel,
onCloseChannel = botCloseChannel,
onChannelEvent = botChannelEvent
onChannelEvent = botChannelEvent,
onImbuementWindow = botOnImbuementWindow,
onModalDialog = botOnModalDialog,
})
disconnect(Tile, {
@@ -702,4 +706,14 @@ end
function botCreatureWalk(creature, oldPos, newPos)
if botExecutor == nil then return false end
safeBotCall(function() botExecutor.callbacks.onWalk(creature, oldPos, newPos) end)
end
end
function botOnImbuementWindow(itemId, slots, activeSlots, imbuements, needItems)
if botExecutor == nil then return false end
safeBotCall(function() botExecutor.callbacks.onImbuementWindow(itemId, slots, activeSlots, imbuements, needItems) end)
end
function botOnModalDialog(id, title, message, buttons, enterButton, escapeButton, choices, priority)
if botExecutor == nil then return false end
safeBotCall(function() botExecutor.callbacks.onModalDialog(id, title, message, buttons, enterButton, escapeButton, choices, priority) end)
end

View File

@@ -1,8 +1,8 @@
Module
name: game_bot
description: Advanced OTClientV8 Bot
author: otclient@otclient.ovh
sandboxed: true
scripts: [ bot ]
@onLoad: init()
@onUnload: terminate()
Module
name: game_bot
description: Advanced OTClientV8 Bot
author: otclient@otclient.ovh
sandboxed: true
scripts: [ bot ]
@onLoad: init()
@onUnload: terminate()

View File

@@ -104,7 +104,7 @@ CaveBot.Editor.setup = function()
title="Go to position",
description="Go to position (x,y,z)",
multiline=false,
validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+),?\\s*([0-9]?)$"
validation="^\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*,\\s*([0-9]+)$"
})
registerAction("use", {
value=function() return posx() .. "," .. posy() .. "," .. posz() end,

View File

@@ -0,0 +1,23 @@
goto:1033,1044,7
goto:1031,1038,7
goto:1030,1038,7
goto:1157,985,7
goto:1161,981,7
goto:1033,1042,7
goto:1034,1038,7
goto:1169,985,7
goto:1175,985,7
goto:1176,983,7
goto:756,846,7
goto:756,846,7
config:{"walk":100,"walk2":false}
extensions:[[
{
"Depositer": [
],
"Supply": [
]
}
]]

View File

@@ -0,0 +1,13 @@
goto:84,112,6
goto:95,108,6
config:{"mapClickDelay":100,"walkDelay":10,"ping":250,"ignoreFields":false,"useDelay":400,"mapClick":false}
extensions:[[
{
"Depositer": [
],
"Supply": [
]
}
]]

View File

@@ -0,0 +1,104 @@
goto:93,129,7
goto:96,123,7
goto:96,117,7
goto:101,114,7
goto:95,111,6
goto:89,111,6
goto:83,108,6
goto:80,102,6
goto:80,96,6
goto:85,90,6
goto:88,92,6
goto:91,86,7
goto:97,85,7
goto:103,84,7
function:[[
TargetBot.enableLuring()
return true
]]
goto:109,79,7
goto:112,79,7
goto:112,79,8
function:[[
TargetBot.disableLuring()
return true
]]
goto:112,79,7
goto:106,84,8
goto:100,80,8
goto:100,74,8
goto:99,80,8
goto:105,83,8
function:[[
TargetBot.setOff()
return true
]]
goto:111,82,8
goto:112,79,8
goto:106,82,7
goto:100,85,7
goto:94,85,7
goto:91,91,7
goto:89,92,7
goto:83,90,6
function:[[
TargetBot.setOff()
return true
]]
goto:77,94,6
goto:75,95,6
goto:69,96,7
goto:63,100,7
goto:61,102,7
goto:62,96,8
use:61,102,8
goto:62,101,8
goto:68,99,7
goto:74,95,7
goto:75,95,7
goto:79,101,6
goto:81,107,6
goto:87,109,6
goto:93,112,6
function:[[
TargetBot.disableLuring()
return true
]]
goto:99,116,6
use:102,114,6
goto:101,115,6
use:100,116,5
goto:101,115,5
goto:100,116,4
goto:102,114,5
goto:101,114,6
goto:96,120,7
goto:95,126,7
function:[[
g_game.safeLogout()
delay(1000)
return "retry"
]]
config:{"useDelay":400,"mapClickDelay":100,"walkDelay":20,"ping":150,"ignoreFields":false,"skipBlocked":true,"mapClick":false}
extensions:[[
{
"Depositer": [
],
"Supply": [
]
}
]]

View File

@@ -0,0 +1,128 @@
{
"hpitem1": {
"max": 90,
"title": "HP%",
"subType": 0,
"item": 266,
"min": 51,
"on": false
},
"foodItems": [
{
"id": 3582,
"count": 1
},
{
"id": 3577,
"count": 1
}
],
"autoEquip": [
{
"item1": 3052,
"title": "Auto Equip",
"item2": 3089,
"on": false,
"slot": 9
},
{
"item1": 0,
"title": "Auto Equip",
"item2": 0,
"on": false,
"slot": 0
},
{
"item1": 0,
"title": "Auto Equip",
"item2": 0,
"on": false,
"slot": 0
},
{
"item1": 0,
"title": "Auto Equip",
"item2": 0,
"on": false,
"slot": 0
}
],
"ingame_hotkeys": "singlehotkey(\"f1\", function()\nlocal shaders = {\"stars\", \"gold\", \"rainbow\", \"sweden\", \"brazil\", \"line\", \"3line\", \"circle\", \"outline\"}\nlocal p = 0\nfor i, c in pairs(getSpectators()) do\n c:setOutfitShader(shaders[1 + p % 10])\n p = p + 1\nend\nend)\n\nsinglehotkey(\"1\", function()\n for _, s in ipairs(getSpectators()) do\n if s:canShoot(3) then\n info(s:getName())\n else\n warn(s:getName())\n end\n end\nend)",
"healing2": {
"max": 50,
"title": "HP%",
"on": false,
"min": 1,
"text": "exura vita"
},
"ingame_macros": "",
"hasteSpell": "utani hur",
"manaitem2": {
"max": 50,
"title": "MP%",
"subType": 0,
"item": 3157,
"min": 0,
"on": false
},
"_configs": {
"cavebot_configs": {
"selected": "test_src",
"enabled": false
},
"targetbot_configs": {
"enabled": false,
"selected": "config_name"
}
},
"healing1": {
"max": 100,
"title": "HP%",
"on": false,
"min": 51,
"text": "exura"
},
"dropItems": [
{
"id": 283,
"count": 1
},
{
"id": 284,
"count": 1
},
{
"id": 285,
"count": 1
}
],
"_macros": {
"": false
},
"manaitem1": {
"max": 90,
"title": "MP%",
"subType": 0,
"item": 268,
"min": 51,
"on": false
},
"hpitem2": {
"max": 50,
"title": "HP%",
"subType": 0,
"item": 3160,
"min": 0,
"on": false
},
"manaShield": "utamo vita",
"autoTradeMessage": "I'm using OTClientV8!",
"antiParalyze": "utani hur",
"manaTrain": {
"max": 100,
"title": "MP%",
"on": false,
"min": 80,
"text": "utevo lux"
}
}

View File

@@ -0,0 +1,53 @@
{
"looting": {
"items": [
],
"maxDanger": 10,
"minCapacity": 100,
"containers": [
{
"count": 1,
"id": 2853
}
],
"everyItem": true
},
"targeting": [
{
"useSpellAttack": false,
"useRuneAttack": false,
"minMana": 200,
"avoidAttacks": false,
"groupAttackTargets": 2,
"groupAttackSpell": "",
"danger": 1,
"runeAttackDelay": 2000,
"lureCavebot": true,
"dontLoot": false,
"useGroupAttackRune": false,
"groupRuneAttackRadius": 1,
"groupAttackIgnorePlayers": true,
"maxDistance": 10,
"groupAttackIgnoreParty": false,
"lureCount": 5,
"useGroupAttack": false,
"groupRuneAttackTargets": 2,
"attackSpell": "",
"groupAttackRune": 0,
"groupAttackRadius": 1,
"keepDistanceRange": 1,
"groupRuneAttackDelay": 5000,
"priority": 1,
"attackRune": 0,
"groupAttackDelay": 5000,
"minManaGroup": 1500,
"lure": true,
"keepDistance": false,
"attackSpellDelay": 2500,
"chase": true,
"name": "cat, w?lf, snake, troll",
"regex": "^cat$|^w.?lf$|^snake$|^troll$"
}
]
}

View File

@@ -2,7 +2,7 @@ if player:getBlessings() == 0 then
say("!bless")
schedule(2000, function()
if player:getBlessings() == 0 then
error("!! Blessings not bought !!")
warn("!! Blessings not bought !!")
end
end)
end

View File

@@ -1,348 +1,360 @@
function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, reloadCallback, websockets)
-- load lua and otui files
local configFiles = g_resources.listDirectoryFiles("/bot/" .. config, true, false)
local luaFiles = {}
local uiFiles = {}
for i, file in ipairs(configFiles) do
local ext = file:split(".")
if ext[#ext]:lower() == "lua" then
table.insert(luaFiles, file)
end
if ext[#ext]:lower() == "ui" or ext[#ext]:lower() == "otui" then
table.insert(uiFiles, file)
end
end
if #luaFiles == 0 then
return error("Config (/bot/" .. config .. ") doesn't have lua files")
end
-- init bot variables
local context = {}
context.configDir = "/bot/".. config
context.tabs = tabs
context.mainTab = context.tabs:addTab("Main", g_ui.createWidget('BotPanel')).tabPanel.content
context.panel = context.mainTab
context.saveConfig = saveConfigCallback
context.reload = reloadCallback
context.storage = storage
if context.storage._macros == nil then
context.storage._macros = {} -- active macros
end
-- websockets, macros, hotkeys, scheduler, icons, callbacks
context._websockets = websockets
context._macros = {}
context._hotkeys = {}
context._scheduler = {}
context._callbacks = {
onKeyDown = {},
onKeyUp = {},
onKeyPress = {},
onTalk = {},
onTextMessage = {},
onLoginAdvice = {},
onAddThing = {},
onRemoveThing = {},
onCreatureAppear = {},
onCreatureDisappear = {},
onCreaturePositionChange = {},
onCreatureHealthPercentChange = {},
onUse = {},
onUseWith = {},
onContainerOpen = {},
onContainerClose = {},
onContainerUpdateItem = {},
onMissle = {},
onAnimatedText = {},
onStaticText = {},
onChannelList = {},
onOpenChannel = {},
onCloseChannel = {},
onChannelEvent = {},
onTurn = {},
onWalk = {}
}
-- basic functions & classes
context.print = print
context.pairs = pairs
context.ipairs = ipairs
context.tostring = tostring
context.math = math
context.table = table
context.string = string
context.tonumber = tonumber
context.type = type
context.pcall = pcall
context.os = {
time = os.time,
date = os.date,
difftime = os.difftime,
date = os.date,
clock = os.clock
}
context.load = function(str) return assert(load(str, nil, nil, context)) end
context.loadstring = context.load
context.assert = assert
context.dofile = function(file) assert(load(g_resources.readFileContents("/bot/" .. config .. "/" .. file), file, nil, context))() end
context.gcinfo = gcinfo
context.tr = tr
context.json = json
context.base64 = base64
context.regexMatch = regexMatch
context.getDistanceBetween = function(p1, p2)
return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y))
end
context.isMobile = g_app.isMobile
context.getVersion = g_app.getVersion
-- classes
context.g_resources = g_resources
context.g_game = g_game
context.g_map = g_map
context.g_ui = g_ui
context.g_sounds = g_sounds
context.g_window = g_window
context.g_mouse = g_mouse
context.g_things = g_things
context.g_platform = {
openUrl = g_platform.openUrl,
openDir = g_platform.openDir,
}
context.Item = Item
context.Creature = Creature
context.ThingType = ThingType
context.Effect = Effect
context.Missile = Missile
context.Player = Player
context.Monster = Monster
context.StaticText = StaticText
context.HTTP = HTTP
context.OutputMessage = OutputMessage
context.modules = modules
-- log functions
context.info = function(text) return msgCallback("info", tostring(text)) end
context.warn = function(text) return msgCallback("warn", tostring(text)) end
context.error = function(text) return msgCallback("error", tostring(text)) end
context.warning = context.warn
-- init context
context.now = g_clock.millis()
context.time = g_clock.millis()
context.player = g_game.getLocalPlayer()
-- init functions
G.botContext = context
dofiles("functions")
context.Panels = {}
dofiles("panels")
G.botContext = nil
-- run ui scripts
for i, file in ipairs(uiFiles) do
g_ui.importStyle(file)
end
-- run lua script
for i, file in ipairs(luaFiles) do
assert(load(g_resources.readFileContents(file), file, nil, context))()
context.panel = context.mainTab -- reset default tab
end
return {
script = function()
context.now = g_clock.millis()
context.time = g_clock.millis()
for i, macro in ipairs(context._macros) do
if macro.lastExecution + macro.timeout <= context.now and macro.enabled then
local status, result = pcall(function()
if macro.callback(macro) then
macro.lastExecution = context.now
end
end)
if not status then
context.error("Macro: " .. macro.name .. " execution error: " .. result)
end
end
end
while #context._scheduler > 0 and context._scheduler[1].execution <= g_clock.millis() do
local status, result = pcall(function()
context._scheduler[1].callback()
end)
if not status then
context.error("Schedule execution error: " .. result)
end
table.remove(context._scheduler, 1)
end
end,
callbacks = {
onKeyDown = function(keyCode, keyboardModifiers)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
for i, macro in ipairs(context._macros) do
if macro.switch and macro.hotkey == keyDesc then
macro.switch:onClick()
end
end
local hotkey = context._hotkeys[keyDesc]
if hotkey then
if hotkey.single then
if hotkey.callback() then
hotkey.lastExecution = context.now
end
end
if hotkey.switch then
hotkey.switch:setOn(true)
end
end
for i, callback in ipairs(context._callbacks.onKeyDown) do
callback(keyDesc)
end
end,
onKeyUp = function(keyCode, keyboardModifiers)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
local hotkey = context._hotkeys[keyDesc]
if hotkey then
if hotkey.switch then
hotkey.switch:setOn(false)
end
end
for i, callback in ipairs(context._callbacks.onKeyUp) do
callback(keyDesc)
end
end,
onKeyPress = function(keyCode, keyboardModifiers, autoRepeatTicks)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
local hotkey = context._hotkeys[keyDesc]
if hotkey and not hotkey.single then
if hotkey.callback() then
hotkey.lastExecution = context.now
end
end
for i, callback in ipairs(context._callbacks.onKeyPress) do
callback(keyDesc, autoRepeatTicks)
end
end,
onTalk = function(name, level, mode, text, channelId, pos)
for i, callback in ipairs(context._callbacks.onTalk) do
callback(name, level, mode, text, channelId, pos)
end
end,
onTextMessage = function(mode, text)
for i, callback in ipairs(context._callbacks.onTextMessage) do
callback(mode, text)
end
end,
onLoginAdvice = function(message)
for i, callback in ipairs(context._callbacks.onLoginAdvice) do
callback(message)
end
end,
onAddThing = function(tile, thing)
for i, callback in ipairs(context._callbacks.onAddThing) do
callback(tile, thing)
end
end,
onRemoveThing = function(tile, thing)
for i, callback in ipairs(context._callbacks.onRemoveThing) do
callback(tile, thing)
end
end,
onCreatureAppear = function(creature)
for i, callback in ipairs(context._callbacks.onCreatureAppear) do
callback(creature)
end
end,
onCreatureDisappear = function(creature)
for i, callback in ipairs(context._callbacks.onCreatureDisappear) do
callback(creature)
end
end,
onCreaturePositionChange = function(creature, newPos, oldPos)
for i, callback in ipairs(context._callbacks.onCreaturePositionChange) do
callback(creature, newPos, oldPos)
end
end,
onCreatureHealthPercentChange = function(creature, healthPercent)
for i, callback in ipairs(context._callbacks.onCreatureHealthPercentChange) do
callback(creature, healthPercent)
end
end,
onUse = function(pos, itemId, stackPos, subType)
for i, callback in ipairs(context._callbacks.onUse) do
callback(pos, itemId, stackPos, subType)
end
end,
onUseWith = function(pos, itemId, target, subType)
for i, callback in ipairs(context._callbacks.onUseWith) do
callback(pos, itemId, target, subType)
end
end,
onContainerOpen = function(container, previousContainer)
for i, callback in ipairs(context._callbacks.onContainerOpen) do
callback(container, previousContainer)
end
end,
onContainerClose = function(container)
for i, callback in ipairs(context._callbacks.onContainerClose) do
callback(container)
end
end,
onContainerUpdateItem = function(container, slot, item)
for i, callback in ipairs(context._callbacks.onContainerUpdateItem) do
callback(container, slot, item)
end
end,
onMissle = function(missle)
for i, callback in ipairs(context._callbacks.onMissle) do
callback(missle)
end
end,
onAnimatedText = function(thing, text)
for i, callback in ipairs(context._callbacks.onAnimatedText) do
callback(thing, text)
end
end,
onStaticText = function(thing, text)
for i, callback in ipairs(context._callbacks.onStaticText) do
callback(thing, text)
end
end,
onChannelList = function(channels)
for i, callback in ipairs(context._callbacks.onChannelList) do
callback(channels)
end
end,
onOpenChannel = function(channelId, channelName)
for i, callback in ipairs(context._callbacks.onOpenChannel) do
callback(channels)
end
end,
onCloseChannel = function(channelId)
for i, callback in ipairs(context._callbacks.onCloseChannel) do
callback(channelId)
end
end,
onChannelEvent = function(channelId, name, event)
for i, callback in ipairs(context._callbacks.onChannelEvent) do
callback(channelId, name, event)
end
end,
onTurn = function(creature, direction)
for i, callback in ipairs(context._callbacks.onTurn) do
callback(creature, direction)
end
end,
onWalk = function(creature, oldPos, newPos)
for i, callback in ipairs(context._callbacks.onWalk) do
callback(creature, oldPos, newPos)
end
end,
}
}
function executeBot(config, storage, tabs, msgCallback, saveConfigCallback, reloadCallback, websockets)
-- load lua and otui files
local configFiles = g_resources.listDirectoryFiles("/bot/" .. config, true, false)
local luaFiles = {}
local uiFiles = {}
for i, file in ipairs(configFiles) do
local ext = file:split(".")
if ext[#ext]:lower() == "lua" then
table.insert(luaFiles, file)
end
if ext[#ext]:lower() == "ui" or ext[#ext]:lower() == "otui" then
table.insert(uiFiles, file)
end
end
if #luaFiles == 0 then
return error("Config (/bot/" .. config .. ") doesn't have lua files")
end
-- init bot variables
local context = {}
context.configDir = "/bot/".. config
context.tabs = tabs
context.mainTab = context.tabs:addTab("Main", g_ui.createWidget('BotPanel')).tabPanel.content
context.panel = context.mainTab
context.saveConfig = saveConfigCallback
context.reload = reloadCallback
context.storage = storage
if context.storage._macros == nil then
context.storage._macros = {} -- active macros
end
-- websockets, macros, hotkeys, scheduler, icons, callbacks
context._websockets = websockets
context._macros = {}
context._hotkeys = {}
context._scheduler = {}
context._callbacks = {
onKeyDown = {},
onKeyUp = {},
onKeyPress = {},
onTalk = {},
onTextMessage = {},
onLoginAdvice = {},
onAddThing = {},
onRemoveThing = {},
onCreatureAppear = {},
onCreatureDisappear = {},
onCreaturePositionChange = {},
onCreatureHealthPercentChange = {},
onUse = {},
onUseWith = {},
onContainerOpen = {},
onContainerClose = {},
onContainerUpdateItem = {},
onMissle = {},
onAnimatedText = {},
onStaticText = {},
onChannelList = {},
onOpenChannel = {},
onCloseChannel = {},
onChannelEvent = {},
onTurn = {},
onWalk = {},
onImbuementWindow = {},
onModalDialog = {}
}
-- basic functions & classes
context.print = print
context.pairs = pairs
context.ipairs = ipairs
context.tostring = tostring
context.math = math
context.table = table
context.string = string
context.tonumber = tonumber
context.type = type
context.pcall = pcall
context.os = {
time = os.time,
date = os.date,
difftime = os.difftime,
date = os.date,
clock = os.clock
}
context.load = function(str) return assert(load(str, nil, nil, context)) end
context.loadstring = context.load
context.assert = assert
context.dofile = function(file) assert(load(g_resources.readFileContents("/bot/" .. config .. "/" .. file), file, nil, context))() end
context.gcinfo = gcinfo
context.tr = tr
context.json = json
context.base64 = base64
context.regexMatch = regexMatch
context.getDistanceBetween = function(p1, p2)
return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y))
end
context.isMobile = g_app.isMobile
context.getVersion = g_app.getVersion
-- classes
context.g_resources = g_resources
context.g_game = g_game
context.g_map = g_map
context.g_ui = g_ui
context.g_sounds = g_sounds
context.g_window = g_window
context.g_mouse = g_mouse
context.g_things = g_things
context.g_platform = {
openUrl = g_platform.openUrl,
openDir = g_platform.openDir,
}
context.Item = Item
context.Creature = Creature
context.ThingType = ThingType
context.Effect = Effect
context.Missile = Missile
context.Player = Player
context.Monster = Monster
context.StaticText = StaticText
context.HTTP = HTTP
context.OutputMessage = OutputMessage
context.modules = modules
-- log functions
context.info = function(text) return msgCallback("info", tostring(text)) end
context.warn = function(text) return msgCallback("warn", tostring(text)) end
context.error = function(text) return msgCallback("error", tostring(text)) end
context.warning = context.warn
-- init context
context.now = g_clock.millis()
context.time = g_clock.millis()
context.player = g_game.getLocalPlayer()
-- init functions
G.botContext = context
dofiles("functions")
context.Panels = {}
dofiles("panels")
G.botContext = nil
-- run ui scripts
for i, file in ipairs(uiFiles) do
g_ui.importStyle(file)
end
-- run lua script
for i, file in ipairs(luaFiles) do
assert(load(g_resources.readFileContents(file), file, nil, context))()
context.panel = context.mainTab -- reset default tab
end
return {
script = function()
context.now = g_clock.millis()
context.time = g_clock.millis()
for i, macro in ipairs(context._macros) do
if macro.lastExecution + macro.timeout <= context.now and macro.enabled then
local status, result = pcall(function()
if macro.callback(macro) then
macro.lastExecution = context.now
end
end)
if not status then
context.error("Macro: " .. macro.name .. " execution error: " .. result)
end
end
end
while #context._scheduler > 0 and context._scheduler[1].execution <= g_clock.millis() do
local status, result = pcall(function()
context._scheduler[1].callback()
end)
if not status then
context.error("Schedule execution error: " .. result)
end
table.remove(context._scheduler, 1)
end
end,
callbacks = {
onKeyDown = function(keyCode, keyboardModifiers)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
for i, macro in ipairs(context._macros) do
if macro.switch and macro.hotkey == keyDesc then
macro.switch:onClick()
end
end
local hotkey = context._hotkeys[keyDesc]
if hotkey then
if hotkey.single then
if hotkey.callback() then
hotkey.lastExecution = context.now
end
end
if hotkey.switch then
hotkey.switch:setOn(true)
end
end
for i, callback in ipairs(context._callbacks.onKeyDown) do
callback(keyDesc)
end
end,
onKeyUp = function(keyCode, keyboardModifiers)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
local hotkey = context._hotkeys[keyDesc]
if hotkey then
if hotkey.switch then
hotkey.switch:setOn(false)
end
end
for i, callback in ipairs(context._callbacks.onKeyUp) do
callback(keyDesc)
end
end,
onKeyPress = function(keyCode, keyboardModifiers, autoRepeatTicks)
local keyDesc = determineKeyComboDesc(keyCode, keyboardModifiers)
local hotkey = context._hotkeys[keyDesc]
if hotkey and not hotkey.single then
if hotkey.callback() then
hotkey.lastExecution = context.now
end
end
for i, callback in ipairs(context._callbacks.onKeyPress) do
callback(keyDesc, autoRepeatTicks)
end
end,
onTalk = function(name, level, mode, text, channelId, pos)
for i, callback in ipairs(context._callbacks.onTalk) do
callback(name, level, mode, text, channelId, pos)
end
end,
onImbuementWindow = function(itemId, slots, activeSlots, imbuements, needItems)
for i, callback in ipairs(context._callbacks.onImbuementWindow) do
callback(itemId, slots, activeSlots, imbuements, needItems)
end
end,
onTextMessage = function(mode, text)
for i, callback in ipairs(context._callbacks.onTextMessage) do
callback(mode, text)
end
end,
onLoginAdvice = function(message)
for i, callback in ipairs(context._callbacks.onLoginAdvice) do
callback(message)
end
end,
onAddThing = function(tile, thing)
for i, callback in ipairs(context._callbacks.onAddThing) do
callback(tile, thing)
end
end,
onRemoveThing = function(tile, thing)
for i, callback in ipairs(context._callbacks.onRemoveThing) do
callback(tile, thing)
end
end,
onCreatureAppear = function(creature)
for i, callback in ipairs(context._callbacks.onCreatureAppear) do
callback(creature)
end
end,
onCreatureDisappear = function(creature)
for i, callback in ipairs(context._callbacks.onCreatureDisappear) do
callback(creature)
end
end,
onCreaturePositionChange = function(creature, newPos, oldPos)
for i, callback in ipairs(context._callbacks.onCreaturePositionChange) do
callback(creature, newPos, oldPos)
end
end,
onCreatureHealthPercentChange = function(creature, healthPercent)
for i, callback in ipairs(context._callbacks.onCreatureHealthPercentChange) do
callback(creature, healthPercent)
end
end,
onUse = function(pos, itemId, stackPos, subType)
for i, callback in ipairs(context._callbacks.onUse) do
callback(pos, itemId, stackPos, subType)
end
end,
onUseWith = function(pos, itemId, target, subType)
for i, callback in ipairs(context._callbacks.onUseWith) do
callback(pos, itemId, target, subType)
end
end,
onContainerOpen = function(container, previousContainer)
for i, callback in ipairs(context._callbacks.onContainerOpen) do
callback(container, previousContainer)
end
end,
onContainerClose = function(container)
for i, callback in ipairs(context._callbacks.onContainerClose) do
callback(container)
end
end,
onContainerUpdateItem = function(container, slot, item)
for i, callback in ipairs(context._callbacks.onContainerUpdateItem) do
callback(container, slot, item)
end
end,
onMissle = function(missle)
for i, callback in ipairs(context._callbacks.onMissle) do
callback(missle)
end
end,
onAnimatedText = function(thing, text)
for i, callback in ipairs(context._callbacks.onAnimatedText) do
callback(thing, text)
end
end,
onStaticText = function(thing, text)
for i, callback in ipairs(context._callbacks.onStaticText) do
callback(thing, text)
end
end,
onChannelList = function(channels)
for i, callback in ipairs(context._callbacks.onChannelList) do
callback(channels)
end
end,
onOpenChannel = function(channelId, channelName)
for i, callback in ipairs(context._callbacks.onOpenChannel) do
callback(channels)
end
end,
onCloseChannel = function(channelId)
for i, callback in ipairs(context._callbacks.onCloseChannel) do
callback(channelId)
end
end,
onChannelEvent = function(channelId, name, event)
for i, callback in ipairs(context._callbacks.onChannelEvent) do
callback(channelId, name, event)
end
end,
onTurn = function(creature, direction)
for i, callback in ipairs(context._callbacks.onTurn) do
callback(creature, direction)
end
end,
onWalk = function(creature, oldPos, newPos)
for i, callback in ipairs(context._callbacks.onWalk) do
callback(creature, oldPos, newPos)
end
end,
onModalDialog = function(id, title, message, buttons, enterButton, escapeButton, choices, priority)
for i, callback in ipairs(context._callbacks.onModalDialog) do
callback(id, title, message, buttons, enterButton, escapeButton, choices, priority)
end
end,
}
}
end

View File

@@ -1,207 +1,217 @@
local context = G.botContext
-- callback(callbackType, callback)
context.callback = function(callbackType, callback)
if not context._callbacks[callbackType] then
return error("Wrong callback type: " .. callbackType)
end
if callbackType == "onAddThing" or callbackType == "onRemoveThing" then
g_game.enableTileThingLuaCallback(true)
end
local desc = "lua"
local info = debug.getinfo(2, "Sl")
if info then
desc = info.short_src .. ":" .. info.currentline
end
local callbackData = {}
table.insert(context._callbacks[callbackType], function(...)
if not callbackData.delay or callbackData.delay < context.now then
local prevExecution = context._currentExecution
context._currentExecution = callbackData
local start = g_clock.realMillis()
callback(...)
local executionTime = g_clock.realMillis() - start
if executionTime > 100 then
context.warning("Slow " .. callbackType .. " (" .. executionTime .. "ms): " .. desc)
end
context._currentExecution = prevExecution
end
end)
local cb = context._callbacks[callbackType]
return {
remove = function()
local index = nil
for i, cb2 in ipairs(context._callbacks[callbackType]) do
if cb == cb2 then
index = i
end
end
if index then
table.remove(context._callbacks[callbackType], index)
end
end
}
end
-- onKeyDown(callback) -- callback = function(keys)
context.onKeyDown = function(callback)
return context.callback("onKeyDown", callback)
end
-- onKeyPress(callback) -- callback = function(keys)
context.onKeyPress = function(callback)
return context.callback("onKeyPress", callback)
end
-- onKeyUp(callback) -- callback = function(keys)
context.onKeyUp = function(callback)
return context.callback("onKeyUp", callback)
end
-- onTalk(callback) -- callback = function(name, level, mode, text, channelId, pos)
context.onTalk = function(callback)
return context.callback("onTalk", callback)
end
-- onTextMessage(callback) -- callback = function(mode, text)
context.onTextMessage = function(callback)
return context.callback("onTextMessage", callback)
end
-- onLoginAdvice(callback) -- callback = function(message)
context.onLoginAdvice = function(callback)
return context.callback("onLoginAdvice", callback)
end
-- onAddThing(callback) -- callback = function(tile, thing)
context.onAddThing = function(callback)
return context.callback("onAddThing", callback)
end
-- onRemoveThing(callback) -- callback = function(tile, thing)
context.onRemoveThing = function(callback)
return context.callback("onRemoveThing", callback)
end
-- onCreatureAppear(callback) -- callback = function(creature)
context.onCreatureAppear = function(callback)
return context.callback("onCreatureAppear", callback)
end
-- onCreatureDisappear(callback) -- callback = function(creature)
context.onCreatureDisappear = function(callback)
return context.callback("onCreatureDisappear", callback)
end
-- onCreaturePositionChange(callback) -- callback = function(creature, newPos, oldPos)
context.onCreaturePositionChange = function(callback)
return context.callback("onCreaturePositionChange", callback)
end
-- onCreatureHealthPercentChange(callback) -- callback = function(creature, healthPercent)
context.onCreatureHealthPercentChange = function(callback)
return context.callback("onCreatureHealthPercentChange", callback)
end
-- onUse(callback) -- callback = function(pos, itemId, stackPos, subType)
context.onUse = function(callback)
return context.callback("onUse", callback)
end
-- onUseWith(callback) -- callback = function(pos, itemId, target, subType)
context.onUseWith = function(callback)
return context.callback("onUseWith", callback)
end
-- onContainerOpen -- callback = function(container, previousContainer)
context.onContainerOpen = function(callback)
return context.callback("onContainerOpen", callback)
end
-- onContainerClose -- callback = function(container)
context.onContainerClose = function(callback)
return context.callback("onContainerClose", callback)
end
-- onContainerUpdateItem -- callback = function(container, slot, item)
context.onContainerUpdateItem = function(callback)
return context.callback("onContainerUpdateItem", callback)
end
-- onMissle -- callback = function(missle)
context.onMissle = function(callback)
return context.callback("onMissle", callback)
end
-- onAnimatedText -- callback = function(thing, text)
context.onAnimatedText = function(callback)
return context.callback("onAnimatedText", callback)
end
-- onStaticText -- callback = function(thing, text)
context.onStaticText = function(callback)
return context.callback("onStaticText", callback)
end
-- onChannelList -- callback = function(channels)
context.onChannelList = function(callback)
return context.callback("onChannelList", callback)
end
-- onOpenChannel -- callback = function(channelId, name)
context.onOpenChannel = function(callback)
return context.callback("onOpenChannel", callback)
end
-- onCloseChannel -- callback = function(channelId)
context.onCloseChannel = function(callback)
return context.callback("onCloseChannel", callback)
end
-- onChannelEvent -- callback = function(channelId, name, event)
context.onChannelEvent = function(callback)
return context.callback("onChannelEvent", callback)
end
-- onTurn -- callback = function(creature, direction)
context.onTurn = function(callback)
return context.callback("onTurn", callback)
end
-- onWalk -- callback = function(creature, oldPos, newPos)
context.onWalk = function(callback)
return context.callback("onWalk", callback)
end
-- CUSTOM CALLBACKS
-- listen(name, callback) -- callback = function(text, channelId, pos)
context.listen = function(name, callback)
if not name then return context.error("listen: invalid name") end
name = name:lower()
return context.onTalk(function(name2, level, mode, text, channelId, pos)
if name == name2:lower() then
callback(text, channelId, pos)
end
end)
end
-- onPlayerPositionChange(callback) -- callback = function(newPos, oldPos)
context.onPlayerPositionChange = function(callback)
return context.onCreaturePositionChange(function(creature, newPos, oldPos)
if creature == context.player then
callback(newPos, oldPos)
end
end)
end
-- onPlayerHealthChange(callback) -- callback = function(healthPercent)
context.onPlayerHealthChange = function(callback)
return context.onCreatureHealthPercentChange(function(creature, healthPercent)
if creature == context.player then
callback(healthPercent)
end
end)
local context = G.botContext
-- callback(callbackType, callback)
context.callback = function(callbackType, callback)
if not context._callbacks[callbackType] then
return error("Wrong callback type: " .. callbackType)
end
if callbackType == "onAddThing" or callbackType == "onRemoveThing" then
g_game.enableTileThingLuaCallback(true)
end
local desc = "lua"
local info = debug.getinfo(2, "Sl")
if info then
desc = info.short_src .. ":" .. info.currentline
end
local callbackData = {}
table.insert(context._callbacks[callbackType], function(...)
if not callbackData.delay or callbackData.delay < context.now then
local prevExecution = context._currentExecution
context._currentExecution = callbackData
local start = g_clock.realMillis()
callback(...)
local executionTime = g_clock.realMillis() - start
if executionTime > 100 then
context.warning("Slow " .. callbackType .. " (" .. executionTime .. "ms): " .. desc)
end
context._currentExecution = prevExecution
end
end)
local cb = context._callbacks[callbackType]
return {
remove = function()
local index = nil
for i, cb2 in ipairs(context._callbacks[callbackType]) do
if cb == cb2 then
index = i
end
end
if index then
table.remove(context._callbacks[callbackType], index)
end
end
}
end
-- onKeyDown(callback) -- callback = function(keys)
context.onKeyDown = function(callback)
return context.callback("onKeyDown", callback)
end
-- onKeyPress(callback) -- callback = function(keys)
context.onKeyPress = function(callback)
return context.callback("onKeyPress", callback)
end
-- onKeyUp(callback) -- callback = function(keys)
context.onKeyUp = function(callback)
return context.callback("onKeyUp", callback)
end
-- onTalk(callback) -- callback = function(name, level, mode, text, channelId, pos)
context.onTalk = function(callback)
return context.callback("onTalk", callback)
end
-- onTextMessage(callback) -- callback = function(mode, text)
context.onTextMessage = function(callback)
return context.callback("onTextMessage", callback)
end
-- onLoginAdvice(callback) -- callback = function(message)
context.onLoginAdvice = function(callback)
return context.callback("onLoginAdvice", callback)
end
-- onAddThing(callback) -- callback = function(tile, thing)
context.onAddThing = function(callback)
return context.callback("onAddThing", callback)
end
-- onRemoveThing(callback) -- callback = function(tile, thing)
context.onRemoveThing = function(callback)
return context.callback("onRemoveThing", callback)
end
-- onCreatureAppear(callback) -- callback = function(creature)
context.onCreatureAppear = function(callback)
return context.callback("onCreatureAppear", callback)
end
-- onCreatureDisappear(callback) -- callback = function(creature)
context.onCreatureDisappear = function(callback)
return context.callback("onCreatureDisappear", callback)
end
-- onCreaturePositionChange(callback) -- callback = function(creature, newPos, oldPos)
context.onCreaturePositionChange = function(callback)
return context.callback("onCreaturePositionChange", callback)
end
-- onCreatureHealthPercentChange(callback) -- callback = function(creature, healthPercent)
context.onCreatureHealthPercentChange = function(callback)
return context.callback("onCreatureHealthPercentChange", callback)
end
-- onUse(callback) -- callback = function(pos, itemId, stackPos, subType)
context.onUse = function(callback)
return context.callback("onUse", callback)
end
-- onUseWith(callback) -- callback = function(pos, itemId, target, subType)
context.onUseWith = function(callback)
return context.callback("onUseWith", callback)
end
-- onContainerOpen -- callback = function(container, previousContainer)
context.onContainerOpen = function(callback)
return context.callback("onContainerOpen", callback)
end
-- onContainerClose -- callback = function(container)
context.onContainerClose = function(callback)
return context.callback("onContainerClose", callback)
end
-- onContainerUpdateItem -- callback = function(container, slot, item)
context.onContainerUpdateItem = function(callback)
return context.callback("onContainerUpdateItem", callback)
end
-- onMissle -- callback = function(missle)
context.onMissle = function(callback)
return context.callback("onMissle", callback)
end
-- onAnimatedText -- callback = function(thing, text)
context.onAnimatedText = function(callback)
return context.callback("onAnimatedText", callback)
end
-- onStaticText -- callback = function(thing, text)
context.onStaticText = function(callback)
return context.callback("onStaticText", callback)
end
-- onChannelList -- callback = function(channels)
context.onChannelList = function(callback)
return context.callback("onChannelList", callback)
end
-- onOpenChannel -- callback = function(channelId, name)
context.onOpenChannel = function(callback)
return context.callback("onOpenChannel", callback)
end
-- onCloseChannel -- callback = function(channelId)
context.onCloseChannel = function(callback)
return context.callback("onCloseChannel", callback)
end
-- onChannelEvent -- callback = function(channelId, name, event)
context.onChannelEvent = function(callback)
return context.callback("onChannelEvent", callback)
end
-- onTurn -- callback = function(creature, direction)
context.onTurn = function(callback)
return context.callback("onTurn", callback)
end
-- onWalk -- callback = function(creature, oldPos, newPos)
context.onWalk = function(callback)
return context.callback("onWalk", callback)
end
-- onImbuementWindow -- callback = function(itemId, slots, activeSlots, imbuements, needItems)
context.onImbuementWindow = function(callback)
return context.callback("onImbuementWindow", callback)
end
-- onModalDialog -- callback = function(id, title, message, buttons, enterButton, escapeButton, choices, priority) -- priority is unused, ignore it
context.onModalDialog = function(callback)
return context.callback("onModalDialog", callback)
end
-- CUSTOM CALLBACKS
-- listen(name, callback) -- callback = function(text, channelId, pos)
context.listen = function(name, callback)
if not name then return context.error("listen: invalid name") end
name = name:lower()
return context.onTalk(function(name2, level, mode, text, channelId, pos)
if name == name2:lower() then
callback(text, channelId, pos)
end
end)
end
-- onPlayerPositionChange(callback) -- callback = function(newPos, oldPos)
context.onPlayerPositionChange = function(callback)
return context.onCreaturePositionChange(function(creature, newPos, oldPos)
if creature == context.player then
callback(newPos, oldPos)
end
end)
end
-- onPlayerHealthChange(callback) -- callback = function(healthPercent)
context.onPlayerHealthChange = function(callback)
return context.onCreatureHealthPercentChange(function(creature, healthPercent)
if creature == context.player then
callback(healthPercent)
end
end)
end

View File

@@ -1,266 +1,266 @@
--[[
Config - create, load and save config file (.json / .cfg)
Used by cavebot and other things
]]--
local context = G.botContext
context.Config = {}
local Config = context.Config
Config.exist = function(dir)
return g_resources.directoryExists(context.configDir .. "/" .. dir)
end
Config.create = function(dir)
g_resources.makeDir(context.configDir .. "/" .. dir)
return Config.exist(dir)
end
Config.list = function(dir)
if not Config.exist(dir) then
if not Config.create(dir) then
return contex.error("Can't create config dir: " .. context.configDir .. "/" .. dir)
end
end
local list = g_resources.listDirectoryFiles(context.configDir .. "/" .. dir)
local correctList = {}
for k,v in ipairs(list) do -- filter files
local nv = v:gsub(".json", ""):gsub(".cfg", "")
if nv ~= v then
table.insert(correctList, nv)
end
end
return correctList
end
-- load config from string insteaf of file
Config.parse = function(data)
local status, result = pcall(function()
if data:len() < 2 then return {} end
return json.decode(data)
end)
if status and type(result) == 'table' then
return result
end
local status, result = pcall(function()
return table.decodeStringPairList(data)
end)
if status and type(result) == 'table' then
return result
end
return context.error("Invalid config format")
end
Config.load = function(dir, name)
local file = context.configDir .. "/" .. dir .. "/" .. name .. ".json"
if g_resources.fileExists(file) then -- load json
local status, result = pcall(function()
local data = g_resources.readFileContents(file)
if data:len() < 2 then return {} end
return json.decode(data)
end)
if not status then
context.error("Invalid json config (" .. name .. "): " .. result)
return {}
end
return result
end
file = context.configDir .. "/" .. dir .. "/" .. name .. ".cfg"
if g_resources.fileExists(file) then -- load cfg
local status, result = pcall(function()
return table.decodeStringPairList(g_resources.readFileContents(file))
end)
if not status then
context.error("Invalid cfg config (" .. name .. "): " .. result)
return {}
end
return result
end
return context.error("Config " .. file .. " doesn't exist")
end
Config.loadRaw = function(dir, name)
local file = context.configDir .. "/" .. dir .. "/" .. name .. ".json"
if g_resources.fileExists(file) then -- load json
return g_resources.readFileContents(file)
end
file = context.configDir .. "/" .. dir .. "/" .. name .. ".cfg"
if g_resources.fileExists(file) then -- load cfg
return g_resources.readFileContents(file)
end
return context.error("Config " .. file .. " doesn't exist")
end
Config.save = function(dir, name, value, forcedExtension)
if not Config.exist(dir) then
if not Config.create(dir) then
return contex.error("Can't create config dir: " .. context.configDir .. "/" .. dir)
end
end
if type(value) ~= 'table' then
return context.error("Invalid config value type: " .. type(value) .. ", should be table")
end
local file = context.configDir .. "/" .. dir .. "/" .. name
if (table.isStringPairList(value) and forcedExtension ~= "json") or forcedExtension == "cfg" then -- cfg
g_resources.writeFileContents(file .. ".cfg", table.encodeStringPairList(value))
else
g_resources.writeFileContents(file .. ".json", json.encode(value, 2))
end
return true
end
Config.remove = function(dir, name)
local file = context.configDir .. "/" .. dir .. "/" .. name .. ".json"
local ret = false
if g_resources.fileExists(file) then
g_resources.deleteFile(file)
ret = true
end
file = context.configDir .. "/" .. dir .. "/" .. name .. ".cfg"
if g_resources.fileExists(file) then
g_resources.deleteFile(file)
ret = true
end
return ret
end
-- setup is used for BotConfig widget
-- not done yet
Config.setup = function(dir, widget, configExtension, callback)
if type(dir) ~= 'string' or dir:len() == 0 then
return context.error("Invalid config dir")
end
if not Config.exist(dir) and not Config.create(dir) then
return context.error("Can't create config dir: " .. dir)
end
if type(context.storage._configs) ~= "table" then
context.storage._configs = {}
end
if type(context.storage._configs[dir]) ~= "table" then
context.storage._configs[dir] = {
enabled = false,
selected = ""
}
else
widget.switch:setOn(context.storage._configs[dir].enabled)
end
local isRefreshing = false
local refresh = function()
isRefreshing = true
local configs = Config.list(dir)
local configIndex = 1
widget.list:clear()
for v,k in ipairs(configs) do
widget.list:addOption(k)
if k == context.storage._configs[dir].selected then
configIndex = v
end
end
local data = nil
if #configs > 0 then
widget.list:setCurrentIndex(configIndex)
context.storage._configs[dir].selected = widget.list:getCurrentOption().text
data = Config.load(dir, configs[configIndex])
else
context.storage._configs[dir].selected = nil
end
context.storage._configs[dir].enabled = widget.switch:isOn()
isRefreshing = false
callback(context.storage._configs[dir].selected, widget.switch:isOn(), data)
end
widget.list.onOptionChange = function(widget)
if not isRefreshing then
context.storage._configs[dir].selected = widget:getCurrentOption().text
refresh()
end
end
widget.switch.onClick = function()
widget.switch:setOn(not widget.switch:isOn())
refresh()
end
widget.add.onClick = function()
context.UI.SinglelineEditorWindow("config_name", {title="Enter config name"}, function(name)
name = name:gsub("%s+", "_")
if name:len() == 0 or name:len() >= 30 or name:find("/") or name:find("\\") then
return context.error("Invalid config name")
end
local file = context.configDir .. "/" .. dir .. "/" .. name .. "." .. configExtension
if g_resources.fileExists(file) then
return context.error("Config " .. name .. " already exist")
end
if configExtension == "json" then
g_resources.writeFileContents(file, json.encode({}))
else
g_resources.writeFileContents(file, "")
end
context.storage._configs[dir].selected = name
widget.switch:setOn(false)
refresh()
end)
end
widget.edit.onClick = function()
local name = context.storage._configs[dir].selected
if not name then return end
context.UI.MultilineEditorWindow(Config.loadRaw(dir, name), {title="Config editor - " .. name .. " in " .. dir}, function(newValue)
local data = Config.parse(newValue)
Config.save(dir, name, data, configExtension)
refresh()
end)
end
widget.remove.onClick = function()
local name = context.storage._configs[dir].selected
if not name then return end
context.UI.ConfirmationWindow("Config removal", "Do you want to remove config " .. name .. " from " .. dir .. "?", function()
Config.remove(dir, name)
widget.switch:setOn(false)
refresh()
end)
end
refresh()
return {
isOn = function()
return widget.switch:isOn()
end,
isOff = function()
return not widget.switch:isOn()
end,
setOn = function(val)
if val == false then
if widget.switch:isOn() then
widget.switch:onClick()
end
return
end
if not widget.switch:isOn() then
widget.switch:onClick()
end
end,
setOff = function(val)
if val == false then
if not widget.switch:isOn() then
widget.switch:onClick()
end
return
end
if widget.switch:isOn() then
widget.switch:onClick()
end
end,
save = function(data)
Config.save(dir, context.storage._configs[dir].selected, data, configExtension)
end,
refresh = refresh,
reload = refresh,
getActiveConfigName = function()
return context.storage._configs[dir].selected
end
}
--[[
Config - create, load and save config file (.json / .cfg)
Used by cavebot and other things
]]--
local context = G.botContext
context.Config = {}
local Config = context.Config
Config.exist = function(dir)
return g_resources.directoryExists(context.configDir .. "/" .. dir)
end
Config.create = function(dir)
g_resources.makeDir(context.configDir .. "/" .. dir)
return Config.exist(dir)
end
Config.list = function(dir)
if not Config.exist(dir) then
if not Config.create(dir) then
return contex.error("Can't create config dir: " .. context.configDir .. "/" .. dir)
end
end
local list = g_resources.listDirectoryFiles(context.configDir .. "/" .. dir)
local correctList = {}
for k,v in ipairs(list) do -- filter files
local nv = v:gsub(".json", ""):gsub(".cfg", "")
if nv ~= v then
table.insert(correctList, nv)
end
end
return correctList
end
-- load config from string insteaf of file
Config.parse = function(data)
local status, result = pcall(function()
if data:len() < 2 then return {} end
return json.decode(data)
end)
if status and type(result) == 'table' then
return result
end
local status, result = pcall(function()
return table.decodeStringPairList(data)
end)
if status and type(result) == 'table' then
return result
end
return context.error("Invalid config format")
end
Config.load = function(dir, name)
local file = context.configDir .. "/" .. dir .. "/" .. name .. ".json"
if g_resources.fileExists(file) then -- load json
local status, result = pcall(function()
local data = g_resources.readFileContents(file)
if data:len() < 2 then return {} end
return json.decode(data)
end)
if not status then
context.error("Invalid json config (" .. name .. "): " .. result)
return {}
end
return result
end
file = context.configDir .. "/" .. dir .. "/" .. name .. ".cfg"
if g_resources.fileExists(file) then -- load cfg
local status, result = pcall(function()
return table.decodeStringPairList(g_resources.readFileContents(file))
end)
if not status then
context.error("Invalid cfg config (" .. name .. "): " .. result)
return {}
end
return result
end
return context.error("Config " .. file .. " doesn't exist")
end
Config.loadRaw = function(dir, name)
local file = context.configDir .. "/" .. dir .. "/" .. name .. ".json"
if g_resources.fileExists(file) then -- load json
return g_resources.readFileContents(file)
end
file = context.configDir .. "/" .. dir .. "/" .. name .. ".cfg"
if g_resources.fileExists(file) then -- load cfg
return g_resources.readFileContents(file)
end
return context.error("Config " .. file .. " doesn't exist")
end
Config.save = function(dir, name, value, forcedExtension)
if not Config.exist(dir) then
if not Config.create(dir) then
return contex.error("Can't create config dir: " .. context.configDir .. "/" .. dir)
end
end
if type(value) ~= 'table' then
return context.error("Invalid config value type: " .. type(value) .. ", should be table")
end
local file = context.configDir .. "/" .. dir .. "/" .. name
if (table.isStringPairList(value) and forcedExtension ~= "json") or forcedExtension == "cfg" then -- cfg
g_resources.writeFileContents(file .. ".cfg", table.encodeStringPairList(value))
else
g_resources.writeFileContents(file .. ".json", json.encode(value, 2))
end
return true
end
Config.remove = function(dir, name)
local file = context.configDir .. "/" .. dir .. "/" .. name .. ".json"
local ret = false
if g_resources.fileExists(file) then
g_resources.deleteFile(file)
ret = true
end
file = context.configDir .. "/" .. dir .. "/" .. name .. ".cfg"
if g_resources.fileExists(file) then
g_resources.deleteFile(file)
ret = true
end
return ret
end
-- setup is used for BotConfig widget
-- not done yet
Config.setup = function(dir, widget, configExtension, callback)
if type(dir) ~= 'string' or dir:len() == 0 then
return context.error("Invalid config dir")
end
if not Config.exist(dir) and not Config.create(dir) then
return context.error("Can't create config dir: " .. dir)
end
if type(context.storage._configs) ~= "table" then
context.storage._configs = {}
end
if type(context.storage._configs[dir]) ~= "table" then
context.storage._configs[dir] = {
enabled = false,
selected = ""
}
else
widget.switch:setOn(context.storage._configs[dir].enabled)
end
local isRefreshing = false
local refresh = function()
isRefreshing = true
local configs = Config.list(dir)
local configIndex = 1
widget.list:clear()
for v,k in ipairs(configs) do
widget.list:addOption(k)
if k == context.storage._configs[dir].selected then
configIndex = v
end
end
local data = nil
if #configs > 0 then
widget.list:setCurrentIndex(configIndex)
context.storage._configs[dir].selected = widget.list:getCurrentOption().text
data = Config.load(dir, configs[configIndex])
else
context.storage._configs[dir].selected = nil
end
context.storage._configs[dir].enabled = widget.switch:isOn()
isRefreshing = false
callback(context.storage._configs[dir].selected, widget.switch:isOn(), data)
end
widget.list.onOptionChange = function(widget)
if not isRefreshing then
context.storage._configs[dir].selected = widget:getCurrentOption().text
refresh()
end
end
widget.switch.onClick = function()
widget.switch:setOn(not widget.switch:isOn())
refresh()
end
widget.add.onClick = function()
context.UI.SinglelineEditorWindow("config_name", {title="Enter config name"}, function(name)
name = name:gsub("%s+", "_")
if name:len() == 0 or name:len() >= 30 or name:find("/") or name:find("\\") then
return context.error("Invalid config name")
end
local file = context.configDir .. "/" .. dir .. "/" .. name .. "." .. configExtension
if g_resources.fileExists(file) then
return context.error("Config " .. name .. " already exist")
end
if configExtension == "json" then
g_resources.writeFileContents(file, json.encode({}))
else
g_resources.writeFileContents(file, "")
end
context.storage._configs[dir].selected = name
widget.switch:setOn(false)
refresh()
end)
end
widget.edit.onClick = function()
local name = context.storage._configs[dir].selected
if not name then return end
context.UI.MultilineEditorWindow(Config.loadRaw(dir, name), {title="Config editor - " .. name .. " in " .. dir}, function(newValue)
local data = Config.parse(newValue)
Config.save(dir, name, data, configExtension)
refresh()
end)
end
widget.remove.onClick = function()
local name = context.storage._configs[dir].selected
if not name then return end
context.UI.ConfirmationWindow("Config removal", "Do you want to remove config " .. name .. " from " .. dir .. "?", function()
Config.remove(dir, name)
widget.switch:setOn(false)
refresh()
end)
end
refresh()
return {
isOn = function()
return widget.switch:isOn()
end,
isOff = function()
return not widget.switch:isOn()
end,
setOn = function(val)
if val == false then
if widget.switch:isOn() then
widget.switch:onClick()
end
return
end
if not widget.switch:isOn() then
widget.switch:onClick()
end
end,
setOff = function(val)
if val == false then
if not widget.switch:isOn() then
widget.switch:onClick()
end
return
end
if widget.switch:isOn() then
widget.switch:onClick()
end
end,
save = function(data)
Config.save(dir, context.storage._configs[dir].selected, data, configExtension)
end,
refresh = refresh,
reload = refresh,
getActiveConfigName = function()
return context.storage._configs[dir].selected
end
}
end

View File

@@ -1,25 +1,25 @@
local context = G.botContext
context.North = 0
context.East = 1
context.South = 2
context.West = 3
context.NorthEast = 4
context.SouthEast = 5
context.SouthWest = 6
context.NorthWest = 7
context.InventorySlotOther = 0
context.InventorySlotHead = 1
context.InventorySlotNeck = 2
context.InventorySlotBack = 3
context.InventorySlotBody = 4
context.InventorySlotRight = 5
context.InventorySlotLeft = 6
context.InventorySlotLeg = 7
context.InventorySlotFeet = 8
context.InventorySlotFinger = 9
context.InventorySlotAmmo = 10
context.InventorySlotPurse = 11
context.InventorySlotFirst = 1
context.InventorySlotLast = 10
local context = G.botContext
context.North = 0
context.East = 1
context.South = 2
context.West = 3
context.NorthEast = 4
context.SouthEast = 5
context.SouthWest = 6
context.NorthWest = 7
context.InventorySlotOther = 0
context.InventorySlotHead = 1
context.InventorySlotNeck = 2
context.InventorySlotBack = 3
context.InventorySlotBody = 4
context.InventorySlotRight = 5
context.InventorySlotLeft = 6
context.InventorySlotLeg = 7
context.InventorySlotFeet = 8
context.InventorySlotFinger = 9
context.InventorySlotAmmo = 10
context.InventorySlotPurse = 11
context.InventorySlotFirst = 1
context.InventorySlotLast = 10

View File

@@ -1,176 +1,176 @@
local context = G.botContext
local iconsWithoutPosition = 0
context.addIcon = function(id, options, callback)
--[[
Available options:
item: {id=2160, count=100}
outfit: outfit table ({})
text: string
x: float (0.0 - 1.0)
y: float (0.0 - 1.0)
hotkey: string
switchable: true / false [default: true]
movable: true / false [default: true]
phantom: true / false [defaule: false]
]]--
local panel = modules.game_interface.gameMapPanel
if type(id) ~= "string" or id:len() < 1 then
return context.error("Invalid id for addIcon")
end
if options.switchable == false and type(callback) ~= 'function' then
return context.error("Invalid callback for addIcon")
end
if type(context.storage._icons) ~= "table" then
context.storage._icons = {}
end
if type(context.storage._icons[id]) ~= "table" then
context.storage._icons[id] = {}
end
local config = context.storage._icons[id]
local widget = g_ui.createWidget("BotIcon", panel)
widget.botWidget = true
widget.botIcon = true
if type(config.x) ~= 'number' and type(config.y) ~= 'number' then
if type(options.x) == 'number' and type(options.y) == 'number' then
config.x = math.min(1.0, math.max(0.0, options.x))
config.y = math.min(1.0, math.max(0.0, options.y))
else
config.x = 0.01 + math.floor(iconsWithoutPosition / 5) / 10
config.y = 0.05 + (iconsWithoutPosition % 5) / 5
iconsWithoutPosition = iconsWithoutPosition + 1
end
end
if options.item then
if type(options.item) == 'number' then
widget.item:setItemId(options.item)
else
widget.item:setItemId(options.item.id)
widget.item:setItemCount(options.item.count or 1)
widget.item:setShowCount(false)
end
end
if options.outfit then
widget.creature:setOutfit(options.outfit)
end
if options.switchable == false then
widget.status:hide()
widget.status:setOn(true)
else
if config.enabled ~= true then
config.enabled = false
end
widget.status:setOn(config.enabled)
end
if options.text then
if options.switchable ~= false then
widget.status:hide()
if widget.status:isOn() then
widget.text:setColor('green')
else
widget.text:setColor('red')
end
end
widget.text:setText(options.text)
end
widget.setOn = function(val)
widget.status:setOn(val)
if widget.status:isOn() then
widget.text:setColor('green')
else
widget.text:setColor('red')
end
config.enabled = widget.status:isOn()
end
widget.onClick = function(widget)
if options.switchable ~= false then
widget.setOn(not widget.status:isOn())
if type(callback) == 'table' then
callback.setOn(config.enabled)
return
end
end
callback(widget, widget.status:isOn())
end
if options.hotkey then
widget.hotkey:setText(options.hotkey)
context.hotkey(options.hotkey, "", function()
widget:onClick()
end, nil, options.switchable ~= false)
else
widget.hotkey:hide()
end
if options.movable ~= false then
widget.onDragEnter = function(widget, mousePos)
if not g_keyboard.isCtrlPressed() then
return false
end
widget:breakAnchors()
widget.movingReference = { x = mousePos.x - widget:getX(), y = mousePos.y - widget:getY() }
return true
end
widget.onDragMove = function(widget, mousePos, moved)
local parentRect = widget:getParent():getRect()
local x = math.min(math.max(parentRect.x, mousePos.x - widget.movingReference.x), parentRect.x + parentRect.width - widget:getWidth())
local y = math.min(math.max(parentRect.y - widget:getParent():getMarginTop(), mousePos.y - widget.movingReference.y), parentRect.y + parentRect.height - widget:getHeight())
widget:move(x, y)
return true
end
widget.onDragLeave = function(widget, pos)
local parent = widget:getParent()
local parentRect = parent:getRect()
local x = widget:getX() - parentRect.x
local y = widget:getY() - parentRect.y
local width = parentRect.width - widget:getWidth()
local height = parentRect.height - widget:getHeight()
config.x = math.min(1, math.max(0, x / width))
config.y = math.min(1, math.max(0, y / height))
widget:addAnchor(AnchorHorizontalCenter, 'parent', AnchorHorizontalCenter)
widget:addAnchor(AnchorVerticalCenter, 'parent', AnchorVerticalCenter)
widget:setMarginTop(math.max(height * (-0.5) - parent:getMarginTop(), height * (-0.5 + config.y)))
widget:setMarginLeft(width * (-0.5 + config.x))
return true
end
end
widget.onGeometryChange = function(widget)
if widget:isDragging() then return end
local parent = widget:getParent()
local parentRect = parent:getRect()
local width = parentRect.width - widget:getWidth()
local height = parentRect.height - widget:getHeight()
widget:setMarginTop(math.max(height * (-0.5) - parent:getMarginTop(), height * (-0.5 + config.y)))
widget:setMarginLeft(width * (-0.5 + config.x))
end
if options.phantom ~= true then
widget.onMouseRelease = function()
return true
end
end
if options.switchable ~= false then
if type(callback) == 'table' then
callback.setOn(config.enabled)
callback.icon = widget
else
callback(widget, widget.status:isOn())
end
end
return widget
local context = G.botContext
local iconsWithoutPosition = 0
context.addIcon = function(id, options, callback)
--[[
Available options:
item: {id=2160, count=100}
outfit: outfit table ({})
text: string
x: float (0.0 - 1.0)
y: float (0.0 - 1.0)
hotkey: string
switchable: true / false [default: true]
movable: true / false [default: true]
phantom: true / false [defaule: false]
]]--
local panel = modules.game_interface.gameMapPanel
if type(id) ~= "string" or id:len() < 1 then
return context.error("Invalid id for addIcon")
end
if options.switchable == false and type(callback) ~= 'function' then
return context.error("Invalid callback for addIcon")
end
if type(context.storage._icons) ~= "table" then
context.storage._icons = {}
end
if type(context.storage._icons[id]) ~= "table" then
context.storage._icons[id] = {}
end
local config = context.storage._icons[id]
local widget = g_ui.createWidget("BotIcon", panel)
widget.botWidget = true
widget.botIcon = true
if type(config.x) ~= 'number' and type(config.y) ~= 'number' then
if type(options.x) == 'number' and type(options.y) == 'number' then
config.x = math.min(1.0, math.max(0.0, options.x))
config.y = math.min(1.0, math.max(0.0, options.y))
else
config.x = 0.01 + math.floor(iconsWithoutPosition / 5) / 10
config.y = 0.05 + (iconsWithoutPosition % 5) / 5
iconsWithoutPosition = iconsWithoutPosition + 1
end
end
if options.item then
if type(options.item) == 'number' then
widget.item:setItemId(options.item)
else
widget.item:setItemId(options.item.id)
widget.item:setItemCount(options.item.count or 1)
widget.item:setShowCount(false)
end
end
if options.outfit then
widget.creature:setOutfit(options.outfit)
end
if options.switchable == false then
widget.status:hide()
widget.status:setOn(true)
else
if config.enabled ~= true then
config.enabled = false
end
widget.status:setOn(config.enabled)
end
if options.text then
if options.switchable ~= false then
widget.status:hide()
if widget.status:isOn() then
widget.text:setColor('green')
else
widget.text:setColor('red')
end
end
widget.text:setText(options.text)
end
widget.setOn = function(val)
widget.status:setOn(val)
if widget.status:isOn() then
widget.text:setColor('green')
else
widget.text:setColor('red')
end
config.enabled = widget.status:isOn()
end
widget.onClick = function(widget)
if options.switchable ~= false then
widget.setOn(not widget.status:isOn())
if type(callback) == 'table' then
callback.setOn(config.enabled)
return
end
end
callback(widget, widget.status:isOn())
end
if options.hotkey then
widget.hotkey:setText(options.hotkey)
context.hotkey(options.hotkey, "", function()
widget:onClick()
end, nil, options.switchable ~= false)
else
widget.hotkey:hide()
end
if options.movable ~= false then
widget.onDragEnter = function(widget, mousePos)
if not g_keyboard.isCtrlPressed() then
return false
end
widget:breakAnchors()
widget.movingReference = { x = mousePos.x - widget:getX(), y = mousePos.y - widget:getY() }
return true
end
widget.onDragMove = function(widget, mousePos, moved)
local parentRect = widget:getParent():getRect()
local x = math.min(math.max(parentRect.x, mousePos.x - widget.movingReference.x), parentRect.x + parentRect.width - widget:getWidth())
local y = math.min(math.max(parentRect.y - widget:getParent():getMarginTop(), mousePos.y - widget.movingReference.y), parentRect.y + parentRect.height - widget:getHeight())
widget:move(x, y)
return true
end
widget.onDragLeave = function(widget, pos)
local parent = widget:getParent()
local parentRect = parent:getRect()
local x = widget:getX() - parentRect.x
local y = widget:getY() - parentRect.y
local width = parentRect.width - widget:getWidth()
local height = parentRect.height - widget:getHeight()
config.x = math.min(1, math.max(0, x / width))
config.y = math.min(1, math.max(0, y / height))
widget:addAnchor(AnchorHorizontalCenter, 'parent', AnchorHorizontalCenter)
widget:addAnchor(AnchorVerticalCenter, 'parent', AnchorVerticalCenter)
widget:setMarginTop(math.max(height * (-0.5) - parent:getMarginTop(), height * (-0.5 + config.y)))
widget:setMarginLeft(width * (-0.5 + config.x))
return true
end
end
widget.onGeometryChange = function(widget)
if widget:isDragging() then return end
local parent = widget:getParent()
local parentRect = parent:getRect()
local width = parentRect.width - widget:getWidth()
local height = parentRect.height - widget:getHeight()
widget:setMarginTop(math.max(height * (-0.5) - parent:getMarginTop(), height * (-0.5 + config.y)))
widget:setMarginLeft(width * (-0.5 + config.x))
end
if options.phantom ~= true then
widget.onMouseRelease = function()
return true
end
end
if options.switchable ~= false then
if type(callback) == 'table' then
callback.setOn(config.enabled)
callback.icon = widget
else
callback(widget, widget.status:isOn())
end
end
return widget
end

View File

@@ -1,211 +1,211 @@
local context = G.botContext
-- MAIN BOT FUNCTION
-- macro(timeout, callback)
-- macro(timeout, name, callback)
-- macro(timeout, name, callback, parent)
-- macro(timeout, name, hotkey, callback)
-- macro(timeout, name, hotkey, callback, parent)
context.macro = function(timeout, name, hotkey, callback, parent)
if type(timeout) ~= 'number' or timeout < 1 then
error("Invalid timeout for macro: " .. tostring(timeout))
end
if type(name) == 'function' then
callback = name
name = ""
hotkey = ""
elseif type(hotkey) == 'function' then
parent = callback
callback = hotkey
hotkey = ""
elseif type(callback) ~= 'function' then
error("Invalid callback for macro: " .. tostring(callback))
end
if hotkey == nil then
hotkey = ""
end
if type(name) ~= 'string' or type(hotkey) ~= 'string' then
error("Invalid name or hotkey for macro")
end
if not parent then
parent = context.panel
end
if hotkey:len() > 0 then
hotkey = retranslateKeyComboDesc(hotkey)
end
-- min timeout is 50, to avoid lags
if timeout < 50 then
timeout = 50
end
table.insert(context._macros, {
enabled = false,
name = name,
timeout = timeout,
lastExecution = context.now + math.random(0, 100),
hotkey = hotkey,
})
local macro = context._macros[#context._macros]
macro.isOn = function()
return macro.enabled
end
macro.isOff = function()
return not macro.enabled
end
macro.toggle = function(widget)
if macro.isOn() then
macro.setOff()
else
macro.setOn()
end
end
macro.setOn = function(val)
if val == false then
return macro.setOff()
end
macro.enabled = true
context.storage._macros[name] = true
if macro.switch then
macro.switch:setOn(true)
end
if macro.icon then
macro.icon.setOn(true)
end
end
macro.setOff = function(val)
if val == false then
return macro.setOn()
end
macro.enabled = false
context.storage._macros[name] = false
if macro.switch then
macro.switch:setOn(false)
end
if macro.icon then
macro.icon.setOn(false)
end
end
if name:len() > 0 then
-- creature switch
local text = name
if hotkey:len() > 0 then
text = name .. " [" .. hotkey .. "]"
end
macro.switch = context.addSwitch("macro_" .. (#context._macros + 1), text, macro.toggle, parent)
-- load state
if context.storage._macros[name] == true then
macro.setOn()
end
else
macro.enabled = true -- unnamed macros are enabled by default
end
local desc = "lua"
local info = debug.getinfo(2, "Sl")
if info then
desc = info.short_src .. ":" .. info.currentline
end
macro.callback = function(macro)
if not macro.delay or macro.delay < context.now then
context._currentExecution = macro
local start = g_clock.realMillis()
callback(macro)
local executionTime = g_clock.realMillis() - start
if executionTime > 100 then
context.warning("Slow macro (" .. executionTime .. "ms): " .. macro.name .. " - " .. desc)
end
context._currentExecution = nil
return true
end
end
return macro
end
-- hotkey(keys, callback)
-- hotkey(keys, name, callback)
-- hotkey(keys, name, callback, parent)
context.hotkey = function(keys, name, callback, parent, single)
if type(name) == 'function' then
callback = name
name = ""
end
if not parent then
parent = context.panel
end
keys = retranslateKeyComboDesc(keys)
if not keys or #keys == 0 then
return context.error("Invalid hotkey keys " .. tostring(name))
end
if context._hotkeys[keys] then
return context.error("Duplicated hotkey: " .. keys)
end
local switch = nil
if name:len() > 0 then
switch = context._addHotkeySwitch(name, keys, parent)
end
context._hotkeys[keys] = {
name = name,
lastExecution = context.now,
switch = switch,
single = single
}
local desc = "lua"
local info = debug.getinfo(2, "Sl")
if info then
desc = info.short_src .. ":" .. info.currentline
end
local hotkeyData = context._hotkeys[keys]
hotkeyData.callback = function()
if not hotkeyData.delay or hotkeyData.delay < context.now then
context._currentExecution = hotkeyData
local start = g_clock.realMillis()
callback()
local executionTime = g_clock.realMillis() - start
if executionTime > 100 then
context.warning("Slow hotkey (" .. executionTime .. "ms): " .. hotkeyData.name .. " - " .. desc)
end
context._currentExecution = nil
return true
end
end
return hotkeyData
end
-- singlehotkey(keys, callback)
-- singlehotkey(keys, name, callback)
-- singlehotkey(keys, name, callback, parent)
context.singlehotkey = function(keys, name, callback, parent)
if type(name) == 'function' then
callback = name
name = ""
end
return context.hotkey(keys, name, callback, parent, true)
end
-- schedule(timeout, callback)
context.schedule = function(timeout, callback)
local extecute_time = g_clock.millis() + timeout
table.insert(context._scheduler, {
execution = extecute_time,
callback = callback
})
table.sort(context._scheduler, function(a, b) return a.execution < b.execution end)
end
-- delay(duration) -- block execution of current macro/hotkey/callback for x milliseconds
context.delay = function(duration)
if not context._currentExecution then
return context.error("Invalid usage of delay function, it should be used inside callbacks")
end
context._currentExecution.delay = context.now + duration
local context = G.botContext
-- MAIN BOT FUNCTION
-- macro(timeout, callback)
-- macro(timeout, name, callback)
-- macro(timeout, name, callback, parent)
-- macro(timeout, name, hotkey, callback)
-- macro(timeout, name, hotkey, callback, parent)
context.macro = function(timeout, name, hotkey, callback, parent)
if type(timeout) ~= 'number' or timeout < 1 then
error("Invalid timeout for macro: " .. tostring(timeout))
end
if type(name) == 'function' then
callback = name
name = ""
hotkey = ""
elseif type(hotkey) == 'function' then
parent = callback
callback = hotkey
hotkey = ""
elseif type(callback) ~= 'function' then
error("Invalid callback for macro: " .. tostring(callback))
end
if hotkey == nil then
hotkey = ""
end
if type(name) ~= 'string' or type(hotkey) ~= 'string' then
error("Invalid name or hotkey for macro")
end
if not parent then
parent = context.panel
end
if hotkey:len() > 0 then
hotkey = retranslateKeyComboDesc(hotkey)
end
-- min timeout is 50, to avoid lags
if timeout < 50 then
timeout = 50
end
table.insert(context._macros, {
enabled = false,
name = name,
timeout = timeout,
lastExecution = context.now + math.random(0, 100),
hotkey = hotkey,
})
local macro = context._macros[#context._macros]
macro.isOn = function()
return macro.enabled
end
macro.isOff = function()
return not macro.enabled
end
macro.toggle = function(widget)
if macro.isOn() then
macro.setOff()
else
macro.setOn()
end
end
macro.setOn = function(val)
if val == false then
return macro.setOff()
end
macro.enabled = true
context.storage._macros[name] = true
if macro.switch then
macro.switch:setOn(true)
end
if macro.icon then
macro.icon.setOn(true)
end
end
macro.setOff = function(val)
if val == false then
return macro.setOn()
end
macro.enabled = false
context.storage._macros[name] = false
if macro.switch then
macro.switch:setOn(false)
end
if macro.icon then
macro.icon.setOn(false)
end
end
if name:len() > 0 then
-- creature switch
local text = name
if hotkey:len() > 0 then
text = name .. " [" .. hotkey .. "]"
end
macro.switch = context.addSwitch("macro_" .. (#context._macros + 1), text, macro.toggle, parent)
-- load state
if context.storage._macros[name] == true then
macro.setOn()
end
else
macro.enabled = true -- unnamed macros are enabled by default
end
local desc = "lua"
local info = debug.getinfo(2, "Sl")
if info then
desc = info.short_src .. ":" .. info.currentline
end
macro.callback = function(macro)
if not macro.delay or macro.delay < context.now then
context._currentExecution = macro
local start = g_clock.realMillis()
callback(macro)
local executionTime = g_clock.realMillis() - start
if executionTime > 100 then
context.warning("Slow macro (" .. executionTime .. "ms): " .. macro.name .. " - " .. desc)
end
context._currentExecution = nil
return true
end
end
return macro
end
-- hotkey(keys, callback)
-- hotkey(keys, name, callback)
-- hotkey(keys, name, callback, parent)
context.hotkey = function(keys, name, callback, parent, single)
if type(name) == 'function' then
callback = name
name = ""
end
if not parent then
parent = context.panel
end
keys = retranslateKeyComboDesc(keys)
if not keys or #keys == 0 then
return context.error("Invalid hotkey keys " .. tostring(name))
end
if context._hotkeys[keys] then
return context.error("Duplicated hotkey: " .. keys)
end
local switch = nil
if name:len() > 0 then
switch = context._addHotkeySwitch(name, keys, parent)
end
context._hotkeys[keys] = {
name = name,
lastExecution = context.now,
switch = switch,
single = single
}
local desc = "lua"
local info = debug.getinfo(2, "Sl")
if info then
desc = info.short_src .. ":" .. info.currentline
end
local hotkeyData = context._hotkeys[keys]
hotkeyData.callback = function()
if not hotkeyData.delay or hotkeyData.delay < context.now then
context._currentExecution = hotkeyData
local start = g_clock.realMillis()
callback()
local executionTime = g_clock.realMillis() - start
if executionTime > 100 then
context.warning("Slow hotkey (" .. executionTime .. "ms): " .. hotkeyData.name .. " - " .. desc)
end
context._currentExecution = nil
return true
end
end
return hotkeyData
end
-- singlehotkey(keys, callback)
-- singlehotkey(keys, name, callback)
-- singlehotkey(keys, name, callback, parent)
context.singlehotkey = function(keys, name, callback, parent)
if type(name) == 'function' then
callback = name
name = ""
end
return context.hotkey(keys, name, callback, parent, true)
end
-- schedule(timeout, callback)
context.schedule = function(timeout, callback)
local extecute_time = g_clock.millis() + timeout
table.insert(context._scheduler, {
execution = extecute_time,
callback = callback
})
table.sort(context._scheduler, function(a, b) return a.execution < b.execution end)
end
-- delay(duration) -- block execution of current macro/hotkey/callback for x milliseconds
context.delay = function(duration)
if not context._currentExecution then
return context.error("Invalid usage of delay function, it should be used inside callbacks")
end
context._currentExecution.delay = context.now + duration
end

View File

@@ -1,256 +1,256 @@
local context = G.botContext
context.getMapView = function() return modules.game_interface.getMapPanel() end
context.getMapPanel = context.getMapView
context.zoomIn = function() modules.game_interface.getMapPanel():zoomIn() end
context.zoomOut = function() modules.game_interface.getMapPanel():zoomOut() end
context.getSpectators = function(param1, param2)
--[[
if param1 is table (position) then it's used for central position, then param2 is used as param1
if param1 is creature, then creature position and direction of creature is used, then param2 is used as param1
if param1 is true/false then it's used for multifloor, example: getSpectators(true)
if param1 is string then it's used for getSpectatorsByPattern
]]--
local pos = context.player:getPosition()
local direction = context.player:getDirection()
if type(param1) == 'table' then
pos = param1
direction = 8 -- invalid direction
param1 = param2
end
if type(param1) == 'userdata' then
pos = param1:getPosition()
direction = param1:getDirection()
param1 = param2
end
if type(param1) == 'string' then
return g_map.getSpectatorsByPattern(pos, param1, direction)
end
local multifloor = false
if type(param1) == 'boolean' and param1 == true then
multifloor = true
end
return g_map.getSpectators(pos, multifloor)
end
context.getCreatureById = function(id, multifloor)
if type(id) ~= 'number' then return nil end
if multifloor ~= true then
multifloor = false
end
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
if spec:getId() == id then
return spec
end
end
return nil
end
context.getCreatureByName = function(name, multifloor)
if not name then return nil end
name = name:lower()
if multifloor ~= true then
multifloor = false
end
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
if spec:getName():lower() == name then
return spec
end
end
return nil
end
context.getPlayerByName = function(name, multifloor)
if not name then return nil end
name = name:lower()
if multifloor ~= true then
multifloor = false
end
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
if spec:isPlayer() and spec:getName():lower() == name then
return spec
end
end
return nil
end
context.findAllPaths = function(start, maxDist, params)
--[[
Available params:
ignoreLastCreature
ignoreCreatures
ignoreNonPathable
ignoreNonWalkable
ignoreStairs
ignoreCost
allowUnseen
allowOnlyVisibleTiles
]]--
if type(params) ~= 'table' then
params = {}
end
for key, value in pairs(params) do
if value == nil or value == false then
params[key] = 0
elseif value == true then
params[key] = 1
end
end
return g_map.findEveryPath(start, maxDist, params)
end
context.findEveryPath = context.findAllPaths
context.translateAllPathsToPath = function(paths, destPos)
local predirections = {}
local directions = {}
local destPosStr = destPos
if type(destPos) ~= 'string' then
destPosStr = destPos.x .. "," .. destPos.y .. "," .. destPos.z
end
while destPosStr:len() > 0 do
local node = paths[destPosStr]
if not node then
break
end
if node[3] < 0 then
break
end
table.insert(predirections, node[3])
destPosStr = node[4]
end
-- reverse
for i=#predirections,1,-1 do
table.insert(directions, predirections[i])
end
return directions
end
context.translateEveryPathToPath = context.translateAllPathsToPath
context.findPath = function(startPos, destPos, maxDist, params)
--[[
Available params:
ignoreLastCreature
ignoreCreatures
ignoreNonPathable
ignoreNonWalkable
ignoreStairs
ignoreCost
allowUnseen
allowOnlyVisibleTiles
precision
marginMin
marginMax
]]--
if not destPos or startPos.z ~= destPos.z then
return
end
if type(maxDist) ~= 'number' then
maxDist = 100
end
if type(params) ~= 'table' then
params = {}
end
local destPosStr = destPos.x .. "," .. destPos.y .. "," .. destPos.z
params["destination"] = destPosStr
local paths = context.findAllPaths(startPos, maxDist, params)
local marginMin = params.marginMin or params.minMargin
local marginMax = params.marginMax or params.maxMargin
if type(marginMin) == 'number' and type(marginMax) == 'number' then
local bestCandidate = nil
local bestCandidatePos = nil
for x = -marginMax, marginMax do
for y = -marginMax, marginMax do
if math.abs(x) >= marginMin or math.abs(y) >= marginMin then
local dest = (destPos.x + x) .. "," .. (destPos.y + y) .. "," .. destPos.z
local node = paths[dest]
if node and (not bestCandidate or bestCandidate[1] > node[1]) then
bestCandidate = node
bestCandidatePos = dest
end
end
end
end
if bestCandidate then
return context.translateAllPathsToPath(paths, bestCandidatePos)
end
return
end
if not paths[destPosStr] then
local precision = params.precision
if type(precision) == 'number' then
for p = 1, precision do
local bestCandidate = nil
local bestCandidatePos = nil
for x = -p, p do
for y = -p, p do
local dest = (destPos.x + x) .. "," .. (destPos.y + y) .. "," .. destPos.z
local node = paths[dest]
if node and (not bestCandidate or bestCandidate[1] > node[1]) then
bestCandidate = node
bestCandidatePos = dest
end
end
end
if bestCandidate then
return context.translateAllPathsToPath(paths, bestCandidatePos)
end
end
end
return nil
end
return context.translateAllPathsToPath(paths, destPos)
end
context.getPath = context.findPath
-- also works as autoWalk(dirs) where dirs is a list eg.: {1,2,3,0,1,1,2,}
context.autoWalk = function(destination, maxDist, params)
if type(destination) == "table" and table.isList(destination) and not maxDist and not params then
g_game.autoWalk(destination, {x=0,y=0,z=0})
return true
end
-- Available params same as for findPath
local path = context.findPath(context.player:getPosition(), destination, maxDist, params)
if not path then
return false
end
-- autowalk without prewalk animation
g_game.autoWalk(path, {x=0,y=0,z=0})
return true
end
context.getTileUnderCursor = function()
if not modules.game_interface.gameMapPanel.mousePos then return end
return modules.game_interface.gameMapPanel:getTile(modules.game_interface.gameMapPanel.mousePos)
end
context.canShoot = function(pos, distance)
if not distance then distance = 5 end
local tile = g_map.getTile(pos, distance)
if tile then
return tile:canShoot(distance)
end
return false
end
context.isTrapped = function(creature)
if not creature then
creature = context.player
end
local pos = creature:getPosition()
local dirs = {{-1,1}, {0,1}, {1,1}, {-1, 0}, {1, 0}, {-1, -1}, {0, -1}, {1, -1}}
for i=1,#dirs do
local tile = g_map.getTile({x=pos.x-dirs[i][1],y=pos.y-dirs[i][2],z=pos.z})
if tile and tile:isWalkable(false) then
return false
end
end
return true
end
local context = G.botContext
context.getMapView = function() return modules.game_interface.getMapPanel() end
context.getMapPanel = context.getMapView
context.zoomIn = function() modules.game_interface.getMapPanel():zoomIn() end
context.zoomOut = function() modules.game_interface.getMapPanel():zoomOut() end
context.getSpectators = function(param1, param2)
--[[
if param1 is table (position) then it's used for central position, then param2 is used as param1
if param1 is creature, then creature position and direction of creature is used, then param2 is used as param1
if param1 is true/false then it's used for multifloor, example: getSpectators(true)
if param1 is string then it's used for getSpectatorsByPattern
]]--
local pos = context.player:getPosition()
local direction = context.player:getDirection()
if type(param1) == 'table' then
pos = param1
direction = 8 -- invalid direction
param1 = param2
end
if type(param1) == 'userdata' then
pos = param1:getPosition()
direction = param1:getDirection()
param1 = param2
end
if type(param1) == 'string' then
return g_map.getSpectatorsByPattern(pos, param1, direction)
end
local multifloor = false
if type(param1) == 'boolean' and param1 == true then
multifloor = true
end
return g_map.getSpectators(pos, multifloor)
end
context.getCreatureById = function(id, multifloor)
if type(id) ~= 'number' then return nil end
if multifloor ~= true then
multifloor = false
end
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
if spec:getId() == id then
return spec
end
end
return nil
end
context.getCreatureByName = function(name, multifloor)
if not name then return nil end
name = name:lower()
if multifloor ~= true then
multifloor = false
end
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
if spec:getName():lower() == name then
return spec
end
end
return nil
end
context.getPlayerByName = function(name, multifloor)
if not name then return nil end
name = name:lower()
if multifloor ~= true then
multifloor = false
end
for i, spec in ipairs(g_map.getSpectators(context.player:getPosition(), multifloor)) do
if spec:isPlayer() and spec:getName():lower() == name then
return spec
end
end
return nil
end
context.findAllPaths = function(start, maxDist, params)
--[[
Available params:
ignoreLastCreature
ignoreCreatures
ignoreNonPathable
ignoreNonWalkable
ignoreStairs
ignoreCost
allowUnseen
allowOnlyVisibleTiles
]]--
if type(params) ~= 'table' then
params = {}
end
for key, value in pairs(params) do
if value == nil or value == false then
params[key] = 0
elseif value == true then
params[key] = 1
end
end
return g_map.findEveryPath(start, maxDist, params)
end
context.findEveryPath = context.findAllPaths
context.translateAllPathsToPath = function(paths, destPos)
local predirections = {}
local directions = {}
local destPosStr = destPos
if type(destPos) ~= 'string' then
destPosStr = destPos.x .. "," .. destPos.y .. "," .. destPos.z
end
while destPosStr:len() > 0 do
local node = paths[destPosStr]
if not node then
break
end
if node[3] < 0 then
break
end
table.insert(predirections, node[3])
destPosStr = node[4]
end
-- reverse
for i=#predirections,1,-1 do
table.insert(directions, predirections[i])
end
return directions
end
context.translateEveryPathToPath = context.translateAllPathsToPath
context.findPath = function(startPos, destPos, maxDist, params)
--[[
Available params:
ignoreLastCreature
ignoreCreatures
ignoreNonPathable
ignoreNonWalkable
ignoreStairs
ignoreCost
allowUnseen
allowOnlyVisibleTiles
precision
marginMin
marginMax
]]--
if not destPos or startPos.z ~= destPos.z then
return
end
if type(maxDist) ~= 'number' then
maxDist = 100
end
if type(params) ~= 'table' then
params = {}
end
local destPosStr = destPos.x .. "," .. destPos.y .. "," .. destPos.z
params["destination"] = destPosStr
local paths = context.findAllPaths(startPos, maxDist, params)
local marginMin = params.marginMin or params.minMargin
local marginMax = params.marginMax or params.maxMargin
if type(marginMin) == 'number' and type(marginMax) == 'number' then
local bestCandidate = nil
local bestCandidatePos = nil
for x = -marginMax, marginMax do
for y = -marginMax, marginMax do
if math.abs(x) >= marginMin or math.abs(y) >= marginMin then
local dest = (destPos.x + x) .. "," .. (destPos.y + y) .. "," .. destPos.z
local node = paths[dest]
if node and (not bestCandidate or bestCandidate[1] > node[1]) then
bestCandidate = node
bestCandidatePos = dest
end
end
end
end
if bestCandidate then
return context.translateAllPathsToPath(paths, bestCandidatePos)
end
return
end
if not paths[destPosStr] then
local precision = params.precision
if type(precision) == 'number' then
for p = 1, precision do
local bestCandidate = nil
local bestCandidatePos = nil
for x = -p, p do
for y = -p, p do
local dest = (destPos.x + x) .. "," .. (destPos.y + y) .. "," .. destPos.z
local node = paths[dest]
if node and (not bestCandidate or bestCandidate[1] > node[1]) then
bestCandidate = node
bestCandidatePos = dest
end
end
end
if bestCandidate then
return context.translateAllPathsToPath(paths, bestCandidatePos)
end
end
end
return nil
end
return context.translateAllPathsToPath(paths, destPos)
end
context.getPath = context.findPath
-- also works as autoWalk(dirs) where dirs is a list eg.: {1,2,3,0,1,1,2,}
context.autoWalk = function(destination, maxDist, params)
if type(destination) == "table" and table.isList(destination) and not maxDist and not params then
g_game.autoWalk(destination, {x=0,y=0,z=0})
return true
end
-- Available params same as for findPath
local path = context.findPath(context.player:getPosition(), destination, maxDist, params)
if not path then
return false
end
-- autowalk without prewalk animation
g_game.autoWalk(path, {x=0,y=0,z=0})
return true
end
context.getTileUnderCursor = function()
if not modules.game_interface.gameMapPanel.mousePos then return end
return modules.game_interface.gameMapPanel:getTile(modules.game_interface.gameMapPanel.mousePos)
end
context.canShoot = function(pos, distance)
if not distance then distance = 5 end
local tile = g_map.getTile(pos, distance)
if tile then
return tile:canShoot(distance)
end
return false
end
context.isTrapped = function(creature)
if not creature then
creature = context.player
end
local pos = creature:getPosition()
local dirs = {{-1,1}, {0,1}, {1,1}, {-1, 0}, {1, 0}, {-1, -1}, {0, -1}, {1, -1}}
for i=1,#dirs do
local tile = g_map.getTile({x=pos.x-dirs[i][1],y=pos.y-dirs[i][2],z=pos.z})
if tile and tile:isWalkable(false) then
return false
end
end
return true
end

View File

@@ -1,130 +1,130 @@
local context = G.botContext
context.NPC = {}
context.NPC.talk = function(text)
if g_game.getClientVersion() >= 810 then
g_game.talkChannel(11, 0, text)
else
return context.say(text)
end
end
context.NPC.say = context.NPC.talk
context.NPC.isTrading = function()
return modules.game_npctrade.npcWindow and modules.game_npctrade.npcWindow:isVisible()
end
context.NPC.hasTrade = context.NPC.isTrading
context.NPC.hasTradeWindow = context.NPC.isTrading
context.NPC.isTradeOpen = context.NPC.isTrading
context.NPC.getSellItems = function()
if not context.NPC.isTrading() then return {} end
local items = {}
for i, item in ipairs(modules.game_npctrade.tradeItems[modules.game_npctrade.SELL]) do
table.insert(items, {
item = item.ptr,
id = item.ptr:getId(),
count = item.ptr:getCount(),
name = item.name,
subType = item.ptr:getSubType(),
weight = item.weight / 100,
price = item.price
})
end
return items
end
context.NPC.getBuyItems = function()
if not context.NPC.isTrading() then return {} end
local items = {}
for i, item in ipairs(modules.game_npctrade.tradeItems[modules.game_npctrade.BUY]) do
table.insert(items, {
item = item.ptr,
id = item.ptr:getId(),
count = item.ptr:getCount(),
name = item.name,
subType = item.ptr:getSubType(),
weight = item.weight / 100,
price = item.price
})
end
return items
end
context.NPC.getSellQuantity = function(item)
if not context.NPC.isTrading() then return 0 end
if type(item) == 'number' then
item = Item.create(item)
end
return modules.game_npctrade.getSellQuantity(item)
end
context.NPC.canTradeItem = function(item)
if not context.NPC.isTrading() then return false end
if type(item) == 'number' then
item = Item.create(item)
end
return modules.game_npctrade.canTradeItem(item)
end
context.NPC.sell = function(item, count, ignoreEquipped)
if type(item) == 'number' then
for i, entry in ipairs(context.NPC.getSellItems()) do
if entry.id == item then
item = entry.item
break
end
end
if type(item) == 'number' then
item = Item.create(item)
end
end
if count == 0 then
count = 1
end
if count == nil or count == -1 then
count = context.NPC.getSellQuantity(item)
end
if ignoreEquipped == nil then
ignoreEquipped = true
end
g_game.sellItem(item, count, ignoreEquipped)
end
context.NPC.buy = function(item, count, ignoreCapacity, withBackpack)
if type(item) == 'number' then
for i, entry in ipairs(context.NPC.getBuyItems()) do
if entry.id == item then
item = entry.item
break
end
end
if type(item) == 'number' then
item = Item.create(item)
end
end
if count == nil or count <= 0 then
count = 1
end
if ignoreCapacity == nil then
ignoreCapacity = false
end
if withBackpack == nil then
withBackpack = false
end
g_game.buyItem(item, count, ignoreCapacity, withBackpack)
end
context.NPC.sellAll = function()
if not context.NPC.isTrading() then return false end
modules.game_npctrade.sellAll()
end
context.NPC.closeTrade = function()
modules.game_npctrade.closeNpcTrade()
end
context.NPC.close = context.NPC.closeTrade
context.NPC.finish = context.NPC.closeTrade
context.NPC.endTrade = context.NPC.closeTrade
local context = G.botContext
context.NPC = {}
context.NPC.talk = function(text)
if g_game.getClientVersion() >= 810 then
g_game.talkChannel(11, 0, text)
else
return context.say(text)
end
end
context.NPC.say = context.NPC.talk
context.NPC.isTrading = function()
return modules.game_npctrade.npcWindow and modules.game_npctrade.npcWindow:isVisible()
end
context.NPC.hasTrade = context.NPC.isTrading
context.NPC.hasTradeWindow = context.NPC.isTrading
context.NPC.isTradeOpen = context.NPC.isTrading
context.NPC.getSellItems = function()
if not context.NPC.isTrading() then return {} end
local items = {}
for i, item in ipairs(modules.game_npctrade.tradeItems[modules.game_npctrade.SELL]) do
table.insert(items, {
item = item.ptr,
id = item.ptr:getId(),
count = item.ptr:getCount(),
name = item.name,
subType = item.ptr:getSubType(),
weight = item.weight / 100,
price = item.price
})
end
return items
end
context.NPC.getBuyItems = function()
if not context.NPC.isTrading() then return {} end
local items = {}
for i, item in ipairs(modules.game_npctrade.tradeItems[modules.game_npctrade.BUY]) do
table.insert(items, {
item = item.ptr,
id = item.ptr:getId(),
count = item.ptr:getCount(),
name = item.name,
subType = item.ptr:getSubType(),
weight = item.weight / 100,
price = item.price
})
end
return items
end
context.NPC.getSellQuantity = function(item)
if not context.NPC.isTrading() then return 0 end
if type(item) == 'number' then
item = Item.create(item)
end
return modules.game_npctrade.getSellQuantity(item)
end
context.NPC.canTradeItem = function(item)
if not context.NPC.isTrading() then return false end
if type(item) == 'number' then
item = Item.create(item)
end
return modules.game_npctrade.canTradeItem(item)
end
context.NPC.sell = function(item, count, ignoreEquipped)
if type(item) == 'number' then
for i, entry in ipairs(context.NPC.getSellItems()) do
if entry.id == item then
item = entry.item
break
end
end
if type(item) == 'number' then
item = Item.create(item)
end
end
if count == 0 then
count = 1
end
if count == nil or count == -1 then
count = context.NPC.getSellQuantity(item)
end
if ignoreEquipped == nil then
ignoreEquipped = true
end
g_game.sellItem(item, count, ignoreEquipped)
end
context.NPC.buy = function(item, count, ignoreCapacity, withBackpack)
if type(item) == 'number' then
for i, entry in ipairs(context.NPC.getBuyItems()) do
if entry.id == item then
item = entry.item
break
end
end
if type(item) == 'number' then
item = Item.create(item)
end
end
if count == nil or count <= 0 then
count = 1
end
if ignoreCapacity == nil then
ignoreCapacity = false
end
if withBackpack == nil then
withBackpack = false
end
g_game.buyItem(item, count, ignoreCapacity, withBackpack)
end
context.NPC.sellAll = function()
if not context.NPC.isTrading() then return false end
modules.game_npctrade.sellAll()
end
context.NPC.closeTrade = function()
modules.game_npctrade.closeNpcTrade()
end
context.NPC.close = context.NPC.closeTrade
context.NPC.finish = context.NPC.closeTrade
context.NPC.endTrade = context.NPC.closeTrade
context.NPC.finishTrade = context.NPC.closeTrade

View File

@@ -1,167 +1,167 @@
local context = G.botContext
context.name = function() return context.player:getName() end
context.hp = function() return context.player:getHealth() end
context.mana = function() return context.player:getMana() end
context.hppercent = function() return context.player:getHealthPercent() end
context.manapercent = function() if context.player:getMaxMana() <= 1 then return 100 else return math.floor(context.player:getMana() * 100 / context.player:getMaxMana()) end end
context.maxhp = function() return context.player:getMaxHealth() end
context.maxmana = function() return context.player:getMaxMana() end
context.hpmax = function() return context.player:getMaxHealth() end
context.manamax = function() return context.player:getMaxMana() end
context.cap = function() return context.player:getCapacity() end
context.freecap = function() return context.player:getFreeCapacity() end
context.maxcap = function() return context.player:getTotalCapacity() end
context.capmax = function() return context.player:getTotalCapacity() end
context.exp = function() return context.player:getExperience() end
context.lvl = function() return context.player:getLevel() end
context.level = function() return context.player:getLevel() end
context.mlev = function() return context.player:getMagicLevel() end
context.magic = function() return context.player:getMagicLevel() end
context.mlevel = function() return context.player:getMagicLevel() end
context.soul = function() return context.player:getSoul() end
context.stamina = function() return context.player:getStamina() end
context.voc = function() return context.player:getVocation() end
context.vocation = function() return context.player:getVocation() end
context.bless = function() return context.player:getBlessings() end
context.blesses = function() return context.player:getBlessings() end
context.blessings = function() return context.player:getBlessings() end
context.pos = function() return context.player:getPosition() end
context.posx = function() return context.player:getPosition().x end
context.posy = function() return context.player:getPosition().y end
context.posz = function() return context.player:getPosition().z end
context.direction = function() return context.player:getDirection() end
context.speed = function() return context.player:getSpeed() end
context.skull = function() return context.player:getSkull() end
context.outfit = function() return context.player:getOutfit() end
context.setOutfit = function(outfit)
modules.game_outfit.ignoreNextOutfitWindow = g_clock.millis()
g_game.requestOutfit()
context.schedule(100, function()
g_game.changeOutfit(outfit)
end)
end
context.changeOutfit = context.setOutfit
context.setSpeed = function(value) context.player:setSpeed(value) end
context.walk = function(dir) return modules.game_walking.walk(dir) end
context.turn = function(dir) return g_game.turn(dir) end
-- game releated
context.getChannels = function()
-- return { channelId = channelName }
return modules.game_console.channels
end
context.getChannelId = function(name)
for id, channel in pairs(context.getChannels()) do
if name:lower() == channel:lower() then
return id
end
end
return nil
end
context.getChannel = context.getChannelId
context.say = g_game.talk
context.talk = g_game.talk
context.yell = function(text) g_game.talkChannel(3, 0, text) end
context.talkChannel = function(channel, text) g_game.talkChannel(7, channel, text) end
context.sayChannel = context.talkChannel
context.talkPrivate = function(receiver, text) g_game.talkPrivate(5, receiver, text) end
context.sayPrivate = context.talkPrivate
context.talkNpc = function(text)
if g_game.getClientVersion() >= 810 then
g_game.talkChannel(11, 0, text)
else
return context.say(text)
end
end
context.sayNpc = context.talkNpc
context.sayNPC = context.talkNpc
context.talkNPC = context.talkNpc
context.saySpell = function(text, lastSpellTimeout)
if not text or text:len() < 1 then
return
end
if context.lastSpell == nil then
context.lastSpell = 0
end
if not lastSpellTimeout then
lastSpellTimeout = 1000
end
if context.lastSpell + lastSpellTimeout > context.now then
return false
end
context.say(text)
context.lastSpell = context.now
return true
end
context.setSpellTimeout = function()
context.lastSpell = context.now
end
context.use = function(thing, subtype)
if type(thing) == 'number' then
return g_game.useInventoryItem(thing, subtype)
else
return g_game.use(thing)
end
end
context.usewith = function(thing, target, subtype)
if type(thing) == 'number' then
return g_game.useInventoryItemWith(thing, target, subtype)
else
return g_game.useWith(item, target, subtype)
end
end
context.useWith = context.usewith
context.useRune = function(itemid, target, lastSpellTimeout)
if context.lastRuneUse == nil then
context.lastRuneUse = 0
end
if not lastRuneTimeout then
lastRuneTimeout = 1000
end
if context.lastRuneUse + lastRuneTimeout > context.now then
return false
end
context.usewith(itemid, target)
context.lastRuneUse = context.now
return true
end
context.userune = context.useRune
context.findItem = function(itemId, subType)
if subType == nil then
subType = -1
end
return g_game.findItemInContainers(itemId, subType)
end
context.attack = g_game.attack
context.cancelAttack = g_game.cancelAttack
context.follow = g_game.follow
context.cancelFollow = g_game.cancelFollow
context.cancelAttackAndFollow = g_game.cancelAttackAndFollow
context.logout = g_game.forceLogout
context.safeLogout = g_game.safeLogout
context.ping = g_game.getPing
modules.game_cooldown.isGroupCooldownIconActive(id)
modules.game_cooldown.isCooldownIconActive(id)
local context = G.botContext
context.name = function() return context.player:getName() end
context.hp = function() return context.player:getHealth() end
context.mana = function() return context.player:getMana() end
context.hppercent = function() return context.player:getHealthPercent() end
context.manapercent = function() if context.player:getMaxMana() <= 1 then return 100 else return math.floor(context.player:getMana() * 100 / context.player:getMaxMana()) end end
context.maxhp = function() return context.player:getMaxHealth() end
context.maxmana = function() return context.player:getMaxMana() end
context.hpmax = function() return context.player:getMaxHealth() end
context.manamax = function() return context.player:getMaxMana() end
context.cap = function() return context.player:getCapacity() end
context.freecap = function() return context.player:getFreeCapacity() end
context.maxcap = function() return context.player:getTotalCapacity() end
context.capmax = function() return context.player:getTotalCapacity() end
context.exp = function() return context.player:getExperience() end
context.lvl = function() return context.player:getLevel() end
context.level = function() return context.player:getLevel() end
context.mlev = function() return context.player:getMagicLevel() end
context.magic = function() return context.player:getMagicLevel() end
context.mlevel = function() return context.player:getMagicLevel() end
context.soul = function() return context.player:getSoul() end
context.stamina = function() return context.player:getStamina() end
context.voc = function() return context.player:getVocation() end
context.vocation = function() return context.player:getVocation() end
context.bless = function() return context.player:getBlessings() end
context.blesses = function() return context.player:getBlessings() end
context.blessings = function() return context.player:getBlessings() end
context.pos = function() return context.player:getPosition() end
context.posx = function() return context.player:getPosition().x end
context.posy = function() return context.player:getPosition().y end
context.posz = function() return context.player:getPosition().z end
context.direction = function() return context.player:getDirection() end
context.speed = function() return context.player:getSpeed() end
context.skull = function() return context.player:getSkull() end
context.outfit = function() return context.player:getOutfit() end
context.setOutfit = function(outfit)
modules.game_outfit.ignoreNextOutfitWindow = g_clock.millis()
g_game.requestOutfit()
context.schedule(100, function()
g_game.changeOutfit(outfit)
end)
end
context.changeOutfit = context.setOutfit
context.setSpeed = function(value) context.player:setSpeed(value) end
context.walk = function(dir) return modules.game_walking.walk(dir) end
context.turn = function(dir) return g_game.turn(dir) end
-- game releated
context.getChannels = function()
-- return { channelId = channelName }
return modules.game_console.channels
end
context.getChannelId = function(name)
for id, channel in pairs(context.getChannels()) do
if name:lower() == channel:lower() then
return id
end
end
return nil
end
context.getChannel = context.getChannelId
context.say = g_game.talk
context.talk = g_game.talk
context.yell = function(text) g_game.talkChannel(3, 0, text) end
context.talkChannel = function(channel, text) g_game.talkChannel(7, channel, text) end
context.sayChannel = context.talkChannel
context.talkPrivate = function(receiver, text) g_game.talkPrivate(5, receiver, text) end
context.sayPrivate = context.talkPrivate
context.talkNpc = function(text)
if g_game.getClientVersion() >= 810 then
g_game.talkChannel(11, 0, text)
else
return context.say(text)
end
end
context.sayNpc = context.talkNpc
context.sayNPC = context.talkNpc
context.talkNPC = context.talkNpc
context.saySpell = function(text, lastSpellTimeout)
if not text or text:len() < 1 then
return
end
if context.lastSpell == nil then
context.lastSpell = 0
end
if not lastSpellTimeout then
lastSpellTimeout = 1000
end
if context.lastSpell + lastSpellTimeout > context.now then
return false
end
context.say(text)
context.lastSpell = context.now
return true
end
context.setSpellTimeout = function()
context.lastSpell = context.now
end
context.use = function(thing, subtype)
if type(thing) == 'number' then
return g_game.useInventoryItem(thing, subtype)
else
return g_game.use(thing)
end
end
context.usewith = function(thing, target, subtype)
if type(thing) == 'number' then
return g_game.useInventoryItemWith(thing, target, subtype)
else
return g_game.useWith(item, target, subtype)
end
end
context.useWith = context.usewith
context.useRune = function(itemid, target, lastSpellTimeout)
if context.lastRuneUse == nil then
context.lastRuneUse = 0
end
if not lastRuneTimeout then
lastRuneTimeout = 1000
end
if context.lastRuneUse + lastRuneTimeout > context.now then
return false
end
context.usewith(itemid, target)
context.lastRuneUse = context.now
return true
end
context.userune = context.useRune
context.findItem = function(itemId, subType)
if subType == nil then
subType = -1
end
return g_game.findItemInContainers(itemId, subType)
end
context.attack = g_game.attack
context.cancelAttack = g_game.cancelAttack
context.follow = g_game.follow
context.cancelFollow = g_game.cancelFollow
context.cancelAttackAndFollow = g_game.cancelAttackAndFollow
context.logout = g_game.forceLogout
context.safeLogout = g_game.safeLogout
context.ping = g_game.getPing
modules.game_cooldown.isGroupCooldownIconActive(id)
modules.game_cooldown.isCooldownIconActive(id)

View File

@@ -1,32 +1,32 @@
local context = G.botContext
for i, state in ipairs(PlayerStates) do
context[state] = state
end
context.hasCondition = function(condition) return bit.band(context.player:getStates(), condition) > 0 end
context.isPoisioned = function() return context.hasCondition(PlayerStates.Poison) end
context.isBurnining = function() return context.hasCondition(PlayerStates.Burn) end
context.isEnergized = function() return context.hasCondition(PlayerStates.Energy) end
context.isDrunk = function() return context.hasCondition(PlayerStates.Drunk) end
context.hasManaShield = function() return context.hasCondition(PlayerStates.ManaShield) end
context.isParalyzed = function() return context.hasCondition(PlayerStates.Paralyze) end
context.hasHaste = function() return context.hasCondition(PlayerStates.Haste) end
context.hasSwords = function() return context.hasCondition(PlayerStates.Swords) end
context.isInFight = function() return context.hasCondition(PlayerStates.Swords) end
context.canLogout = function() return not context.hasCondition(PlayerStates.Swords) end
context.isDrowning = function() return context.hasCondition(PlayerStates.Drowning) end
context.isFreezing = function() return context.hasCondition(PlayerStates.Freezing) end
context.isDazzled = function() return context.hasCondition(PlayerStates.Dazzled) end
context.isCursed = function() return context.hasCondition(PlayerStates.Cursed) end
context.hasPartyBuff = function() return context.hasCondition(PlayerStates.PartyBuff) end
context.hasPzLock = function() return context.hasCondition(PlayerStates.PzBlock) end
context.hasPzBlock = function() return context.hasCondition(PlayerStates.PzBlock) end
context.isPzLocked = function() return context.hasCondition(PlayerStates.PzBlock) end
context.isPzBlocked = function() return context.hasCondition(PlayerStates.PzBlock) end
context.isInProtectionZone = function() return context.hasCondition(PlayerStates.Pz) end
context.hasPz = function() return context.hasCondition(PlayerStates.Pz) end
context.isInPz = function() return context.hasCondition(PlayerStates.Pz) end
context.isBleeding = function() return context.hasCondition(PlayerStates.Bleeding) end
context.isHungry = function() return context.hasCondition(PlayerStates.Hungry) end
local context = G.botContext
for i, state in ipairs(PlayerStates) do
context[state] = state
end
context.hasCondition = function(condition) return bit.band(context.player:getStates(), condition) > 0 end
context.isPoisioned = function() return context.hasCondition(PlayerStates.Poison) end
context.isBurnining = function() return context.hasCondition(PlayerStates.Burn) end
context.isEnergized = function() return context.hasCondition(PlayerStates.Energy) end
context.isDrunk = function() return context.hasCondition(PlayerStates.Drunk) end
context.hasManaShield = function() return context.hasCondition(PlayerStates.ManaShield) end
context.isParalyzed = function() return context.hasCondition(PlayerStates.Paralyze) end
context.hasHaste = function() return context.hasCondition(PlayerStates.Haste) end
context.hasSwords = function() return context.hasCondition(PlayerStates.Swords) end
context.isInFight = function() return context.hasCondition(PlayerStates.Swords) end
context.canLogout = function() return not context.hasCondition(PlayerStates.Swords) end
context.isDrowning = function() return context.hasCondition(PlayerStates.Drowning) end
context.isFreezing = function() return context.hasCondition(PlayerStates.Freezing) end
context.isDazzled = function() return context.hasCondition(PlayerStates.Dazzled) end
context.isCursed = function() return context.hasCondition(PlayerStates.Cursed) end
context.hasPartyBuff = function() return context.hasCondition(PlayerStates.PartyBuff) end
context.hasPzLock = function() return context.hasCondition(PlayerStates.PzBlock) end
context.hasPzBlock = function() return context.hasCondition(PlayerStates.PzBlock) end
context.isPzLocked = function() return context.hasCondition(PlayerStates.PzBlock) end
context.isPzBlocked = function() return context.hasCondition(PlayerStates.PzBlock) end
context.isInProtectionZone = function() return context.hasCondition(PlayerStates.Pz) end
context.hasPz = function() return context.hasCondition(PlayerStates.Pz) end
context.isInPz = function() return context.hasCondition(PlayerStates.Pz) end
context.isBleeding = function() return context.hasCondition(PlayerStates.Bleeding) end
context.isHungry = function() return context.hasCondition(PlayerStates.Hungry) end

View File

@@ -1,45 +1,45 @@
local context = G.botContext
context.SlotOther = InventorySlotOther
context.SlotHead = InventorySlotHead
context.SlotNeck = InventorySlotNeck
context.SlotBack = InventorySlotBack
context.SlotBody = InventorySlotBody
context.SlotRight = InventorySlotRight
context.SlotLeft = InventorySlotLeft
context.SlotLeg = InventorySlotLeg
context.SlotFeet = InventorySlotFeet
context.SlotFinger = InventorySlotFinger
context.SlotAmmo = InventorySlotAmmo
context.SlotPurse = InventorySlotPurse
context.getInventoryItem = function(slot) return context.player:getInventoryItem(slot) end
context.getSlot = context.getInventoryItem
context.getHead = function() return context.getInventoryItem(context.SlotHead) end
context.getNeck = function() return context.getInventoryItem(context.SlotNeck) end
context.getBack = function() return context.getInventoryItem(context.SlotBack) end
context.getBody = function() return context.getInventoryItem(context.SlotBody) end
context.getRight = function() return context.getInventoryItem(context.SlotRight) end
context.getLeft = function() return context.getInventoryItem(context.SlotLeft) end
context.getLeg = function() return context.getInventoryItem(context.SlotLeg) end
context.getFeet = function() return context.getInventoryItem(context.SlotFeet) end
context.getFinger = function() return context.getInventoryItem(context.SlotFinger) end
context.getAmmo = function() return context.getInventoryItem(context.SlotAmmo) end
context.getPurse = function() return context.getInventoryItem(context.SlotPurse) end
context.getContainers = function() return g_game.getContainers() end
context.getContainer = function(index) return g_game.getContainer(index) end
context.moveToSlot = function(item, slot, count)
if type(item) == 'number' then
item = context.findItem(item)
end
if not item then
return
end
if count == nil then
count = item:getCount()
end
return g_game.move(item, {x=65535, y=slot, z=0}, count)
local context = G.botContext
context.SlotOther = InventorySlotOther
context.SlotHead = InventorySlotHead
context.SlotNeck = InventorySlotNeck
context.SlotBack = InventorySlotBack
context.SlotBody = InventorySlotBody
context.SlotRight = InventorySlotRight
context.SlotLeft = InventorySlotLeft
context.SlotLeg = InventorySlotLeg
context.SlotFeet = InventorySlotFeet
context.SlotFinger = InventorySlotFinger
context.SlotAmmo = InventorySlotAmmo
context.SlotPurse = InventorySlotPurse
context.getInventoryItem = function(slot) return context.player:getInventoryItem(slot) end
context.getSlot = context.getInventoryItem
context.getHead = function() return context.getInventoryItem(context.SlotHead) end
context.getNeck = function() return context.getInventoryItem(context.SlotNeck) end
context.getBack = function() return context.getInventoryItem(context.SlotBack) end
context.getBody = function() return context.getInventoryItem(context.SlotBody) end
context.getRight = function() return context.getInventoryItem(context.SlotRight) end
context.getLeft = function() return context.getInventoryItem(context.SlotLeft) end
context.getLeg = function() return context.getInventoryItem(context.SlotLeg) end
context.getFeet = function() return context.getInventoryItem(context.SlotFeet) end
context.getFinger = function() return context.getInventoryItem(context.SlotFinger) end
context.getAmmo = function() return context.getInventoryItem(context.SlotAmmo) end
context.getPurse = function() return context.getInventoryItem(context.SlotPurse) end
context.getContainers = function() return g_game.getContainers() end
context.getContainer = function(index) return g_game.getContainer(index) end
context.moveToSlot = function(item, slot, count)
if type(item) == 'number' then
item = context.findItem(item)
end
if not item then
return
end
if count == nil then
count = item:getCount()
end
return g_game.move(item, {x=65535, y=slot, z=0}, count)
end

View File

@@ -1,59 +1,59 @@
local context = G.botContext
context.loadScript = function(path, onLoadCallback)
if type(path) ~= 'string' then
return context.error("Invalid path for loadScript: " .. tostring(path))
end
if path:lower():find("http") == 1 then
return context.loadRemoteScript(path)
end
if not g_resources.fileExists(path) then
return context.error("File " .. path .. " doesn't exist")
end
local status, result = pcall(function()
assert(load(g_resources.readFileContents(path), path, nil, context))()
end)
if not status then
return context.error("Error while loading script from: " .. path .. ":\n" .. result)
end
if onLoadCallback then
onLoadCallback()
end
end
context.loadRemoteScript = function(url, onLoadCallback)
if type(url) ~= 'string' or url:lower():find("http") ~= 1 then
return context.error("Invalid url for loadRemoteScript: " .. tostring(url))
end
HTTP.get(url, function(data, err)
if err or data:len() == 0 then
-- try to load from cache
if type(context.storage.scriptsCache) ~= 'table' then
context.storage.scriptsCache = {}
end
local cache = context.storage.scriptsCache[url]
if cache and type(cache) == 'string' and cache:len() > 0 then
data = cache
else
return context.error("Can't load script from: " .. url .. ", error: " .. err)
end
end
local status, result = pcall(function()
assert(load(data, url, nil, context))()
end)
if not status then
return context.error("Error while loading script from: " .. url .. ":\n" .. result)
end
-- cache script
if type(context.storage.scriptsCache) ~= 'table' then
context.storage.scriptsCache = {}
end
context.storage.scriptsCache[url] = data
if onLoadCallback then
onLoadCallback()
end
end)
end
local context = G.botContext
context.loadScript = function(path, onLoadCallback)
if type(path) ~= 'string' then
return context.error("Invalid path for loadScript: " .. tostring(path))
end
if path:lower():find("http") == 1 then
return context.loadRemoteScript(path)
end
if not g_resources.fileExists(path) then
return context.error("File " .. path .. " doesn't exist")
end
local status, result = pcall(function()
assert(load(g_resources.readFileContents(path), path, nil, context))()
end)
if not status then
return context.error("Error while loading script from: " .. path .. ":\n" .. result)
end
if onLoadCallback then
onLoadCallback()
end
end
context.loadRemoteScript = function(url, onLoadCallback)
if type(url) ~= 'string' or url:lower():find("http") ~= 1 then
return context.error("Invalid url for loadRemoteScript: " .. tostring(url))
end
HTTP.get(url, function(data, err)
if err or data:len() == 0 then
-- try to load from cache
if type(context.storage.scriptsCache) ~= 'table' then
context.storage.scriptsCache = {}
end
local cache = context.storage.scriptsCache[url]
if cache and type(cache) == 'string' and cache:len() > 0 then
data = cache
else
return context.error("Can't load script from: " .. url .. ", error: " .. err)
end
end
local status, result = pcall(function()
assert(load(data, url, nil, context))()
end)
if not status then
return context.error("Error while loading script from: " .. url .. ":\n" .. result)
end
-- cache script
if type(context.storage.scriptsCache) ~= 'table' then
context.storage.scriptsCache = {}
end
context.storage.scriptsCache[url] = data
if onLoadCallback then
onLoadCallback()
end
end)
end

View File

@@ -1,91 +1,91 @@
local context = G.botContext
context.BotServer = {}
context.BotServer.url = "ws://bot.otclient.ovh:8000/"
context.BotServer.timeout = 3
context.BotServer.ping = 0
context.BotServer._callbacks = {}
context.BotServer._lastMessageId = 0
context.BotServer._wasConnected = true -- show first warning
context.BotServer.init = function(name, channel)
if not channel or not name or channel:len() < 1 or name:len() < 1 then
return context.error("Invalid params for BotServer.init")
end
if context.BotServer._websocket then
return context.error("BotServer is already initialized")
end
context.BotServer._websocket = HTTP.WebSocketJSON(context.BotServer.url, {
onMessage = function(message, socketId)
if not context._websockets[socketId] then
return g_http.cancel(socketId)
end
if not context.BotServer._websocket or context.BotServer._websocket.id ~= socketId then
return g_http.cancel(socketId)
end
context.BotServer._wasConnected = true
if message["type"] == "ping" then
context.BotServer.ping = message["ping"]
return context.BotServer._websocket.send({type="ping"})
end
if message["type"] == "message" then
context.BotServer._lastMessageId = message["id"]
local topics = context.BotServer._callbacks[message["topic"]]
if topics then
for i=1,#topics do
topics[i](message["name"], message["message"], message["topic"])
end
end
topics = context.BotServer._callbacks["*"]
if topics then
for i=1,#topics do
topics[i](message["name"], message["message"], message["topic"])
end
end
return
end
end,
onClose = function(message, socketId)
if not context._websockets[socketId] then
return
end
context._websockets[socketId] = nil
if not context.BotServer._websocket or context.BotServer._websocket.id ~= socketId then
return
end
if context.BotServer._wasConnected then
context.warn("BotServer disconnected")
end
context.BotServer._wasConnected = false
context.BotServer._websocket = nil
context.BotServer.ping = 0
context.BotServer.init(name, channel)
end
}, context.BotServer.timeout)
context._websockets[context.BotServer._websocket.id] = 1
context.BotServer._websocket.send({type="init", name=name, channel=channel, lastMessage=context.BotServer._lastMessageId})
end
context.BotServer.terminate = function()
if context.BotServer._websocket then
context.BotServer._websocket:close()
context.BotServer._websocket = nil
end
end
context.BotServer.listen = function(topic, callback) -- callback = function(name, message, topic) -- message is parsed json = table
if not context.BotServer._websocket then
return context.error("BotServer is not initialized")
end
if not context.BotServer._callbacks[topic] then
context.BotServer._callbacks[topic] = {}
end
table.insert(context.BotServer._callbacks[topic], callback)
end
context.BotServer.send = function(topic, message)
if not context.BotServer._websocket then
return context.error("BotServer is not initialized")
end
context.BotServer._websocket.send({type="message", topic=topic, message=message})
end
local context = G.botContext
context.BotServer = {}
context.BotServer.url = "ws://bot.otclient.ovh:8000/"
context.BotServer.timeout = 3
context.BotServer.ping = 0
context.BotServer._callbacks = {}
context.BotServer._lastMessageId = 0
context.BotServer._wasConnected = true -- show first warning
context.BotServer.init = function(name, channel)
if not channel or not name or channel:len() < 1 or name:len() < 1 then
return context.error("Invalid params for BotServer.init")
end
if context.BotServer._websocket then
return context.error("BotServer is already initialized")
end
context.BotServer._websocket = HTTP.WebSocketJSON(context.BotServer.url, {
onMessage = function(message, socketId)
if not context._websockets[socketId] then
return g_http.cancel(socketId)
end
if not context.BotServer._websocket or context.BotServer._websocket.id ~= socketId then
return g_http.cancel(socketId)
end
context.BotServer._wasConnected = true
if message["type"] == "ping" then
context.BotServer.ping = message["ping"]
return context.BotServer._websocket.send({type="ping"})
end
if message["type"] == "message" then
context.BotServer._lastMessageId = message["id"]
local topics = context.BotServer._callbacks[message["topic"]]
if topics then
for i=1,#topics do
topics[i](message["name"], message["message"], message["topic"])
end
end
topics = context.BotServer._callbacks["*"]
if topics then
for i=1,#topics do
topics[i](message["name"], message["message"], message["topic"])
end
end
return
end
end,
onClose = function(message, socketId)
if not context._websockets[socketId] then
return
end
context._websockets[socketId] = nil
if not context.BotServer._websocket or context.BotServer._websocket.id ~= socketId then
return
end
if context.BotServer._wasConnected then
context.warn("BotServer disconnected")
end
context.BotServer._wasConnected = false
context.BotServer._websocket = nil
context.BotServer.ping = 0
context.BotServer.init(name, channel)
end
}, context.BotServer.timeout)
context._websockets[context.BotServer._websocket.id] = 1
context.BotServer._websocket.send({type="init", name=name, channel=channel, lastMessage=context.BotServer._lastMessageId})
end
context.BotServer.terminate = function()
if context.BotServer._websocket then
context.BotServer._websocket:close()
context.BotServer._websocket = nil
end
end
context.BotServer.listen = function(topic, callback) -- callback = function(name, message, topic) -- message is parsed json = table
if not context.BotServer._websocket then
return context.error("BotServer is not initialized")
end
if not context.BotServer._callbacks[topic] then
context.BotServer._callbacks[topic] = {}
end
table.insert(context.BotServer._callbacks[topic], callback)
end
context.BotServer.send = function(topic, message)
if not context.BotServer._websocket then
return context.error("BotServer is not initialized")
end
context.BotServer._websocket.send({type="message", topic=topic, message=message})
end

View File

@@ -1,31 +1,31 @@
local context = G.botContext
context.getSoundChannel = function()
if not g_sounds then
return
end
return g_sounds.getChannel(SoundChannels.Bot)
end
context.playSound = function(file)
local botSoundChannel = context.getSoundChannel()
if not botSoundChannel then
return
end
botSoundChannel:setEnabled(true)
botSoundChannel:stop(0)
botSoundChannel:play(file, 0, 1.0)
return botSoundChannel
end
context.stopSound = function()
local botSoundChannel = context.getSoundChannel()
if not botSoundChannel then
return
end
botSoundChannel:stop()
end
context.playAlarm = function()
return context.playSound("/sounds/alarm.ogg")
end
local context = G.botContext
context.getSoundChannel = function()
if not g_sounds then
return
end
return g_sounds.getChannel(SoundChannels.Bot)
end
context.playSound = function(file)
local botSoundChannel = context.getSoundChannel()
if not botSoundChannel then
return
end
botSoundChannel:setEnabled(true)
botSoundChannel:stop(0)
botSoundChannel:play(file, 0, 1.0)
return botSoundChannel
end
context.stopSound = function()
local botSoundChannel = context.getSoundChannel()
if not botSoundChannel then
return
end
botSoundChannel:stop()
end
context.playAlarm = function()
return context.playSound("/sounds/alarm.ogg")
end

View File

@@ -1,3 +1,3 @@
local context = G.botContext
local context = G.botContext
context.test = function() return context.info("test") end

View File

@@ -1,19 +1,19 @@
local context = G.botContext
context.encode = function(data, indent) return json.encode(data, indent or 2) end
context.decode = function(text) local status, result = pcall(function() return json.decode(text) end) if status then return result end return {} end
context.displayGeneralBox = function(title, message, buttons, onEnterCallback, onEscapeCallback)
local box = displayGeneralBox(title, message, buttons, onEnterCallback, onEscapeCallback)
box.botWidget = true
return box
end
context.doScreenshot = function(filename)
g_app.doScreenshot(filename)
end
context.screenshot = context.doScreenshot
context.getVersion = function()
return g_app.getVersion()
local context = G.botContext
context.encode = function(data, indent) return json.encode(data, indent or 2) end
context.decode = function(text) local status, result = pcall(function() return json.decode(text) end) if status then return result end return {} end
context.displayGeneralBox = function(title, message, buttons, onEnterCallback, onEscapeCallback)
local box = displayGeneralBox(title, message, buttons, onEnterCallback, onEscapeCallback)
box.botWidget = true
return box
end
context.doScreenshot = function(filename)
g_app.doScreenshot(filename)
end
context.screenshot = context.doScreenshot
context.getVersion = function()
return g_app.getVersion()
end

View File

@@ -1,24 +1,24 @@
local context = G.botContext
if type(context.UI) ~= "table" then
context.UI = {}
end
local UI = context.UI
UI.createWidget = function(name, parent)
if parent == nil then
parent = context.panel
end
local widget = g_ui.createWidget(name, parent)
widget.botWidget = true
return widget
end
UI.createWindow = function(name)
local widget = g_ui.createWidget(name, g_ui.getRootWidget())
widget.botWidget = true
widget:show()
widget:raise()
widget:focus()
return widget
end
local context = G.botContext
if type(context.UI) ~= "table" then
context.UI = {}
end
local UI = context.UI
UI.createWidget = function(name, parent)
if parent == nil then
parent = context.panel
end
local widget = g_ui.createWidget(name, parent)
widget.botWidget = true
return widget
end
UI.createWindow = function(name)
local widget = g_ui.createWidget(name, g_ui.getRootWidget())
widget.botWidget = true
widget:show()
widget:raise()
widget:focus()
return widget
end

View File

@@ -1,273 +1,273 @@
local context = G.botContext
if type(context.UI) ~= "table" then
context.UI = {}
end
local UI = context.UI
UI.Button = function(text, callback, parent)
local widget = UI.createWidget("BotButton", parent)
widget:setText(text)
widget.onClick = callback
return widget
end
UI.Config = function(parent)
return UI.createWidget("BotConfig", parent)
end
-- call :setItems(table) to set items, call :getItems() to get them
-- unique if true, won't allow duplicates
-- callback (can be nil) gets table with new item list, eg: {{id=2160, count=1}, {id=268, count=100}, {id=269, count=20}}
UI.Container = function(callback, unique, parent, widget)
if not widget then
widget = UI.createWidget("BotContainer", parent)
end
local oldItems = {}
local updateItems = function()
local items = widget:getItems()
-- callback part
local somethingNew = (#items ~= #oldItems)
for i, item in ipairs(items) do
if type(oldItems[i]) ~= "table" then
somethingNew = true
break
end
if oldItems[i].id ~= item.id or oldItems[i].count ~= item.count then
somethingNew = true
break
end
end
if somethingNew then
oldItems = items
callback(widget, items)
end
widget:setItems(items)
end
widget.setItems = function(self, items)
if type(self) == 'table' then
items = self
end
local itemsToShow = math.max(10, #items + 2)
if itemsToShow % 5 ~= 0 then
itemsToShow = itemsToShow + 5 - itemsToShow % 5
end
widget.items:destroyChildren()
for i = 1, itemsToShow do
local widget = g_ui.createWidget("BotItem", widget.items)
if type(items[i]) == 'number' then
items[i] = {id=items[i], count=1}
end
if type(items[i]) == 'table' then
widget:setItem(Item.create(items[i].id, items[i].count))
end
end
oldItems = items
for i, child in ipairs(widget.items:getChildren()) do
child.onItemChange = updateItems
end
end
widget.getItems = function()
local items = {}
local duplicates = {}
for i, child in ipairs(widget.items:getChildren()) do
if child:getItemId() >= 100 then
if not duplicates[child:getItemId()] or not unique then
table.insert(items, {id=child:getItemId(), count=child:getItemCountOrSubType()})
duplicates[child:getItemId()] = true
end
end
end
return items
end
widget:setItems({})
return widget
end
UI.DualScrollPanel = function(params, callback, parent) -- callback = function(widget, newParams)
--[[ params:
on - bool,
text - string,
title - string,
min - number,
max - number,
]]
params.title = params.title or "title"
params.text = params.text or ""
params.min = params.min or 20
params.max = params.max or 80
local widget = UI.createWidget('DualScrollPanel', parent)
widget.title:setOn(params.on)
widget.title.onClick = function()
params.on = not params.on
widget.title:setOn(params.on)
if callback then
callback(widget, params)
end
end
widget.text:setText(params.text or "")
widget.text.onTextChange = function(widget, text)
params.text = text
if callback then
callback(widget, params)
end
end
local update = function(dontSignal)
widget.title:setText("" .. params.min .. "% <= " .. params.title .. " <= " .. params.max .. "%")
if callback and not dontSignal then
callback(widget, params)
end
end
widget.scroll1:setValue(params.min)
widget.scroll2:setValue(params.max)
widget.scroll1.onValueChange = function(scroll, value)
params.min = value
update()
end
widget.scroll2.onValueChange = function(scroll, value)
params.max = value
update()
end
update(true)
end
UI.DualScrollItemPanel = function(params, callback, parent) -- callback = function(widget, newParams)
--[[ params:
on - bool,
item - number,
subType - number,
title - string,
min - number,
max - number,
]]
params.title = params.title or "title"
params.item = params.item or 0
params.subType = params.subType or 0
params.min = params.min or 20
params.max = params.max or 80
local widget = UI.createWidget('DualScrollItemPanel', parent)
widget.title:setOn(params.on)
widget.title.onClick = function()
params.on = not params.on
widget.title:setOn(params.on)
if callback then
callback(widget, params)
end
end
widget.item:setItem(Item.create(params.item, params.subType))
widget.item.onItemChange = function()
params.item = widget.item:getItemId()
params.subType = widget.item:getItemSubType()
if callback then
callback(widget, params)
end
end
local update = function(dontSignal)
widget.title:setText("" .. params.min .. "% <= " .. params.title .. " <= " .. params.max .. "%")
if callback and not dontSignal then
callback(widget, params)
end
end
widget.scroll1:setValue(params.min)
widget.scroll2:setValue(params.max)
widget.scroll1.onValueChange = function(scroll, value)
params.min = value
update()
end
widget.scroll2.onValueChange = function(scroll, value)
params.max = value
update()
end
update(true)
end
UI.Label = function(text, parent)
local label = UI.createWidget('BotLabel', parent)
label:setText(text)
return label
end
UI.Separator = function(parent)
local separator = UI.createWidget('BotSeparator', parent)
return separator
end
UI.TextEdit = function(text, callback, parent)
local widget = UI.createWidget('BotTextEdit', parent)
widget.onTextChange = callback
widget:setText(text)
return widget
end
UI.TwoItemsAndSlotPanel = function(params, callback, parent)
--[[ params:
on - bool,
title - string,
item1 - number,
item2 - number,
slot - number,
]]
params.title = params.title or "title"
params.item1 = params.item1 or 0
params.item2 = params.item2 or 0
params.slot = params.slot or 1
local widget = UI.createWidget("TwoItemsAndSlotPanel", parent)
widget.title:setText(params.title)
widget.title:setOn(params.on)
widget.title.onClick = function()
params.on = not params.on
widget.title:setOn(params.on)
if callback then
callback(widget, params)
end
end
widget.slot:setCurrentIndex(params.slot)
widget.slot.onOptionChange = function()
params.slot = widget.slot.currentIndex
if callback then
callback(widget, params)
end
end
widget.item1:setItemId(params.item1)
widget.item1.onItemChange = function()
params.item1 = widget.item1:getItemId()
if callback then
callback(widget, params)
end
end
widget.item2:setItemId(params.item2)
widget.item2.onItemChange = function()
params.item2 = widget.item2:getItemId()
if callback then
callback(widget, params)
end
end
return widget
end
local context = G.botContext
if type(context.UI) ~= "table" then
context.UI = {}
end
local UI = context.UI
UI.Button = function(text, callback, parent)
local widget = UI.createWidget("BotButton", parent)
widget:setText(text)
widget.onClick = callback
return widget
end
UI.Config = function(parent)
return UI.createWidget("BotConfig", parent)
end
-- call :setItems(table) to set items, call :getItems() to get them
-- unique if true, won't allow duplicates
-- callback (can be nil) gets table with new item list, eg: {{id=2160, count=1}, {id=268, count=100}, {id=269, count=20}}
UI.Container = function(callback, unique, parent, widget)
if not widget then
widget = UI.createWidget("BotContainer", parent)
end
local oldItems = {}
local updateItems = function()
local items = widget:getItems()
-- callback part
local somethingNew = (#items ~= #oldItems)
for i, item in ipairs(items) do
if type(oldItems[i]) ~= "table" then
somethingNew = true
break
end
if oldItems[i].id ~= item.id or oldItems[i].count ~= item.count then
somethingNew = true
break
end
end
if somethingNew then
oldItems = items
callback(widget, items)
end
widget:setItems(items)
end
widget.setItems = function(self, items)
if type(self) == 'table' then
items = self
end
local itemsToShow = math.max(10, #items + 2)
if itemsToShow % 5 ~= 0 then
itemsToShow = itemsToShow + 5 - itemsToShow % 5
end
widget.items:destroyChildren()
for i = 1, itemsToShow do
local widget = g_ui.createWidget("BotItem", widget.items)
if type(items[i]) == 'number' then
items[i] = {id=items[i], count=1}
end
if type(items[i]) == 'table' then
widget:setItem(Item.create(items[i].id, items[i].count))
end
end
oldItems = items
for i, child in ipairs(widget.items:getChildren()) do
child.onItemChange = updateItems
end
end
widget.getItems = function()
local items = {}
local duplicates = {}
for i, child in ipairs(widget.items:getChildren()) do
if child:getItemId() >= 100 then
if not duplicates[child:getItemId()] or not unique then
table.insert(items, {id=child:getItemId(), count=child:getItemCountOrSubType()})
duplicates[child:getItemId()] = true
end
end
end
return items
end
widget:setItems({})
return widget
end
UI.DualScrollPanel = function(params, callback, parent) -- callback = function(widget, newParams)
--[[ params:
on - bool,
text - string,
title - string,
min - number,
max - number,
]]
params.title = params.title or "title"
params.text = params.text or ""
params.min = params.min or 20
params.max = params.max or 80
local widget = UI.createWidget('DualScrollPanel', parent)
widget.title:setOn(params.on)
widget.title.onClick = function()
params.on = not params.on
widget.title:setOn(params.on)
if callback then
callback(widget, params)
end
end
widget.text:setText(params.text or "")
widget.text.onTextChange = function(widget, text)
params.text = text
if callback then
callback(widget, params)
end
end
local update = function(dontSignal)
widget.title:setText("" .. params.min .. "% <= " .. params.title .. " <= " .. params.max .. "%")
if callback and not dontSignal then
callback(widget, params)
end
end
widget.scroll1:setValue(params.min)
widget.scroll2:setValue(params.max)
widget.scroll1.onValueChange = function(scroll, value)
params.min = value
update()
end
widget.scroll2.onValueChange = function(scroll, value)
params.max = value
update()
end
update(true)
end
UI.DualScrollItemPanel = function(params, callback, parent) -- callback = function(widget, newParams)
--[[ params:
on - bool,
item - number,
subType - number,
title - string,
min - number,
max - number,
]]
params.title = params.title or "title"
params.item = params.item or 0
params.subType = params.subType or 0
params.min = params.min or 20
params.max = params.max or 80
local widget = UI.createWidget('DualScrollItemPanel', parent)
widget.title:setOn(params.on)
widget.title.onClick = function()
params.on = not params.on
widget.title:setOn(params.on)
if callback then
callback(widget, params)
end
end
widget.item:setItem(Item.create(params.item, params.subType))
widget.item.onItemChange = function()
params.item = widget.item:getItemId()
params.subType = widget.item:getItemSubType()
if callback then
callback(widget, params)
end
end
local update = function(dontSignal)
widget.title:setText("" .. params.min .. "% <= " .. params.title .. " <= " .. params.max .. "%")
if callback and not dontSignal then
callback(widget, params)
end
end
widget.scroll1:setValue(params.min)
widget.scroll2:setValue(params.max)
widget.scroll1.onValueChange = function(scroll, value)
params.min = value
update()
end
widget.scroll2.onValueChange = function(scroll, value)
params.max = value
update()
end
update(true)
end
UI.Label = function(text, parent)
local label = UI.createWidget('BotLabel', parent)
label:setText(text)
return label
end
UI.Separator = function(parent)
local separator = UI.createWidget('BotSeparator', parent)
return separator
end
UI.TextEdit = function(text, callback, parent)
local widget = UI.createWidget('BotTextEdit', parent)
widget.onTextChange = callback
widget:setText(text)
return widget
end
UI.TwoItemsAndSlotPanel = function(params, callback, parent)
--[[ params:
on - bool,
title - string,
item1 - number,
item2 - number,
slot - number,
]]
params.title = params.title or "title"
params.item1 = params.item1 or 0
params.item2 = params.item2 or 0
params.slot = params.slot or 1
local widget = UI.createWidget("TwoItemsAndSlotPanel", parent)
widget.title:setText(params.title)
widget.title:setOn(params.on)
widget.title.onClick = function()
params.on = not params.on
widget.title:setOn(params.on)
if callback then
callback(widget, params)
end
end
widget.slot:setCurrentIndex(params.slot)
widget.slot.onOptionChange = function()
params.slot = widget.slot.currentIndex
if callback then
callback(widget, params)
end
end
widget.item1:setItemId(params.item1)
widget.item1.onItemChange = function()
params.item1 = widget.item1:getItemId()
if callback then
callback(widget, params)
end
end
widget.item2:setItemId(params.item2)
widget.item2.onItemChange = function()
params.item2 = widget.item2:getItemId()
if callback then
callback(widget, params)
end
end
return widget
end

View File

@@ -1,135 +1,135 @@
local context = G.botContext
-- DO NOT USE THIS CODE.
-- IT'S ONLY HERE FOR BACKWARD COMPATIBILITY, MAY BE REMOVED IN THE FUTURE
context.createWidget = function(name, parent)
if parent == nil then
parent = context.panel
end
g_ui.createWidget(name, parent)
end
context.setupUI = function(otml, parent)
if parent == nil then
parent = context.panel
end
local widget = g_ui.loadUIFromString(otml, parent)
widget.botWidget = true
return widget
end
context.importStyle = function(otml)
if type(otml) ~= "string" then
return error("Invalid parameter for importStyle, should be string")
end
if otml:find(".otui") and not otml:find("\n") then
return g_ui.importStyle(context.configDir .. "/" .. otml)
end
return g_ui.importStyleFromString(otml)
end
context.addTab = function(name)
local tab = context.tabs:getTab(name)
if tab then -- return existing tab
return tab.tabPanel.content
end
local smallTabs = #(context.tabs.tabs) >= 5
local newTab = context.tabs:addTab(name, g_ui.createWidget('BotPanel')).tabPanel.content
context.tabs:setOn(true)
if smallTabs then
for k,tab in pairs(context.tabs.tabs) do
tab:setFont('small-9px')
end
end
return newTab
end
context.getTab = context.addTab
context.setDefaultTab = function(name)
local tab = context.addTab(name)
context.panel = tab
end
context.addSwitch = function(id, text, onClickCallback, parent)
if not parent then
parent = context.panel
end
local switch = g_ui.createWidget('BotSwitch', parent)
switch:setId(id)
switch:setText(text)
switch.onClick = onClickCallback
return switch
end
context.addButton = function(id, text, onClickCallback, parent)
if not parent then
parent = context.panel
end
local button = g_ui.createWidget('BotButton', parent)
button:setId(id)
button:setText(text)
button.onClick = onClickCallback
return button
end
context.addLabel = function(id, text, parent)
if not parent then
parent = context.panel
end
local label = g_ui.createWidget('BotLabel', parent)
label:setId(id)
label:setText(text)
return label
end
context.addTextEdit = function(id, text, onTextChangeCallback, parent)
if not parent then
parent = context.panel
end
local widget = g_ui.createWidget('BotTextEdit', parent)
widget:setId(id)
widget.onTextChange = onTextChangeCallback
widget:setText(text)
return widget
end
context.addSeparator = function(id, parent)
if not parent then
parent = context.panel
end
local separator = g_ui.createWidget('BotSeparator', parent)
separator:setId(id)
return separator
end
context._addMacroSwitch = function(name, keys, parent)
if not parent then
parent = context.panel
end
local text = name
if keys:len() > 0 then
text = name .. " [" .. keys .. "]"
end
local switch = context.addSwitch("macro_" .. #context._macros, text, function(widget)
context.storage._macros[name] = not context.storage._macros[name]
widget:setOn(context.storage._macros[name])
end, parent)
switch:setOn(context.storage._macros[name])
return switch
end
context._addHotkeySwitch = function(name, keys, parent)
if not parent then
parent = context.panel
end
local text = name
if keys:len() > 0 then
text = name .. " [" .. keys .. "]"
end
local switch = context.addSwitch("hotkey_" .. #context._hotkeys, text, nil, parent)
switch:setOn(false)
return switch
local context = G.botContext
-- DO NOT USE THIS CODE.
-- IT'S ONLY HERE FOR BACKWARD COMPATIBILITY, MAY BE REMOVED IN THE FUTURE
context.createWidget = function(name, parent)
if parent == nil then
parent = context.panel
end
g_ui.createWidget(name, parent)
end
context.setupUI = function(otml, parent)
if parent == nil then
parent = context.panel
end
local widget = g_ui.loadUIFromString(otml, parent)
widget.botWidget = true
return widget
end
context.importStyle = function(otml)
if type(otml) ~= "string" then
return error("Invalid parameter for importStyle, should be string")
end
if otml:find(".otui") and not otml:find("\n") then
return g_ui.importStyle(context.configDir .. "/" .. otml)
end
return g_ui.importStyleFromString(otml)
end
context.addTab = function(name)
local tab = context.tabs:getTab(name)
if tab then -- return existing tab
return tab.tabPanel.content
end
local smallTabs = #(context.tabs.tabs) >= 5
local newTab = context.tabs:addTab(name, g_ui.createWidget('BotPanel')).tabPanel.content
context.tabs:setOn(true)
if smallTabs then
for k,tab in pairs(context.tabs.tabs) do
tab:setFont('small-9px')
end
end
return newTab
end
context.getTab = context.addTab
context.setDefaultTab = function(name)
local tab = context.addTab(name)
context.panel = tab
end
context.addSwitch = function(id, text, onClickCallback, parent)
if not parent then
parent = context.panel
end
local switch = g_ui.createWidget('BotSwitch', parent)
switch:setId(id)
switch:setText(text)
switch.onClick = onClickCallback
return switch
end
context.addButton = function(id, text, onClickCallback, parent)
if not parent then
parent = context.panel
end
local button = g_ui.createWidget('BotButton', parent)
button:setId(id)
button:setText(text)
button.onClick = onClickCallback
return button
end
context.addLabel = function(id, text, parent)
if not parent then
parent = context.panel
end
local label = g_ui.createWidget('BotLabel', parent)
label:setId(id)
label:setText(text)
return label
end
context.addTextEdit = function(id, text, onTextChangeCallback, parent)
if not parent then
parent = context.panel
end
local widget = g_ui.createWidget('BotTextEdit', parent)
widget:setId(id)
widget.onTextChange = onTextChangeCallback
widget:setText(text)
return widget
end
context.addSeparator = function(id, parent)
if not parent then
parent = context.panel
end
local separator = g_ui.createWidget('BotSeparator', parent)
separator:setId(id)
return separator
end
context._addMacroSwitch = function(name, keys, parent)
if not parent then
parent = context.panel
end
local text = name
if keys:len() > 0 then
text = name .. " [" .. keys .. "]"
end
local switch = context.addSwitch("macro_" .. #context._macros, text, function(widget)
context.storage._macros[name] = not context.storage._macros[name]
widget:setOn(context.storage._macros[name])
end, parent)
switch:setOn(context.storage._macros[name])
return switch
end
context._addHotkeySwitch = function(name, keys, parent)
if not parent then
parent = context.panel
end
local text = name
if keys:len() > 0 then
text = name .. " [" .. keys .. "]"
end
local switch = context.addSwitch("hotkey_" .. #context._hotkeys, text, nil, parent)
switch:setOn(false)
return switch
end

View File

@@ -1,49 +1,49 @@
local context = G.botContext
if type(context.UI) ~= "table" then
context.UI = {}
end
local UI = context.UI
UI.EditorWindow = function(text, options, callback)
--[[
Available options:
title = text
description = text
multiline = true / false
width = number
validation = text (regex)
examples = {{name, text}, {name, text}}
]]--
local window = modules.client_textedit.edit(text, options, callback)
window.botWidget = true
return window
end
UI.SinglelineEditorWindow = function(text, options, callback)
options = options or {}
options.multiline = false
return UI.EditorWindow(text, options, callback)
end
UI.MultilineEditorWindow = function(text, options, callback)
options = options or {}
options.multiline = true
return UI.EditorWindow(text, options, callback)
end
UI.ConfirmationWindow = function(title, question, callback)
local window = nil
local onConfirm = function()
window:destroy()
callback()
end
local closeWindow = function()
window:destroy()
end
window = context.displayGeneralBox(title, question, {
{ text=tr('Yes'), callback=onConfirm },
{ text=tr('No'), callback=closeWindow },
anchor=AnchorHorizontalCenter}, onConfirm, closeWindow)
window.botWidget = true
return window
local context = G.botContext
if type(context.UI) ~= "table" then
context.UI = {}
end
local UI = context.UI
UI.EditorWindow = function(text, options, callback)
--[[
Available options:
title = text
description = text
multiline = true / false
width = number
validation = text (regex)
examples = {{name, text}, {name, text}}
]]--
local window = modules.client_textedit.edit(text, options, callback)
window.botWidget = true
return window
end
UI.SinglelineEditorWindow = function(text, options, callback)
options = options or {}
options.multiline = false
return UI.EditorWindow(text, options, callback)
end
UI.MultilineEditorWindow = function(text, options, callback)
options = options or {}
options.multiline = true
return UI.EditorWindow(text, options, callback)
end
UI.ConfirmationWindow = function(title, question, callback)
local window = nil
local onConfirm = function()
window:destroy()
callback()
end
local closeWindow = function()
window:destroy()
end
window = context.displayGeneralBox(title, question, {
{ text=tr('Yes'), callback=onConfirm },
{ text=tr('No'), callback=closeWindow },
anchor=AnchorHorizontalCenter}, onConfirm, closeWindow)
window.botWidget = true
return window
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,57 +1,57 @@
local context = G.botContext
local Panels = context.Panels
Panels.Turning = function(parent)
context.macro(1000, "Turning / AntiIdle", nil, function()
context.turn(math.random(1, 4))
end, parent)
end
Panels.AntiIdle = Panels.Turning
Panels.AttackSpell = function(parent)
context.macro(500, "Auto attack spell", nil, function()
local target = g_game.getAttackingCreature()
if target and context.getCreatureById(target:getId()) and context.storage.autoAttackText:len() > 0 then
if context.saySpell(context.storage.autoAttackText, 1000) then
context.delay(1000)
end
end
end, parent)
context.addTextEdit("autoAttackText", context.storage.autoAttackText or "exori vis", function(widget, text)
context.storage.autoAttackText = text
end, parent)
end
Panels.AttackItem = function(parent)
if not parent then
parent = context.panel
end
local panelName = "attackItem"
local ui = g_ui.createWidget("ItemAndButtonPanel", parent)
ui:setId(panelName)
ui.title:setText("Auto attack item")
if not context.storage.attackItem then
context.storage.attackItem = {}
end
ui.title:setOn(context.storage.attackItem.enabled)
ui.title.onClick = function(widget)
context.storage.attackItem.enabled = not context.storage.attackItem.enabled
widget:setOn(context.storage.attackItem.enabled)
end
ui.item.onItemChange = function(widget)
context.storage.attackItem.item = widget:getItemId()
end
ui.item:setItemId(context.storage.attackItem.item or 3155)
context.macro(500, function()
local target = g_game.getAttackingCreature()
if context.storage.attackItem.enabled and target and context.getCreatureById(target:getId()) and context.storage.attackItem.item and context.storage.attackItem.item >= 100 then
context.useWith(context.storage.attackItem.item, target)
end
end)
end
local context = G.botContext
local Panels = context.Panels
Panels.Turning = function(parent)
context.macro(1000, "Turning / AntiIdle", nil, function()
context.turn(math.random(1, 4))
end, parent)
end
Panels.AntiIdle = Panels.Turning
Panels.AttackSpell = function(parent)
context.macro(500, "Auto attack spell", nil, function()
local target = g_game.getAttackingCreature()
if target and context.getCreatureById(target:getId()) and context.storage.autoAttackText:len() > 0 then
if context.saySpell(context.storage.autoAttackText, 1000) then
context.delay(1000)
end
end
end, parent)
context.addTextEdit("autoAttackText", context.storage.autoAttackText or "exori vis", function(widget, text)
context.storage.autoAttackText = text
end, parent)
end
Panels.AttackItem = function(parent)
if not parent then
parent = context.panel
end
local panelName = "attackItem"
local ui = g_ui.createWidget("ItemAndButtonPanel", parent)
ui:setId(panelName)
ui.title:setText("Auto attack item")
if not context.storage.attackItem then
context.storage.attackItem = {}
end
ui.title:setOn(context.storage.attackItem.enabled)
ui.title.onClick = function(widget)
context.storage.attackItem.enabled = not context.storage.attackItem.enabled
widget:setOn(context.storage.attackItem.enabled)
end
ui.item.onItemChange = function(widget)
context.storage.attackItem.item = widget:getItemId()
end
ui.item:setItemId(context.storage.attackItem.item or 3155)
context.macro(500, function()
local target = g_game.getAttackingCreature()
if context.storage.attackItem.enabled and target and context.getCreatureById(target:getId()) and context.storage.attackItem.item and context.storage.attackItem.item >= 100 then
context.useWith(context.storage.attackItem.item, target)
end
end)
end

View File

@@ -1,346 +1,346 @@
local context = G.botContext
local Panels = context.Panels
Panels.Haste = function(parent)
context.macro(500, "Auto Haste", nil, function()
if not context.hasHaste() and context.storage.autoHasteText:len() > 0 then
if context.saySpell(context.storage.autoHasteText, 2500) then
context.delay(5000)
end
end
end, parent)
context.addTextEdit("autoHasteText", context.storage.autoHasteText or "utani hur", function(widget, text)
context.storage.autoHasteText = text
end, parent)
end
Panels.ManaShield = function(parent)
local lastManaShield = 0
context.macro(100, "Auto Mana Shield", nil, function()
if not context.hasManaShield() or context.now > lastManaShield + 90000 then
if context.saySpell("utamo vita", 200) then
lastManaShield = context.now
end
end
end, parent)
end
Panels.AntiParalyze = function(parent)
context.macro(100, "Anti Paralyze", nil, function()
if context.isParalyzed() and context.storage.autoAntiParalyzeText:len() > 0 then
context.saySpell(context.storage.autoAntiParalyzeText, 750)
end
end, parent)
context.addTextEdit("autoAntiParalyzeText", context.storage.autoAntiParalyzeText or "utani hur", function(widget, text)
context.storage.autoAntiParalyzeText = text
end, parent)
end
Panels.Health = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoHealthPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = g_ui.createWidget("DualScrollPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {
item = 266,
min = 20,
max = 80,
text = "exura"
}
end
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
ui.text.onTextChange = function(widget, text)
context.storage[panelName].text = text
end
ui.text:setText(context.storage[panelName].text or "exura")
local updateText = function()
ui.title:setText("" .. context.storage[panelName].min .. "% <= hp <= " .. context.storage[panelName].max .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage[panelName].min = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage[panelName].max = value
updateText()
end
ui.scroll1:setValue(context.storage[panelName].min)
ui.scroll2:setValue(context.storage[panelName].max)
context.macro(25, function()
if context.storage[panelName].enabled and context.storage[panelName].text:len() > 0 and context.storage[panelName].min <= context.hppercent() and context.hppercent() <= context.storage[panelName].max then
if context.saySpell(context.storage[panelName].text, 500) then
context.delay(200)
end
end
end)
end
Panels.HealthItem = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoHealthItemPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = g_ui.createWidget("DualScrollItemPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {
item = 266,
min = 0,
max = 60
}
end
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
ui.item.onItemChange = function(widget)
context.storage[panelName].item = widget:getItemId()
end
ui.item:setItemId(context.storage[panelName].item)
local updateText = function()
ui.title:setText("" .. (context.storage[panelName].min or "") .. "% <= hp <= " .. (context.storage[panelName].max or "") .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage[panelName].min = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage[panelName].max = value
updateText()
end
ui.scroll1:setValue(context.storage[panelName].min)
ui.scroll2:setValue(context.storage[panelName].max)
context.macro(25, function()
if context.storage[panelName].enabled and context.storage[panelName].item >= 100 and context.storage[panelName].min <= context.hppercent() and context.hppercent() <= context.storage[panelName].max then
if context.useRune(context.storage[panelName].item, context.player, 500) then
context.delay(300)
end
end
end)
end
Panels.Mana = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoManaItemPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = g_ui.createWidget("DualScrollItemPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {
item = 268,
min = 0,
max = 60
}
end
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
ui.item.onItemChange = function(widget)
context.storage[panelName].item = widget:getItemId()
end
ui.item:setItemId(context.storage[panelName].item)
local updateText = function()
ui.title:setText("" .. (context.storage[panelName].min or "") .. "% <= mana <= " .. (context.storage[panelName].max or "") .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage[panelName].min = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage[panelName].max = value
updateText()
end
ui.scroll1:setValue(context.storage[panelName].min)
ui.scroll2:setValue(context.storage[panelName].max)
context.macro(25, function()
if context.storage[panelName].enabled and context.storage[panelName].item >= 100 and context.storage[panelName].min <= context.manapercent() and context.manapercent() <= context.storage[panelName].max then
if context.useRune(context.storage[panelName].item, context.player, 500) then
context.delay(300)
end
end
end)
end
Panels.ManaItem = Panels.Mana
Panels.Equip = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoEquipItem"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = g_ui.createWidget("TwoItemsAndSlotPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {}
if panelId == 1 then
context.storage[panelName].item1 = 3052
context.storage[panelName].item2 = 3089
context.storage[panelName].slot = 9
end
end
ui.title:setText("Auto equip")
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
ui.item1:setItemId(context.storage[panelName].item1 or 0)
ui.item1.onItemChange = function(widget)
context.storage[panelName].item1 = widget:getItemId()
end
ui.item2:setItemId(context.storage[panelName].item2 or 0)
ui.item2.onItemChange = function(widget)
context.storage[panelName].item2 = widget:getItemId()
end
if not context.storage[panelName].slot then
context.storage[panelName].slot = 1
end
ui.slot:setCurrentIndex(context.storage[panelName].slot)
ui.slot.onOptionChange = function(widget)
context.storage[panelName].slot = widget.currentIndex
end
context.macro(250, function()
if context.storage[panelName].enabled and context.storage[panelName].slot > 0 then
local item1 = context.storage[panelName].item1 or 0
local item2 = context.storage[panelName].item2 or 0
if item1 < 100 and item2 < 100 then
return
end
local slotItem = context.getSlot(context.storage[panelName].slot)
if slotItem and (slotItem:getId() == item1 or slotItem:getId() == item2) then
return
end
local newItem = context.findItem(context.storage[panelName].item1)
if not newItem then
newItem = context.findItem(context.storage[panelName].item2)
if not newItem then
return
end
end
g_game.move(newItem, {x=65535, y=context.storage[panelName].slot, z=0})
context.delay(1000)
end
end)
end
Panels.AutoEquip = Panels.Equip
Panels.Eating = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoEatingPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = g_ui.createWidget("ItemsPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {}
end
ui.title:setText("Auto eating")
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
if type(context.storage[panelName].items) ~= 'table' then
context.storage[panelName].items = {3725, 0, 0, 0, 0}
end
for i=1,5 do
ui.items:getChildByIndex(i).onItemChange = function(widget)
context.storage[panelName].items[i] = widget:getItemId()
end
ui.items:getChildByIndex(i):setItemId(context.storage[panelName].items[i])
end
context.macro(15000, function()
if not context.storage[panelName].enabled then
return
end
local candidates = {}
for i, item in pairs(context.storage[panelName].items) do
if item >= 100 then
table.insert(candidates, item)
end
end
if #candidates == 0 then
return
end
context.use(candidates[math.random(1, #candidates)])
end)
end
local context = G.botContext
local Panels = context.Panels
Panels.Haste = function(parent)
context.macro(500, "Auto Haste", nil, function()
if not context.hasHaste() and context.storage.autoHasteText:len() > 0 then
if context.saySpell(context.storage.autoHasteText, 2500) then
context.delay(5000)
end
end
end, parent)
context.addTextEdit("autoHasteText", context.storage.autoHasteText or "utani hur", function(widget, text)
context.storage.autoHasteText = text
end, parent)
end
Panels.ManaShield = function(parent)
local lastManaShield = 0
context.macro(100, "Auto Mana Shield", nil, function()
if not context.hasManaShield() or context.now > lastManaShield + 90000 then
if context.saySpell("utamo vita", 200) then
lastManaShield = context.now
end
end
end, parent)
end
Panels.AntiParalyze = function(parent)
context.macro(100, "Anti Paralyze", nil, function()
if context.isParalyzed() and context.storage.autoAntiParalyzeText:len() > 0 then
context.saySpell(context.storage.autoAntiParalyzeText, 750)
end
end, parent)
context.addTextEdit("autoAntiParalyzeText", context.storage.autoAntiParalyzeText or "utani hur", function(widget, text)
context.storage.autoAntiParalyzeText = text
end, parent)
end
Panels.Health = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoHealthPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = g_ui.createWidget("DualScrollPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {
item = 266,
min = 20,
max = 80,
text = "exura"
}
end
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
ui.text.onTextChange = function(widget, text)
context.storage[panelName].text = text
end
ui.text:setText(context.storage[panelName].text or "exura")
local updateText = function()
ui.title:setText("" .. context.storage[panelName].min .. "% <= hp <= " .. context.storage[panelName].max .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage[panelName].min = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage[panelName].max = value
updateText()
end
ui.scroll1:setValue(context.storage[panelName].min)
ui.scroll2:setValue(context.storage[panelName].max)
context.macro(25, function()
if context.storage[panelName].enabled and context.storage[panelName].text:len() > 0 and context.storage[panelName].min <= context.hppercent() and context.hppercent() <= context.storage[panelName].max then
if context.saySpell(context.storage[panelName].text, 500) then
context.delay(200)
end
end
end)
end
Panels.HealthItem = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoHealthItemPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = g_ui.createWidget("DualScrollItemPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {
item = 266,
min = 0,
max = 60
}
end
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
ui.item.onItemChange = function(widget)
context.storage[panelName].item = widget:getItemId()
end
ui.item:setItemId(context.storage[panelName].item)
local updateText = function()
ui.title:setText("" .. (context.storage[panelName].min or "") .. "% <= hp <= " .. (context.storage[panelName].max or "") .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage[panelName].min = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage[panelName].max = value
updateText()
end
ui.scroll1:setValue(context.storage[panelName].min)
ui.scroll2:setValue(context.storage[panelName].max)
context.macro(25, function()
if context.storage[panelName].enabled and context.storage[panelName].item >= 100 and context.storage[panelName].min <= context.hppercent() and context.hppercent() <= context.storage[panelName].max then
if context.useRune(context.storage[panelName].item, context.player, 500) then
context.delay(300)
end
end
end)
end
Panels.Mana = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoManaItemPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = g_ui.createWidget("DualScrollItemPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {
item = 268,
min = 0,
max = 60
}
end
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
ui.item.onItemChange = function(widget)
context.storage[panelName].item = widget:getItemId()
end
ui.item:setItemId(context.storage[panelName].item)
local updateText = function()
ui.title:setText("" .. (context.storage[panelName].min or "") .. "% <= mana <= " .. (context.storage[panelName].max or "") .. "%")
end
ui.scroll1.onValueChange = function(scroll, value)
context.storage[panelName].min = value
updateText()
end
ui.scroll2.onValueChange = function(scroll, value)
context.storage[panelName].max = value
updateText()
end
ui.scroll1:setValue(context.storage[panelName].min)
ui.scroll2:setValue(context.storage[panelName].max)
context.macro(25, function()
if context.storage[panelName].enabled and context.storage[panelName].item >= 100 and context.storage[panelName].min <= context.manapercent() and context.manapercent() <= context.storage[panelName].max then
if context.useRune(context.storage[panelName].item, context.player, 500) then
context.delay(300)
end
end
end)
end
Panels.ManaItem = Panels.Mana
Panels.Equip = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoEquipItem"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = g_ui.createWidget("TwoItemsAndSlotPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {}
if panelId == 1 then
context.storage[panelName].item1 = 3052
context.storage[panelName].item2 = 3089
context.storage[panelName].slot = 9
end
end
ui.title:setText("Auto equip")
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
ui.item1:setItemId(context.storage[panelName].item1 or 0)
ui.item1.onItemChange = function(widget)
context.storage[panelName].item1 = widget:getItemId()
end
ui.item2:setItemId(context.storage[panelName].item2 or 0)
ui.item2.onItemChange = function(widget)
context.storage[panelName].item2 = widget:getItemId()
end
if not context.storage[panelName].slot then
context.storage[panelName].slot = 1
end
ui.slot:setCurrentIndex(context.storage[panelName].slot)
ui.slot.onOptionChange = function(widget)
context.storage[panelName].slot = widget.currentIndex
end
context.macro(250, function()
if context.storage[panelName].enabled and context.storage[panelName].slot > 0 then
local item1 = context.storage[panelName].item1 or 0
local item2 = context.storage[panelName].item2 or 0
if item1 < 100 and item2 < 100 then
return
end
local slotItem = context.getSlot(context.storage[panelName].slot)
if slotItem and (slotItem:getId() == item1 or slotItem:getId() == item2) then
return
end
local newItem = context.findItem(context.storage[panelName].item1)
if not newItem then
newItem = context.findItem(context.storage[panelName].item2)
if not newItem then
return
end
end
g_game.move(newItem, {x=65535, y=context.storage[panelName].slot, z=0})
context.delay(1000)
end
end)
end
Panels.AutoEquip = Panels.Equip
Panels.Eating = function(parent)
if not parent then
parent = context.panel
end
local panelName = "autoEatingPanel"
local panelId = 1
while parent:getChildById(panelName .. panelId) do
panelId = panelId + 1
end
panelName = panelName .. panelId
local ui = g_ui.createWidget("ItemsPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {}
end
ui.title:setText("Auto eating")
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
if type(context.storage[panelName].items) ~= 'table' then
context.storage[panelName].items = {3725, 0, 0, 0, 0}
end
for i=1,5 do
ui.items:getChildByIndex(i).onItemChange = function(widget)
context.storage[panelName].items[i] = widget:getItemId()
end
ui.items:getChildByIndex(i):setItemId(context.storage[panelName].items[i])
end
context.macro(15000, function()
if not context.storage[panelName].enabled then
return
end
local candidates = {}
for i, item in pairs(context.storage[panelName].items) do
if item >= 100 then
table.insert(candidates, item)
end
end
if #candidates == 0 then
return
end
context.use(candidates[math.random(1, #candidates)])
end)
end

View File

@@ -1,431 +1,431 @@
local context = G.botContext
local Panels = context.Panels
Panels.Looting = function(parent)
local ui = context.setupUI([[
Panel
id: looting
height: 180
BotLabel
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
text: Looting
ComboBox
id: config
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 5
text-offset: 3 0
width: 130
Button
id: enableButton
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 5
Button
margin-top: 1
id: add
anchors.top: prev.bottom
anchors.left: parent.left
text: Add
width: 60
height: 17
Button
id: edit
anchors.top: prev.top
anchors.horizontalCenter: parent.horizontalCenter
text: Edit
width: 60
height: 17
Button
id: remove
anchors.top: prev.top
anchors.right: parent.right
text: Remove
width: 60
height: 17
ScrollablePanel
id: items
anchors.top: prev.bottom
anchors.right: parent.right
anchors.left: parent.left
vertical-scrollbar: scrollBar
margin-right: 5
margin-top: 2
height: 70
layout:
type: grid
cell-size: 34 34
flow: true
BotSmallScrollBar
id: scrollBar
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.right: parent.right
step: 10
pixels-scroll: true
BotLabel
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 4
text: Loot Containers
ItemsRow
id: containers
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 33
margin-top: 2
]], parent)
local lootContainers = { ui.containers.item1, ui.containers.item2, ui.containers.item3, ui.containers.item4, ui.containers.item5 }
if type(context.storage.looting) ~= "table" then
context.storage.looting = {}
end
if type(context.storage.looting.configs) ~= "table" then
context.storage.looting.configs = {}
end
local getConfigName = function(config)
local matches = regexMatch(config, [[name:\s*([^\n]*)$]])
if matches[1] and matches[1][2] then
return matches[1][2]:trim()
end
return nil
end
local items = {}
local itemsByKey = {}
local containers = {}
local commands = {}
local refreshConfig = nil -- declared later
local createNewConfig = function(focusedWidget)
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
local tmpItems = {}
local tmpContainers = {}
local focusIndex = 0
local newConfig = ""
for i, text in ipairs(commands) do
newConfig = newConfig .. text .. "\n"
end
for i=1,ui.items:getChildCount() do
local widget = ui.items:getChildByIndex(i)
if widget and widget:getItemId() >= 100 then
if tmpItems[widget:getItemId()] == nil then
tmpItems[widget:getItemId()] = 1
newConfig = newConfig .. "\n" .. widget:getItemId()
end
end
if widget == focusedWidget then
focusIndex = i
end
end
for i, widget in ipairs(lootContainers) do
if widget:getItemId() >= 100 then
if tmpContainers[widget:getItemId()] == nil then
tmpContainers[widget:getItemId()] = 1 -- remove duplicates
newConfig = newConfig .. "\ncontainer:" .. widget:getItemId()
end
end
end
context.storage.looting.configs[context.storage.looting.activeConfig] = newConfig
refreshConfig(focusIndex)
end
local parseConfig = function(config)
items = {}
itemsByKey = {}
containers = {}
commands = {}
local matches = regexMatch(config, [[([^:^\n^\s]+)(:?)([^\n]*)]])
for i=1,#matches do
local command = matches[i][2]
local validation = (matches[i][3] == ":")
local text = matches[i][4]
local commandAsNumber = tonumber(command)
local textAsNumber = tonumber(text)
if commandAsNumber and commandAsNumber >= 100 then
table.insert(items, commandAsNumber)
itemsByKey[commandAsNumber] = 1
elseif command == "container" and validation and textAsNumber and textAsNumber >= 100 then
containers[textAsNumber] = 1
elseif validation then
table.insert(commands, command .. ":" .. text)
end
end
local itemsToShow = #items + 2
if itemsToShow % 5 ~= 0 then
itemsToShow = itemsToShow + 5 - itemsToShow % 5
end
if itemsToShow < 10 then
itemsToShow = 10
end
for i=1,itemsToShow do
local widget = g_ui.createWidget("BotItem", ui.items)
local itemId = 0
if i <= #items then
itemId = items[i]
end
widget:setItemId(itemId)
widget.onItemChange = createNewConfig
end
for i, widget in ipairs(lootContainers) do
widget:setItemId(0)
end
local containerIndex = 1
for containerId, i in pairs(containers) do
if lootContainers[containerIndex] then
lootContainers[containerIndex]:setItemId(containerId)
end
containerIndex = containerIndex + 1
end
for i, widget in ipairs(lootContainers) do
widget.onItemChange = createNewConfig
end
end
local ignoreOnOptionChange = true
refreshConfig = function(focusIndex)
ignoreOnOptionChange = true
if context.storage.looting.enabled then
ui.enableButton:setText("On")
ui.enableButton:setColor('#00AA00FF')
else
ui.enableButton:setText("Off")
ui.enableButton:setColor('#FF0000FF')
end
ui.config:clear()
for i, config in ipairs(context.storage.looting.configs) do
local name = getConfigName(config)
if not name then
name = "Unnamed config"
end
ui.config:addOption(name)
end
if (not context.storage.looting.activeConfig or context.storage.looting.activeConfig == 0) and #context.storage.looting.configs > 0 then
context.storage.looting.activeConfig = 1
end
ui.items:destroyChildren()
for i, widget in ipairs(lootContainers) do
widget.onItemChange = nil
widget:setItemId(0)
widget:setItemCount(0)
end
if context.storage.looting.activeConfig and context.storage.looting.configs[context.storage.looting.activeConfig] then
ui.config:setCurrentIndex(context.storage.looting.activeConfig)
parseConfig(context.storage.looting.configs[context.storage.looting.activeConfig])
end
context.saveConfig()
if focusIndex and focusIndex > 0 and ui.items:getChildByIndex(focusIndex) then
ui.items:focusChild(ui.items:getChildByIndex(focusIndex))
end
ignoreOnOptionChange = false
end
ui.config.onOptionChange = function(widget)
if not ignoreOnOptionChange then
context.storage.looting.activeConfig = widget.currentIndex
refreshConfig()
end
end
ui.enableButton.onClick = function()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
context.storage.looting.enabled = not context.storage.looting.enabled
refreshConfig()
end
ui.add.onClick = function()
modules.client_textedit.multilineEditor("Looting editor", "name:Config name", function(newText)
table.insert(context.storage.looting.configs, newText)
context.storage.looting.activeConfig = #context.storage.looting.configs
refreshConfig()
end)
end
ui.edit.onClick = function()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
modules.client_textedit.multilineEditor("Looting editor", context.storage.looting.configs[context.storage.looting.activeConfig], function(newText)
context.storage.looting.configs[context.storage.looting.activeConfig] = newText
refreshConfig()
end)
end
ui.remove.onClick = function()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
local questionWindow = nil
local closeWindow = function()
questionWindow:destroy()
end
local removeConfig = function()
closeWindow()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
context.storage.looting.enabled = false
table.remove(context.storage.looting.configs, context.storage.looting.activeConfig)
context.storage.looting.activeConfig = 0
refreshConfig()
end
questionWindow = context.displayGeneralBox(tr('Remove config'), tr('Do you want to remove current looting config?'), {
{ text=tr('Yes'), callback=removeConfig },
{ text=tr('No'), callback=closeWindow },
anchor=AnchorHorizontalCenter}, removeConfig, closeWindow)
end
refreshConfig()
context.onContainerOpen(function(container, prevContainer)
if context.storage.attacking.enabled then
return
end
if prevContainer then
container.autoLooting = prevContainer.autoLooting
else
container.autoLooting = true
end
end)
context.macro(200, function()
if not context.storage.looting.enabled then
return
end
local candidates = {}
local lootContainersCandidates = {}
for containerId, container in pairs(g_game.getContainers()) do
local containerItem = container:getContainerItem()
if container.autoLooting and container:getItemsCount() > 0 and (not containerItem or containers[containerItem:getId()] == nil) then
table.insert(candidates, container)
elseif containerItem and containers[containerItem:getId()] ~= nil then
table.insert(lootContainersCandidates, container)
end
end
if #lootContainersCandidates == 0 then
for slot = InventorySlotFirst, InventorySlotLast do
local item = context.getInventoryItem(slot)
if item and item:isContainer() and containers[item:getId()] ~= nil then
table.insert(lootContainersCandidates, item)
end
end
if #lootContainersCandidates > 0 then
-- try to open inventory backpack
local target = lootContainersCandidates[math.random(1,#lootContainersCandidates)]
g_game.open(target, nil)
context.delay(200)
end
return
end
if #candidates == 0 then
return
end
local container = candidates[math.random(1,#candidates)]
local nextContainers = {}
local foundItem = nil
for i, item in ipairs(container:getItems()) do
if item:isContainer() then
table.insert(nextContainers, item)
elseif itemsByKey[item:getId()] ~= nil then
foundItem = item
break
end
end
-- found item to loot
if foundItem then
-- find backpack for it, first backpack with same items
for i, container in ipairs(lootContainersCandidates) do
if container:getItemsCount() < container:getCapacity() or foundItem:isStackable() then -- has space
for j, item in ipairs(container:getItems()) do
if item:getId() == foundItem:getId() then
if foundItem:isStackable() then
if item:getCount() ~= 100 then
g_game.move(foundItem, container:getSlotPosition(j - 1), foundItem:getCount())
return
end
else
g_game.move(foundItem, container:getSlotPosition(container:getItemsCount()), foundItem:getCount())
return
end
end
end
end
end
-- now any backpack with empty slot
for i, container in ipairs(lootContainersCandidates) do
if container:getItemsCount() < container:getCapacity() then -- has space
g_game.move(foundItem, container:getSlotPosition(container:getItemsCount()), foundItem:getCount())
return
end
end
-- can't find backpack, try to open new
for i, container in ipairs(lootContainersCandidates) do
local candidates = {}
for j, item in ipairs(container:getItems()) do
if item:isContainer() and containers[item:getId()] ~= nil then
table.insert(candidates, item)
end
end
if #candidates > 0 then
g_game.open(candidates[math.random(1,#candidates)], container)
return
end
-- full, close it
g_game.close(container)
return
end
return
end
-- open remaining containers
if #nextContainers == 0 then
return
end
local delay = 1
for i=2,#nextContainers do
-- if more than 1 container, open them in new window
context.schedule(delay, function()
g_game.open(nextContainers[i], nil)
end)
delay = delay + 250
end
context.schedule(delay, function()
g_game.open(nextContainers[1], container)
end)
context.delay(150 + delay)
end)
end
local context = G.botContext
local Panels = context.Panels
Panels.Looting = function(parent)
local ui = context.setupUI([[
Panel
id: looting
height: 180
BotLabel
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
text: Looting
ComboBox
id: config
anchors.top: prev.bottom
anchors.left: parent.left
margin-top: 5
text-offset: 3 0
width: 130
Button
id: enableButton
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 5
Button
margin-top: 1
id: add
anchors.top: prev.bottom
anchors.left: parent.left
text: Add
width: 60
height: 17
Button
id: edit
anchors.top: prev.top
anchors.horizontalCenter: parent.horizontalCenter
text: Edit
width: 60
height: 17
Button
id: remove
anchors.top: prev.top
anchors.right: parent.right
text: Remove
width: 60
height: 17
ScrollablePanel
id: items
anchors.top: prev.bottom
anchors.right: parent.right
anchors.left: parent.left
vertical-scrollbar: scrollBar
margin-right: 5
margin-top: 2
height: 70
layout:
type: grid
cell-size: 34 34
flow: true
BotSmallScrollBar
id: scrollBar
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.right: parent.right
step: 10
pixels-scroll: true
BotLabel
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
margin-top: 4
text: Loot Containers
ItemsRow
id: containers
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 33
margin-top: 2
]], parent)
local lootContainers = { ui.containers.item1, ui.containers.item2, ui.containers.item3, ui.containers.item4, ui.containers.item5 }
if type(context.storage.looting) ~= "table" then
context.storage.looting = {}
end
if type(context.storage.looting.configs) ~= "table" then
context.storage.looting.configs = {}
end
local getConfigName = function(config)
local matches = regexMatch(config, [[name:\s*([^\n]*)$]])
if matches[1] and matches[1][2] then
return matches[1][2]:trim()
end
return nil
end
local items = {}
local itemsByKey = {}
local containers = {}
local commands = {}
local refreshConfig = nil -- declared later
local createNewConfig = function(focusedWidget)
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
local tmpItems = {}
local tmpContainers = {}
local focusIndex = 0
local newConfig = ""
for i, text in ipairs(commands) do
newConfig = newConfig .. text .. "\n"
end
for i=1,ui.items:getChildCount() do
local widget = ui.items:getChildByIndex(i)
if widget and widget:getItemId() >= 100 then
if tmpItems[widget:getItemId()] == nil then
tmpItems[widget:getItemId()] = 1
newConfig = newConfig .. "\n" .. widget:getItemId()
end
end
if widget == focusedWidget then
focusIndex = i
end
end
for i, widget in ipairs(lootContainers) do
if widget:getItemId() >= 100 then
if tmpContainers[widget:getItemId()] == nil then
tmpContainers[widget:getItemId()] = 1 -- remove duplicates
newConfig = newConfig .. "\ncontainer:" .. widget:getItemId()
end
end
end
context.storage.looting.configs[context.storage.looting.activeConfig] = newConfig
refreshConfig(focusIndex)
end
local parseConfig = function(config)
items = {}
itemsByKey = {}
containers = {}
commands = {}
local matches = regexMatch(config, [[([^:^\n^\s]+)(:?)([^\n]*)]])
for i=1,#matches do
local command = matches[i][2]
local validation = (matches[i][3] == ":")
local text = matches[i][4]
local commandAsNumber = tonumber(command)
local textAsNumber = tonumber(text)
if commandAsNumber and commandAsNumber >= 100 then
table.insert(items, commandAsNumber)
itemsByKey[commandAsNumber] = 1
elseif command == "container" and validation and textAsNumber and textAsNumber >= 100 then
containers[textAsNumber] = 1
elseif validation then
table.insert(commands, command .. ":" .. text)
end
end
local itemsToShow = #items + 2
if itemsToShow % 5 ~= 0 then
itemsToShow = itemsToShow + 5 - itemsToShow % 5
end
if itemsToShow < 10 then
itemsToShow = 10
end
for i=1,itemsToShow do
local widget = g_ui.createWidget("BotItem", ui.items)
local itemId = 0
if i <= #items then
itemId = items[i]
end
widget:setItemId(itemId)
widget.onItemChange = createNewConfig
end
for i, widget in ipairs(lootContainers) do
widget:setItemId(0)
end
local containerIndex = 1
for containerId, i in pairs(containers) do
if lootContainers[containerIndex] then
lootContainers[containerIndex]:setItemId(containerId)
end
containerIndex = containerIndex + 1
end
for i, widget in ipairs(lootContainers) do
widget.onItemChange = createNewConfig
end
end
local ignoreOnOptionChange = true
refreshConfig = function(focusIndex)
ignoreOnOptionChange = true
if context.storage.looting.enabled then
ui.enableButton:setText("On")
ui.enableButton:setColor('#00AA00FF')
else
ui.enableButton:setText("Off")
ui.enableButton:setColor('#FF0000FF')
end
ui.config:clear()
for i, config in ipairs(context.storage.looting.configs) do
local name = getConfigName(config)
if not name then
name = "Unnamed config"
end
ui.config:addOption(name)
end
if (not context.storage.looting.activeConfig or context.storage.looting.activeConfig == 0) and #context.storage.looting.configs > 0 then
context.storage.looting.activeConfig = 1
end
ui.items:destroyChildren()
for i, widget in ipairs(lootContainers) do
widget.onItemChange = nil
widget:setItemId(0)
widget:setItemCount(0)
end
if context.storage.looting.activeConfig and context.storage.looting.configs[context.storage.looting.activeConfig] then
ui.config:setCurrentIndex(context.storage.looting.activeConfig)
parseConfig(context.storage.looting.configs[context.storage.looting.activeConfig])
end
context.saveConfig()
if focusIndex and focusIndex > 0 and ui.items:getChildByIndex(focusIndex) then
ui.items:focusChild(ui.items:getChildByIndex(focusIndex))
end
ignoreOnOptionChange = false
end
ui.config.onOptionChange = function(widget)
if not ignoreOnOptionChange then
context.storage.looting.activeConfig = widget.currentIndex
refreshConfig()
end
end
ui.enableButton.onClick = function()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
context.storage.looting.enabled = not context.storage.looting.enabled
refreshConfig()
end
ui.add.onClick = function()
modules.client_textedit.multilineEditor("Looting editor", "name:Config name", function(newText)
table.insert(context.storage.looting.configs, newText)
context.storage.looting.activeConfig = #context.storage.looting.configs
refreshConfig()
end)
end
ui.edit.onClick = function()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
modules.client_textedit.multilineEditor("Looting editor", context.storage.looting.configs[context.storage.looting.activeConfig], function(newText)
context.storage.looting.configs[context.storage.looting.activeConfig] = newText
refreshConfig()
end)
end
ui.remove.onClick = function()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
local questionWindow = nil
local closeWindow = function()
questionWindow:destroy()
end
local removeConfig = function()
closeWindow()
if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
return
end
context.storage.looting.enabled = false
table.remove(context.storage.looting.configs, context.storage.looting.activeConfig)
context.storage.looting.activeConfig = 0
refreshConfig()
end
questionWindow = context.displayGeneralBox(tr('Remove config'), tr('Do you want to remove current looting config?'), {
{ text=tr('Yes'), callback=removeConfig },
{ text=tr('No'), callback=closeWindow },
anchor=AnchorHorizontalCenter}, removeConfig, closeWindow)
end
refreshConfig()
context.onContainerOpen(function(container, prevContainer)
if context.storage.attacking.enabled then
return
end
if prevContainer then
container.autoLooting = prevContainer.autoLooting
else
container.autoLooting = true
end
end)
context.macro(200, function()
if not context.storage.looting.enabled then
return
end
local candidates = {}
local lootContainersCandidates = {}
for containerId, container in pairs(g_game.getContainers()) do
local containerItem = container:getContainerItem()
if container.autoLooting and container:getItemsCount() > 0 and (not containerItem or containers[containerItem:getId()] == nil) then
table.insert(candidates, container)
elseif containerItem and containers[containerItem:getId()] ~= nil then
table.insert(lootContainersCandidates, container)
end
end
if #lootContainersCandidates == 0 then
for slot = InventorySlotFirst, InventorySlotLast do
local item = context.getInventoryItem(slot)
if item and item:isContainer() and containers[item:getId()] ~= nil then
table.insert(lootContainersCandidates, item)
end
end
if #lootContainersCandidates > 0 then
-- try to open inventory backpack
local target = lootContainersCandidates[math.random(1,#lootContainersCandidates)]
g_game.open(target, nil)
context.delay(200)
end
return
end
if #candidates == 0 then
return
end
local container = candidates[math.random(1,#candidates)]
local nextContainers = {}
local foundItem = nil
for i, item in ipairs(container:getItems()) do
if item:isContainer() then
table.insert(nextContainers, item)
elseif itemsByKey[item:getId()] ~= nil then
foundItem = item
break
end
end
-- found item to loot
if foundItem then
-- find backpack for it, first backpack with same items
for i, container in ipairs(lootContainersCandidates) do
if container:getItemsCount() < container:getCapacity() or foundItem:isStackable() then -- has space
for j, item in ipairs(container:getItems()) do
if item:getId() == foundItem:getId() then
if foundItem:isStackable() then
if item:getCount() ~= 100 then
g_game.move(foundItem, container:getSlotPosition(j - 1), foundItem:getCount())
return
end
else
g_game.move(foundItem, container:getSlotPosition(container:getItemsCount()), foundItem:getCount())
return
end
end
end
end
end
-- now any backpack with empty slot
for i, container in ipairs(lootContainersCandidates) do
if container:getItemsCount() < container:getCapacity() then -- has space
g_game.move(foundItem, container:getSlotPosition(container:getItemsCount()), foundItem:getCount())
return
end
end
-- can't find backpack, try to open new
for i, container in ipairs(lootContainersCandidates) do
local candidates = {}
for j, item in ipairs(container:getItems()) do
if item:isContainer() and containers[item:getId()] ~= nil then
table.insert(candidates, item)
end
end
if #candidates > 0 then
g_game.open(candidates[math.random(1,#candidates)], container)
return
end
-- full, close it
g_game.close(container)
return
end
return
end
-- open remaining containers
if #nextContainers == 0 then
return
end
local delay = 1
for i=2,#nextContainers do
-- if more than 1 container, open them in new window
context.schedule(delay, function()
g_game.open(nextContainers[i], nil)
end)
delay = delay + 250
end
context.schedule(delay, function()
g_game.open(nextContainers[1], container)
end)
context.delay(150 + delay)
end)
end

View File

@@ -1,36 +1,36 @@
local context = G.botContext
local Panels = context.Panels
Panels.TradeMessage = function(parent)
context.macro(60000, "Send message on trade", nil, function()
local trade = context.getChannelId("advertising")
if not trade then
trade = context.getChannelId("trade")
end
if context.storage.autoTradeMessage:len() > 0 and trade then
context.sayChannel(trade, context.storage.autoTradeMessage)
end
end, parent)
context.addTextEdit("autoTradeMessage", context.storage.autoTradeMessage or "I'm using OTClientV8 - https://github.com/OTCv8/otclientv8", function(widget, text)
context.storage.autoTradeMessage = text
end, parent)
end
Panels.AutoStackItems = function(parent)
context.macro(500, "Auto stacking items", nil, function()
local containers = context.getContainers()
for i, container in pairs(containers) do
local toStack = {}
for j, item in ipairs(container:getItems()) do
if item:isStackable() and item:getCount() ~= 100 then
local otherItem = toStack[item:getId()]
if otherItem then
g_game.move(item, otherItem, item:getCount())
return
end
toStack[item:getId()] = container:getSlotPosition(j - 1)
end
end
end
end, parent)
local context = G.botContext
local Panels = context.Panels
Panels.TradeMessage = function(parent)
context.macro(60000, "Send message on trade", nil, function()
local trade = context.getChannelId("advertising")
if not trade then
trade = context.getChannelId("trade")
end
if context.storage.autoTradeMessage:len() > 0 and trade then
context.sayChannel(trade, context.storage.autoTradeMessage)
end
end, parent)
context.addTextEdit("autoTradeMessage", context.storage.autoTradeMessage or "I'm using OTClientV8 - https://github.com/OTCv8/otclientv8", function(widget, text)
context.storage.autoTradeMessage = text
end, parent)
end
Panels.AutoStackItems = function(parent)
context.macro(500, "Auto stacking items", nil, function()
local containers = context.getContainers()
for i, container in pairs(containers) do
local toStack = {}
for j, item in ipairs(container:getItems()) do
if item:isStackable() and item:getCount() ~= 100 then
local otherItem = toStack[item:getId()]
if otherItem then
g_game.move(item, otherItem, item:getCount())
return
end
toStack[item:getId()] = container:getSlotPosition(j - 1)
end
end
end
end, parent)
end

View File

@@ -1,127 +1,127 @@
local context = G.botContext
local Panels = context.Panels
Panels.AttackLeaderTarget = function(parent)
local toAttack = nil
context.onMissle(function(missle)
if not context.storage.attackLeader or context.storage.attackLeader:len() == 0 then
return
end
local src = missle:getSource()
if src.z ~= context.posz() then
return
end
local from = g_map.getTile(src)
local to = g_map.getTile(missle:getDestination())
if not from or not to then
return
end
local fromCreatures = from:getCreatures()
local toCreatures = to:getCreatures()
if #fromCreatures ~= 1 or #toCreatures ~= 1 then
return
end
local c1 = fromCreatures[1]
if c1:getName():lower() == context.storage.attackLeader:lower() then
toAttack = toCreatures[1]
end
end)
context.macro(50, "Attack leader's target", nil, function()
if toAttack and context.storage.attackLeader:len() > 0 and toAttack ~= g_game.getAttackingCreature() then
g_game.attack(toAttack)
toAttack = nil
end
end, parent)
context.addTextEdit("attackLeader", context.storage.attackLeader or "player name", function(widget, text)
context.storage.attackLeader = text
end, parent)
end
Panels.LimitFloor = function(parent)
context.onPlayerPositionChange(function(pos)
if context.storage.limitFloor then
local gameMapPanel = modules.game_interface.getMapPanel()
if gameMapPanel then
gameMapPanel:lockVisibleFloor(pos.z)
end
end
end)
local switch = context.addSwitch("limitFloor", "Don't show higher floors", function(widget)
widget:setOn(not widget:isOn())
context.storage.limitFloor = widget:isOn()
local gameMapPanel = modules.game_interface.getMapPanel()
if gameMapPanel then
if context.storage.limitFloor then
gameMapPanel:lockVisibleFloor(context.posz())
else
gameMapPanel:unlockVisibleFloor()
end
end
end, parent)
switch:setOn(context.storage.limitFloor)
end
Panels.AntiPush = function(parent)
if not parent then
parent = context.panel
end
local panelName = "antiPushPanel"
local ui = g_ui.createWidget("ItemsPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {}
end
ui.title:setText("Anti push")
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
if type(context.storage[panelName].items) ~= 'table' then
context.storage[panelName].items = {3031, 3035, 0, 0, 0}
end
for i=1,5 do
ui.items:getChildByIndex(i).onItemChange = function(widget)
context.storage[panelName].items[i] = widget:getItemId()
end
ui.items:getChildByIndex(i):setItemId(context.storage[panelName].items[i])
end
context.macro(100, function()
if not context.storage[panelName].enabled then
return
end
local tile = g_map.getTile(context.player:getPosition())
if not tile then
return
end
local topItem = tile:getTopUseThing()
if topItem and topItem:isStackable() then
topItem = topItem:getId()
else
topItem = 0
end
local candidates = {}
for i, item in pairs(context.storage[panelName].items) do
if item >= 100 and item ~= topItem and context.findItem(item) then
table.insert(candidates, item)
end
end
if #candidates == 0 then
return
end
if type(context.storage[panelName].lastItem) ~= 'number' or context.storage[panelName].lastItem > #candidates then
context.storage[panelName].lastItem = 1
end
local item = context.findItem(candidates[context.storage[panelName].lastItem])
g_game.move(item, context.player:getPosition(), 1)
context.storage[panelName].lastItem = context.storage[panelName].lastItem + 1
end)
end
local context = G.botContext
local Panels = context.Panels
Panels.AttackLeaderTarget = function(parent)
local toAttack = nil
context.onMissle(function(missle)
if not context.storage.attackLeader or context.storage.attackLeader:len() == 0 then
return
end
local src = missle:getSource()
if src.z ~= context.posz() then
return
end
local from = g_map.getTile(src)
local to = g_map.getTile(missle:getDestination())
if not from or not to then
return
end
local fromCreatures = from:getCreatures()
local toCreatures = to:getCreatures()
if #fromCreatures ~= 1 or #toCreatures ~= 1 then
return
end
local c1 = fromCreatures[1]
if c1:getName():lower() == context.storage.attackLeader:lower() then
toAttack = toCreatures[1]
end
end)
context.macro(50, "Attack leader's target", nil, function()
if toAttack and context.storage.attackLeader:len() > 0 and toAttack ~= g_game.getAttackingCreature() then
g_game.attack(toAttack)
toAttack = nil
end
end, parent)
context.addTextEdit("attackLeader", context.storage.attackLeader or "player name", function(widget, text)
context.storage.attackLeader = text
end, parent)
end
Panels.LimitFloor = function(parent)
context.onPlayerPositionChange(function(pos)
if context.storage.limitFloor then
local gameMapPanel = modules.game_interface.getMapPanel()
if gameMapPanel then
gameMapPanel:lockVisibleFloor(pos.z)
end
end
end)
local switch = context.addSwitch("limitFloor", "Don't show higher floors", function(widget)
widget:setOn(not widget:isOn())
context.storage.limitFloor = widget:isOn()
local gameMapPanel = modules.game_interface.getMapPanel()
if gameMapPanel then
if context.storage.limitFloor then
gameMapPanel:lockVisibleFloor(context.posz())
else
gameMapPanel:unlockVisibleFloor()
end
end
end, parent)
switch:setOn(context.storage.limitFloor)
end
Panels.AntiPush = function(parent)
if not parent then
parent = context.panel
end
local panelName = "antiPushPanel"
local ui = g_ui.createWidget("ItemsPanel", parent)
ui:setId(panelName)
if not context.storage[panelName] then
context.storage[panelName] = {}
end
ui.title:setText("Anti push")
ui.title:setOn(context.storage[panelName].enabled)
ui.title.onClick = function(widget)
context.storage[panelName].enabled = not context.storage[panelName].enabled
widget:setOn(context.storage[panelName].enabled)
end
if type(context.storage[panelName].items) ~= 'table' then
context.storage[panelName].items = {3031, 3035, 0, 0, 0}
end
for i=1,5 do
ui.items:getChildByIndex(i).onItemChange = function(widget)
context.storage[panelName].items[i] = widget:getItemId()
end
ui.items:getChildByIndex(i):setItemId(context.storage[panelName].items[i])
end
context.macro(100, function()
if not context.storage[panelName].enabled then
return
end
local tile = g_map.getTile(context.player:getPosition())
if not tile then
return
end
local topItem = tile:getTopUseThing()
if topItem and topItem:isStackable() then
topItem = topItem:getId()
else
topItem = 0
end
local candidates = {}
for i, item in pairs(context.storage[panelName].items) do
if item >= 100 and item ~= topItem and context.findItem(item) then
table.insert(candidates, item)
end
end
if #candidates == 0 then
return
end
if type(context.storage[panelName].lastItem) ~= 'number' or context.storage[panelName].lastItem > #candidates then
context.storage[panelName].lastItem = 1
end
local item = context.findItem(candidates[context.storage[panelName].lastItem])
g_game.move(item, context.player:getPosition(), 1)
context.storage[panelName].lastItem = context.storage[panelName].lastItem + 1
end)
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,88 +1,88 @@
BotButton < Button
height: 17
margin-top: 2
BotSwitch < Button
margin-top: 2
height: 17
image-color: green
$!on:
image-color: red
SmallBotSwitch < Button
margin-top: 2
height: 15
image-color: green
$!on:
image-color: red
BotLabel < Label
margin-top: 2
height: 15
text-auto-resize: true
text-align: center
text-wrap: true
BotItem < Item
virtual: true
&selectable: true
&editable: true
BotTextEdit < TextEdit
@onClick: modules.client_textedit.show(self)
text-align: center
multiline: false
focusable: false
height: 20
BotSeparator < HorizontalSeparator
margin-top: 5
margin-bottom: 3
BotSmallScrollBar < SmallScrollBar
BotPanel < Panel
margin-top: 1
ScrollablePanel
id: content
anchors.fill: parent
margin-right: 8
margin-left: 1
margin-bottom: 5
vertical-scrollbar: botPanelScroll
layout:
type: verticalBox
$mobile:
margin-right: 16
BotSmallScrollBar
id: botPanelScroll
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
CaveBotLabel < Label
background-color: alpha
text-offset: 2 0
focusable: true
$focus:
background-color: #00000055
SlotComboBoxPopupMenu < ComboBoxPopupMenu
SlotComboBoxPopupMenuButton < ComboBoxPopupMenuButton
SlotComboBox < ComboBox
@onSetup: |
self:addOption("Head")
self:addOption("Neck")
self:addOption("Back")
self:addOption("Body")
self:addOption("Right")
self:addOption("Left")
self:addOption("Leg")
self:addOption("Feet")
self:addOption("Finger")
self:addOption("Ammo")
self:addOption("Purse")
BotButton < Button
height: 17
margin-top: 2
BotSwitch < Button
margin-top: 2
height: 17
image-color: green
$!on:
image-color: red
SmallBotSwitch < Button
margin-top: 2
height: 15
image-color: green
$!on:
image-color: red
BotLabel < Label
margin-top: 2
height: 15
text-auto-resize: true
text-align: center
text-wrap: true
BotItem < Item
virtual: true
&selectable: true
&editable: true
BotTextEdit < TextEdit
@onClick: modules.client_textedit.show(self)
text-align: center
multiline: false
focusable: false
height: 20
BotSeparator < HorizontalSeparator
margin-top: 5
margin-bottom: 3
BotSmallScrollBar < SmallScrollBar
BotPanel < Panel
margin-top: 1
ScrollablePanel
id: content
anchors.fill: parent
margin-right: 8
margin-left: 1
margin-bottom: 5
vertical-scrollbar: botPanelScroll
layout:
type: verticalBox
$mobile:
margin-right: 16
BotSmallScrollBar
id: botPanelScroll
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
CaveBotLabel < Label
background-color: alpha
text-offset: 2 0
focusable: true
$focus:
background-color: #00000055
SlotComboBoxPopupMenu < ComboBoxPopupMenu
SlotComboBoxPopupMenuButton < ComboBoxPopupMenuButton
SlotComboBox < ComboBox
@onSetup: |
self:addOption("Head")
self:addOption("Neck")
self:addOption("Back")
self:addOption("Body")
self:addOption("Right")
self:addOption("Left")
self:addOption("Leg")
self:addOption("Feet")
self:addOption("Finger")
self:addOption("Ammo")
self:addOption("Purse")

View File

@@ -1,54 +1,54 @@
BotConfig < Panel
id: botConfig
height: 45
margin-left: 2
margin-right: 2
ComboBox
id: list
anchors.top: parent.top
anchors.left: parent.left
text-offset: 3 0
width: 130
Button
id: switch
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 5
$on:
text: On
color: #00AA00
$!on:
text: Off
color: #FF0000
Button
margin-top: 2
id: add
anchors.top: prev.bottom
anchors.left: parent.left
text: Add
width: 56
height: 18
text-offet: 0 2
Button
id: edit
anchors.top: prev.top
anchors.horizontalCenter: parent.horizontalCenter
text: Edit
width: 56
height: 18
text-offet: 0 2
Button
id: remove
anchors.top: prev.top
anchors.right: parent.right
text: Remove
width: 56
height: 18
BotConfig < Panel
id: botConfig
height: 45
margin-left: 2
margin-right: 2
ComboBox
id: list
anchors.top: parent.top
anchors.left: parent.left
text-offset: 3 0
width: 130
Button
id: switch
anchors.top: prev.top
anchors.left: prev.right
anchors.right: parent.right
margin-left: 5
$on:
text: On
color: #00AA00
$!on:
text: Off
color: #FF0000
Button
margin-top: 2
id: add
anchors.top: prev.bottom
anchors.left: parent.left
text: Add
width: 56
height: 18
text-offet: 0 2
Button
id: edit
anchors.top: prev.top
anchors.horizontalCenter: parent.horizontalCenter
text: Edit
width: 56
height: 18
text-offet: 0 2
Button
id: remove
anchors.top: prev.top
anchors.right: parent.right
text: Remove
width: 56
height: 18
text-offet: 0 2

View File

@@ -1,19 +1,19 @@
BotContainer < Panel
height: 68
ScrollablePanel
id: items
anchors.fill: parent
vertical-scrollbar: scroll
layout:
type: grid
cell-size: 34 34
flow: true
BotSmallScrollBar
id: scroll
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.right: parent.right
step: 10
pixels-scroll: true
BotContainer < Panel
height: 68
ScrollablePanel
id: items
anchors.fill: parent
vertical-scrollbar: scroll
layout:
type: grid
cell-size: 34 34
flow: true
BotSmallScrollBar
id: scroll
anchors.top: prev.top
anchors.bottom: prev.bottom
anchors.right: parent.right
step: 10
pixels-scroll: true

View File

@@ -1,60 +1,60 @@
BotIcon < UIWidget
size: 50 50
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
focusable: false
phantom: false
draggable: true
UIItem
id: item
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 6
virtual: true
phantom: true
size: 32 32
UICreature
id: creature
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 0
size: 48 48
phantom: true
UIWidget
id: status
anchors.top: parent.top
anchors.left: parent.left
size: 18 10
color: black
font: terminus-10px
phantom: true
$on:
text: ON
background: green
$!on:
text: OFF
background: red
UIWidget
id: hotkey
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
size: 18 10
color: white
phantom: true
text-align: right
UIWidget
id: text
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
text-wrap: true
text-auto-resize: true
phantom: true
BotIcon < UIWidget
size: 50 50
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
focusable: false
phantom: false
draggable: true
UIItem
id: item
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 6
virtual: true
phantom: true
size: 32 32
UICreature
id: creature
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin-top: 0
size: 48 48
phantom: true
UIWidget
id: status
anchors.top: parent.top
anchors.left: parent.left
size: 18 10
color: black
font: terminus-10px
phantom: true
$on:
text: ON
background: green
$!on:
text: OFF
background: red
UIWidget
id: hotkey
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
size: 18 10
color: white
phantom: true
text-align: right
UIWidget
id: text
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
text-wrap: true
text-auto-resize: true
phantom: true

View File

@@ -1,240 +1,240 @@
DualScrollPanel < Panel
height: 51
margin-top: 3
SmallBotSwitch
id: title
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
HorizontalScrollBar
id: scroll1
anchors.left: title.left
anchors.right: title.horizontalCenter
anchors.top: title.bottom
margin-right: 2
margin-top: 2
minimum: 0
maximum: 100
step: 1
&disableScroll: true
HorizontalScrollBar
id: scroll2
anchors.left: title.horizontalCenter
anchors.right: title.right
anchors.top: prev.top
margin-left: 2
minimum: 0
maximum: 100
step: 1
&disableScroll: true
BotTextEdit
id: text
anchors.left: parent.left
anchors.right: parent.right
anchors.top: scroll1.bottom
margin-top: 3
margin-left: 2
margin-right: 1
SingleScrollItemPanel < Panel
height: 45
margin-top: 2
BotItem
id: item
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 3
SmallBotSwitch
id: title
anchors.left: prev.right
anchors.right: parent.right
anchors.top: prev.top
margin-left: 2
text-align: center
HorizontalScrollBar
id: scroll
anchors.left: title.left
anchors.right: title.right
anchors.top: title.bottom
margin-top: 2
minimum: 0
maximum: 100
step: 1
&disableScroll: true
DualScrollItemPanel < Panel
height: 33
margin-top: 3
BotItem
id: item
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 3
SmallBotSwitch
id: title
anchors.left: prev.right
anchors.right: parent.right
anchors.top: prev.top
margin-left: 2
text-align: center
HorizontalScrollBar
id: scroll1
anchors.left: title.left
anchors.right: title.horizontalCenter
anchors.top: title.bottom
margin-top: 2
margin-right: 2
minimum: 0
maximum: 100
step: 1
&disableScroll: true
HorizontalScrollBar
id: scroll2
anchors.left: title.horizontalCenter
anchors.right: title.right
anchors.top: prev.top
margin-left: 2
minimum: 0
maximum: 100
step: 1
&disableScroll: true
ItemsRow < Panel
height: 33
margin-top: 2
BotItem
id: item1
anchors.top: parent.top
anchors.left: parent.left
BotItem
id: item2
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
BotItem
id: item3
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
BotItem
id: item4
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
BotItem
id: item5
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
ItemsPanel < Panel
height: 55
SmallBotSwitch
id: title
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
ItemsRow
id: items
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
ItemAndButtonPanel < Panel
height: 40
BotItem
id: item
anchors.left: parent.left
anchors.top: parent.top
BotSwitch
id: title
anchors.left: prev.right
anchors.right: parent.right
anchors.verticalCenter: prev.verticalCenter
text-align: center
margin-left: 2
margin-top: 0
ItemAndSlotPanel < Panel
height: 40
BotItem
id: item
anchors.left: parent.left
anchors.top: parent.top
SmallBotSwitch
id: title
anchors.left: prev.right
anchors.right: parent.right
anchors.top: prev.top
text-align: center
margin-left: 2
margin-top: 0
SlotComboBox
id: slot
anchors.left: prev.left
anchors.right: prev.right
anchors.top: prev.bottom
margin-top: 2
height: 20
&disableScroll: true
TwoItemsAndSlotPanel < Panel
height: 35
margin-top: 4
BotItem
id: item1
anchors.left: parent.left
anchors.top: parent.top
margin-top: 1
BotItem
id: item2
anchors.left: prev.right
anchors.top: prev.top
margin-left: 1
SmallBotSwitch
id: title
anchors.left: prev.right
anchors.right: parent.right
anchors.top: parent.top
text-align: center
margin-left: 2
margin-top: 0
SlotComboBox
id: slot
anchors.left: prev.left
anchors.right: prev.right
anchors.top: prev.bottom
margin-top: 2
height: 20
&disableScroll: true
DualScrollPanel < Panel
height: 51
margin-top: 3
SmallBotSwitch
id: title
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
HorizontalScrollBar
id: scroll1
anchors.left: title.left
anchors.right: title.horizontalCenter
anchors.top: title.bottom
margin-right: 2
margin-top: 2
minimum: 0
maximum: 100
step: 1
&disableScroll: true
HorizontalScrollBar
id: scroll2
anchors.left: title.horizontalCenter
anchors.right: title.right
anchors.top: prev.top
margin-left: 2
minimum: 0
maximum: 100
step: 1
&disableScroll: true
BotTextEdit
id: text
anchors.left: parent.left
anchors.right: parent.right
anchors.top: scroll1.bottom
margin-top: 3
margin-left: 2
margin-right: 1
SingleScrollItemPanel < Panel
height: 45
margin-top: 2
BotItem
id: item
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 3
SmallBotSwitch
id: title
anchors.left: prev.right
anchors.right: parent.right
anchors.top: prev.top
margin-left: 2
text-align: center
HorizontalScrollBar
id: scroll
anchors.left: title.left
anchors.right: title.right
anchors.top: title.bottom
margin-top: 2
minimum: 0
maximum: 100
step: 1
&disableScroll: true
DualScrollItemPanel < Panel
height: 33
margin-top: 3
BotItem
id: item
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 3
SmallBotSwitch
id: title
anchors.left: prev.right
anchors.right: parent.right
anchors.top: prev.top
margin-left: 2
text-align: center
HorizontalScrollBar
id: scroll1
anchors.left: title.left
anchors.right: title.horizontalCenter
anchors.top: title.bottom
margin-top: 2
margin-right: 2
minimum: 0
maximum: 100
step: 1
&disableScroll: true
HorizontalScrollBar
id: scroll2
anchors.left: title.horizontalCenter
anchors.right: title.right
anchors.top: prev.top
margin-left: 2
minimum: 0
maximum: 100
step: 1
&disableScroll: true
ItemsRow < Panel
height: 33
margin-top: 2
BotItem
id: item1
anchors.top: parent.top
anchors.left: parent.left
BotItem
id: item2
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
BotItem
id: item3
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
BotItem
id: item4
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
BotItem
id: item5
anchors.top: prev.top
anchors.left: prev.right
margin-left: 2
ItemsPanel < Panel
height: 55
SmallBotSwitch
id: title
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
text-align: center
ItemsRow
id: items
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
ItemAndButtonPanel < Panel
height: 40
BotItem
id: item
anchors.left: parent.left
anchors.top: parent.top
BotSwitch
id: title
anchors.left: prev.right
anchors.right: parent.right
anchors.verticalCenter: prev.verticalCenter
text-align: center
margin-left: 2
margin-top: 0
ItemAndSlotPanel < Panel
height: 40
BotItem
id: item
anchors.left: parent.left
anchors.top: parent.top
SmallBotSwitch
id: title
anchors.left: prev.right
anchors.right: parent.right
anchors.top: prev.top
text-align: center
margin-left: 2
margin-top: 0
SlotComboBox
id: slot
anchors.left: prev.left
anchors.right: prev.right
anchors.top: prev.bottom
margin-top: 2
height: 20
&disableScroll: true
TwoItemsAndSlotPanel < Panel
height: 35
margin-top: 4
BotItem
id: item1
anchors.left: parent.left
anchors.top: parent.top
margin-top: 1
BotItem
id: item2
anchors.left: prev.right
anchors.top: prev.top
margin-left: 1
SmallBotSwitch
id: title
anchors.left: prev.right
anchors.right: parent.right
anchors.top: parent.top
text-align: center
margin-left: 2
margin-top: 0
SlotComboBox
id: slot
anchors.left: prev.left
anchors.right: prev.right
anchors.top: prev.bottom
margin-top: 2
height: 20
&disableScroll: true

View File

@@ -1,119 +1,119 @@
extraHotkeys = {}
function addExtraHotkey(name, description, callback)
table.insert(extraHotkeys, {
name = name:lower(),
description = tr(description),
callback = callback
})
end
function setupExtraHotkeys(combobox)
addExtraHotkey("none", "None", nil)
addExtraHotkey("cancelAttack", "Stop attacking", function(repeated)
if not repeated then
g_game.attack(nil)
end
end)
addExtraHotkey("attackNext", "Attack next target from battle list", function(repeated)
if repeated or not modules.game_battle then
return
end
local battlePanel = modules.game_battle.battlePanel
local attackedCreature = g_game.getAttackingCreature()
local nextChild = nil
local breakNext = false
for i, child in ipairs(battlePanel:getChildren()) do
if not child.creature or not child:isOn() then
break
end
nextChild = child
if breakNext then
break
end
if child.creature == attackedCreature then
breakNext = true
nextChild = battlePanel:getFirstChild()
end
end
if not breakNext then
nextChild = battlePanel:getFirstChild()
end
if nextChild and nextChild.creature ~= attackedCreature then
g_game.attack(nextChild.creature)
end
end)
addExtraHotkey("attackPrevious", "Attack previous target from battle list", function(repeated)
if repeated or not modules.game_battle then
return
end
local battlePanel = modules.game_battle.battlePanel
local attackedCreature = g_game.getAttackingCreature()
local prevChild = nil
for i, child in ipairs(battlePanel:getChildren()) do
if not child.creature or not child:isOn() then
break
end
if child.creature == attackedCreature then
break
end
prevChild = child
end
if prevChild and prevChild.creature ~= attackedCreature then
g_game.attack(prevChild.creature)
end
end)
addExtraHotkey("toogleWsad", "Enable/disable wsad walking", function(repeated)
if repeated or not modules.game_console then
return
end
if not modules.game_console.consoleToggleChat:isChecked() then
modules.game_console.disableChat(true)
else
modules.game_console.enableChat(true)
end
end)
for index, actionDetails in ipairs(extraHotkeys) do
combobox:addOption(actionDetails.description)
end
end
function executeExtraHotkey(action, repeated)
action = action:lower()
for index, actionDetails in ipairs(extraHotkeys) do
if actionDetails.name == action and actionDetails.callback then
actionDetails.callback(repeated)
end
end
end
function translateActionToActionComboboxIndex(action)
action = action:lower()
for index, actionDetails in ipairs(extraHotkeys) do
if actionDetails.name == action then
return index
end
end
return 1
end
function translateActionComboboxIndexToAction(index)
if index > 1 and index <= #extraHotkeys then
return extraHotkeys[index].name
end
return nil
end
function getActionDescription(action)
action = action:lower()
for index, actionDetails in ipairs(extraHotkeys) do
if actionDetails.name == action then
return actionDetails.description
end
end
return "invalid action"
extraHotkeys = {}
function addExtraHotkey(name, description, callback)
table.insert(extraHotkeys, {
name = name:lower(),
description = tr(description),
callback = callback
})
end
function setupExtraHotkeys(combobox)
addExtraHotkey("none", "None", nil)
addExtraHotkey("cancelAttack", "Stop attacking", function(repeated)
if not repeated then
g_game.attack(nil)
end
end)
addExtraHotkey("attackNext", "Attack next target from battle list", function(repeated)
if repeated or not modules.game_battle then
return
end
local battlePanel = modules.game_battle.battlePanel
local attackedCreature = g_game.getAttackingCreature()
local nextChild = nil
local breakNext = false
for i, child in ipairs(battlePanel:getChildren()) do
if not child.creature or not child:isOn() then
break
end
nextChild = child
if breakNext then
break
end
if child.creature == attackedCreature then
breakNext = true
nextChild = battlePanel:getFirstChild()
end
end
if not breakNext then
nextChild = battlePanel:getFirstChild()
end
if nextChild and nextChild.creature ~= attackedCreature then
g_game.attack(nextChild.creature)
end
end)
addExtraHotkey("attackPrevious", "Attack previous target from battle list", function(repeated)
if repeated or not modules.game_battle then
return
end
local battlePanel = modules.game_battle.battlePanel
local attackedCreature = g_game.getAttackingCreature()
local prevChild = nil
for i, child in ipairs(battlePanel:getChildren()) do
if not child.creature or not child:isOn() then
break
end
if child.creature == attackedCreature then
break
end
prevChild = child
end
if prevChild and prevChild.creature ~= attackedCreature then
g_game.attack(prevChild.creature)
end
end)
addExtraHotkey("toogleWsad", "Enable/disable wsad walking", function(repeated)
if repeated or not modules.game_console then
return
end
if not modules.game_console.consoleToggleChat:isChecked() then
modules.game_console.disableChat(true)
else
modules.game_console.enableChat(true)
end
end)
for index, actionDetails in ipairs(extraHotkeys) do
combobox:addOption(actionDetails.description)
end
end
function executeExtraHotkey(action, repeated)
action = action:lower()
for index, actionDetails in ipairs(extraHotkeys) do
if actionDetails.name == action and actionDetails.callback then
actionDetails.callback(repeated)
end
end
end
function translateActionToActionComboboxIndex(action)
action = action:lower()
for index, actionDetails in ipairs(extraHotkeys) do
if actionDetails.name == action then
return index
end
end
return 1
end
function translateActionComboboxIndexToAction(index)
if index > 1 and index <= #extraHotkeys then
return extraHotkeys[index].name
end
return nil
end
function getActionDescription(action)
action = action:lower()
for index, actionDetails in ipairs(extraHotkeys) do
if actionDetails.name == action then
return actionDetails.description
end
end
return "invalid action"
end

View File

@@ -161,6 +161,7 @@ function hide()
countWindow = nil
end
gameRootPanel:hide()
gameMapPanel:setShader("")
modules.client_background.show()
end

View File

@@ -1384,7 +1384,7 @@ function Market.onMarketEnter(depotItems, offers, balance, vocation, items)
for i = 1, #marketItems[MarketCategory.TibiaCoins] do
local item = marketItems[MarketCategory.TibiaCoins][i].displayItem
information.depotItems[item:getId()] = tibiaCoins
depotItems[item:getId()] = tibiaCoins
end
-- update the items widget to match depot items
@@ -1425,10 +1425,9 @@ end
function Market.onCoinBalance(coins, transferableCoins)
tibiaCoins = coins
if not information or type(information.depotItems) ~= "table" then return end
if not marketItems[MarketCategory.TibiaCoins] then return end
for i = 1, #marketItems[MarketCategory.TibiaCoins] do
local item = marketItems[MarketCategory.TibiaCoins][i].displayItem
information.depotItems[item:getId()] = tibiaCoins
depotItems[item:getId()] = tibiaCoins
end
end

View File

@@ -1,6 +1,22 @@
function init()
-- add manually your shaders from /data/shaders
g_shaders.createOutfitShader("default", "/shaders/outfit_default_vertex", "/shaders/outfit_default_fragment")
-- map shaders
g_shaders.createShader("map_default", "/shaders/map_default_vertex", "/shaders/map_default_fragment")
g_shaders.createShader("map_rainbow", "/shaders/map_rainbow_vertex", "/shaders/map_rainbow_fragment")
g_shaders.addTexture("map_rainbow", "/images/shaders/rainbow.png")
-- use modules.game_interface.gameMapPanel:setShader("map_rainbow") to set shader
-- outfit shaders
g_shaders.createOutfitShader("outfit_default", "/shaders/outfit_default_vertex", "/shaders/outfit_default_fragment")
g_shaders.createOutfitShader("outfit_rainbow", "/shaders/outfit_rainbow_vertex", "/shaders/outfit_rainbow_fragment")
g_shaders.addTexture("outfit_rainbow", "/images/shaders/rainbow.png")
-- you can use creature:setOutfitShader("outfit_rainbow") to set shader
end
function terminate()

View File

@@ -282,13 +282,24 @@ function onSkillButtonClick(button)
end
function onExperienceChange(localPlayer, value)
setSkillValue('experience', comma_value(value))
local postFix = ""
if value > 1e15 then
postFix = "B"
value = math.floor(value / 1e9)
elseif value > 1e12 then
postFix = "M"
value = math.floor(value / 1e6)
elseif value > 1e9 then
postFix = "K"
value = math.floor(value / 1e3)
end
setSkillValue('experience', comma_value(value) .. postFix)
end
function onLevelChange(localPlayer, value, percent)
setSkillValue('level', value)
local text = tr('You have %s percent to go', 100 - percent) .. '\n' ..
tr('%s of experience left', comma_value(expToAdvance(localPlayer:getLevel(), localPlayer:getExperience())))
tr('%s of experience left', expToAdvance(localPlayer:getLevel(), localPlayer:getExperience()))
if localPlayer.expSpeed ~= nil then
local expPerHour = math.floor(localPlayer.expSpeed * 3600)
@@ -297,7 +308,7 @@ function onLevelChange(localPlayer, value, percent)
local hoursLeft = (nextLevelExp - localPlayer:getExperience()) / expPerHour
local minutesLeft = math.floor((hoursLeft - math.floor(hoursLeft))*60)
hoursLeft = math.floor(hoursLeft)
text = text .. '\n' .. comma_value(expPerHour) .. tr(' of experience per hour')
text = text .. '\n' .. tr('%d of experience per hour', expPerHour)
text = text .. '\n' .. tr('Next level in %d hours and %d minutes', hoursLeft, minutesLeft)
end
end

View File

@@ -14,32 +14,16 @@ function load()
local things = g_settings.getNode('things')
local datPath, sprPath
if things["data"] ~= nil and things["sprites"] ~= nil then
if things and things["data"] ~= nil and things["sprites"] ~= nil then
datPath = '/things/' .. things["data"]
if G.hdSprites and things["sprites_hd"] then
sprPath = '/things/' .. things["sprites_hd"]
else
sprPath = '/things/' .. things["sprites"]
end
sprPath = '/things/' .. things["sprites"]
else
if filename then
datPath = resolvepath('/things/' .. filename)
sprPath = resolvepath('/things/' .. filename)
if G.hdSprites then
local hdsprPath = resolvepath('/things/' .. filename .. '_hd')
if g_resources.fileExists(hdsprPath) then
sprPath = hdsprPath
end
end
else
datPath = resolvepath('/things/' .. version .. '/Tibia')
sprPath = resolvepath('/things/' .. version .. '/Tibia')
if G.hdSprites then
local hdsprPath = resolvepath('/things/' .. version .. '/Tibia_hd')
if g_resources.fileExists(hdsprPath) then
sprPath = hdsprPath
end
end
end
end
@@ -54,7 +38,7 @@ function load()
errorMessage = errorMessage .. tr("Unable to load dat file, please place a valid dat in '%s'", datPath) .. '\n'
end
end
if not g_sprites.loadSpr(sprPath, G.hdSprites or false) then
if not g_sprites.loadSpr(sprPath, false) then
errorMessage = errorMessage .. tr("Unable to load spr file, please place a valid spr in '%s'", sprPath)
end

View File

@@ -196,6 +196,8 @@ GameDrawAuraOnTop = 109
GamePacketSizeU32 = 110
GamePacketCompression = 111
GameOldInformationBar = 112
LastGameFeature = 130
TextColors = {

Binary file not shown.

Binary file not shown.

Binary file not shown.