Compare commits
132 Commits
v0.6.6
...
mobile_por
Author | SHA1 | Date | |
---|---|---|---|
![]() |
50180b594d | ||
![]() |
2fbd29ff8b | ||
![]() |
7cb1c9fae8 | ||
![]() |
0deeabbfda | ||
![]() |
01992aae7e | ||
![]() |
90ff929291 | ||
![]() |
0e87c8355b | ||
![]() |
ae25dbf6a5 | ||
![]() |
058b926a94 | ||
![]() |
4591a37844 | ||
![]() |
8abefb1505 | ||
![]() |
0afbfd58ce | ||
![]() |
6c5549dd46 | ||
![]() |
0e0da9ecbf | ||
![]() |
7a7f63586f | ||
![]() |
07a2995285 | ||
![]() |
b822e92c0e | ||
![]() |
0ecf2229e6 | ||
![]() |
47272519b5 | ||
![]() |
57b9ad88eb | ||
![]() |
2d65a0a3ed | ||
![]() |
b18c60eb77 | ||
![]() |
481adcdc21 | ||
![]() |
78bdf20603 | ||
![]() |
0642fb66cd | ||
![]() |
5ef55307f5 | ||
![]() |
b9848f360c | ||
![]() |
b5a14ddb68 | ||
![]() |
e4302562ff | ||
![]() |
471b8362e2 | ||
![]() |
a33fcd19b4 | ||
![]() |
7f2f70e1a6 | ||
![]() |
f9d183837a | ||
![]() |
02c6b1b6c7 | ||
![]() |
0c1540e531 | ||
![]() |
6893a5e98a | ||
![]() |
559e545e36 | ||
![]() |
cf90bb9807 | ||
![]() |
f35c939fc3 | ||
![]() |
34e2fa1d49 | ||
![]() |
944b220c90 | ||
![]() |
c5ea8c98fb | ||
![]() |
02ab50d8dd | ||
![]() |
48fefb03cb | ||
![]() |
7ea6c46b2c | ||
![]() |
0597ded1d3 | ||
![]() |
c3c2ac80e7 | ||
![]() |
6bd0e37670 | ||
![]() |
11990815a6 | ||
![]() |
ded8fef16b | ||
![]() |
53dbbd2ba3 | ||
![]() |
e4cdb3834b | ||
![]() |
86dd7958e1 | ||
![]() |
d0a365144e | ||
![]() |
d88505bf8d | ||
![]() |
789f86a778 | ||
![]() |
eecf8beb2f | ||
![]() |
121e6b29ef | ||
![]() |
c2b25abd37 | ||
![]() |
6016c87337 | ||
![]() |
11a81650e4 | ||
![]() |
44ddbc34e8 | ||
![]() |
4605c72546 | ||
![]() |
fe98efdc21 | ||
![]() |
8e5bbcd3a1 | ||
![]() |
2b96ae7f09 | ||
![]() |
f936ab9aab | ||
![]() |
84f6cdec86 | ||
![]() |
ab5bed456b | ||
![]() |
01c107ba62 | ||
![]() |
ff0947c270 | ||
![]() |
a3e6cc54b5 | ||
![]() |
fcd481ee15 | ||
![]() |
b237b713ef | ||
![]() |
3bffa6b04a | ||
![]() |
83dc129f03 | ||
![]() |
ca60efd786 | ||
![]() |
04b516a1a0 | ||
![]() |
5387f8fe83 | ||
![]() |
b5c7374890 | ||
![]() |
f51a160bde | ||
![]() |
cbf70c1d63 | ||
![]() |
28ff65be5a | ||
![]() |
74af47f4d6 | ||
![]() |
4c4e0b9d07 | ||
![]() |
6961492e00 | ||
![]() |
1c3cfddab0 | ||
![]() |
71931b961a | ||
![]() |
64e9406488 | ||
![]() |
900ebbd985 | ||
![]() |
cb7cea6809 | ||
![]() |
4e2ded571e | ||
![]() |
da2762dac3 | ||
![]() |
eb3c244023 | ||
![]() |
3157e7924f | ||
![]() |
92e2e8224f | ||
![]() |
1d022905ab | ||
![]() |
607dab01d6 | ||
![]() |
6edc73a8ba | ||
![]() |
596717bf32 | ||
![]() |
b5cea41f87 | ||
![]() |
8542f8bfd4 | ||
![]() |
fc76ca4523 | ||
![]() |
63f95317a2 | ||
![]() |
26fb35fd4d | ||
![]() |
a8f2bb19db | ||
![]() |
ddec9627b8 | ||
![]() |
24b1526534 | ||
![]() |
fcf545133b | ||
![]() |
fd97ccd402 | ||
![]() |
bf30fc0dc3 | ||
![]() |
4807c4a19d | ||
![]() |
15b3d439d6 | ||
![]() |
4b7770361d | ||
![]() |
50c36bb2ba | ||
![]() |
16f6a0019c | ||
![]() |
7f3f18f991 | ||
![]() |
6ab69b499d | ||
![]() |
f724506550 | ||
![]() |
389c7f2a60 | ||
![]() |
c28d2c1555 | ||
![]() |
1eb2bbd389 | ||
![]() |
7e34c452a1 | ||
![]() |
b3b314f01b | ||
![]() |
997daa2d49 | ||
![]() |
5ada7eb5ec | ||
![]() |
c49a6f3bf2 | ||
![]() |
aa924dc348 | ||
![]() |
f6fb785cea | ||
![]() |
25e7b1d7a3 | ||
![]() |
bdfb77166e | ||
![]() |
ac23b8e624 |
14
.gitignore
vendored
@@ -4,12 +4,26 @@ CMakeFiles
|
||||
cmake_install.cmake
|
||||
Makefile
|
||||
/otclient
|
||||
/android/project/build.xml
|
||||
/android/project/proguard-project.txt
|
||||
/android/project/gen
|
||||
/android/project/bin
|
||||
/android/project/libs
|
||||
/android/project/.settings
|
||||
/android/project/.classpath
|
||||
/android/project/.project
|
||||
/android/project/.cproject
|
||||
/android/project/local.properties
|
||||
/android/project/project.properties
|
||||
libs*
|
||||
.idea*
|
||||
/*.h
|
||||
/*.cxx
|
||||
*.o
|
||||
*.gch
|
||||
*.a
|
||||
*.exe
|
||||
*.so
|
||||
*.spr
|
||||
*.dat
|
||||
*.kdev*
|
||||
|
@@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 2.6)
|
||||
project(otclient)
|
||||
|
||||
set(VERSION "0.6.6")
|
||||
set(LIB_NAME "otc_framework")
|
||||
|
||||
option(FRAMEWORK_SOUND "Use SOUND " ON)
|
||||
option(FRAMEWORK_GRAPHICS "Use GRAPHICS " ON)
|
||||
@@ -20,10 +19,6 @@ endif()
|
||||
|
||||
option(USE_PCH "Use precompiled header (speed up compile)" OFF)
|
||||
|
||||
set(executable_SOURCES
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
# add executable icon for win32 platforms
|
||||
if(WIN32)
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/otcicon.o
|
||||
@@ -36,20 +31,19 @@ endif()
|
||||
|
||||
add_definitions(-D"VERSION=\\"${VERSION}\\"")
|
||||
|
||||
# we want framework to be a library for faster compilation/linking
|
||||
if(USE_STATIC_LIBS)
|
||||
add_library(${LIB_NAME} ${framework_SOURCES})
|
||||
set(executable_SOURCES
|
||||
src/main.cpp
|
||||
)
|
||||
|
||||
if(ANDROID)
|
||||
# add shared library for android
|
||||
add_library(${PROJECT_NAME} SHARED ${framework_SOURCES} ${client_SOURCES} ${executable_SOURCES})
|
||||
else()
|
||||
add_library(${LIB_NAME} SHARED ${framework_SOURCES})
|
||||
message(STATUS "Linking to shared ${LIB_NAME}, make sure you copy the DLL/SO/dylib with the executable!")
|
||||
# add client executable
|
||||
add_executable(${PROJECT_NAME} ${framework_SOURCES} ${client_SOURCES} ${executable_SOURCES})
|
||||
endif()
|
||||
target_link_libraries(${LIB_NAME} ${framework_LIBRARIES})
|
||||
|
||||
# add client executable
|
||||
add_executable(${PROJECT_NAME} ${client_SOURCES} ${executable_SOURCES})
|
||||
|
||||
# target link libraries
|
||||
target_link_libraries(${PROJECT_NAME} ${LIB_NAME})
|
||||
target_link_libraries(${PROJECT_NAME} ${framework_LIBRARIES})
|
||||
|
||||
if(USE_PCH)
|
||||
include(cotire)
|
||||
@@ -61,7 +55,7 @@ endif()
|
||||
|
||||
# installation
|
||||
set(DATA_INSTALL_DIR share/${PROJECT_NAME})
|
||||
install(TARGETS ${PROJECT_NAME} ${LIB_NAME}
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib)
|
||||
|
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
OTClient is made available under the MIT License
|
||||
|
||||
Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
|
||||
Copyright (c) 2010-2015 OTClient <https://github.com/edubart/otclient>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
15
README.md
@@ -1,4 +1,4 @@
|
||||
[](http://travis-ci.org/edubart/otclient)
|
||||
[](http://travis-ci.org/edubart/otclient) [](https://gitter.im/edubart/otclient?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
### What is otclient?
|
||||
|
||||
Otclient is an alternative Tibia client for usage with otserv. It aims to be complete and flexible,
|
||||
@@ -8,6 +8,17 @@ that each functionality is a separated module, giving the possibility to users m
|
||||
anything easily. Users can also create new mods and extend game interface for their own purposes.
|
||||
Otclient is written in C++2011, the upcoming C++ standard and heavily scripted in lua.
|
||||
|
||||
## The Mobile Project
|
||||
This is a fork of edubart's otclient. The objective of this fork it's to develop a runnable otclient on mobiles devices.
|
||||
|
||||
Tasks that need to do:
|
||||
- [X] Compile on Android devices
|
||||
- [ ] Compile on Apple devices
|
||||
- [ ] Adapt the UI reusing the existing lua code
|
||||
|
||||
Current compiling tutorials:
|
||||
* [Compiling for Android](https://github.com/edubart/otclient/wiki/Compiling-for-Android)
|
||||
|
||||
### Where do I download?
|
||||
|
||||
The latest commits compiled for Windows can be found here.
|
||||
@@ -41,7 +52,7 @@ A package with all required libraries for compiling OTClient on Windows can be f
|
||||
In short, if you need to compile OTClient, follow these tutorials:
|
||||
* [Compiling on Windows](https://github.com/edubart/otclient/wiki/Compiling-on-Windows)
|
||||
* [Compiling on Linux](https://github.com/edubart/otclient/wiki/Compiling-on-Linux)
|
||||
|
||||
* [Compiling on OS X](https://github.com/edubart/otclient/wiki/Compiling-on-Mac-OS-X)
|
||||
|
||||
|
||||
### Need help?
|
||||
|
1767
android/android.toolchain.cmake
Normal file
18
android/compile_android_unix.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
mkdir -p ../build_android && cd ../build
|
||||
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=../android/android.toolchain.cmake -DANDROID_ABI=armeabi-v7a -DANDROID_NATIVE_API_LEVEL=android-16 -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.6 ..
|
||||
make
|
||||
|
||||
cd ../
|
||||
|
||||
cp -r libs android/project/
|
||||
cp $ANDROID_NDK/libraries/lib/libSDL2.so android/project/libs/armeabi-v7a
|
||||
|
||||
cd android/project
|
||||
|
||||
android update project -p . --name OTClientMob --target android-16
|
||||
ant debug
|
||||
|
||||
cd bin
|
||||
adb install -r OTClientMob-debug.apk
|
18
android/compile_android_windows.bat
Normal file
@@ -0,0 +1,18 @@
|
||||
cd ..
|
||||
mkdir build_android
|
||||
cd build_android
|
||||
|
||||
cmake -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=../android/android.toolchain.cmake -DANDROID_ABI=armeabi-v7a -DANDROID_NATIVE_API_LEVEL=android-16 -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.6 ..
|
||||
make
|
||||
|
||||
cd ../
|
||||
|
||||
xcopy /E /Y libs android\project\libs
|
||||
|
||||
cd android\project
|
||||
|
||||
call android update project -p . --name OTClient --target android-16
|
||||
call ant debug
|
||||
|
||||
cd bin
|
||||
adb install -r OTClient-debug.apk
|
21
android/compile_android_windows_debug.bat
Normal file
@@ -0,0 +1,21 @@
|
||||
cd ..
|
||||
mkdir build_android
|
||||
cd build_android
|
||||
|
||||
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../android/android.toolchain.cmake -DANDROID_ABI=armeabi-v7a -DANDROID_NATIVE_API_LEVEL=android-16 -DANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.6 ..
|
||||
make
|
||||
|
||||
cd ../
|
||||
|
||||
ECHO D|xcopy /E /Y android\project\jni\libSDL2.so android\project\libs\armeabi-v7a
|
||||
xcopy /E /Y libs\armeabi-v7a\libotclient.so android\project\jni
|
||||
|
||||
cd android\project
|
||||
|
||||
call android update project -p . --name OTClient --target android-16
|
||||
call ndk-build.cmd all NDK_DEBUG=1
|
||||
call ant clean
|
||||
call ant debug
|
||||
|
||||
cd bin
|
||||
adb install -r OTClient-debug.apk
|
36
android/project/AndroidManifest.xml
Normal file
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.otclient.mobile" android:versionCode="1" android:versionName="1.0">
|
||||
|
||||
<application
|
||||
android:debuggable="true"
|
||||
android:label="@string/app_name"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:hardwareAccelerated="true" >
|
||||
|
||||
<activity android:name="MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:screenOrientation="landscape">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="otclient"/>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<!-- Android 2.3.3 -->
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="16"/>
|
||||
|
||||
<!-- OpenGL ES 2.0 -->
|
||||
<uses-feature android:glEsVersion="0x00020000"/>
|
||||
|
||||
<!-- Allow writing to external storage -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<!-- Allow make internet connections -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
6
android/project/jni/Android.mk
Normal file
@@ -0,0 +1,6 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libotclient
|
||||
LOCAL_SRC_FILES := libotclient.so
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
2
android/project/jni/Application.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
APP_PLATFORM := android-9
|
||||
APP_ABI := armeabi-v7a
|
BIN
android/project/res/drawable-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 538 B |
BIN
android/project/res/drawable-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 429 B |
BIN
android/project/res/drawable-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 822 B |
BIN
android/project/res/drawable-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
android/project/res/drawable-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
4
android/project/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">OTClient</string>
|
||||
</resources>
|
52
android/project/src/com/otclient/mobile/FakeEditText.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package com.otclient.mobile;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
public class FakeEditText extends View implements View.OnKeyListener {
|
||||
InputConnection ic;
|
||||
|
||||
public FakeEditText() {
|
||||
super(MainActivity.getInstance());
|
||||
setFocusableInTouchMode(true);
|
||||
setFocusable(true);
|
||||
setOnKeyListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCheckIsTextEditor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override // This handles the hardware keyboard input
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
if (event.isPrintingKey()) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
onNativeKeyDown(keyCode);
|
||||
return true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
onNativeKeyUp(keyCode);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
ic = new InputConnectionForNative(this, true);
|
||||
|
||||
return ic;
|
||||
}
|
||||
|
||||
public static native void onNativeKeyDown(int keyCode);
|
||||
public static native void onNativeKeyUp(int keyCode);
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package com.otclient.mobile;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
|
||||
public class InputConnectionForNative extends BaseInputConnection {
|
||||
|
||||
public InputConnectionForNative(View targetView, boolean fullEditor) {
|
||||
super(targetView, fullEditor);
|
||||
}
|
||||
|
||||
@Override // This handles the keycodes from soft keyboard
|
||||
public boolean sendKeyEvent(KeyEvent event) {
|
||||
int keyCode = event.getKeyCode();
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (event.isPrintingKey()) {
|
||||
commitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
}
|
||||
FakeEditText.onNativeKeyDown(keyCode);
|
||||
return true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
FakeEditText.onNativeKeyUp(keyCode);
|
||||
return true;
|
||||
}
|
||||
return super.sendKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override // Typed text
|
||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||
nativeCommitText(text.toString(), newCursorPosition);
|
||||
|
||||
return super.commitText(text, newCursorPosition);
|
||||
}
|
||||
|
||||
@Override // Workaround to capture backspace key
|
||||
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
if (beforeLength == 1 && afterLength == 0) {
|
||||
return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
|
||||
&& super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
|
||||
}
|
||||
|
||||
return super.deleteSurroundingText(beforeLength, afterLength);
|
||||
}
|
||||
|
||||
public static native void nativeCommitText(String text, int newCursorPosition);
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package com.otclient.mobile;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
public class KeyboardSoftHandler{
|
||||
private View editText;
|
||||
|
||||
public KeyboardSoftHandler() {
|
||||
editText = new FakeEditText();
|
||||
MainActivity.getInstance()
|
||||
.addViewToLayout(editText);
|
||||
}
|
||||
|
||||
public void showKeyboardSoft() {
|
||||
editText.setVisibility(View.VISIBLE);
|
||||
editText.requestFocus();
|
||||
|
||||
InputMethodManager imm = (InputMethodManager) MainActivity
|
||||
.getInstance().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(editText, 0);
|
||||
}
|
||||
|
||||
public void hideKeyboardSoft() {
|
||||
editText.setVisibility(View.GONE);
|
||||
|
||||
InputMethodManager imm = (InputMethodManager) MainActivity
|
||||
.getInstance().getSystemService(
|
||||
Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||
}
|
||||
}
|
110
android/project/src/com/otclient/mobile/MainActivity.java
Normal file
@@ -0,0 +1,110 @@
|
||||
package com.otclient.mobile;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.RelativeLayout.LayoutParams;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
public static final String APP_TAG = "OTClientMob";
|
||||
|
||||
private static MainActivity instance;
|
||||
private RelativeLayout layout;
|
||||
|
||||
private static boolean started;
|
||||
|
||||
static {
|
||||
started = false;
|
||||
//android.os.Debug.waitForDebugger();
|
||||
System.loadLibrary("otclient");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Log.v(APP_TAG, "onCreate()");
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
instance = this;
|
||||
|
||||
layout = new RelativeLayout(this);
|
||||
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
layout.setLayoutParams(params);
|
||||
|
||||
setContentView(layout);
|
||||
|
||||
if( !started ) {
|
||||
nativeInit();
|
||||
started = true;
|
||||
}
|
||||
|
||||
NativeFacadeCalls.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Log.v(APP_TAG, "onPause()");
|
||||
super.onPause();
|
||||
nativePause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
Log.v(APP_TAG, "onResume()");
|
||||
super.onResume();
|
||||
|
||||
if( NativeFacadeCalls.isSurfaceReady() )
|
||||
nativeResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Log.v(APP_TAG, "onDestroy()");
|
||||
super.onDestroy();
|
||||
|
||||
if(isFinishing()) {
|
||||
NativeFacadeCalls.destroy();
|
||||
destroy();
|
||||
nativeDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
private void destroy() {
|
||||
instance = null;
|
||||
layout = null;
|
||||
}
|
||||
|
||||
@Override // Ignore certain special keys so they're handled by Android
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
int keyCode = event.getKeyCode();
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
|
||||
keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
|
||||
keyCode == KeyEvent.KEYCODE_CAMERA ||
|
||||
keyCode == 168 || /* API 11: KeyEvent.KEYCODE_ZOOM_IN */
|
||||
keyCode == 169 /* API 11: KeyEvent.KEYCODE_ZOOM_OUT */
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
public static MainActivity getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void addViewToLayout(View view) {
|
||||
layout.addView(view);
|
||||
}
|
||||
|
||||
public native void nativeInit();
|
||||
public native void nativePause();
|
||||
public native void nativeResume();
|
||||
public native void nativeDestroy();
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package com.otclient.mobile;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.Surface;
|
||||
|
||||
public class NativeFacadeCalls {
|
||||
private static NativeSurfaceView nativeSurfaceView;
|
||||
private static KeyboardSoftHandler keyboardSoftHandler;
|
||||
private static Handler handler;
|
||||
|
||||
public static void initialize() {
|
||||
handler = new Handler(Looper.getMainLooper());
|
||||
keyboardSoftHandler = new KeyboardSoftHandler();
|
||||
nativeSurfaceView = new NativeSurfaceView();
|
||||
MainActivity.getInstance().addViewToLayout(nativeSurfaceView);
|
||||
}
|
||||
|
||||
public static void destroy() {
|
||||
handler = null;
|
||||
nativeSurfaceView = null;
|
||||
keyboardSoftHandler = null;
|
||||
}
|
||||
|
||||
public static boolean isSurfaceReady() {
|
||||
return nativeSurfaceView.isSurfaceReady();
|
||||
}
|
||||
|
||||
/*
|
||||
* Static methods called from JNI
|
||||
*/
|
||||
|
||||
public static Surface getNativeSurface() {
|
||||
return nativeSurfaceView.getSurface();
|
||||
}
|
||||
|
||||
public static void showKeyboardSoft() {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
keyboardSoftHandler.showKeyboardSoft();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package com.otclient.mobile;
|
||||
|
||||
public class NativeMainThread {
|
||||
private static final NativeMainThread instance;
|
||||
private Thread nativeThread;
|
||||
|
||||
static {
|
||||
instance = new NativeMainThread();
|
||||
}
|
||||
|
||||
private NativeMainThread() {}
|
||||
|
||||
public void start() {
|
||||
if( nativeThread == null ) {
|
||||
nativeThread = new Thread(
|
||||
new NativeThread(), "NativeThread" );
|
||||
nativeThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static NativeMainThread getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Native methods implemented on C++
|
||||
*/
|
||||
|
||||
public native void nativeStartApp();
|
||||
|
||||
private class NativeThread implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
nativeStartApp();
|
||||
}
|
||||
}
|
||||
}
|
130
android/project/src/com/otclient/mobile/NativeSurfaceView.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package com.otclient.mobile;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
|
||||
public class NativeSurfaceView extends SurfaceView implements
|
||||
SurfaceHolder.Callback, View.OnTouchListener {
|
||||
|
||||
private Surface surface;
|
||||
private GestureDetector gestureDetector;
|
||||
private int currentWidth;
|
||||
private int currentHeight;
|
||||
private boolean surfaceReady;
|
||||
|
||||
private final int LONGPRESS_EVENT = 3;
|
||||
|
||||
public NativeSurfaceView() {
|
||||
super(MainActivity.getInstance());
|
||||
getHolder().addCallback(this);
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnTouchListener(this);
|
||||
|
||||
currentWidth = 0;
|
||||
currentHeight = 0;
|
||||
surfaceReady = false;
|
||||
|
||||
gestureDetector = new GestureDetector(
|
||||
new GestureDetector.SimpleOnGestureListener() {
|
||||
public void onLongPress(MotionEvent event) {
|
||||
onNativeTouch(LONGPRESS_EVENT, event.getX(), event.getY());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.d(MainActivity.APP_TAG, "surfaceCreated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width,
|
||||
int height) {
|
||||
Log.d(MainActivity.APP_TAG, "surfaceChanged");
|
||||
|
||||
surface = holder.getSurface();
|
||||
|
||||
currentWidth = width;
|
||||
currentHeight = height;
|
||||
surfaceReady = true;
|
||||
|
||||
onNativeResize(width, height);
|
||||
onNativeSurfaceChanged();
|
||||
|
||||
NativeMainThread.getInstance().start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.d(MainActivity.APP_TAG, "surfaceDestroyed");
|
||||
surface = null;
|
||||
surfaceReady = false;
|
||||
|
||||
onNativeSurfaceDestroyed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
gestureDetector.onTouchEvent(event);
|
||||
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||
final int pointerCount = event.getPointerCount();
|
||||
int action = event.getActionMasked();
|
||||
int i = -1;
|
||||
float x,y;
|
||||
|
||||
switch(action) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
x = event.getX(i);
|
||||
y = event.getY(i);
|
||||
onNativeTouch(action, x, y);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Primary pointer up/down, the index is always zero
|
||||
i = 0;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
// Non primary pointer up/down
|
||||
if (i == -1) {
|
||||
i = event.getActionIndex();
|
||||
}
|
||||
|
||||
x = event.getX(i);
|
||||
y = event.getY(i);
|
||||
onNativeTouch(action, x, y);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Surface getSurface() {
|
||||
return surface;
|
||||
}
|
||||
|
||||
public boolean isSurfaceReady() {
|
||||
return surfaceReady;
|
||||
}
|
||||
|
||||
/*
|
||||
* Native methods implemented on C++
|
||||
*/
|
||||
public native void onNativeSurfaceChanged();
|
||||
public native void onNativeSurfaceDestroyed();
|
||||
public native void onNativeResize(int width, int height);
|
||||
public native void onNativeTouch(int actionType, float x, float y);
|
||||
}
|
BIN
data/images/game/skull_socket.png
Normal file
After Width: | Height: | Size: 338 B |
BIN
data/images/game/slots/ammo-blessed.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
data/images/game/slots/back-blessed.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
data/images/game/slots/body-blessed.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
data/images/game/slots/feet-blessed.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
data/images/game/slots/finger-blessed.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
data/images/game/slots/head-blessed.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
data/images/game/slots/left-hand-blessed.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
data/images/game/slots/legs-blessed.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
data/images/game/slots/neck-blessed.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
data/images/game/slots/right-hand-blessed.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
data/images/topbuttons/unjustifiedpoints.png
Normal file
After Width: | Height: | Size: 421 B |
BIN
data/images/ui/item-blessed.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
@@ -4,6 +4,10 @@ locale = {
|
||||
charset = "cp1252",
|
||||
languageName = "Deutsch",
|
||||
|
||||
formatNumbers = true,
|
||||
decimalSeperator = ',',
|
||||
thousandsSeperator = ' ',
|
||||
|
||||
translation = {
|
||||
["1a) Offensive Name"] = false,
|
||||
["1b) Invalid Name Format"] = false,
|
||||
|
@@ -3,6 +3,10 @@ locale = {
|
||||
charset = "cp1252",
|
||||
languageName = "English",
|
||||
|
||||
formatNumbers = true,
|
||||
decimalSeperator = '.',
|
||||
thousandsSeperator = ',',
|
||||
|
||||
-- translations are not needed because everything is already in english
|
||||
translation = {}
|
||||
}
|
||||
|
@@ -7,6 +7,10 @@ locale = {
|
||||
charset = "cp1252",
|
||||
languageName = "Espa<EFBFBD>ol",
|
||||
|
||||
formatNumbers = true,
|
||||
decimalSeperator = ',',
|
||||
thousandsSeperator = '.',
|
||||
|
||||
translation = {
|
||||
["1a) Offensive Name"] = "1a) Nombre ofensivo",
|
||||
["1b) Invalid Name Format"] = "1b) Formato invalido para nombre",
|
||||
|
@@ -2,6 +2,10 @@ locale = {
|
||||
name = "pl",
|
||||
languageName = "Polski",
|
||||
|
||||
formatNumbers = true,
|
||||
decimalSeperator = '.',
|
||||
thousandsSeperator = ' ',
|
||||
|
||||
translation = {
|
||||
["1a) Offensive Name"] = "1a) Obrazliwe Imie",
|
||||
["1b) Invalid Name Format"] = "1b) Niepoprawny Format Imienia",
|
||||
|
@@ -3,6 +3,10 @@ locale = {
|
||||
charset = "cp1252",
|
||||
languageName = "Portugu<EFBFBD>s",
|
||||
|
||||
formatNumbers = true,
|
||||
decimalSeperator = ',',
|
||||
thousandsSeperator = '.',
|
||||
|
||||
-- As tradu<64><75>es devem vir sempre em ordem alfab<61>tica.
|
||||
translation = {
|
||||
["%d of experience per hour"] = "%d de experi<72>ncia por hora",
|
||||
|
@@ -5,6 +5,10 @@ locale = {
|
||||
charset = "cp1252",
|
||||
languageName = "Svenska",
|
||||
|
||||
formatNumbers = true,
|
||||
decimalSeperator = ',',
|
||||
thousandsSeperator = ' ',
|
||||
|
||||
translation = {
|
||||
["1a) Offensive Name"] = "1a) Offensivt Namn",
|
||||
["1b) Invalid Name Format"] = "1b) Ogiltigt Namnformat",
|
||||
|
@@ -8,6 +8,7 @@ Button < UIButton
|
||||
image-clip: 0 0 22 23
|
||||
image-border: 3
|
||||
padding: 5 10 5 10
|
||||
opacity: 1.0
|
||||
|
||||
$hover !disabled:
|
||||
image-clip: 0 23 22 23
|
||||
@@ -45,6 +46,7 @@ NextButton < UIButton
|
||||
size: 12 21
|
||||
image-source: /images/ui/arrow_horizontal
|
||||
image-clip: 12 0 12 21
|
||||
image-color: #ffffff
|
||||
|
||||
$hover !disabled:
|
||||
image-clip: 12 21 12 21
|
||||
@@ -53,12 +55,13 @@ NextButton < UIButton
|
||||
image-clip: 12 21 12 21
|
||||
|
||||
$disabled:
|
||||
image-color: #dfdfdf55
|
||||
image-color: #dfdfdf88
|
||||
|
||||
PreviousButton < UIButton
|
||||
size: 12 21
|
||||
image-source: /images/ui/arrow_horizontal
|
||||
image-clip: 0 0 12 21
|
||||
image-color: #ffffff
|
||||
|
||||
$hover !disabled:
|
||||
image-clip: 0 21 12 21
|
||||
@@ -67,7 +70,7 @@ PreviousButton < UIButton
|
||||
image-clip: 0 21 12 21
|
||||
|
||||
$disabled:
|
||||
image-color: #dfdfdf55
|
||||
image-color: #dfdfdf88
|
||||
|
||||
AddButton < UIButton
|
||||
size: 20 20
|
||||
|
@@ -27,7 +27,7 @@ MoveableTabBarButton < UIButton
|
||||
color: #dfdfdf
|
||||
|
||||
$on !checked:
|
||||
color: #dfdfdf
|
||||
color: #de6f6f
|
||||
|
||||
TabBar < UITabBar
|
||||
size: 80 21
|
||||
@@ -36,10 +36,11 @@ TabBar < UITabBar
|
||||
anchors.fill: parent
|
||||
TabBarPanel < Panel
|
||||
TabBarButton < UIButton
|
||||
size: 22 23
|
||||
size: 20 21
|
||||
image-source: /images/ui/tabbutton_square
|
||||
image-source: /images/ui/tabbutton_square
|
||||
image-color: #dfdfdf
|
||||
image-clip: 0 0 22 23
|
||||
image-clip: 0 0 20 21
|
||||
image-border: 3
|
||||
image-border-bottom: 0
|
||||
icon-color: #dfdfdf
|
||||
@@ -55,7 +56,7 @@ TabBarButton < UIButton
|
||||
margin-left: 5
|
||||
|
||||
$hover !checked:
|
||||
image-clip: 0 23 22 23
|
||||
image-clip: 0 21 20 21
|
||||
color: #dfdfdf
|
||||
|
||||
$disabled:
|
||||
@@ -63,7 +64,7 @@ TabBarButton < UIButton
|
||||
icon-color: #dfdfdf
|
||||
|
||||
$checked:
|
||||
image-clip: 0 46 22 23
|
||||
image-clip: 0 42 20 21
|
||||
color: #dfdfdf
|
||||
|
||||
$on !checked:
|
||||
@@ -73,6 +74,14 @@ TabBarRounded < TabBar
|
||||
TabBarRoundedPanel < TabBarPanel
|
||||
TabBarRoundedButton < TabBarButton
|
||||
image-source: /images/ui/tabbutton_rounded
|
||||
size: 22 23
|
||||
image-clip: 0 0 22 23
|
||||
|
||||
$hover !checked:
|
||||
image-clip: 0 23 22 23
|
||||
|
||||
$checked:
|
||||
image-clip: 0 46 22 23
|
||||
|
||||
TabBarVertical < UITabBar
|
||||
width: 96
|
||||
|
@@ -1,26 +1,62 @@
|
||||
Table < UITable
|
||||
layout: verticalBox
|
||||
header-column-style: HeaderTableColumn
|
||||
header-row-style: HeaderTableRow
|
||||
header-column-style: TableHeaderColumn
|
||||
header-row-style: TableHeaderRow
|
||||
column-style: TableColumn
|
||||
row-style: TableRow
|
||||
|
||||
TableData < UIScrollArea
|
||||
layout: verticalBox
|
||||
|
||||
TableRow < Label
|
||||
TableRow < UITableRow
|
||||
layout: horizontalBox
|
||||
height: 10
|
||||
text-wrap: true
|
||||
focusable: true
|
||||
even-background-color: alpha
|
||||
odd-background-color: #00000022
|
||||
|
||||
$focus:
|
||||
background-color: #294f6d
|
||||
color: #ffffff
|
||||
|
||||
TableColumn < Label
|
||||
width: 30
|
||||
text-wrap: true
|
||||
focusable: false
|
||||
|
||||
TableHeaderRow < Label
|
||||
layout: horizontalBox
|
||||
focusable: false
|
||||
height: 10
|
||||
text-wrap: true
|
||||
|
||||
TableHeaderColumn < Button
|
||||
width: 30
|
||||
TableHeaderColumn < UITableHeaderColumn
|
||||
font: verdana-11px-antialised
|
||||
background-color: alpha
|
||||
color: #dfdfdfff
|
||||
height: 23
|
||||
focusable: true
|
||||
text-offset: 0 0
|
||||
image-source: /images/ui/button
|
||||
image-color: #dfdfdf
|
||||
image-clip: 0 0 22 23
|
||||
image-border: 3
|
||||
padding: 5 10 5 10
|
||||
enabled: false
|
||||
focusable: false
|
||||
|
||||
$hover !disabled:
|
||||
image-clip: 0 23 22 23
|
||||
|
||||
$pressed:
|
||||
image-clip: 0 46 22 23
|
||||
text-offset: 1 1
|
||||
|
||||
$disabled:
|
||||
color: #dfdfdf88
|
||||
opacity: 0.8
|
||||
|
||||
SortableTableHeaderColumn < TableHeaderColumn
|
||||
enabled: true
|
||||
focusable: true
|
@@ -27,7 +27,7 @@ local function tryLogin(charInfo, tries)
|
||||
|
||||
CharacterList.hide()
|
||||
|
||||
g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName)
|
||||
g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName, G.authenticatorToken, G.sessionKey)
|
||||
|
||||
loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to game server...'))
|
||||
connect(loadBox, { onCancel = function()
|
||||
@@ -109,6 +109,16 @@ function onGameLoginError(message)
|
||||
end
|
||||
end
|
||||
|
||||
function onGameLoginToken(unknown)
|
||||
CharacterList.destroyLoadBox()
|
||||
-- TODO: make it possible to enter a new token here / prompt token
|
||||
errorBox = displayErrorBox(tr("Two-Factor Authentification"), 'A new authentification token is required.\nPlease login again.')
|
||||
errorBox.onOk = function()
|
||||
errorBox = nil
|
||||
EnterGame.show()
|
||||
end
|
||||
end
|
||||
|
||||
function onGameConnectionError(message, code)
|
||||
CharacterList.destroyLoadBox()
|
||||
local text = translateNetworkError(code, g_game.getProtocolGame() and g_game.getProtocolGame():isConnecting(), message)
|
||||
@@ -131,6 +141,7 @@ end
|
||||
-- public functions
|
||||
function CharacterList.init()
|
||||
connect(g_game, { onLoginError = onGameLoginError })
|
||||
connect(g_game, { onLoginToken = onGameLoginToken })
|
||||
connect(g_game, { onUpdateNeeded = onGameUpdateNeeded })
|
||||
connect(g_game, { onConnectionError = onGameConnectionError })
|
||||
connect(g_game, { onGameStart = CharacterList.destroyLoadBox })
|
||||
@@ -144,6 +155,7 @@ end
|
||||
|
||||
function CharacterList.terminate()
|
||||
disconnect(g_game, { onLoginError = onGameLoginError })
|
||||
disconnect(g_game, { onLoginToken = onGameLoginToken })
|
||||
disconnect(g_game, { onUpdateNeeded = onGameUpdateNeeded })
|
||||
disconnect(g_game, { onConnectionError = onGameConnectionError })
|
||||
disconnect(g_game, { onGameStart = CharacterList.destroyLoadBox })
|
||||
|
@@ -16,7 +16,7 @@ CharacterWidget < UIWidget
|
||||
|
||||
Label
|
||||
id: name
|
||||
color: #aaaaaa
|
||||
color: #bbbbbb
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
font: verdana-11px-monochrome
|
||||
@@ -29,8 +29,7 @@ CharacterWidget < UIWidget
|
||||
|
||||
Label
|
||||
id: worldName
|
||||
color: #ffffff
|
||||
color: #aaaaaa
|
||||
color: #bbbbbb
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
margin-right: 5
|
||||
@@ -45,16 +44,21 @@ CharacterWidget < UIWidget
|
||||
MainWindow
|
||||
id: charactersWindow
|
||||
!text: tr('Character List')
|
||||
size: 250 248
|
||||
visible: false
|
||||
@onEnter: CharacterList.doLogin()
|
||||
@onEscape: CharacterList.hide(true)
|
||||
@onSetup: |
|
||||
g_keyboard.bindKeyPress('Up', function() self:getChildById('characters'):focusPreviousChild(KeyboardFocusReason) end, self)
|
||||
g_keyboard.bindKeyPress('Down', function() self:getChildById('characters'):focusNextChild(KeyboardFocusReason) end, self)
|
||||
if g_game.getFeature(GamePreviewState) then
|
||||
self:setSize({width = 350, height = 400})
|
||||
else
|
||||
self:setSize({width = 250, height = 248})
|
||||
end
|
||||
|
||||
TextList
|
||||
id: characters
|
||||
background-color: #565656
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: characterListScrollBar.left
|
||||
|
@@ -33,10 +33,17 @@ local function onMotd(protocol, motd)
|
||||
end
|
||||
end
|
||||
|
||||
local function onSessionKey(protocol, sessionKey)
|
||||
G.sessionKey = sessionKey
|
||||
end
|
||||
|
||||
local function onCharacterList(protocol, characters, account, otui)
|
||||
-- Try add server to the server list
|
||||
ServerList.add(G.host, G.port, g_game.getClientVersion())
|
||||
|
||||
-- Save 'Stay logged in' setting
|
||||
g_settings.set('staylogged', enterGame:getChildById('stayLoggedBox'):isChecked())
|
||||
|
||||
if enterGame:getChildById('rememberPasswordBox'):isChecked() then
|
||||
local account = g_crypt.encrypt(G.account)
|
||||
local password = g_crypt.encrypt(G.password)
|
||||
@@ -59,13 +66,19 @@ local function onCharacterList(protocol, characters, account, otui)
|
||||
loadBox:destroy()
|
||||
loadBox = nil
|
||||
|
||||
for _, characterInfo in pairs(characters) do
|
||||
if characterInfo.previewState and characterInfo.previewState ~= PreviewState.Default then
|
||||
characterInfo.worldName = characterInfo.worldName .. ', Preview'
|
||||
end
|
||||
end
|
||||
|
||||
CharacterList.create(characters, account, otui)
|
||||
CharacterList.show()
|
||||
|
||||
if motdEnabled then
|
||||
local lastMotdNumber = g_settings.getNumber("motd")
|
||||
if G.motdNumber and G.motdNumber ~= lastMotdNumber then
|
||||
g_settings.set("motd", motdNumber)
|
||||
g_settings.set("motd", G.motdNumber)
|
||||
motdWindow = displayInfoBox(tr('Message of the day'), G.motdMessage)
|
||||
connect(motdWindow, { onOk = function() CharacterList.show() motdWindow = nil end })
|
||||
CharacterList.hide()
|
||||
@@ -103,9 +116,10 @@ function EnterGame.init()
|
||||
local password = g_settings.get('password')
|
||||
local host = g_settings.get('host')
|
||||
local port = g_settings.get('port')
|
||||
local stayLogged = g_settings.getBoolean('staylogged')
|
||||
local autologin = g_settings.getBoolean('autologin')
|
||||
local clientVersion = g_settings.getInteger('client-version')
|
||||
if clientVersion == 0 then clientVersion = 860 end
|
||||
if clientVersion == 0 then clientVersion = 1074 end
|
||||
|
||||
if port == nil or port == 0 then port = 7171 end
|
||||
|
||||
@@ -115,6 +129,7 @@ function EnterGame.init()
|
||||
enterGame:getChildById('serverHostTextEdit'):setText(host)
|
||||
enterGame:getChildById('serverPortTextEdit'):setText(port)
|
||||
enterGame:getChildById('autoLoginBox'):setChecked(autologin)
|
||||
enterGame:getChildById('stayLoggedBox'):setChecked(stayLogged)
|
||||
|
||||
clientBox = enterGame:getChildById('clientComboBox')
|
||||
for _, proto in pairs(g_game.getSupportedClients()) do
|
||||
@@ -122,6 +137,10 @@ function EnterGame.init()
|
||||
end
|
||||
clientBox:setCurrentOption(clientVersion)
|
||||
|
||||
EnterGame.toggleAuthenticatorToken(clientVersion, true)
|
||||
EnterGame.toggleStayLoggedBox(clientVersion, true)
|
||||
connect(clientBox, { onOptionChange = EnterGame.onClientVersionChange })
|
||||
|
||||
enterGame:hide()
|
||||
|
||||
if g_app.isRunning() and not g_game.isOnline() then
|
||||
@@ -146,6 +165,7 @@ end
|
||||
|
||||
function EnterGame.terminate()
|
||||
g_keyboard.unbindKeyDown('Ctrl+G')
|
||||
disconnect(clientBox, { onOptionChange = EnterGame.onClientVersionChange })
|
||||
enterGame:destroy()
|
||||
enterGame = nil
|
||||
enterGameButton:destroy()
|
||||
@@ -204,14 +224,80 @@ end
|
||||
function EnterGame.clearAccountFields()
|
||||
enterGame:getChildById('accountNameTextEdit'):clearText()
|
||||
enterGame:getChildById('accountPasswordTextEdit'):clearText()
|
||||
enterGame:getChildById('authenticatorTokenTextEdit'):clearText()
|
||||
enterGame:getChildById('accountNameTextEdit'):focus()
|
||||
g_settings.remove('account')
|
||||
g_settings.remove('password')
|
||||
end
|
||||
|
||||
function EnterGame.toggleAuthenticatorToken(clientVersion, init)
|
||||
local enabled = (clientVersion >= 1072)
|
||||
if enabled == enterGame.authenticatorEnabled then
|
||||
return
|
||||
end
|
||||
|
||||
enterGame:getChildById('authenticatorTokenLabel'):setOn(enabled)
|
||||
enterGame:getChildById('authenticatorTokenTextEdit'):setOn(enabled)
|
||||
|
||||
local newHeight = enterGame:getHeight()
|
||||
local newY = enterGame:getY()
|
||||
if enabled then
|
||||
newY = newY - enterGame.authenticatorHeight
|
||||
newHeight = newHeight + enterGame.authenticatorHeight
|
||||
else
|
||||
newY = newY + enterGame.authenticatorHeight
|
||||
newHeight = newHeight - enterGame.authenticatorHeight
|
||||
end
|
||||
|
||||
if not init then
|
||||
enterGame:breakAnchors()
|
||||
enterGame:setY(newY)
|
||||
enterGame:bindRectToParent()
|
||||
end
|
||||
enterGame:setHeight(newHeight)
|
||||
|
||||
enterGame.authenticatorEnabled = enabled
|
||||
end
|
||||
|
||||
function EnterGame.toggleStayLoggedBox(clientVersion, init)
|
||||
local enabled = (clientVersion >= 1074)
|
||||
if enabled == enterGame.stayLoggedBoxEnabled then
|
||||
return
|
||||
end
|
||||
|
||||
enterGame:getChildById('stayLoggedBox'):setOn(enabled)
|
||||
|
||||
local newHeight = enterGame:getHeight()
|
||||
local newY = enterGame:getY()
|
||||
if enabled then
|
||||
newY = newY - enterGame.stayLoggedBoxHeight
|
||||
newHeight = newHeight + enterGame.stayLoggedBoxHeight
|
||||
else
|
||||
newY = newY + enterGame.stayLoggedBoxHeight
|
||||
newHeight = newHeight - enterGame.stayLoggedBoxHeight
|
||||
end
|
||||
|
||||
if not init then
|
||||
enterGame:breakAnchors()
|
||||
enterGame:setY(newY)
|
||||
enterGame:bindRectToParent()
|
||||
end
|
||||
enterGame:setHeight(newHeight)
|
||||
|
||||
enterGame.stayLoggedBoxEnabled = enabled
|
||||
end
|
||||
|
||||
function EnterGame.onClientVersionChange(comboBox, text, data)
|
||||
local clientVersion = tonumber(text)
|
||||
EnterGame.toggleAuthenticatorToken(clientVersion)
|
||||
EnterGame.toggleStayLoggedBox(clientVersion)
|
||||
end
|
||||
|
||||
function EnterGame.doLogin()
|
||||
G.account = enterGame:getChildById('accountNameTextEdit'):getText()
|
||||
G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
|
||||
G.authenticatorToken = enterGame:getChildById('authenticatorTokenTextEdit'):getText()
|
||||
G.stayLogged = enterGame:getChildById('stayLoggedBox'):isChecked()
|
||||
G.host = enterGame:getChildById('serverHostTextEdit'):getText()
|
||||
G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText())
|
||||
local clientVersion = tonumber(clientBox:getText())
|
||||
@@ -230,6 +316,7 @@ function EnterGame.doLogin()
|
||||
protocolLogin = ProtocolLogin.create()
|
||||
protocolLogin.onLoginError = onError
|
||||
protocolLogin.onMotd = onMotd
|
||||
protocolLogin.onSessionKey = onSessionKey
|
||||
protocolLogin.onCharacterList = onCharacterList
|
||||
protocolLogin.onUpdateNeeded = onUpdateNeeded
|
||||
|
||||
@@ -240,12 +327,12 @@ function EnterGame.doLogin()
|
||||
EnterGame.show()
|
||||
end })
|
||||
|
||||
g_game.chooseRsa(G.host)
|
||||
g_game.setClientVersion(clientVersion)
|
||||
g_game.setProtocolVersion(g_game.getClientProtocolVersion(clientVersion))
|
||||
g_game.chooseRsa(G.host)
|
||||
|
||||
if modules.game_things.isLoaded() then
|
||||
protocolLogin:login(G.host, G.port, G.account, G.password)
|
||||
protocolLogin:login(G.host, G.port, G.account, G.password, G.authenticatorToken, G.stayLogged)
|
||||
else
|
||||
loadBox:destroy()
|
||||
loadBox = nil
|
||||
@@ -266,6 +353,7 @@ function EnterGame.setDefaultServer(host, port, protocol)
|
||||
local clientLabel = enterGame:getChildById('clientLabel')
|
||||
local accountTextEdit = enterGame:getChildById('accountNameTextEdit')
|
||||
local passwordTextEdit = enterGame:getChildById('accountPasswordTextEdit')
|
||||
local authenticatorTokenTextEdit = enterGame:getChildById('authenticatorTokenTextEdit')
|
||||
|
||||
if hostTextEdit:getText() ~= host then
|
||||
hostTextEdit:setText(host)
|
||||
@@ -273,6 +361,7 @@ function EnterGame.setDefaultServer(host, port, protocol)
|
||||
clientBox:setCurrentOption(protocol)
|
||||
accountTextEdit:setText('')
|
||||
passwordTextEdit:setText('')
|
||||
authenticatorTokenTextEdit:setText('')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -286,6 +375,16 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig
|
||||
portTextEdit:setVisible(false)
|
||||
portTextEdit:setHeight(0)
|
||||
|
||||
local authenticatorTokenTextEdit = enterGame:getChildById('authenticatorTokenTextEdit')
|
||||
authenticatorTokenTextEdit:setText('')
|
||||
authenticatorTokenTextEdit:setOn(false)
|
||||
local authenticatorTokenLabel = enterGame:getChildById('authenticatorTokenLabel')
|
||||
authenticatorTokenLabel:setOn(false)
|
||||
|
||||
local stayLoggedBox = enterGame:getChildById('stayLoggedBox')
|
||||
stayLoggedBox:setChecked(false)
|
||||
stayLoggedBox:setOn(false)
|
||||
|
||||
clientBox:setCurrentOption(protocol)
|
||||
clientBox:setVisible(false)
|
||||
clientBox:setHeight(0)
|
||||
@@ -306,11 +405,11 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig
|
||||
serverListButton:setWidth(0)
|
||||
|
||||
local rememberPasswordBox = enterGame:getChildById('rememberPasswordBox')
|
||||
rememberPasswordBox:setMarginTop(-5)
|
||||
rememberPasswordBox:setMarginTop(-8)
|
||||
|
||||
if not windowWidth then windowWidth = 236 end
|
||||
enterGame:setWidth(windowWidth)
|
||||
if not windowHeight then windowHeight = 200 end
|
||||
if not windowHeight then windowHeight = 210 end
|
||||
enterGame:setHeight(windowHeight)
|
||||
end
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
EnterGameWindow < MainWindow
|
||||
!text: tr('Enter Game')
|
||||
size: 236 274
|
||||
size: 236 298
|
||||
|
||||
EnterGameButton < Button
|
||||
width: 64
|
||||
@@ -21,6 +21,10 @@ ServerListButton < UIButton
|
||||
|
||||
EnterGameWindow
|
||||
id: enterGame
|
||||
&authenticatorEnabled: false
|
||||
&authenticatorHeight: 44
|
||||
&stayLoggedBoxEnabled: false
|
||||
&stayLoggedBoxHeight: 24
|
||||
@onEnter: EnterGame.doLogin()
|
||||
|
||||
MenuLabel
|
||||
@@ -50,6 +54,52 @@ EnterGameWindow
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 2
|
||||
|
||||
MenuLabel
|
||||
id: authenticatorTokenLabel
|
||||
!text: tr('Authenticator Token')
|
||||
anchors.left: prev.left
|
||||
anchors.top: prev.bottom
|
||||
text-auto-resize: true
|
||||
margin-top: -12
|
||||
visible: false
|
||||
|
||||
$on:
|
||||
visible: true
|
||||
margin-top: 8
|
||||
|
||||
TextEdit
|
||||
id: authenticatorTokenTextEdit
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: -22
|
||||
visible: false
|
||||
max-length: 8
|
||||
|
||||
$on:
|
||||
visible: true
|
||||
margin-top: 2
|
||||
|
||||
CheckBox
|
||||
id: stayLoggedBox
|
||||
!text: tr('Stay logged during session')
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 6
|
||||
margin-top: -16
|
||||
visible: false
|
||||
|
||||
$on:
|
||||
visible: true
|
||||
margin-top: 8
|
||||
|
||||
HorizontalSeparator
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 8
|
||||
|
||||
MenuLabel
|
||||
id: serverLabel
|
||||
!text: tr('Server')
|
||||
@@ -132,16 +182,24 @@ EnterGameWindow
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 2
|
||||
|
||||
HorizontalSeparator
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 6
|
||||
|
||||
EnterGameButton
|
||||
!text: tr('Ok')
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 4
|
||||
@onClick: EnterGame.doLogin()
|
||||
|
||||
Label
|
||||
id: serverInfoLabel
|
||||
font: verdana-11px-rounded
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.top: prev.top
|
||||
anchors.left: parent.left
|
||||
margin-top: 5
|
||||
color: green
|
||||
text-auto-resize: true
|
||||
text-auto-resize: true
|
@@ -166,16 +166,20 @@ end
|
||||
-- global function used to translate texts
|
||||
function _G.tr(text, ...)
|
||||
if currentLocale then
|
||||
if tonumber(text) then
|
||||
-- todo: use locale information to calculate this. also detect floating numbers
|
||||
if tonumber(text) and currentLocale.formatNumbers then
|
||||
local number = tostring(text):split('.')
|
||||
local out = ''
|
||||
local number = tostring(text):reverse()
|
||||
for i=1,#number do
|
||||
out = out .. number:sub(i, i)
|
||||
local reverseNumber = number[1]:reverse()
|
||||
for i=1,#reverseNumber do
|
||||
out = out .. reverseNumber:sub(i, i)
|
||||
if i % 3 == 0 and i ~= #number then
|
||||
out = out .. ','
|
||||
out = out .. currentLocale.thousandsSeperator
|
||||
end
|
||||
end
|
||||
|
||||
if number[2] then
|
||||
out = number[2] .. currentLocale.decimalSeperator .. out
|
||||
end
|
||||
return out:reverse()
|
||||
elseif tostring(text) then
|
||||
local translation = currentLocale.translation[text]
|
||||
|
@@ -19,7 +19,7 @@ function AddServer.add()
|
||||
|
||||
local added, error = ServerList.add(host, port, protocol)
|
||||
if not added then
|
||||
displayErrorBox(tr('Add Error'), tr(error))
|
||||
displayErrorBox(tr('Error'), tr(error))
|
||||
else
|
||||
AddServer.hide()
|
||||
end
|
||||
|
@@ -12,7 +12,9 @@ function ServerList.init()
|
||||
serverTextList = serverListWindow:getChildById('serverList')
|
||||
|
||||
servers = g_settings.getNode('ServerList') or {}
|
||||
ServerList.load()
|
||||
if servers then
|
||||
ServerList.load()
|
||||
end
|
||||
end
|
||||
|
||||
function ServerList.terminate()
|
||||
@@ -24,8 +26,8 @@ function ServerList.terminate()
|
||||
end
|
||||
|
||||
function ServerList.load()
|
||||
for k,server in pairs(servers) do
|
||||
ServerList.add(k, server.port, server.protocol, true)
|
||||
for host, server in pairs(servers) do
|
||||
ServerList.add(host, server.port, server.protocol, true)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -43,7 +45,9 @@ function ServerList.select()
|
||||
end
|
||||
|
||||
function ServerList.add(host, port, protocol, load)
|
||||
if not load and servers[host] then
|
||||
if not host or not port or not protocol then
|
||||
return false, 'Failed to load settings'
|
||||
elseif not load and servers[host] then
|
||||
return false, 'Server already exists'
|
||||
elseif host == '' or port == '' then
|
||||
return false, 'Required fields are missing'
|
||||
|
@@ -30,6 +30,10 @@ local allLines = {}
|
||||
|
||||
-- private functions
|
||||
local function navigateCommand(step)
|
||||
if commandTextEdit:isMultiline() then
|
||||
return
|
||||
end
|
||||
|
||||
local numCommands = #commandHistory
|
||||
if numCommands > 0 then
|
||||
currentHistoryIndex = math.min(math.max(currentHistoryIndex + step, 0), numCommands)
|
||||
@@ -96,16 +100,29 @@ local function completeCommand()
|
||||
end
|
||||
end
|
||||
|
||||
local function doCommand()
|
||||
local currentCommand = commandTextEdit:getText()
|
||||
local function doCommand(textWidget)
|
||||
local currentCommand = textWidget:getText()
|
||||
executeCommand(currentCommand)
|
||||
|
||||
if commandTextEdit then
|
||||
commandTextEdit:clearText()
|
||||
end
|
||||
textWidget:clearText()
|
||||
return true
|
||||
end
|
||||
|
||||
local function addNewline(textWidget)
|
||||
if not textWidget:isOn() then
|
||||
textWidget:setOn(true)
|
||||
end
|
||||
textWidget:appendText('\n')
|
||||
end
|
||||
|
||||
local function onCommandChange(textWidget, newText, oldText)
|
||||
local _, newLineCount = string.gsub(newText, '\n', '\n')
|
||||
textWidget:setHeight((newLineCount + 1) * textWidget.baseHeight)
|
||||
|
||||
if newLineCount == 0 and textWidget:isOn() then
|
||||
textWidget:setOn(false)
|
||||
end
|
||||
end
|
||||
|
||||
local function onLog(level, message, time)
|
||||
if disabled then return end
|
||||
-- avoid logging while reporting logs (would cause a infinite loop)
|
||||
@@ -129,6 +146,8 @@ function init()
|
||||
commandHistory = g_settings.getList('terminal-history')
|
||||
|
||||
commandTextEdit = terminalWindow:getChildById('commandTextEdit')
|
||||
commandTextEdit:setHeight(commandTextEdit.baseHeight)
|
||||
connect(commandTextEdit, {onTextChange = onCommandChange})
|
||||
g_keyboard.bindKeyPress('Up', function() navigateCommand(1) end, commandTextEdit)
|
||||
g_keyboard.bindKeyPress('Down', function() navigateCommand(-1) end, commandTextEdit)
|
||||
g_keyboard.bindKeyPress('Ctrl+C',
|
||||
@@ -138,6 +157,7 @@ function init()
|
||||
return true
|
||||
end, commandTextEdit)
|
||||
g_keyboard.bindKeyDown('Tab', completeCommand, commandTextEdit)
|
||||
g_keyboard.bindKeyPress('Shift+Enter', addNewline, commandTextEdit)
|
||||
g_keyboard.bindKeyDown('Enter', doCommand, commandTextEdit)
|
||||
g_keyboard.bindKeyDown('Escape', hide, terminalWindow)
|
||||
|
||||
@@ -293,7 +313,7 @@ function addLine(text, color)
|
||||
end
|
||||
|
||||
function executeCommand(command)
|
||||
if command == nil or #command == 0 then return end
|
||||
if command == nil or #string.gsub(command, '\n', '') == 0 then return end
|
||||
|
||||
-- add command line
|
||||
addLine("> " .. command, "#ffffff")
|
||||
|
@@ -47,7 +47,7 @@ UIWindow
|
||||
anchors.left: parent.left
|
||||
anchors.right: terminalScroll.left
|
||||
anchors.top: terminalScroll.top
|
||||
anchors.bottom: commandSymbolLabel.top
|
||||
anchors.bottom: commandTextEdit.top
|
||||
layout:
|
||||
type: verticalBox
|
||||
align-bottom: true
|
||||
@@ -80,14 +80,25 @@ UIWindow
|
||||
|
||||
UITextEdit
|
||||
id: commandTextEdit
|
||||
height: 12
|
||||
background: #aaaaaa11
|
||||
border-color: #aaaaaa88
|
||||
&baseHeight: 12
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: commandSymbolLabel.right
|
||||
anchors.right: parent.right
|
||||
anchors.right: terminalScroll.left
|
||||
margin-left: 1
|
||||
padding-left: 2
|
||||
font: terminus-10px
|
||||
selection-color: black
|
||||
selection-background-color: white
|
||||
border-width-left: 0
|
||||
border-width-top: 0
|
||||
multiline: false
|
||||
|
||||
$on:
|
||||
border-width-left: 1
|
||||
border-width-top: 1
|
||||
multiline: true
|
||||
|
||||
ResizeBorder
|
||||
id: bottomResizeBorder
|
||||
|
@@ -10,6 +10,7 @@ Module
|
||||
dofile 'string'
|
||||
dofile 'table'
|
||||
dofile 'bitwise'
|
||||
dofile 'struct'
|
||||
|
||||
dofile 'const'
|
||||
dofile 'util'
|
||||
|
173
modules/corelib/struct.lua
Normal file
@@ -0,0 +1,173 @@
|
||||
Struct = {}
|
||||
|
||||
function Struct.pack(format, ...)
|
||||
local stream = {}
|
||||
local vars = {...}
|
||||
local endianness = true
|
||||
|
||||
for i = 1, format:len() do
|
||||
local opt = format:sub(i, i)
|
||||
|
||||
if opt == '>' then
|
||||
endianness = false
|
||||
elseif opt:find('[bBhHiIlL]') then
|
||||
local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1
|
||||
local val = tonumber(table.remove(vars, 1))
|
||||
|
||||
if val < 0 then
|
||||
val = val + 2 ^ (n * 8 - 1)
|
||||
end
|
||||
|
||||
local bytes = {}
|
||||
for j = 1, n do
|
||||
table.insert(bytes, string.char(val % (2 ^ 8)))
|
||||
val = math.floor(val / (2 ^ 8))
|
||||
end
|
||||
|
||||
if not endianness then
|
||||
table.insert(stream, string.reverse(table.concat(bytes)))
|
||||
else
|
||||
table.insert(stream, table.concat(bytes))
|
||||
end
|
||||
elseif opt:find('[fd]') then
|
||||
local val = tonumber(table.remove(vars, 1))
|
||||
local sign = 0
|
||||
|
||||
if val < 0 then
|
||||
sign = 1
|
||||
val = -val
|
||||
end
|
||||
|
||||
local mantissa, exponent = math.frexp(val)
|
||||
if val == 0 then
|
||||
mantissa = 0
|
||||
exponent = 0
|
||||
else
|
||||
mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == 'd') and 53 or 24)
|
||||
exponent = exponent + ((opt == 'd') and 1022 or 126)
|
||||
end
|
||||
|
||||
local bytes = {}
|
||||
if opt == 'd' then
|
||||
val = mantissa
|
||||
for i = 1, 6 do
|
||||
table.insert(bytes, string.char(math.floor(val) % (2 ^ 8)))
|
||||
val = math.floor(val / (2 ^ 8))
|
||||
end
|
||||
else
|
||||
table.insert(bytes, string.char(math.floor(mantissa) % (2 ^ 8)))
|
||||
val = math.floor(mantissa / (2 ^ 8))
|
||||
table.insert(bytes, string.char(math.floor(val) % (2 ^ 8)))
|
||||
val = math.floor(val / (2 ^ 8))
|
||||
end
|
||||
|
||||
table.insert(bytes, string.char(math.floor(exponent * ((opt == 'd') and 16 or 128) + val) % (2 ^ 8)))
|
||||
val = math.floor((exponent * ((opt == 'd') and 16 or 128) + val) / (2 ^ 8))
|
||||
table.insert(bytes, string.char(math.floor(sign * 128 + val) % (2 ^ 8)))
|
||||
val = math.floor((sign * 128 + val) / (2 ^ 8))
|
||||
|
||||
if not endianness then
|
||||
table.insert(stream, string.reverse(table.concat(bytes)))
|
||||
else
|
||||
table.insert(stream, table.concat(bytes))
|
||||
end
|
||||
elseif opt == 's' then
|
||||
table.insert(stream, tostring(table.remove(vars, 1)))
|
||||
table.insert(stream, string.char(0))
|
||||
elseif opt == 'c' then
|
||||
local n = format:sub(i + 1):match('%d+')
|
||||
local length = tonumber(n)
|
||||
|
||||
if length > 0 then
|
||||
local str = tostring(table.remove(vars, 1))
|
||||
if length - str:len() > 0 then
|
||||
str = str .. string.rep(' ', length - str:len())
|
||||
end
|
||||
table.insert(stream, str:sub(1, length))
|
||||
end
|
||||
i = i + n:len()
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(stream)
|
||||
end
|
||||
|
||||
function Struct.unpack(format, stream)
|
||||
local vars = {}
|
||||
local iterator = 1
|
||||
local endianness = true
|
||||
|
||||
for i = 1, format:len() do
|
||||
local opt = format:sub(i, i)
|
||||
|
||||
if opt == '>' then
|
||||
endianness = false
|
||||
elseif opt:find('[bBhHiIlL]') then
|
||||
local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1
|
||||
local signed = opt:lower() == opt
|
||||
|
||||
local val = 0
|
||||
for j = 1, n do
|
||||
local byte = string.byte(stream:sub(iterator, iterator))
|
||||
if endianness then
|
||||
val = val + byte * (2 ^ ((j - 1) * 8))
|
||||
else
|
||||
val = val + byte * (2 ^ ((n - j) * 8))
|
||||
end
|
||||
iterator = iterator + 1
|
||||
end
|
||||
|
||||
if signed then
|
||||
val = val - 2 ^ (n * 8 - 1)
|
||||
end
|
||||
|
||||
table.insert(vars, val)
|
||||
elseif opt:find('[fd]') then
|
||||
local n = (opt == 'd') and 8 or 4
|
||||
local x = stream:sub(iterator, iterator + n - 1)
|
||||
iterator = iterator + n
|
||||
|
||||
if not endianness then
|
||||
x = string.reverse(x)
|
||||
end
|
||||
|
||||
local sign = 1
|
||||
local mantissa = string.byte(x, (opt == 'd') and 7 or 3) % ((opt == 'd') and 16 or 128)
|
||||
for i = n - 2, 1, -1 do
|
||||
mantissa = mantissa * (2 ^ 8) + string.byte(x, i)
|
||||
end
|
||||
|
||||
if string.byte(x, n) > 127 then
|
||||
sign = -1
|
||||
end
|
||||
|
||||
local exponent = (string.byte(x, n) % 128) * ((opt == 'd') and 16 or 2) + math.floor(string.byte(x, n - 1) / ((opt == 'd') and 16 or 128))
|
||||
if exponent == 0 then
|
||||
table.insert(vars, 0.0)
|
||||
else
|
||||
mantissa = (math.ldexp(mantissa, (opt == 'd') and -52 or -23) + 1) * sign
|
||||
table.insert(vars, math.ldexp(mantissa, exponent - ((opt == 'd') and 1023 or 127)))
|
||||
end
|
||||
elseif opt == 's' then
|
||||
local bytes = {}
|
||||
for j = iterator, stream:len() do
|
||||
if stream:sub(j, j) == string.char(0) then
|
||||
break
|
||||
end
|
||||
|
||||
table.insert(bytes, stream:sub(j, j))
|
||||
end
|
||||
|
||||
local str = table.concat(bytes)
|
||||
iterator = iterator + str:len() + 1
|
||||
table.insert(vars, str)
|
||||
elseif opt == 'c' then
|
||||
local n = format:sub(i + 1):match('%d+')
|
||||
table.insert(vars, stream:sub(iterator, iterator + tonumber(n)))
|
||||
iterator = iterator + tonumber(n)
|
||||
i = i + n:len()
|
||||
end
|
||||
end
|
||||
|
||||
return unpack(vars)
|
||||
end
|
@@ -6,7 +6,7 @@ local function onTabClick(tab)
|
||||
tab.tabBar:selectTab(tab)
|
||||
end
|
||||
|
||||
local function updateMargins(tabBar, ignored)
|
||||
local function updateMargins(tabBar)
|
||||
if #tabBar.tabs == 0 then return end
|
||||
|
||||
local currentMargin = 0
|
||||
@@ -17,19 +17,19 @@ local function updateMargins(tabBar, ignored)
|
||||
end
|
||||
|
||||
local function updateNavigation(tabBar)
|
||||
if prevNavigation then
|
||||
if tabBar.prevNavigation then
|
||||
if #tabBar.preTabs > 0 or table.find(tabBar.tabs, tabBar.currentTab) ~= 1 then
|
||||
prevNavigation:enable()
|
||||
tabBar.prevNavigation:enable()
|
||||
else
|
||||
prevNavigation:disable()
|
||||
tabBar.prevNavigation:disable()
|
||||
end
|
||||
end
|
||||
|
||||
if nextNavigation then
|
||||
if tabBar.nextNavigation then
|
||||
if #tabBar.postTabs > 0 or table.find(tabBar.tabs, tabBar.currentTab) ~= #tabBar.tabs then
|
||||
nextNavigation:enable()
|
||||
tabBar.nextNavigation:enable()
|
||||
else
|
||||
nextNavigation:disable()
|
||||
tabBar.nextNavigation:disable()
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -187,7 +187,7 @@ local function onTabDragMove(tab, mousePos, mouseMoved)
|
||||
end
|
||||
|
||||
local function tabBlink(tab, step)
|
||||
step = step or 0
|
||||
local step = step or 0
|
||||
tab:setOn(not tab:isOn())
|
||||
|
||||
removeEvent(tab.blinkEvent)
|
||||
@@ -218,6 +218,19 @@ function UIMoveableTabBar.create()
|
||||
return tabbar
|
||||
end
|
||||
|
||||
function UIMoveableTabBar:onDestroy()
|
||||
if self.prevNavigation then
|
||||
self.prevNavigation:disable()
|
||||
end
|
||||
|
||||
if self.nextNavigation then
|
||||
self.nextNavigation:disable()
|
||||
end
|
||||
|
||||
self.nextNavigation = nil
|
||||
self.prevNavigation = nil
|
||||
end
|
||||
|
||||
function UIMoveableTabBar:setContentWidget(widget)
|
||||
self.contentWidget = widget
|
||||
if #self.tabs > 0 then
|
||||
@@ -296,8 +309,11 @@ function UIMoveableTabBar:moveTab(tab, units)
|
||||
end
|
||||
|
||||
function UIMoveableTabBar:onStyleApply(styleName, styleNode)
|
||||
if styleNode['moveable'] then
|
||||
self.tabsMoveable = styleNode['moveable']
|
||||
if styleNode['movable'] then
|
||||
self.tabsMoveable = styleNode['movable']
|
||||
end
|
||||
if styleNode['tab-spacing'] then
|
||||
self:setTabSpacing(styleNode['tab-spacing'])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -467,14 +483,14 @@ function UIMoveableTabBar:getCurrentTab()
|
||||
end
|
||||
|
||||
function UIMoveableTabBar:setNavigation(prevButton, nextButton)
|
||||
prevNavigation = prevButton
|
||||
nextNavigation = nextButton
|
||||
self.prevNavigation = prevButton
|
||||
self.nextNavigation = nextButton
|
||||
|
||||
if prevNavigation then
|
||||
prevNavigation.onClick = function() self:selectPrevTab() end
|
||||
if self.prevNavigation then
|
||||
self.prevNavigation.onClick = function() self:selectPrevTab() end
|
||||
end
|
||||
if nextNavigation then
|
||||
nextNavigation.onClick = function() self:selectNextTab() end
|
||||
if self.nextNavigation then
|
||||
self.nextNavigation.onClick = function() self:selectNextTab() end
|
||||
end
|
||||
updateNavigation(self)
|
||||
end
|
||||
|
@@ -8,6 +8,7 @@ function UIPopupMenu.create()
|
||||
local layout = UIVerticalLayout.create(menu)
|
||||
layout:setFitChildren(true)
|
||||
menu:setLayout(layout)
|
||||
menu.isGameMenu = false
|
||||
return menu
|
||||
end
|
||||
|
||||
@@ -34,6 +35,7 @@ function UIPopupMenu:display(pos)
|
||||
rootWidget:addChild(self)
|
||||
self:setPosition(pos)
|
||||
self:grabMouse()
|
||||
self:focus()
|
||||
--self:grabKeyboard()
|
||||
currentMenu = self
|
||||
end
|
||||
@@ -76,6 +78,10 @@ function UIPopupMenu:addSeparator()
|
||||
g_ui.createWidget(self:getStyleName() .. 'Separator', self)
|
||||
end
|
||||
|
||||
function UIPopupMenu:setGameMenu(state)
|
||||
self.isGameMenu = state
|
||||
end
|
||||
|
||||
function UIPopupMenu:onDestroy()
|
||||
if currentMenu == self then
|
||||
currentMenu = nil
|
||||
@@ -105,4 +111,12 @@ local function onRootGeometryUpdate()
|
||||
currentMenu:destroy()
|
||||
end
|
||||
end
|
||||
connect(rootWidget, { onGeometryChange = onRootGeometryUpdate} )
|
||||
|
||||
local function onGameEnd()
|
||||
if currentMenu and currentMenu.isGameMenu then
|
||||
currentMenu:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
connect(rootWidget, { onGeometryChange = onRootGeometryUpdate })
|
||||
connect(g_game, { onGameEnd = onGameEnd } )
|
||||
|
@@ -28,7 +28,7 @@ local function calcValues(self)
|
||||
proportion = math.min(math.max(self.step, 1), range)/range
|
||||
end
|
||||
|
||||
local px = math.max(proportion * pxrange, 10)
|
||||
local px = math.max(proportion * pxrange, 6)
|
||||
px = px - px % 2 + 1
|
||||
|
||||
local offset = 0
|
||||
|
@@ -12,7 +12,7 @@ function UISpinBox.create()
|
||||
spinbox.step = 1
|
||||
spinbox.firstchange = true
|
||||
spinbox.mouseScroll = true
|
||||
spinbox:setText("0")
|
||||
spinbox:setText("1")
|
||||
spinbox:setValue(1)
|
||||
return spinbox
|
||||
end
|
||||
@@ -23,7 +23,7 @@ function UISpinBox:onSetup()
|
||||
end
|
||||
|
||||
function UISpinBox:onMouseWheel(mousePos, direction)
|
||||
if not self.mouseScroll then
|
||||
if not self.mouseScroll then
|
||||
return false
|
||||
end
|
||||
if direction == MouseWheelUp then
|
||||
@@ -66,7 +66,15 @@ function UISpinBox:onTextChange(text, oldText)
|
||||
end
|
||||
|
||||
function UISpinBox:onValueChange(value)
|
||||
-- nothing todo
|
||||
-- nothing to do
|
||||
end
|
||||
|
||||
function UISpinBox:onFocusChange(focused)
|
||||
if not focused then
|
||||
if self:getText():len() == 0 then
|
||||
self:setText(self.minimum)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UISpinBox:onStyleApply(styleName, styleNode)
|
||||
@@ -109,14 +117,16 @@ function UISpinBox:down()
|
||||
self:setValue(self.value - self.step)
|
||||
end
|
||||
|
||||
function UISpinBox:setValue(value)
|
||||
function UISpinBox:setValue(value, dontSignal)
|
||||
value = value or 0
|
||||
value = math.max(math.min(self.maximum, value), self.minimum)
|
||||
|
||||
if value == self.value then return end
|
||||
|
||||
self.value = value
|
||||
if self:getText():len() > 0 then
|
||||
self:setText(value)
|
||||
end
|
||||
self.value = value
|
||||
|
||||
local upButton = self:getChildById('up')
|
||||
local downButton = self:getChildById('down')
|
||||
@@ -127,7 +137,9 @@ function UISpinBox:setValue(value)
|
||||
downButton:setEnabled(self.maximum ~= self.minimum and self.value ~= self.minimum)
|
||||
end
|
||||
|
||||
signalcall(self.onValueChange, self, value)
|
||||
if not dontSignal then
|
||||
signalcall(self.onValueChange, self, value)
|
||||
end
|
||||
end
|
||||
|
||||
function UISpinBox:getValue()
|
||||
|
@@ -38,6 +38,7 @@ function UITabBar:addTab(text, panel, icon)
|
||||
end
|
||||
|
||||
local tab = g_ui.createWidget(self:getStyleName() .. 'Button', self.buttonsPanel)
|
||||
|
||||
panel.isTab = true
|
||||
tab.tabPanel = panel
|
||||
tab.tabBar = self
|
||||
|
@@ -3,33 +3,45 @@
|
||||
TODO:
|
||||
* Make table headers more robust.
|
||||
* Get dynamic row heights working with text wrapping.
|
||||
* Every second row different background color applied.
|
||||
]]
|
||||
|
||||
TABLE_SORTING_ASC = 0
|
||||
TABLE_SORTING_DESC = 1
|
||||
|
||||
UITable = extends(UIWidget, "UITable")
|
||||
|
||||
local HEADER_ID = 'row0'
|
||||
|
||||
-- Initialize default values
|
||||
function UITable.create()
|
||||
local table = UITable.internalCreate()
|
||||
table.headerRow = nil
|
||||
table.headerColumns = {}
|
||||
table.dataSpace = nil
|
||||
table.rows = {}
|
||||
table.rowBaseStyle = nil
|
||||
table.columns = {}
|
||||
table.columnWidth = {}
|
||||
table.columBaseStyle = nil
|
||||
table.headerRowBaseStyle = nil
|
||||
table.headerColumnBaseStyle = nil
|
||||
table.selectedRow = nil
|
||||
table.defaultColumnWidth = 80
|
||||
table.sortColumn = -1
|
||||
table.sortType = TABLE_SORTING_ASC
|
||||
table.autoSort = false
|
||||
|
||||
return table
|
||||
end
|
||||
|
||||
-- Clear table values
|
||||
function UITable:onDestroy()
|
||||
for k,row in pairs(self.rows) do
|
||||
for _,row in pairs(self.rows) do
|
||||
row.onClick = nil
|
||||
end
|
||||
self.rows = {}
|
||||
self.columns = {}
|
||||
self.headerRow = {}
|
||||
self.headerRow = nil
|
||||
self.headerColumns = {}
|
||||
self.columnWidth = {}
|
||||
self.selectedRow = nil
|
||||
|
||||
if self.dataSpace then
|
||||
@@ -38,36 +50,58 @@ function UITable:onDestroy()
|
||||
end
|
||||
end
|
||||
|
||||
-- Detect if a header is already defined
|
||||
function UITable:onSetup()
|
||||
local header = self:getChildById('header')
|
||||
if header then
|
||||
self:setHeader(header)
|
||||
end
|
||||
end
|
||||
|
||||
-- Parse table related styles
|
||||
function UITable:onStyleApply(styleName, styleNode)
|
||||
for name, value in pairs(styleNode) do
|
||||
if name == 'table-data' then
|
||||
addEvent(function()
|
||||
self:setTableData(self:getParent():getChildById(value))
|
||||
end)
|
||||
elseif name == 'column-style' then
|
||||
addEvent(function()
|
||||
self:setColumnStyle(value)
|
||||
end)
|
||||
elseif name == 'row-style' then
|
||||
addEvent(function()
|
||||
self:setRowStyle(value)
|
||||
end)
|
||||
elseif name == 'header-column-style' then
|
||||
addEvent(function()
|
||||
self:setHeaderColumnStyle(value)
|
||||
end)
|
||||
elseif name == 'header-row-style' then
|
||||
addEvent(function()
|
||||
self:setHeaderRowStyle(value)
|
||||
end)
|
||||
if value ~= false then
|
||||
if name == 'table-data' then
|
||||
addEvent(function()
|
||||
self:setTableData(self:getParent():getChildById(value))
|
||||
end)
|
||||
elseif name == 'column-style' then
|
||||
addEvent(function()
|
||||
self:setColumnStyle(value)
|
||||
end)
|
||||
elseif name == 'row-style' then
|
||||
addEvent(function()
|
||||
self:setRowStyle(value)
|
||||
end)
|
||||
elseif name == 'header-column-style' then
|
||||
addEvent(function()
|
||||
self:setHeaderColumnStyle(value)
|
||||
end)
|
||||
elseif name == 'header-row-style' then
|
||||
addEvent(function()
|
||||
self:setHeaderRowStyle(value)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UITable:setColumnWidth(width)
|
||||
if self:hasHeader() then return end
|
||||
self.columnWidth = width
|
||||
end
|
||||
|
||||
function UITable:setDefaultColumnWidth(width)
|
||||
self.defaultColumnWidth = width
|
||||
end
|
||||
|
||||
-- Check if the table has a header
|
||||
function UITable:hasHeader()
|
||||
return self.headerRow ~= nil
|
||||
end
|
||||
|
||||
-- Clear all rows
|
||||
function UITable:clearData()
|
||||
if not self.dataSpace then
|
||||
return
|
||||
@@ -78,16 +112,42 @@ function UITable:clearData()
|
||||
self.rows = {}
|
||||
end
|
||||
|
||||
function UITable:addHeaderRow(data)
|
||||
-- Set existing child as header
|
||||
function UITable:setHeader(headerWidget)
|
||||
self:removeHeader()
|
||||
|
||||
if self.dataSpace then
|
||||
local newHeight = self.dataSpace:getHeight()-headerRow:getHeight()-self.dataSpace:getMarginTop()
|
||||
self.dataSpace:applyStyle({ height = newHeight })
|
||||
end
|
||||
|
||||
self.headerColumns = {}
|
||||
self.columnWidth = {}
|
||||
for colId, column in pairs(headerWidget:getChildren()) do
|
||||
column.colId = colId
|
||||
column.table = self
|
||||
table.insert(self.columnWidth, column:getWidth())
|
||||
table.insert(self.headerColumns, column)
|
||||
end
|
||||
|
||||
self.headerRow = headerWidget
|
||||
end
|
||||
|
||||
-- Create and add header from table data
|
||||
function UITable:addHeader(data)
|
||||
if not data or type(data) ~= 'table' then
|
||||
g_logger.error('UITable:addHeaderRow - table columns must be provided in a table')
|
||||
return
|
||||
end
|
||||
|
||||
self:removeHeader()
|
||||
|
||||
-- build header columns
|
||||
local columns = {}
|
||||
for _, column in pairs(data) do
|
||||
for colId, column in pairs(data) do
|
||||
local col = g_ui.createWidget(self.headerColumnBaseStyle)
|
||||
col.colId = colId
|
||||
col.table = self
|
||||
for type, value in pairs(column) do
|
||||
if type == 'width' then
|
||||
col:setWidth(value)
|
||||
@@ -104,26 +164,37 @@ function UITable:addHeaderRow(data)
|
||||
|
||||
-- create a new header
|
||||
local headerRow = g_ui.createWidget(self.headerRowBaseStyle, self)
|
||||
local newHeight = (self.dataSpace:getHeight()-headerRow:getHeight())-self.dataSpace:getMarginTop()
|
||||
local newHeight = self.dataSpace:getHeight()-headerRow:getHeight()-self.dataSpace:getMarginTop()
|
||||
self.dataSpace:applyStyle({ height = newHeight })
|
||||
|
||||
headerRow:setId(HEADER_ID)
|
||||
headerRow:setId('header')
|
||||
self.headerColumns = {}
|
||||
self.columnWidth = {}
|
||||
for _, column in pairs(columns) do
|
||||
headerRow:addChild(column)
|
||||
self.columns[HEADER_ID] = column
|
||||
table.insert(self.columnWidth, column:getWidth())
|
||||
table.insert(self.headerColumns, column)
|
||||
end
|
||||
|
||||
headerRow.onClick = function(headerRow) self:selectRow(headerRow) end
|
||||
self.headerRow = headerRow
|
||||
return headerRow
|
||||
end
|
||||
|
||||
function UITable:removeHeaderRow()
|
||||
self.headerRow:destroy()
|
||||
self.headerRow = nil
|
||||
-- Remove header
|
||||
function UITable:removeHeader()
|
||||
if self:hasHeader() then
|
||||
if self.dataSpace then
|
||||
local newHeight = self.dataSpace:getHeight()+self.headerRow:getHeight()+self.dataSpace:getMarginTop()
|
||||
self.dataSpace:applyStyle({ height = newHeight })
|
||||
end
|
||||
self.headerColumns = {}
|
||||
self.columnWidth = {}
|
||||
self.headerRow:destroy()
|
||||
self.headerRow = nil
|
||||
end
|
||||
end
|
||||
|
||||
function UITable:addRow(data, ref, height)
|
||||
function UITable:addRow(data, height)
|
||||
if not self.dataSpace then
|
||||
g_logger.error('UITable:addRow - table data space has not been set, cannot add rows.')
|
||||
return
|
||||
@@ -134,41 +205,123 @@ function UITable:addRow(data, ref, height)
|
||||
end
|
||||
|
||||
local row = g_ui.createWidget(self.rowBaseStyle)
|
||||
if ref then row.ref = ref end
|
||||
row.table = self
|
||||
if height then row:setHeight(height) end
|
||||
|
||||
local rowId = #self.rows
|
||||
row:setId('row'..(rowId < 1 and 1 or rowId))
|
||||
local rowId = #self.rows + 1
|
||||
row.rowId = rowId
|
||||
row:setId('row'..rowId)
|
||||
row:updateBackgroundColor()
|
||||
|
||||
for _, column in pairs(data) do
|
||||
self.columns[rowId] = {}
|
||||
for colId, column in pairs(data) do
|
||||
local col = g_ui.createWidget(self.columBaseStyle, row)
|
||||
for type, value in pairs(column) do
|
||||
if type == 'width' then
|
||||
col:setWidth(value)
|
||||
elseif type == 'height' then
|
||||
col:setHeight(value)
|
||||
elseif type == 'text' then
|
||||
col:setText(value)
|
||||
end
|
||||
if column.width then
|
||||
col:setWidth(column.width)
|
||||
else
|
||||
col:setWidth(self.columnWidth[colId] or self.defaultColumnWidth)
|
||||
end
|
||||
self.columns[rowId] = col
|
||||
if column.height then
|
||||
col:setHeight(column.height)
|
||||
end
|
||||
if column.text then
|
||||
col:setText(column.text)
|
||||
end
|
||||
if column.sortvalue then
|
||||
col.sortvalue = column.sortvalue
|
||||
else
|
||||
col.sortvalue = column.text or 0
|
||||
end
|
||||
table.insert(self.columns[rowId], col)
|
||||
end
|
||||
|
||||
row.onFocusChange = function(row, focused)
|
||||
if focused then self:selectRow(row) end
|
||||
end
|
||||
self.dataSpace:addChild(row)
|
||||
|
||||
table.insert(self.rows, row)
|
||||
|
||||
if self.autoSort then
|
||||
self:sort()
|
||||
end
|
||||
|
||||
return row
|
||||
end
|
||||
|
||||
-- Update row indices and background color
|
||||
function UITable:updateRows()
|
||||
for rowId = 1, #self.rows do
|
||||
local row = self.rows[rowId]
|
||||
row.rowId = rowId
|
||||
row:setId('row'..rowId)
|
||||
row:updateBackgroundColor()
|
||||
end
|
||||
end
|
||||
|
||||
-- Removes the given row widget from the table
|
||||
function UITable:removeRow(row)
|
||||
if self.selectedRow == row then
|
||||
self:selectRow(nil)
|
||||
end
|
||||
row.onClick = nil
|
||||
table.removevalue(self.rows, row)
|
||||
row.table = nil
|
||||
table.remove(self.columns, row.rowId)
|
||||
table.remove(self.rows, row.rowId)
|
||||
self.dataSpace:removeChild(row)
|
||||
self:updateRows()
|
||||
end
|
||||
|
||||
function UITable:toggleSorting(enabled)
|
||||
self.autoSort = enabled
|
||||
end
|
||||
|
||||
function UITable:setSorting(colId, sortType)
|
||||
self.headerColumns[colId]:focus()
|
||||
|
||||
if sortType then
|
||||
self.sortType = sortType
|
||||
elseif self.sortColumn == colId then
|
||||
if self.sortType == TABLE_SORTING_ASC then
|
||||
self.sortType = TABLE_SORTING_DESC
|
||||
else
|
||||
self.sortType = TABLE_SORTING_ASC
|
||||
end
|
||||
else
|
||||
self.sortType = TABLE_SORTING_ASC
|
||||
end
|
||||
self.sortColumn = colId
|
||||
end
|
||||
|
||||
function UITable:sort()
|
||||
if self.sortColumn <= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
if self.sortType == TABLE_SORTING_ASC then
|
||||
table.sort(self.rows, function(rowA, b)
|
||||
return rowA:getChildByIndex(self.sortColumn).sortvalue < b:getChildByIndex(self.sortColumn).sortvalue
|
||||
end)
|
||||
else
|
||||
table.sort(self.rows, function(rowA, b)
|
||||
return rowA:getChildByIndex(self.sortColumn).sortvalue > b:getChildByIndex(self.sortColumn).sortvalue
|
||||
end)
|
||||
end
|
||||
|
||||
if self.dataSpace then
|
||||
for _, child in pairs(self.dataSpace:getChildren()) do
|
||||
self.dataSpace:removeChild(child)
|
||||
end
|
||||
end
|
||||
|
||||
self:updateRows()
|
||||
self.columns = {}
|
||||
for _, row in pairs(self.rows) do
|
||||
if self.dataSpace then
|
||||
self.dataSpace:addChild(row)
|
||||
end
|
||||
|
||||
self.columns[row.rowId] = {}
|
||||
for _, column in pairs(row:getChildren()) do
|
||||
table.insert(self.columns[row.rowId], column)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UITable:selectRow(selectedRow)
|
||||
@@ -189,21 +342,34 @@ function UITable:selectRow(selectedRow)
|
||||
end
|
||||
|
||||
function UITable:setTableData(tableData)
|
||||
local headerHeight = 0
|
||||
if self.headerRow then
|
||||
headerHeight = self.headerRow:getHeight()
|
||||
end
|
||||
|
||||
self.dataSpace = tableData
|
||||
self.dataSpace:applyStyle({ height = self:getHeight() })
|
||||
self.dataSpace:applyStyle({ height = self:getHeight()-headerHeight-self:getMarginTop() })
|
||||
end
|
||||
|
||||
function UITable:setRowStyle(style)
|
||||
function UITable:setRowStyle(style, dontUpdate)
|
||||
self.rowBaseStyle = style
|
||||
for _, row in pairs(self.rows) do
|
||||
row:setStyle(style)
|
||||
|
||||
if not dontUpdate then
|
||||
for _, row in pairs(self.rows) do
|
||||
row:setStyle(style)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UITable:setColumnStyle(style)
|
||||
function UITable:setColumnStyle(style, dontUpdate)
|
||||
self.columBaseStyle = style
|
||||
for _, col in pairs(self.columns) do
|
||||
col:setStyle(style)
|
||||
|
||||
if not dontUpdate then
|
||||
for _, columns in pairs(self.columns) do
|
||||
for _, col in pairs(columns) do
|
||||
col:setStyle(style)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -216,7 +382,51 @@ end
|
||||
|
||||
function UITable:setHeaderColumnStyle(style)
|
||||
self.headerColumnBaseStyle = style
|
||||
if table.haskey(HEADER_ID) then
|
||||
self.columns[HEADER_ID]:setStyle(style)
|
||||
for _, col in pairs(self.headerColumns) do
|
||||
col:setStyle(style)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
UITableRow = extends(UIWidget, "UITableRow")
|
||||
|
||||
function UITableRow:onFocusChange(focused)
|
||||
if focused then
|
||||
if self.table then self.table:selectRow(self) end
|
||||
end
|
||||
end
|
||||
|
||||
function UITableRow:onStyleApply(styleName, styleNode)
|
||||
for name,value in pairs(styleNode) do
|
||||
if name == 'even-background-color' then
|
||||
self.evenBackgroundColor = value
|
||||
elseif name == 'odd-background-color' then
|
||||
self.oddBackgroundColor = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function UITableRow:updateBackgroundColor()
|
||||
self.backgroundColor = nil
|
||||
|
||||
local isEven = (self.rowId % 2 == 0)
|
||||
if isEven and self.evenBackgroundColor then
|
||||
self.backgroundColor = self.evenBackgroundColor
|
||||
elseif not isEven and self.oddBackgroundColor then
|
||||
self.backgroundColor = self.oddBackgroundColor
|
||||
end
|
||||
|
||||
if self.backgroundColor then
|
||||
self:mergeStyle({ ['background-color'] = self.backgroundColor })
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
UITableHeaderColumn = extends(UIButton, "UITableHeaderColumn")
|
||||
|
||||
function UITableHeaderColumn:onClick()
|
||||
if self.table then
|
||||
self.table:setSorting(self.colId)
|
||||
self.table:sort()
|
||||
end
|
||||
end
|
||||
|
@@ -296,6 +296,16 @@ function numbertoboolean(number)
|
||||
end
|
||||
end
|
||||
|
||||
function protectedcall(func, ...)
|
||||
local status, ret = pcall(func, ...)
|
||||
if status then
|
||||
return ret
|
||||
end
|
||||
|
||||
perror(ret)
|
||||
return false
|
||||
end
|
||||
|
||||
function signalcall(param, ...)
|
||||
if type(param) == 'function' then
|
||||
local status, ret = pcall(param, ...)
|
||||
@@ -313,7 +323,7 @@ function signalcall(param, ...)
|
||||
perror(ret)
|
||||
end
|
||||
end
|
||||
elseif func ~= nil then
|
||||
elseif param ~= nil then
|
||||
error('attempt to call a non function value')
|
||||
end
|
||||
return false
|
||||
|
@@ -15,7 +15,8 @@ fightModeRadioGroup = nil
|
||||
pvpModeRadioGroup = nil
|
||||
|
||||
function init()
|
||||
combatControlsButton = modules.client_topmenu.addRightGameToggleButton('combatControlsButton', tr('Combat Controls'), '/images/topbuttons/combatcontrols', toggle)
|
||||
combatControlsButton = modules.client_topmenu.addRightGameToggleButton('combatControlsButton',
|
||||
tr('Combat Controls'), '/images/topbuttons/combatcontrols', toggle)
|
||||
combatControlsButton:setOn(true)
|
||||
combatControlsWindow = g_ui.loadUI('combatcontrols', modules.game_interface.getRightPanel())
|
||||
combatControlsWindow:disableResize()
|
||||
|
@@ -3,7 +3,7 @@ SpeakTypesSettings = {
|
||||
say = { speakType = MessageModes.Say, color = '#FFFF00' },
|
||||
whisper = { speakType = MessageModes.Whisper, color = '#FFFF00' },
|
||||
yell = { speakType = MessageModes.Yell, color = '#FFFF00' },
|
||||
broadcast = { speakType = MessageModes.GamemasterPrivateFrom, color = '#F55E5E' },
|
||||
broadcast = { speakType = MessageModes.GamemasterBroadcast, color = '#F55E5E' },
|
||||
private = { speakType = MessageModes.PrivateTo, color = '#5FF7F7', private = true },
|
||||
privateRed = { speakType = MessageModes.GamemasterTo, color = '#F55E5E', private = true },
|
||||
privatePlayerToPlayer = { speakType = MessageModes.PrivateTo, color = '#9F9DFD', private = true },
|
||||
@@ -38,6 +38,7 @@ SpeakTypes = {
|
||||
[MessageModes.RVRChannel] = SpeakTypesSettings.channelWhite,
|
||||
[MessageModes.RVRContinue] = SpeakTypesSettings.rvrContinue,
|
||||
[MessageModes.RVRAnswer] = SpeakTypesSettings.rvrAnswerFrom,
|
||||
[MessageModes.NpcFromStartBlock] = SpeakTypesSettings.privateNpcToPlayer,
|
||||
|
||||
-- ignored types
|
||||
[MessageModes.Spell] = SpeakTypesSettings.none,
|
||||
@@ -105,7 +106,6 @@ function init()
|
||||
consoleContentPanel = consolePanel:getChildById('consoleContentPanel')
|
||||
consoleTabBar = consolePanel:getChildById('consoleTabBar')
|
||||
consoleTabBar:setContentWidget(consoleContentPanel)
|
||||
consoleTabBar:setTabSpacing(-1)
|
||||
channels = {}
|
||||
|
||||
consolePanel.onKeyPress = function(self, keyCode, keyboardModifiers)
|
||||
@@ -114,13 +114,10 @@ function init()
|
||||
local tab = consoleTabBar:getCurrentTab()
|
||||
if not tab then return false end
|
||||
|
||||
local consoleBuffer = tab.tabPanel:getChildById('consoleBuffer')
|
||||
if not consoleBuffer then return false end
|
||||
local selection = tab.tabPanel:getChildById('consoleBuffer').selectionText
|
||||
if not selection then return false end
|
||||
|
||||
local consoleLabel = consoleBuffer:getFocusedChild()
|
||||
if not consoleLabel or not consoleLabel:hasSelection() then return false end
|
||||
|
||||
g_window.setClipboardText(consoleLabel:getSelection())
|
||||
g_window.setClipboardText(selection)
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -148,6 +145,27 @@ function init()
|
||||
end
|
||||
end
|
||||
|
||||
function clearSelection(consoleBuffer)
|
||||
for _,label in pairs(consoleBuffer:getChildren()) do
|
||||
label:clearSelection()
|
||||
end
|
||||
consoleBuffer.selectionText = nil
|
||||
consoleBuffer.selection = nil
|
||||
end
|
||||
|
||||
function selectAll(consoleBuffer)
|
||||
clearSelection(consoleBuffer)
|
||||
if consoleBuffer:getChildCount() > 0 then
|
||||
local text = {}
|
||||
for _,label in pairs(consoleBuffer:getChildren()) do
|
||||
label:selectAll()
|
||||
table.insert(text, label:getSelection())
|
||||
end
|
||||
consoleBuffer.selectionText = table.concat(text, '\n')
|
||||
consoleBuffer.selection = { first = consoleBuffer:getChildIndex(consoleBuffer:getFirstChild()), last = consoleBuffer:getChildIndex(consoleBuffer:getLastChild()) }
|
||||
end
|
||||
end
|
||||
|
||||
function toggleChat()
|
||||
if consoleToggleChat:isChecked() then
|
||||
disableChat()
|
||||
@@ -164,12 +182,18 @@ function enableChat()
|
||||
|
||||
g_keyboard.unbindKeyUp("Space")
|
||||
g_keyboard.unbindKeyUp("Enter")
|
||||
g_keyboard.unbindKeyUp("Escape")
|
||||
|
||||
gameInterface.unbindWalkKey("W")
|
||||
gameInterface.unbindWalkKey("D")
|
||||
gameInterface.unbindWalkKey("S")
|
||||
gameInterface.unbindWalkKey("A")
|
||||
|
||||
gameInterface.unbindWalkKey("E")
|
||||
gameInterface.unbindWalkKey("Q")
|
||||
gameInterface.unbindWalkKey("C")
|
||||
gameInterface.unbindWalkKey("Z")
|
||||
|
||||
consoleToggleChat:setTooltip(tr("Disable chat mode, allow to walk using ASDW"))
|
||||
end
|
||||
|
||||
@@ -187,12 +211,18 @@ function disableChat()
|
||||
end
|
||||
g_keyboard.bindKeyUp("Space", quickFunc)
|
||||
g_keyboard.bindKeyUp("Enter", quickFunc)
|
||||
g_keyboard.bindKeyUp("Escape", quickFunc)
|
||||
|
||||
gameInterface.bindWalkKey("W", North)
|
||||
gameInterface.bindWalkKey("D", East)
|
||||
gameInterface.bindWalkKey("S", South)
|
||||
gameInterface.bindWalkKey("A", West)
|
||||
|
||||
gameInterface.bindWalkKey("E", NorthEast)
|
||||
gameInterface.bindWalkKey("Q", NorthWest)
|
||||
gameInterface.bindWalkKey("C", SouthEast)
|
||||
gameInterface.bindWalkKey("Z", SouthWest)
|
||||
|
||||
consoleToggleChat:setTooltip(tr("Enable chat mode"))
|
||||
end
|
||||
|
||||
@@ -233,7 +263,13 @@ function terminate()
|
||||
violationWindow:destroy()
|
||||
end
|
||||
|
||||
consoleTabBar = nil
|
||||
consoleContentPanel = nil
|
||||
consoleToggleChat = nil
|
||||
consoleTextEdit = nil
|
||||
|
||||
consolePanel:destroy()
|
||||
consolePanel = nil
|
||||
ownPrivateName = nil
|
||||
|
||||
Console = nil
|
||||
@@ -288,11 +324,14 @@ function clear()
|
||||
channels = {}
|
||||
|
||||
consoleTabBar:removeTab(defaultTab)
|
||||
defaultTab = nil
|
||||
consoleTabBar:removeTab(serverTab)
|
||||
serverTab = nil
|
||||
|
||||
local npcTab = consoleTabBar:getTab('NPCs')
|
||||
if npcTab then
|
||||
consoleTabBar:removeTab(npcTab)
|
||||
npcTab = nil
|
||||
end
|
||||
|
||||
if violationReportTab then
|
||||
@@ -557,12 +596,102 @@ function addTabText(text, speaktype, tab, creatureName)
|
||||
end
|
||||
|
||||
label.name = creatureName
|
||||
label.onMouseRelease = function (self, mousePos, mouseButton)
|
||||
consoleBuffer.onMouseRelease = function(self, mousePos, mouseButton)
|
||||
processMessageMenu(mousePos, mouseButton, nil, nil, nil, tab)
|
||||
end
|
||||
label.onMouseRelease = function(self, mousePos, mouseButton)
|
||||
processMessageMenu(mousePos, mouseButton, creatureName, text, self, tab)
|
||||
end
|
||||
label.onMousePress = function(self, mousePos, button)
|
||||
if button == MouseLeftButton then clearSelection(consoleBuffer) end
|
||||
end
|
||||
label.onDragEnter = function(self, mousePos)
|
||||
clearSelection(consoleBuffer)
|
||||
return true
|
||||
end
|
||||
label.onDragLeave = function(self, droppedWidget, mousePos)
|
||||
local text = {}
|
||||
for selectionChild = consoleBuffer.selection.first, consoleBuffer.selection.last do
|
||||
local label = self:getParent():getChildByIndex(selectionChild)
|
||||
table.insert(text, label:getSelection())
|
||||
end
|
||||
consoleBuffer.selectionText = table.concat(text, '\n')
|
||||
return true
|
||||
end
|
||||
label.onDragMove = function(self, mousePos, mouseMoved)
|
||||
local parent = self:getParent()
|
||||
local parentRect = parent:getPaddingRect()
|
||||
local selfIndex = parent:getChildIndex(self)
|
||||
local child = parent:getChildByPos(mousePos)
|
||||
|
||||
-- find bonding children
|
||||
if not child then
|
||||
if mousePos.y < self:getY() then
|
||||
for index = selfIndex - 1, 1, -1 do
|
||||
local label = parent:getChildByIndex(index)
|
||||
if label:getY() + label:getHeight() > parentRect.y then
|
||||
if (mousePos.y >= label:getY() and mousePos.y <= label:getY() + label:getHeight()) or index == 1 then
|
||||
child = label
|
||||
break
|
||||
end
|
||||
else
|
||||
child = parent:getChildByIndex(index + 1)
|
||||
break
|
||||
end
|
||||
end
|
||||
elseif mousePos.y > self:getY() + self:getHeight() then
|
||||
for index = selfIndex + 1, parent:getChildCount(), 1 do
|
||||
local label = parent:getChildByIndex(index)
|
||||
if label:getY() < parentRect.y + parentRect.height then
|
||||
if (mousePos.y >= label:getY() and mousePos.y <= label:getY() + label:getHeight()) or index == parent:getChildCount() then
|
||||
child = label
|
||||
break
|
||||
end
|
||||
else
|
||||
child = parent:getChildByIndex(index - 1)
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
child = self
|
||||
end
|
||||
end
|
||||
|
||||
if not child then return false end
|
||||
|
||||
local childIndex = parent:getChildIndex(child)
|
||||
|
||||
-- remove old selection
|
||||
clearSelection(consoleBuffer)
|
||||
|
||||
-- update self selection
|
||||
local textBegin = self:getTextPos(self:getLastClickPosition())
|
||||
local textPos = self:getTextPos(mousePos)
|
||||
self:setSelection(textBegin, textPos)
|
||||
|
||||
consoleBuffer.selection = { first = math.min(selfIndex, childIndex), last = math.max(selfIndex, childIndex) }
|
||||
|
||||
-- update siblings selection
|
||||
if child ~= self then
|
||||
for selectionChild = consoleBuffer.selection.first + 1, consoleBuffer.selection.last - 1 do
|
||||
parent:getChildByIndex(selectionChild):selectAll()
|
||||
end
|
||||
|
||||
local textPos = child:getTextPos(mousePos)
|
||||
if childIndex > selfIndex then
|
||||
child:setSelection(0, textPos)
|
||||
else
|
||||
child:setSelection(string.len(child:getText()), textPos)
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if consoleBuffer:getChildCount() > MAX_LINES then
|
||||
consoleBuffer:getFirstChild():destroy()
|
||||
local child = consoleBuffer:getFirstChild()
|
||||
clearSelection(consoleBuffer)
|
||||
child:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -578,6 +707,7 @@ end
|
||||
|
||||
function processChannelTabMenu(tab, mousePos, mouseButton)
|
||||
local menu = g_ui.createWidget('PopupMenu')
|
||||
menu:setGameMenu(true)
|
||||
|
||||
channelName = tab:getText()
|
||||
if tab ~= defaultTab and tab ~= serverTab then
|
||||
@@ -588,8 +718,28 @@ function processChannelTabMenu(tab, mousePos, mouseButton)
|
||||
|
||||
if consoleTabBar:getCurrentTab() == tab then
|
||||
menu:addOption(tr('Clear Messages'), function() clearChannel(consoleTabBar) end)
|
||||
menu:addOption(tr('Save Messages'), function()
|
||||
local panel = consoleTabBar:getTabPanel(tab)
|
||||
local consoleBuffer = panel:getChildById('consoleBuffer')
|
||||
local lines = {}
|
||||
for _,label in pairs(consoleBuffer:getChildren()) do
|
||||
table.insert(lines, label:getText())
|
||||
end
|
||||
|
||||
local filename = channelName .. '.txt'
|
||||
local filepath = '/' .. filename
|
||||
|
||||
-- extra information at the beginning
|
||||
table.insert(lines, 1, os.date('\nChannel saved at %a %b %d %H:%M:%S %Y'))
|
||||
|
||||
if g_resources.fileExists(filepath) then
|
||||
table.insert(lines, 1, protectedcall(g_resources.readFileContents, filepath) or '')
|
||||
end
|
||||
|
||||
g_resources.writeFileContents(filepath, table.concat(lines, '\n'))
|
||||
modules.game_textmessage.displayStatusMessage(tr('Channel appended to %s', filename))
|
||||
end)
|
||||
end
|
||||
--menu:addOption(tr('Save Messages'), function() --[[TODO]] end)
|
||||
|
||||
menu:display(mousePos)
|
||||
end
|
||||
@@ -597,6 +747,7 @@ end
|
||||
function processMessageMenu(mousePos, mouseButton, creatureName, text, label, tab)
|
||||
if mouseButton == MouseRightButton then
|
||||
local menu = g_ui.createWidget('PopupMenu')
|
||||
menu:setGameMenu(true)
|
||||
if creatureName and #creatureName > 0 then
|
||||
if creatureName ~= g_game.getCharacterName() then
|
||||
menu:addOption(tr('Message to ' .. creatureName), function () g_game.openPrivateChannel(creatureName) end)
|
||||
@@ -622,12 +773,15 @@ function processMessageMenu(mousePos, mouseButton, creatureName, text, label, ta
|
||||
|
||||
menu:addOption(tr('Copy name'), function () g_window.setClipboardText(creatureName) end)
|
||||
end
|
||||
if label:hasSelection() then
|
||||
menu:addOption(tr('Copy'), function() g_window.setClipboardText(label:getSelection()) end, '(Ctrl+C)')
|
||||
local selection = tab.tabPanel:getChildById('consoleBuffer').selectionText
|
||||
if selection and #selection > 0 then
|
||||
menu:addOption(tr('Copy'), function() g_window.setClipboardText(selection) end, '(Ctrl+C)')
|
||||
end
|
||||
menu:addOption(tr('Copy message'), function() g_window.setClipboardText(text) end)
|
||||
menu:addOption(tr('Select all'), function() label:selectAll() end)
|
||||
if tab.violations then
|
||||
if text then
|
||||
menu:addOption(tr('Copy message'), function() g_window.setClipboardText(text) end)
|
||||
end
|
||||
menu:addOption(tr('Select all'), function() selectAll(tab.tabPanel:getChildById('consoleBuffer')) end)
|
||||
if tab.violations and creatureName then
|
||||
menu:addSeparator()
|
||||
menu:addOption(tr('Process') .. ' ' .. creatureName, function() processViolation(creatureName, text) end)
|
||||
menu:addOption(tr('Remove') .. ' ' .. creatureName, function() g_game.closeRuleViolation(creatureName) end)
|
||||
@@ -687,7 +841,7 @@ function sendMessage(message, tab)
|
||||
end
|
||||
|
||||
-- player used whisper
|
||||
local chatCommandMessage = message:match("^%#[w|W] (.*)")
|
||||
chatCommandMessage = message:match("^%#[w|W] (.*)")
|
||||
if chatCommandMessage ~= nil then
|
||||
chatCommandSayMode = 'whisper'
|
||||
message = chatCommandMessage
|
||||
@@ -695,12 +849,27 @@ function sendMessage(message, tab)
|
||||
end
|
||||
|
||||
-- player say
|
||||
local chatCommandMessage = message:match("^%#[s|S] (.*)")
|
||||
chatCommandMessage = message:match("^%#[s|S] (.*)")
|
||||
if chatCommandMessage ~= nil then
|
||||
chatCommandSayMode = 'say'
|
||||
message = chatCommandMessage
|
||||
channel = 0
|
||||
end
|
||||
|
||||
-- player red talk on channel
|
||||
chatCommandMessage = message:match("^%#[c|C] (.*)")
|
||||
if chatCommandMessage ~= nil then
|
||||
chatCommandSayMode = 'channelRed'
|
||||
message = chatCommandMessage
|
||||
end
|
||||
|
||||
-- player broadcast
|
||||
chatCommandMessage = message:match("^%#[b|B] (.*)")
|
||||
if chatCommandMessage ~= nil then
|
||||
chatCommandSayMode = 'broadcast'
|
||||
message = chatCommandMessage
|
||||
channel = 0
|
||||
end
|
||||
|
||||
local findIni, findEnd, chatCommandInitial, chatCommandPrivate, chatCommandEnd, chatCommandMessage = message:find("([%*%@])(.+)([%*%@])(.*)")
|
||||
if findIni ~= nil and findIni == 1 then -- player used private chat command
|
||||
@@ -852,21 +1021,21 @@ function onTalk(name, level, mode, message, channelId, creaturePos)
|
||||
|
||||
if (mode == MessageModes.Say or mode == MessageModes.Whisper or mode == MessageModes.Yell or
|
||||
mode == MessageModes.Spell or mode == MessageModes.MonsterSay or mode == MessageModes.MonsterYell or
|
||||
mode == MessageModes.NpcFrom or mode == MessageModes.BarkLow or mode == MessageModes.BarkLoud) and
|
||||
creaturePos then
|
||||
-- Remove curly braces from screen message
|
||||
local staticMessage = message
|
||||
if mode == MessageModes.NpcFrom then
|
||||
local highlightData = getHighlightedText(staticMessage)
|
||||
if #highlightData > 0 then
|
||||
for i = 1, #highlightData / 3 do
|
||||
local dataBlock = { _start = highlightData[(i-1)*3+1], _end = highlightData[(i-1)*3+2], words = highlightData[(i-1)*3+3] }
|
||||
staticMessage = staticMessage:gsub("{"..dataBlock.words.."}", dataBlock.words)
|
||||
end
|
||||
mode == MessageModes.NpcFrom or mode == MessageModes.BarkLow or mode == MessageModes.BarkLoud or
|
||||
mode == MessageModes.NpcFromStartBlock) and creaturePos then
|
||||
local staticText = StaticText.create()
|
||||
-- Remove curly braces from screen message
|
||||
local staticMessage = message
|
||||
if mode == MessageModes.NpcFrom or mode == MessageModes.NpcFromStartBlock then
|
||||
local highlightData = getHighlightedText(staticMessage)
|
||||
if #highlightData > 0 then
|
||||
for i = 1, #highlightData / 3 do
|
||||
local dataBlock = { _start = highlightData[(i-1)*3+1], _end = highlightData[(i-1)*3+2], words = highlightData[(i-1)*3+3] }
|
||||
staticMessage = staticMessage:gsub("{"..dataBlock.words.."}", dataBlock.words)
|
||||
end
|
||||
end
|
||||
|
||||
local staticText = StaticText.create()
|
||||
staticText:setColor(speaktype.color)
|
||||
end
|
||||
staticText:addMessage(name, mode, staticMessage)
|
||||
g_map.addThing(staticText, creaturePos, -1)
|
||||
end
|
||||
|
@@ -10,6 +10,9 @@ ConsoleLabel < UITextEdit
|
||||
change-cursor-image: false
|
||||
cursor-visible: false
|
||||
editable: false
|
||||
draggable: true
|
||||
selectable: false
|
||||
focusable: false
|
||||
|
||||
ConsolePhantomLabel < UILabel
|
||||
font: verdana-11px-antialised
|
||||
@@ -81,7 +84,8 @@ Panel
|
||||
margin-left: 5
|
||||
margin-top: 3
|
||||
margin-right: 5
|
||||
moveable: true
|
||||
tab-spacing: 2
|
||||
movable: true
|
||||
|
||||
TabButton
|
||||
id: nextChannelButton
|
||||
|
@@ -27,6 +27,15 @@ function init()
|
||||
onLoginAdvice = onLoginAdvice,
|
||||
}, true)
|
||||
|
||||
-- Call load AFTER game window has been created and
|
||||
-- resized to a stable state, otherwise the saved
|
||||
-- settings can get overridden by false onGeometryChange
|
||||
-- events
|
||||
connect(g_app, {
|
||||
onRun = load,
|
||||
onExit = save
|
||||
})
|
||||
|
||||
gameRootPanel = g_ui.displayUI('gameinterface')
|
||||
gameRootPanel:hide()
|
||||
gameRootPanel:lower()
|
||||
@@ -49,7 +58,6 @@ function init()
|
||||
setupViewMode(0)
|
||||
|
||||
bindKeys()
|
||||
load()
|
||||
|
||||
if g_game.isOnline() then
|
||||
show()
|
||||
@@ -102,7 +110,6 @@ function unbindWalkKey(key)
|
||||
end
|
||||
|
||||
function terminate()
|
||||
save()
|
||||
hide()
|
||||
|
||||
hookedMenuOptions = {}
|
||||
@@ -442,7 +449,10 @@ end
|
||||
|
||||
function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
|
||||
if not g_game.isOnline() then return end
|
||||
|
||||
local menu = g_ui.createWidget('PopupMenu')
|
||||
menu:setGameMenu(true)
|
||||
|
||||
local classic = modules.client_options.getOption('classicControl')
|
||||
local shortcut = nil
|
||||
|
||||
|
@@ -30,5 +30,6 @@ Module
|
||||
- game_spelllist
|
||||
- game_cooldown
|
||||
- game_modaldialog
|
||||
- game_unjustifiedpoints
|
||||
@onLoad: init()
|
||||
@onUnload: terminate()
|
||||
|
@@ -17,7 +17,10 @@ inventoryButton = nil
|
||||
purseButton = nil
|
||||
|
||||
function init()
|
||||
connect(LocalPlayer, { onInventoryChange = onInventoryChange })
|
||||
connect(LocalPlayer, {
|
||||
onInventoryChange = onInventoryChange,
|
||||
onBlessingsChange = onBlessingsChange
|
||||
})
|
||||
connect(g_game, { onGameStart = refresh })
|
||||
|
||||
g_keyboard.bindKeyDown('Ctrl+I', toggle)
|
||||
@@ -43,7 +46,10 @@ function init()
|
||||
end
|
||||
|
||||
function terminate()
|
||||
disconnect(LocalPlayer, { onInventoryChange = onInventoryChange })
|
||||
disconnect(LocalPlayer, {
|
||||
onInventoryChange = onInventoryChange,
|
||||
onBlessingsChange = onBlessingsChange
|
||||
})
|
||||
disconnect(g_game, { onGameStart = refresh })
|
||||
|
||||
g_keyboard.unbindKeyDown('Ctrl+I')
|
||||
@@ -52,6 +58,15 @@ function terminate()
|
||||
inventoryButton:destroy()
|
||||
end
|
||||
|
||||
function toggleAdventurerStyle(hasBlessing)
|
||||
for slot = InventorySlotFirst, InventorySlotLast do
|
||||
local itemWidget = inventoryPanel:getChildById('slot' .. slot)
|
||||
if itemWidget then
|
||||
itemWidget:setOn(hasBlessing)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function refresh()
|
||||
local player = g_game.getLocalPlayer()
|
||||
for i = InventorySlotFirst, InventorySlotPurse do
|
||||
@@ -60,6 +75,7 @@ function refresh()
|
||||
else
|
||||
onInventoryChange(player, i, nil)
|
||||
end
|
||||
toggleAdventurerStyle(player and Bit.hasBit(player:getBlessings(), Blessings.Adventurer) or false)
|
||||
end
|
||||
|
||||
purseButton:setVisible(g_game.getFeature(GamePurseSlot))
|
||||
@@ -92,10 +108,17 @@ function onInventoryChange(player, slot, item, oldItem)
|
||||
|
||||
local itemWidget = inventoryPanel:getChildById('slot' .. slot)
|
||||
if item then
|
||||
itemWidget:setStyle('Item')
|
||||
itemWidget:setStyle('InventoryItem')
|
||||
itemWidget:setItem(item)
|
||||
else
|
||||
itemWidget:setStyle(InventorySlotStyles[slot])
|
||||
itemWidget:setItem(nil)
|
||||
end
|
||||
end
|
||||
|
||||
function onBlessingsChange(player, blessings, oldBlessings)
|
||||
local hasAdventurerBlessing = Bit.hasBit(blessings, Blessings.Adventurer)
|
||||
if hasAdventurerBlessing ~= Bit.hasBit(oldBlessings, Blessings.Adventurer) then
|
||||
toggleAdventurerStyle(hasAdventurerBlessing)
|
||||
end
|
||||
end
|
@@ -1,54 +1,76 @@
|
||||
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 < Button
|
||||
id: purseButton
|
||||
|
@@ -13,12 +13,8 @@
|
||||
* Clean up the interface building
|
||||
- Add a new market interface file to handle building?
|
||||
|
||||
* Add offer table column ordering.
|
||||
- Player Name, Amount, Total Price, Peice Price and Ends At
|
||||
|
||||
* Extend information features
|
||||
- Hover over offers for purchase information (balance after transaction, etc)
|
||||
- Display out of trend market offers based on their previous statistics (like cipsoft does)
|
||||
]]
|
||||
|
||||
Market = {}
|
||||
@@ -42,6 +38,7 @@ currentOffersPanel = nil
|
||||
offerHistoryPanel = nil
|
||||
itemsPanel = nil
|
||||
selectedOffer = {}
|
||||
selectedMyOffer = {}
|
||||
|
||||
nameLabel = nil
|
||||
feeLabel = nil
|
||||
@@ -68,6 +65,11 @@ detailsTable = nil
|
||||
buyStatsTable = nil
|
||||
sellStatsTable = nil
|
||||
|
||||
buyCancelButton = nil
|
||||
sellCancelButton = nil
|
||||
buyMyOfferTable = nil
|
||||
sellMyOfferTable = nil
|
||||
|
||||
offerExhaust = {}
|
||||
marketOffers = {}
|
||||
marketItems = {}
|
||||
@@ -75,17 +77,10 @@ information = {}
|
||||
currentItems = {}
|
||||
lastCreatedOffer = 0
|
||||
fee = 0
|
||||
averagePrice = 0
|
||||
|
||||
loaded = false
|
||||
|
||||
local offerTableHeader = {
|
||||
{['text'] = 'Player Name', ['width'] = 100},
|
||||
{['text'] = 'Amount', ['width'] = 60},
|
||||
{['text'] = 'Total Price', ['width'] = 90},
|
||||
{['text'] = 'Piece Price', ['width'] = 80},
|
||||
{['text'] = 'Ends at', ['width'] = 120}
|
||||
}
|
||||
|
||||
local function isItemValid(item, category, searchFilter)
|
||||
if not item or not item.marketData then
|
||||
return false
|
||||
@@ -110,7 +105,7 @@ local function isItemValid(item, category, searchFilter)
|
||||
local filterDepot = filterButtons[MarketFilters.Depot]:isChecked()
|
||||
|
||||
if slotFilter then
|
||||
if slotFilter ~= 255 and item.ptr:getClothSlot() ~= slotFilter then
|
||||
if slotFilter ~= 255 and item.thingType:getClothSlot() ~= slotFilter then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -124,7 +119,7 @@ local function isItemValid(item, category, searchFilter)
|
||||
return false
|
||||
end
|
||||
end
|
||||
if filterDepot and Market.depotContains(item.ptr:getId()) <= 0 then
|
||||
if filterDepot and Market.getDepotCount(item.marketData.tradeAs) <= 0 then
|
||||
return false
|
||||
end
|
||||
if searchFilter then
|
||||
@@ -145,17 +140,24 @@ local function clearOffers()
|
||||
sellOfferTable:clearData()
|
||||
end
|
||||
|
||||
local function clearMyOffers()
|
||||
marketOffers[MarketAction.Buy] = {}
|
||||
marketOffers[MarketAction.Sell] = {}
|
||||
buyMyOfferTable:clearData()
|
||||
sellMyOfferTable:clearData()
|
||||
end
|
||||
|
||||
local function clearFilters()
|
||||
for _, filter in pairs(filterButtons) do
|
||||
if filter and filter:isChecked() then
|
||||
filter:setChecked(false)
|
||||
if filter and filter:isChecked() ~= filter.default then
|
||||
filter:setChecked(filter.default)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function clearFee()
|
||||
feeLabel:setText('')
|
||||
fee = 0
|
||||
fee = 20
|
||||
end
|
||||
|
||||
local function refreshTypeList()
|
||||
@@ -163,13 +165,13 @@ local function refreshTypeList()
|
||||
offerTypeList:addOption('Buy')
|
||||
|
||||
if Market.isItemSelected() then
|
||||
if Market.depotContains(selectedItem.item.ptr:getId()) > 0 then
|
||||
if Market.getDepotCount(selectedItem.item.marketData.tradeAs) > 0 then
|
||||
offerTypeList:addOption('Sell')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function addOffer(offer, type)
|
||||
local function addOffer(offer, offerType)
|
||||
if not offer then
|
||||
return false
|
||||
end
|
||||
@@ -178,27 +180,85 @@ local function addOffer(offer, type)
|
||||
local amount = offer:getAmount()
|
||||
local price = offer:getPrice()
|
||||
local timestamp = offer:getTimeStamp()
|
||||
local itemName = offer:getItem():getMarketData().name
|
||||
|
||||
buyOfferTable:toggleSorting(false)
|
||||
sellOfferTable:toggleSorting(false)
|
||||
|
||||
buyMyOfferTable:toggleSorting(false)
|
||||
sellMyOfferTable:toggleSorting(false)
|
||||
|
||||
if amount < 1 then return false end
|
||||
if type == MarketAction.Buy then
|
||||
local data = {
|
||||
{['text'] = player, ['width'] = 100},
|
||||
{['text'] = amount, ['width'] = 60},
|
||||
{['text'] = price*amount, ['width'] = 90},
|
||||
{['text'] = price, ['width'] = 80},
|
||||
{['text'] = string.gsub(os.date('%c', timestamp), " ", " "), ['width'] = 120}
|
||||
}
|
||||
buyOfferTable:addRow(data, id)
|
||||
if offerType == MarketAction.Buy then
|
||||
if offer.warn then
|
||||
buyOfferTable:setColumnStyle('OfferTableWarningColumn', true)
|
||||
end
|
||||
|
||||
local row = nil
|
||||
if offer.var == MarketRequest.MyOffers then
|
||||
row = buyMyOfferTable:addRow({
|
||||
{text = itemName},
|
||||
{text = price*amount},
|
||||
{text = price},
|
||||
{text = amount},
|
||||
{text = string.gsub(os.date('%c', timestamp), " ", " "), sortvalue = timestamp}
|
||||
})
|
||||
else
|
||||
row = buyOfferTable:addRow({
|
||||
{text = player},
|
||||
{text = amount},
|
||||
{text = price*amount},
|
||||
{text = price},
|
||||
{text = string.gsub(os.date('%c', timestamp), " ", " ")}
|
||||
})
|
||||
end
|
||||
row.ref = id
|
||||
|
||||
if offer.warn then
|
||||
row:setTooltip(tr('This offer is 25%% below the average market price'))
|
||||
buyOfferTable:setColumnStyle('OfferTableColumn', true)
|
||||
end
|
||||
else
|
||||
local data = {
|
||||
{['text'] = player, ['width'] = 100},
|
||||
{['text'] = amount, ['width'] = 60},
|
||||
{['text'] = price*amount, ['width'] = 90},
|
||||
{['text'] = price, ['width'] = 80},
|
||||
{['text'] = string.gsub(os.date('%c', timestamp), " ", " "), ['width'] = 120}
|
||||
}
|
||||
sellOfferTable:addRow(data, id)
|
||||
if offer.warn then
|
||||
sellOfferTable:setColumnStyle('OfferTableWarningColumn', true)
|
||||
end
|
||||
|
||||
local row = nil
|
||||
if offer.var == MarketRequest.MyOffers then
|
||||
row = sellMyOfferTable:addRow({
|
||||
{text = itemName},
|
||||
{text = price*amount},
|
||||
{text = price},
|
||||
{text = amount},
|
||||
{text = string.gsub(os.date('%c', timestamp), " ", " "), sortvalue = timestamp}
|
||||
})
|
||||
else
|
||||
row = sellOfferTable:addRow({
|
||||
{text = player},
|
||||
{text = amount},
|
||||
{text = price*amount},
|
||||
{text = price},
|
||||
{text = string.gsub(os.date('%c', timestamp), " ", " "), sortvalue = timestamp}
|
||||
})
|
||||
end
|
||||
row.ref = id
|
||||
|
||||
if offer.warn then
|
||||
row:setTooltip(tr('This offer is 25%% above the average market price'))
|
||||
sellOfferTable:setColumnStyle('OfferTableColumn', true)
|
||||
end
|
||||
end
|
||||
|
||||
buyOfferTable:toggleSorting(false)
|
||||
sellOfferTable:toggleSorting(false)
|
||||
buyOfferTable:sort()
|
||||
sellOfferTable:sort()
|
||||
|
||||
buyMyOfferTable:toggleSorting(false)
|
||||
sellMyOfferTable:toggleSorting(false)
|
||||
buyMyOfferTable:sort()
|
||||
sellMyOfferTable:sort()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -206,12 +266,17 @@ local function mergeOffer(offer)
|
||||
if not offer then
|
||||
return false
|
||||
end
|
||||
|
||||
local id = offer:getId()
|
||||
local type = offer:getType()
|
||||
local offerType = offer:getType()
|
||||
local amount = offer:getAmount()
|
||||
local replaced = false
|
||||
|
||||
if type == MarketAction.Buy then
|
||||
if offerType == MarketAction.Buy then
|
||||
if averagePrice > 0 then
|
||||
offer.warn = offer:getPrice() <= averagePrice - math.floor(averagePrice / 4)
|
||||
end
|
||||
|
||||
for i = 1, #marketOffers[MarketAction.Buy] do
|
||||
local o = marketOffers[MarketAction.Buy][i]
|
||||
-- replace existing offer
|
||||
@@ -224,6 +289,10 @@ local function mergeOffer(offer)
|
||||
table.insert(marketOffers[MarketAction.Buy], offer)
|
||||
end
|
||||
else
|
||||
if averagePrice > 0 then
|
||||
offer.warn = offer:getPrice() >= averagePrice + math.floor(averagePrice / 4)
|
||||
end
|
||||
|
||||
for i = 1, #marketOffers[MarketAction.Sell] do
|
||||
local o = marketOffers[MarketAction.Sell][i]
|
||||
-- replace existing offer
|
||||
@@ -248,13 +317,21 @@ local function updateOffers(offers)
|
||||
selectedOffer[MarketAction.Buy] = nil
|
||||
selectedOffer[MarketAction.Sell] = nil
|
||||
|
||||
selectedMyOffer[MarketAction.Buy] = nil
|
||||
selectedMyOffer[MarketAction.Sell] = nil
|
||||
|
||||
-- clear existing offer data
|
||||
buyOfferTable:clearData()
|
||||
buyOfferTable:setSorting(4, TABLE_SORTING_DESC)
|
||||
sellOfferTable:clearData()
|
||||
sellOfferTable:setSorting(4, TABLE_SORTING_ASC)
|
||||
|
||||
sellButton:setEnabled(false)
|
||||
buyButton:setEnabled(false)
|
||||
|
||||
buyCancelButton:setEnabled(false)
|
||||
sellCancelButton:setEnabled(false)
|
||||
|
||||
for _, offer in pairs(offers) do
|
||||
mergeOffer(offer)
|
||||
end
|
||||
@@ -274,8 +351,8 @@ local function updateDetails(itemId, descriptions, purchaseStats, saleStats)
|
||||
detailsTable:clearData()
|
||||
for k, desc in pairs(descriptions) do
|
||||
local columns = {
|
||||
{['text'] = getMarketDescriptionName(desc[1])..':'},
|
||||
{['text'] = desc[2], ['width'] = 330}
|
||||
{text = getMarketDescriptionName(desc[1])..':'},
|
||||
{text = desc[2]}
|
||||
}
|
||||
detailsTable:addRow(columns)
|
||||
end
|
||||
@@ -283,11 +360,13 @@ local function updateDetails(itemId, descriptions, purchaseStats, saleStats)
|
||||
-- update sale item statistics
|
||||
sellStatsTable:clearData()
|
||||
if table.empty(saleStats) then
|
||||
sellStatsTable:addRow({{['text'] = 'No information'}})
|
||||
sellStatsTable:addRow({{text = 'No information'}})
|
||||
else
|
||||
local offerAmount = 0
|
||||
local transactions, totalPrice, highestPrice, lowestPrice = 0, 0, 0, 0
|
||||
for _, stat in pairs(saleStats) do
|
||||
if not stat:isNull() then
|
||||
offerAmount = offerAmount + 1
|
||||
transactions = transactions + stat:getTransactions()
|
||||
totalPrice = totalPrice + stat:getTotalPrice()
|
||||
local newHigh = stat:getHighestPrice()
|
||||
@@ -301,28 +380,30 @@ local function updateDetails(itemId, descriptions, purchaseStats, saleStats)
|
||||
end
|
||||
end
|
||||
end
|
||||
sellStatsTable:addRow({{['text'] = 'Total Transations:'},
|
||||
{['text'] = transactions, ['width'] = 270}})
|
||||
|
||||
sellStatsTable:addRow({{['text'] = 'Highest Price:'},
|
||||
{['text'] = highestPrice, ['width'] = 270}})
|
||||
|
||||
if totalPrice > 0 and transactions > 0 then
|
||||
sellStatsTable:addRow({{['text'] = 'Average Price:'},
|
||||
{['text'] = math.floor(totalPrice/transactions), ['width'] = 270}})
|
||||
if offerAmount >= 5 and transactions >= 10 then
|
||||
averagePrice = math.round(totalPrice / transactions)
|
||||
else
|
||||
sellStatsTable:addRow({{['text'] = 'Average Price:'},
|
||||
{['text'] = 0, ['width'] = 270}})
|
||||
averagePrice = 0
|
||||
end
|
||||
|
||||
sellStatsTable:addRow({{['text'] = 'Lowest Price:'},
|
||||
{['text'] = lowestPrice, ['width'] = 270}})
|
||||
sellStatsTable:addRow({{text = 'Total Transations:'}, {text = transactions}})
|
||||
sellStatsTable:addRow({{text = 'Highest Price:'}, {text = highestPrice}})
|
||||
|
||||
if totalPrice > 0 and transactions > 0 then
|
||||
sellStatsTable:addRow({{text = 'Average Price:'},
|
||||
{text = math.floor(totalPrice/transactions)}})
|
||||
else
|
||||
sellStatsTable:addRow({{text = 'Average Price:'}, {text = 0}})
|
||||
end
|
||||
|
||||
sellStatsTable:addRow({{text = 'Lowest Price:'}, {text = lowestPrice}})
|
||||
end
|
||||
|
||||
-- update buy item statistics
|
||||
buyStatsTable:clearData()
|
||||
if table.empty(purchaseStats) then
|
||||
buyStatsTable:addRow({{['text'] = 'No information'}})
|
||||
buyStatsTable:addRow({{text = 'No information'}})
|
||||
else
|
||||
local transactions, totalPrice, highestPrice, lowestPrice = 0, 0, 0, 0
|
||||
for _, stat in pairs(purchaseStats) do
|
||||
@@ -340,22 +421,18 @@ local function updateDetails(itemId, descriptions, purchaseStats, saleStats)
|
||||
end
|
||||
end
|
||||
end
|
||||
buyStatsTable:addRow({{['text'] = 'Total Transations:'},
|
||||
{['text'] = transactions, ['width'] = 270}})
|
||||
|
||||
buyStatsTable:addRow({{['text'] = 'Highest Price:'},
|
||||
{['text'] = highestPrice, ['width'] = 270}})
|
||||
buyStatsTable:addRow({{text = 'Total Transations:'},{text = transactions}})
|
||||
buyStatsTable:addRow({{text = 'Highest Price:'}, {text = highestPrice}})
|
||||
|
||||
if totalPrice > 0 and transactions > 0 then
|
||||
buyStatsTable:addRow({{['text'] = 'Average Price:'},
|
||||
{['text'] = math.floor(totalPrice/transactions), ['width'] = 270}})
|
||||
buyStatsTable:addRow({{text = 'Average Price:'},
|
||||
{text = math.floor(totalPrice/transactions)}})
|
||||
else
|
||||
buyStatsTable:addRow({{['text'] = 'Average Price:'},
|
||||
{['text'] = 0, ['width'] = 270}})
|
||||
buyStatsTable:addRow({{text = 'Average Price:'}, {text = 0}})
|
||||
end
|
||||
|
||||
buyStatsTable:addRow({{['text'] = 'Lowest Price:'},
|
||||
{['text'] = lowestPrice, ['width'] = 270}})
|
||||
buyStatsTable:addRow({{text = 'Lowest Price:'}, {text = lowestPrice}})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -365,12 +442,12 @@ local function updateSelectedItem(widget)
|
||||
|
||||
Market.resetCreateOffer()
|
||||
if Market.isItemSelected() then
|
||||
selectedItem:setItem(selectedItem.item.ptr)
|
||||
selectedItem:setItem(selectedItem.item.displayItem)
|
||||
nameLabel:setText(selectedItem.item.marketData.name)
|
||||
clearOffers()
|
||||
|
||||
Market.enableCreateOffer(true) -- update offer types
|
||||
MarketProtocol.sendMarketBrowse(selectedItem.item.ptr:getId()) -- send browsed msg
|
||||
MarketProtocol.sendMarketBrowse(selectedItem.item.marketData.tradeAs) -- send browsed msg
|
||||
else
|
||||
Market.clearSelectedItem()
|
||||
end
|
||||
@@ -400,55 +477,75 @@ local function updateFee(price, amount)
|
||||
feeLabel:resizeToText()
|
||||
end
|
||||
|
||||
local function openAmountWindow(callback, type, actionText)
|
||||
local actionText = actionText or ''
|
||||
if not Market.isOfferSelected(type) then
|
||||
local function destroyAmountWindow()
|
||||
if amountWindow then
|
||||
amountWindow:destroy()
|
||||
amountWindow = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function cancelMyOffer(actionType)
|
||||
local offer = selectedMyOffer[actionType]
|
||||
MarketProtocol.sendMarketCancelOffer(offer:getTimeStamp(), offer:getCounter())
|
||||
Market.refreshMyOffers()
|
||||
end
|
||||
|
||||
local function openAmountWindow(callback, actionType, actionText)
|
||||
if not Market.isOfferSelected(actionType) then
|
||||
return
|
||||
end
|
||||
|
||||
amountWindow = g_ui.createWidget('AmountWindow', rootWidget)
|
||||
amountWindow:lock()
|
||||
local item = selectedOffer[type]:getItem()
|
||||
|
||||
local max = selectedOffer[type]:getAmount(item:getId())
|
||||
if type == MarketAction.Sell then
|
||||
local depot = Market.depotContains(item:getId())
|
||||
if max > depot then
|
||||
max = depot
|
||||
local offer = selectedOffer[actionType]
|
||||
local item = offer:getItem()
|
||||
|
||||
local maximum = offer:getAmount()
|
||||
if actionType == MarketAction.Sell then
|
||||
local depot = Market.getDepotCount(item:getId())
|
||||
if maximum > depot then
|
||||
maximum = depot
|
||||
end
|
||||
else
|
||||
maximum = math.min(maximum, math.floor(information.balance / offer:getPrice()))
|
||||
end
|
||||
|
||||
if item:isStackable() then
|
||||
maximum = math.min(maximum, MarketMaxAmountStackable)
|
||||
else
|
||||
maximum = math.min(maximum, MarketMaxAmount)
|
||||
end
|
||||
|
||||
local itembox = amountWindow:getChildById('item')
|
||||
itembox:setItemId(item:getId())
|
||||
itembox:setText(1)
|
||||
|
||||
local scrollbar = amountWindow:getChildById('amountScrollBar')
|
||||
scrollbar:setText(tostring(selectedOffer[type]:getPrice())..'gp')
|
||||
scrollbar:setMaximum(max)
|
||||
scrollbar:setMinimum(1)
|
||||
scrollbar:setValue(1)
|
||||
scrollbar:setText(offer:getPrice()..'gp')
|
||||
|
||||
scrollbar.onValueChange = function(widget, value)
|
||||
widget:setText(tostring(value*selectedOffer[type]:getPrice())..'gp')
|
||||
itembox:setText(tostring(value))
|
||||
widget:setText((value*offer:getPrice())..'gp')
|
||||
itembox:setText(value)
|
||||
end
|
||||
|
||||
scrollbar:setRange(1, maximum)
|
||||
scrollbar:setValue(1)
|
||||
|
||||
local okButton = amountWindow:getChildById('buttonOk')
|
||||
if actionText ~= '' then
|
||||
if actionText then
|
||||
okButton:setText(actionText)
|
||||
end
|
||||
|
||||
local okFunc = function()
|
||||
local counter = selectedOffer[type]:getCounter()
|
||||
local timestamp = selectedOffer[type]:getTimeStamp()
|
||||
local counter = offer:getCounter()
|
||||
local timestamp = offer:getTimeStamp()
|
||||
callback(scrollbar:getValue(), timestamp, counter)
|
||||
okButton:getParent():destroy()
|
||||
amountWindow = nil
|
||||
destroyAmountWindow()
|
||||
end
|
||||
|
||||
local cancelButton = amountWindow:getChildById('buttonCancel')
|
||||
local cancelFunc = function()
|
||||
cancelButton:getParent():destroy()
|
||||
amountWindow = nil
|
||||
destroyAmountWindow()
|
||||
end
|
||||
|
||||
amountWindow.onEnter = okFunc
|
||||
@@ -492,7 +589,7 @@ local function onSelectBuyOffer(table, selectedRow, previousSelectedRow)
|
||||
for _, offer in pairs(marketOffers[MarketAction.Buy]) do
|
||||
if offer:isEqual(selectedRow.ref) then
|
||||
selectedOffer[MarketAction.Sell] = offer
|
||||
if Market.depotContains(offer:getItem():getId()) > 0 then
|
||||
if Market.getDepotCount(offer:getItem():getId()) > 0 then
|
||||
sellButton:setEnabled(true)
|
||||
else
|
||||
sellButton:setEnabled(false)
|
||||
@@ -501,6 +598,24 @@ local function onSelectBuyOffer(table, selectedRow, previousSelectedRow)
|
||||
end
|
||||
end
|
||||
|
||||
local function onSelectMyBuyOffer(table, selectedRow, previousSelectedRow)
|
||||
for _, offer in pairs(marketOffers[MarketAction.Buy]) do
|
||||
if offer:isEqual(selectedRow.ref) then
|
||||
selectedMyOffer[MarketAction.Buy] = offer
|
||||
buyCancelButton:setEnabled(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function onSelectMySellOffer(table, selectedRow, previousSelectedRow)
|
||||
for _, offer in pairs(marketOffers[MarketAction.Sell]) do
|
||||
if offer:isEqual(selectedRow.ref) then
|
||||
selectedMyOffer[MarketAction.Sell] = offer
|
||||
sellCancelButton:setEnabled(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function onChangeCategory(combobox, option)
|
||||
local id = getMarketCategoryId(option)
|
||||
if id == MarketCategory.MetaWeapons then
|
||||
@@ -535,74 +650,84 @@ local function onChangeSlotFilter(combobox, option)
|
||||
end
|
||||
|
||||
local function onChangeOfferType(combobox, option)
|
||||
local id = selectedItem.item.ptr:getId()
|
||||
local item = selectedItem.item
|
||||
local maximum = item.thingType:isStackable() and MarketMaxAmountStackable or MarketMaxAmount
|
||||
|
||||
if option == 'Sell' then
|
||||
local max = Market.depotContains(id)
|
||||
amountEdit:setMaximum(max)
|
||||
maximum = math.min(maximum, Market.getDepotCount(item.marketData.tradeAs))
|
||||
amountEdit:setMaximum(maximum)
|
||||
else
|
||||
amountEdit:setMaximum(999999)
|
||||
amountEdit:setMaximum(maximum)
|
||||
end
|
||||
end
|
||||
|
||||
local function onTotalPriceChange()
|
||||
local totalPrice = totalPriceEdit:getValue()
|
||||
local piecePrice = piecePriceEdit:getValue()
|
||||
local amount = amountEdit:getValue()
|
||||
local totalPrice = totalPriceEdit:getValue()
|
||||
local piecePrice = math.floor(totalPrice/amount)
|
||||
|
||||
piecePriceEdit:setValue(math.floor(totalPrice/amount))
|
||||
piecePriceEdit:setValue(piecePrice, true)
|
||||
if Market.isItemSelected() then
|
||||
updateFee(totalPrice, amount)
|
||||
updateFee(piecePrice, amount)
|
||||
end
|
||||
end
|
||||
|
||||
local function onPiecePriceChange()
|
||||
local amount = amountEdit:getValue()
|
||||
local totalPrice = totalPriceEdit:getValue()
|
||||
local piecePrice = piecePriceEdit:getValue()
|
||||
local amount = amountEdit:getValue()
|
||||
|
||||
totalPriceEdit:setValue(piecePrice*amount)
|
||||
totalPriceEdit:setValue(piecePrice*amount, true)
|
||||
if Market.isItemSelected() then
|
||||
updateFee(totalPrice, amount)
|
||||
updateFee(piecePrice, amount)
|
||||
end
|
||||
end
|
||||
|
||||
local function onAmountChange()
|
||||
local totalPrice = totalPriceEdit:getValue()
|
||||
local piecePrice = piecePriceEdit:getValue()
|
||||
local amount = amountEdit:getValue()
|
||||
local piecePrice = piecePriceEdit:getValue()
|
||||
local totalPrice = piecePrice * amount
|
||||
|
||||
piecePriceEdit:setValue(math.floor(totalPrice/amount))
|
||||
totalPriceEdit:setValue(piecePrice*amount)
|
||||
totalPriceEdit:setValue(piecePrice*amount, true)
|
||||
if Market.isItemSelected() then
|
||||
updateFee(totalPrice, amount)
|
||||
updateFee(piecePrice, amount)
|
||||
end
|
||||
end
|
||||
|
||||
local function initMarketItems(category)
|
||||
local function onMarketMessage(messageMode, message)
|
||||
Market.displayMessage(message)
|
||||
end
|
||||
|
||||
local function initMarketItems()
|
||||
for c = MarketCategory.First, MarketCategory.Last do
|
||||
marketItems[c] = {}
|
||||
end
|
||||
|
||||
-- save a list of items which are already added
|
||||
local itemSet = {}
|
||||
|
||||
-- populate all market items
|
||||
local types = g_things.findThingTypeByAttr(ThingAttrMarket, 0)
|
||||
for i = 1, #types do
|
||||
local t = types[i]
|
||||
local itemType = types[i]
|
||||
|
||||
local newItem = Item.create(t:getId())
|
||||
if newItem then
|
||||
local marketData = t:getMarketData()
|
||||
if not table.empty(marketData) then
|
||||
if marketData.category == category or category == MarketCategory.All then
|
||||
local item = Item.create(itemType:getId())
|
||||
if item then
|
||||
local marketData = itemType:getMarketData()
|
||||
if not table.empty(marketData) and not itemSet[marketData.tradeAs] then
|
||||
-- Some items use a different sprite in Market
|
||||
item:setId(marketData.showAs)
|
||||
|
||||
-- create new item block
|
||||
local item = {
|
||||
ptr = newItem,
|
||||
-- create new marketItem block
|
||||
local marketItem = {
|
||||
displayItem = item,
|
||||
thingType = itemType,
|
||||
marketData = marketData
|
||||
}
|
||||
|
||||
-- add new market item
|
||||
table.insert(marketItems[marketData.category], item)
|
||||
end
|
||||
table.insert(marketItems[marketData.category], marketItem)
|
||||
itemSet[marketData.tradeAs] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -624,8 +749,10 @@ local function initInterface()
|
||||
browsePanel = g_ui.loadUI('ui/marketoffers/browse')
|
||||
selectionTabBar:addTab(tr('Browse'), browsePanel)
|
||||
|
||||
overviewPanel = g_ui.loadUI('ui/marketoffers/overview')
|
||||
selectionTabBar:addTab(tr('Overview'), overviewPanel)
|
||||
-- Currently not used
|
||||
-- "Reserved for more functionality later"
|
||||
--overviewPanel = g_ui.loadUI('ui/marketoffers/overview')
|
||||
--selectionTabBar:addTab(tr('Overview'), overviewPanel)
|
||||
|
||||
displaysTabBar = marketOffersPanel:getChildById('rightTabBar')
|
||||
displaysTabBar:setContentWidget(marketOffersPanel:getChildById('rightTabContent'))
|
||||
@@ -665,7 +792,6 @@ local function initInterface()
|
||||
-- setup selected item
|
||||
nameLabel = marketOffersPanel:getChildById('nameLabel')
|
||||
selectedItem = marketOffersPanel:getChildById('selectedItem')
|
||||
selectedItem.item = {}
|
||||
|
||||
-- setup create new offer
|
||||
totalPriceEdit = marketOffersPanel:getChildById('totalPriceEdit')
|
||||
@@ -690,6 +816,14 @@ local function initInterface()
|
||||
filterButtons[MarketFilters.Depot] = browsePanel:getChildById('filterDepot')
|
||||
filterButtons[MarketFilters.SearchAll] = browsePanel:getChildById('filterSearchAll')
|
||||
|
||||
-- set filter default values
|
||||
clearFilters()
|
||||
|
||||
-- hook filters
|
||||
for _, filter in pairs(filterButtons) do
|
||||
filter.onCheckChange = Market.updateCurrentItems
|
||||
end
|
||||
|
||||
searchEdit = browsePanel:getChildById('searchEdit')
|
||||
categoryList = browsePanel:getChildById('categoryComboBox')
|
||||
subCategoryList = browsePanel:getChildById('subCategoryComboBox')
|
||||
@@ -722,6 +856,29 @@ local function initInterface()
|
||||
sellStatsTable = itemStatsPanel:recursiveGetChildById('sellStatsTable')
|
||||
buyOfferTable.onSelectionChange = onSelectBuyOffer
|
||||
sellOfferTable.onSelectionChange = onSelectSellOffer
|
||||
|
||||
-- setup my offers
|
||||
buyMyOfferTable = currentOffersPanel:recursiveGetChildById('myBuyingTable')
|
||||
sellMyOfferTable = currentOffersPanel:recursiveGetChildById('mySellingTable')
|
||||
buyMyOfferTable.onSelectionChange = onSelectMyBuyOffer
|
||||
sellMyOfferTable.onSelectionChange = onSelectMySellOffer
|
||||
|
||||
buyCancelButton = currentOffersPanel:getChildById('buyCancelButton')
|
||||
buyCancelButton.onClick = function() cancelMyOffer(MarketAction.Buy) end
|
||||
|
||||
sellCancelButton = currentOffersPanel:getChildById('sellCancelButton')
|
||||
sellCancelButton.onClick = function() cancelMyOffer(MarketAction.Sell) end
|
||||
|
||||
|
||||
buyStatsTable:setColumnWidth({120, 270})
|
||||
sellStatsTable:setColumnWidth({120, 270})
|
||||
detailsTable:setColumnWidth({80, 330})
|
||||
|
||||
buyOfferTable:setSorting(4, TABLE_SORTING_DESC)
|
||||
sellOfferTable:setSorting(4, TABLE_SORTING_ASC)
|
||||
|
||||
buyMyOfferTable:setSorting(3, TABLE_SORTING_DESC)
|
||||
sellMyOfferTable:setSorting(3, TABLE_SORTING_DESC)
|
||||
end
|
||||
|
||||
function init()
|
||||
@@ -734,6 +891,8 @@ function init()
|
||||
offerExhaust[MarketAction.Sell] = 10
|
||||
offerExhaust[MarketAction.Buy] = 20
|
||||
|
||||
registerMessageMode(MessageModes.Market, onMarketMessage)
|
||||
|
||||
protocol.initProtocol()
|
||||
connect(g_game, { onGameEnd = Market.reset })
|
||||
connect(g_game, { onGameEnd = Market.close })
|
||||
@@ -744,10 +903,15 @@ function init()
|
||||
end
|
||||
|
||||
function terminate()
|
||||
Market.close()
|
||||
|
||||
unregisterMessageMode(MessageModes.Market, onMarketMessage)
|
||||
|
||||
protocol.terminateProtocol()
|
||||
disconnect(g_game, { onGameEnd = Market.reset })
|
||||
disconnect(g_game, { onGameEnd = Market.close })
|
||||
|
||||
destroyAmountWindow()
|
||||
marketWindow:destroy()
|
||||
|
||||
Market = nil
|
||||
@@ -758,14 +922,22 @@ function Market.reset()
|
||||
categoryList:setCurrentOption(getMarketCategoryName(MarketCategory.First))
|
||||
searchEdit:setText('')
|
||||
clearFilters()
|
||||
clearMyOffers()
|
||||
if not table.empty(information) then
|
||||
Market.updateCurrentItems()
|
||||
end
|
||||
end
|
||||
|
||||
function Market.displayMessage(message)
|
||||
if marketWindow:isHidden() then return end
|
||||
|
||||
local infoBox = displayInfoBox(tr('Market Error'), message)
|
||||
infoBox:lock()
|
||||
end
|
||||
|
||||
function Market.clearSelectedItem()
|
||||
if Market.isItemSelected() then
|
||||
Market.resetCreateOffer()
|
||||
Market.resetCreateOffer(true)
|
||||
offerTypeList:clearOptions()
|
||||
offerTypeList:setText('Please Select')
|
||||
offerTypeList:setEnabled(false)
|
||||
@@ -787,22 +959,15 @@ function Market.clearSelectedItem()
|
||||
end
|
||||
|
||||
function Market.isItemSelected()
|
||||
return selectedItem and not table.empty(selectedItem.item) and selectedItem.item.ptr
|
||||
return selectedItem and selectedItem.item
|
||||
end
|
||||
|
||||
function Market.isOfferSelected(type)
|
||||
return selectedOffer[type] and not selectedOffer[type]:isNull()
|
||||
end
|
||||
|
||||
function Market.depotContains(itemId)
|
||||
local count = 0
|
||||
for i = 1, #information.depotItems do
|
||||
local item = information.depotItems[i]
|
||||
if item and item.ptr:getId() == itemId then
|
||||
count = count + item.ptr:getCount()
|
||||
end
|
||||
end
|
||||
return count
|
||||
function Market.getDepotCount(itemId)
|
||||
return information.depotItems[itemId] or 0
|
||||
end
|
||||
|
||||
function Market.enableCreateOffer(enable)
|
||||
@@ -825,6 +990,8 @@ function Market.close(notify)
|
||||
if not marketWindow:isHidden() then
|
||||
marketWindow:hide()
|
||||
marketWindow:unlock()
|
||||
Market.clearSelectedItem()
|
||||
Market.reset()
|
||||
if notify then
|
||||
MarketProtocol.sendMarketLeave()
|
||||
end
|
||||
@@ -847,12 +1014,17 @@ function Market.updateCurrentItems()
|
||||
Market.loadMarketItems(id)
|
||||
end
|
||||
|
||||
function Market.resetCreateOffer()
|
||||
function Market.resetCreateOffer(resetFee)
|
||||
piecePriceEdit:setValue(1)
|
||||
totalPriceEdit:setValue(1)
|
||||
amountEdit:setValue(1)
|
||||
refreshTypeList()
|
||||
clearFee()
|
||||
|
||||
if resetFee then
|
||||
clearFee()
|
||||
else
|
||||
updateFee(0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function Market.refreshItemsWidget(selectItem)
|
||||
@@ -877,15 +1049,15 @@ function Market.refreshItemsWidget(selectItem)
|
||||
itemBox.onCheckChange = Market.onItemBoxChecked
|
||||
itemBox.item = item
|
||||
|
||||
if selectItem > 0 and item.ptr:getId() == selectItem then
|
||||
if selectItem > 0 and item.marketData.tradeAs == selectItem then
|
||||
select = itemBox
|
||||
selectItem = 0
|
||||
end
|
||||
|
||||
local itemWidget = itemBox:getChildById('item')
|
||||
item.ptr:setCount(1) -- reset item count for image
|
||||
itemWidget:setItem(item.ptr)
|
||||
itemWidget:setItem(item.displayItem)
|
||||
|
||||
local amount = Market.depotContains(item.ptr:getId())
|
||||
local amount = Market.getDepotCount(item.marketData.tradeAs)
|
||||
if amount > 0 then
|
||||
itemWidget:setText(amount)
|
||||
itemBox:setTooltip('You have '.. amount ..' in your depot.')
|
||||
@@ -893,8 +1065,9 @@ function Market.refreshItemsWidget(selectItem)
|
||||
|
||||
radioItemSet:addWidget(itemBox)
|
||||
end
|
||||
|
||||
if select then
|
||||
select:setChecked(true)
|
||||
radioItemSet:selectWidget(select, false)
|
||||
end
|
||||
|
||||
layout:enableUpdates()
|
||||
@@ -904,9 +1077,16 @@ end
|
||||
function Market.refreshOffers()
|
||||
if Market.isItemSelected() then
|
||||
Market.onItemBoxChecked(selectedItem.ref)
|
||||
else
|
||||
Market.refreshMyOffers()
|
||||
end
|
||||
end
|
||||
|
||||
function Market.refreshMyOffers()
|
||||
clearMyOffers()
|
||||
MarketProtocol.sendMarketBrowseMyOffers()
|
||||
end
|
||||
|
||||
|
||||
function Market.loadMarketItems(category)
|
||||
clearItems()
|
||||
@@ -925,6 +1105,7 @@ function Market.loadMarketItems(category)
|
||||
for i = 1, #marketItems[category] do
|
||||
local item = marketItems[category][i]
|
||||
if isItemValid(item, category, searchFilter) then
|
||||
|
||||
table.insert(currentItems, item)
|
||||
end
|
||||
end
|
||||
@@ -942,56 +1123,6 @@ function Market.loadMarketItems(category)
|
||||
Market.refreshItemsWidget()
|
||||
end
|
||||
|
||||
function Market.loadDepotItems(depotItems)
|
||||
information.depotItems = {}
|
||||
|
||||
local items = {}
|
||||
for i = 1, #depotItems do
|
||||
local data = depotItems[i]
|
||||
local id, count = data[1], data[2]
|
||||
|
||||
local tmpItem = Item.create(id)
|
||||
if tmpItem:isStackable() then
|
||||
if count > 100 then
|
||||
local createCount = math.floor(count/100)
|
||||
local remainder = count % 100
|
||||
if remainder > 0 then
|
||||
createCount = createCount + 1
|
||||
end
|
||||
for i = 1, createCount do
|
||||
local newItem = Item.create(id)
|
||||
if i == createCount and remainder > 0 then
|
||||
newItem:setCount(remainder)
|
||||
else
|
||||
newItem:setCount(100)
|
||||
end
|
||||
table.insert(items, newItem)
|
||||
end
|
||||
else
|
||||
local newItem = Item.create(id)
|
||||
newItem:setCount(count)
|
||||
table.insert(items, newItem)
|
||||
end
|
||||
else
|
||||
for i = 1, count do
|
||||
table.insert(items, Item.create(id))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, newItem in pairs(items) do
|
||||
local marketData = newItem:getMarketData()
|
||||
|
||||
if not table.empty(marketData) then
|
||||
local item = {
|
||||
ptr = newItem,
|
||||
marketData = marketData
|
||||
}
|
||||
table.insert(information.depotItems, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Market.createNewOffer()
|
||||
local type = offerTypeList:getCurrentOption().text
|
||||
if type == 'Sell' then
|
||||
@@ -1003,12 +1134,10 @@ function Market.createNewOffer()
|
||||
if not Market.isItemSelected() then
|
||||
return
|
||||
end
|
||||
local item = selectedItem.item
|
||||
local spriteId = item.ptr:getId()
|
||||
|
||||
local spriteId = selectedItem.item.marketData.tradeAs
|
||||
|
||||
local piecePrice = piecePriceEdit:getValue()
|
||||
local totalPrice = totalPriceEdit:getValue()
|
||||
|
||||
local amount = amountEdit:getValue()
|
||||
local anonymous = anonymous:isChecked() and 1 or 0
|
||||
|
||||
@@ -1019,7 +1148,10 @@ function Market.createNewOffer()
|
||||
errorMsg = errorMsg..'Not enough balance to create this offer.\n'
|
||||
end
|
||||
elseif type == MarketAction.Sell then
|
||||
if Market.depotContains(spriteId) < amount then
|
||||
if information.balance < fee then
|
||||
errorMsg = errorMsg..'Not enough balance to create this offer.\n'
|
||||
end
|
||||
if Market.getDepotCount(spriteId) < amount then
|
||||
errorMsg = errorMsg..'Not enough items in your depot to create this offer.\n'
|
||||
end
|
||||
end
|
||||
@@ -1029,12 +1161,21 @@ function Market.createNewOffer()
|
||||
elseif piecePrice < piecePriceEdit.minimum then
|
||||
errorMsg = errorMsg..'Price is too low.\n'
|
||||
end
|
||||
|
||||
if amount > amountEdit.maximum then
|
||||
errorMsg = errorMsg..'Amount is too high.\n'
|
||||
elseif amount < amountEdit.minimum then
|
||||
errorMsg = errorMsg..'Amount is too low.\n'
|
||||
end
|
||||
|
||||
if amount * piecePrice > MarketMaxPrice then
|
||||
errorMsg = errorMsg..'Total price is too high.\n'
|
||||
end
|
||||
|
||||
if information.totalOffers >= MarketMaxOffers then
|
||||
errorMsg = errorMsg..'You cannot create more offers.\n'
|
||||
end
|
||||
|
||||
local timeCheck = os.time() - lastCreatedOffer
|
||||
if timeCheck < offerExhaust[type] then
|
||||
local waitTime = math.ceil(offerExhaust[type] - timeCheck)
|
||||
@@ -1042,7 +1183,7 @@ function Market.createNewOffer()
|
||||
end
|
||||
|
||||
if errorMsg ~= '' then
|
||||
displayInfoBox('Error', errorMsg)
|
||||
Market.displayMessage(errorMsg)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1060,9 +1201,6 @@ end
|
||||
|
||||
function Market.onItemBoxChecked(widget)
|
||||
if widget:isChecked() then
|
||||
if selectedItem.ref and widget ~= selectedItem.ref then
|
||||
selectedItem.ref:setChecked(false) -- temporary fix?
|
||||
end
|
||||
updateSelectedItem(widget)
|
||||
end
|
||||
end
|
||||
@@ -1071,12 +1209,12 @@ end
|
||||
|
||||
function Market.onMarketEnter(depotItems, offers, balance, vocation)
|
||||
if not loaded then
|
||||
initMarketItems(MarketCategory.All)
|
||||
initMarketItems()
|
||||
loaded = true
|
||||
end
|
||||
|
||||
Market.clearSelectedItem()
|
||||
updateBalance(balance)
|
||||
averagePrice = 0
|
||||
|
||||
information.totalOffers = offers
|
||||
local player = g_game.getLocalPlayer()
|
||||
@@ -1092,10 +1230,12 @@ function Market.onMarketEnter(depotItems, offers, balance, vocation)
|
||||
information.vocation = vocation
|
||||
end
|
||||
|
||||
Market.loadDepotItems(depotItems)
|
||||
-- set list of depot items
|
||||
information.depotItems = depotItems
|
||||
|
||||
-- update the items widget to match depot items
|
||||
if Market.isItemSelected() then
|
||||
local spriteId = selectedItem.item.ptr:getId()
|
||||
local spriteId = selectedItem.item.marketData.tradeAs
|
||||
MarketProtocol.silent(true) -- disable protocol messages
|
||||
Market.refreshItemsWidget(spriteId)
|
||||
MarketProtocol.silent(false) -- enable protocol messages
|
||||
@@ -1107,14 +1247,6 @@ function Market.onMarketEnter(depotItems, offers, balance, vocation)
|
||||
Market.loadMarketItems(MarketCategory.First)
|
||||
end
|
||||
|
||||
-- build offer table header
|
||||
if buyOfferTable and not buyOfferTable:hasHeader() then
|
||||
buyOfferTable:addHeaderRow(offerTableHeader)
|
||||
end
|
||||
if sellOfferTable and not sellOfferTable:hasHeader() then
|
||||
sellOfferTable:addHeaderRow(offerTableHeader)
|
||||
end
|
||||
|
||||
if g_game.isOnline() then
|
||||
marketWindow:lock()
|
||||
marketWindow:show()
|
||||
|
@@ -3,7 +3,7 @@ MarketWindow < MainWindow
|
||||
!text: tr('Market')
|
||||
size: 700 530
|
||||
|
||||
@onEscape: Market.close(true)
|
||||
@onEscape: Market.close()
|
||||
|
||||
// Main Panel Window
|
||||
|
||||
@@ -54,6 +54,7 @@ MarketWindow < MainWindow
|
||||
Button
|
||||
id: resetButton
|
||||
!text: tr('Reset Market')
|
||||
!tooltip: tr('Reset selection, filters & search')
|
||||
anchors.top: mainTabContent.bottom
|
||||
anchors.left: mainTabContent.left
|
||||
margin-top: 5
|
||||
|
@@ -4,7 +4,7 @@ MarketOffer.__index = MarketOffer
|
||||
local OFFER_TIMESTAMP = 1
|
||||
local OFFER_COUNTER = 2
|
||||
|
||||
MarketOffer.new = function(offerId, t, item, amount, price, playerName, state)
|
||||
MarketOffer.new = function(offerId, t, item, amount, price, playerName, state, var)
|
||||
local offer = {
|
||||
id = {},
|
||||
type = nil,
|
||||
@@ -12,7 +12,8 @@ MarketOffer.new = function(offerId, t, item, amount, price, playerName, state)
|
||||
amount = 0,
|
||||
price = 0,
|
||||
player = '',
|
||||
state = 0
|
||||
state = 0,
|
||||
var = nil
|
||||
}
|
||||
|
||||
if not offerId or type(offerId) ~= 'table' then
|
||||
@@ -41,6 +42,7 @@ MarketOffer.new = function(offerId, t, item, amount, price, playerName, state)
|
||||
g_logger.error('MarketOffer.new - invalid state provided.')
|
||||
end
|
||||
offer.state = state
|
||||
offer.var = var
|
||||
|
||||
setmetatable(offer, MarketOffer)
|
||||
return offer
|
||||
@@ -153,4 +155,4 @@ function MarketOffer:getCounter()
|
||||
return
|
||||
end
|
||||
return self.id[OFFER_COUNTER]
|
||||
end
|
||||
end
|
||||
|
@@ -29,11 +29,12 @@ local function readMarketOffer(msg, action, var)
|
||||
local state = MarketOfferState.Active
|
||||
if var == MarketRequest.MyHistory then
|
||||
state = msg:getU8()
|
||||
elseif var == MarketRequest.MyOffers then
|
||||
else
|
||||
playerName = msg:getString()
|
||||
end
|
||||
|
||||
return MarketOffer.new({timestamp, counter}, action, Item.create(itemId), amount, price, playerName, state)
|
||||
return MarketOffer.new({timestamp, counter}, action, Item.create(itemId), amount, price, playerName, state, var)
|
||||
end
|
||||
|
||||
-- parsing protocols
|
||||
@@ -50,14 +51,14 @@ local function parseMarketEnter(protocol, msg)
|
||||
vocation = msg:getU8() -- get vocation id
|
||||
end
|
||||
local offers = msg:getU8()
|
||||
local depotItems = {}
|
||||
|
||||
local depotItems = {}
|
||||
local depotCount = msg:getU16()
|
||||
for i = 1, depotCount do
|
||||
local itemId = msg:getU16() -- item id
|
||||
local itemCount = msg:getU16() -- item count
|
||||
|
||||
table.insert(depotItems, {itemId, itemCount})
|
||||
depotItems[itemId] = itemCount
|
||||
end
|
||||
|
||||
signalcall(Market.onMarketEnter, depotItems, offers, balance, vocation)
|
||||
@@ -201,6 +202,10 @@ function MarketProtocol.sendMarketBrowse(browseId)
|
||||
end
|
||||
end
|
||||
|
||||
function MarketProtocol.sendMarketBrowseMyOffers()
|
||||
MarketProtocol.sendMarketBrowse(MarketRequest.MyOffers)
|
||||
end
|
||||
|
||||
function MarketProtocol.sendMarketCreateOffer(type, spriteId, amount, price, anonymous)
|
||||
if g_game.getFeature(GamePlayerMarket) then
|
||||
local msg = OutputMessage.create()
|
||||
|
@@ -1,7 +1,7 @@
|
||||
AmountWindow < MainWindow
|
||||
id: amountWindow
|
||||
!text: tr('Amount')
|
||||
size: 270 80
|
||||
size: 270 90
|
||||
|
||||
Item
|
||||
id: item
|
||||
|
@@ -4,14 +4,8 @@ MarketButtonBox < ButtonBoxRounded
|
||||
size: 106 22
|
||||
text-offset: 0 2
|
||||
text-align: center
|
||||
image-clip: 0 0 20 20
|
||||
image-border: 2
|
||||
|
||||
$hover !disabled:
|
||||
image-clip: 0 20 20 20
|
||||
|
||||
$checked:
|
||||
image-clip: 0 40 20 20
|
||||
color: white
|
||||
|
||||
$disabled:
|
||||
|
@@ -10,15 +10,9 @@ MarketTabBarButton < TabBarButton
|
||||
margin-left: 0
|
||||
|
||||
$hover !checked:
|
||||
image-clip: 0 20 20 20
|
||||
color: white
|
||||
|
||||
$disabled:
|
||||
image-color: #ffffff66
|
||||
icon-color: #888888
|
||||
color: #ffffff
|
||||
|
||||
$checked:
|
||||
image-clip: 0 20 20 20
|
||||
color: #ffffff
|
||||
|
||||
$on !checked:
|
||||
@@ -26,36 +20,24 @@ MarketTabBarButton < TabBarButton
|
||||
|
||||
MarketRightTabBar < TabBar
|
||||
MarketRightTabBarPanel < TabBarPanel
|
||||
// TODO: inherit style from TabBarButton and adjust it
|
||||
// anchors.left: none did not seem to work for me
|
||||
MarketRightTabBarButton < UIButton
|
||||
MarketRightTabBarButton < TabBarButton
|
||||
size: 20 25
|
||||
font: verdana-11px-rounded
|
||||
text-offset: 0 2
|
||||
image-source: /images/ui/tabbutton_square
|
||||
image-color: white
|
||||
image-clip: 0 0 20 20
|
||||
image-border: 3
|
||||
icon-color: white
|
||||
color: #aaaaaa
|
||||
anchors.top: parent.top
|
||||
padding: 5
|
||||
|
||||
anchors.right: prev.left
|
||||
color: #929292
|
||||
|
||||
$first:
|
||||
anchors.right: parent.right
|
||||
anchors.left: none
|
||||
|
||||
$!first:
|
||||
anchors.right: prev.left
|
||||
anchors.left: none
|
||||
|
||||
$hover !checked:
|
||||
image-clip: 0 20 20 20
|
||||
color: white
|
||||
|
||||
$disabled:
|
||||
image-color: #ffffff66
|
||||
icon-color: #888888
|
||||
color: #ffffff
|
||||
|
||||
$checked:
|
||||
image-clip: 0 20 20 20
|
||||
color: #ffffff
|
||||
|
||||
$on !checked:
|
||||
|
@@ -135,8 +135,7 @@ Panel
|
||||
font: verdana-11px-rounded
|
||||
text-offset: 0 2
|
||||
anchors.top: offerTypeLabel.top
|
||||
anchors.left: prev.right
|
||||
margin-left: 32
|
||||
anchors.left: amountEdit.left
|
||||
|
||||
PreviousButton
|
||||
id: prevAmountButton
|
||||
@@ -147,7 +146,7 @@ Panel
|
||||
|
||||
SpinBox
|
||||
id: amountEdit
|
||||
anchors.verticalCenter: prev.verticalCenter
|
||||
anchors.top: prev.top
|
||||
anchors.left: prev.right
|
||||
margin-left: 3
|
||||
width: 55
|
||||
@@ -184,8 +183,6 @@ Panel
|
||||
Label
|
||||
id: feeLabel
|
||||
font: verdana-11px-rounded
|
||||
text-offset: 0 2
|
||||
anchors.top: createOfferButton.bottom
|
||||
anchors.right: parent.right
|
||||
margin-right: 8
|
||||
margin-top: 3
|
||||
anchors.left: createOfferButton.left
|
||||
margin: 2
|
@@ -52,7 +52,7 @@ Panel
|
||||
|
||||
MarketButtonBox
|
||||
id: filterLevel
|
||||
checked: false
|
||||
&default: false
|
||||
!text: tr('Level')
|
||||
!tooltip: tr('Filter list to match your level')
|
||||
anchors.top: prev.bottom
|
||||
@@ -62,11 +62,10 @@ Panel
|
||||
margin-left: 3
|
||||
width: 40
|
||||
height: 20
|
||||
@onCheckChange: Market.updateCurrentItems()
|
||||
|
||||
MarketButtonBox
|
||||
id: filterVocation
|
||||
checked: false
|
||||
&default: false
|
||||
!text: tr('Voc.')
|
||||
!tooltip: tr('Filter list to match your vocation')
|
||||
anchors.top: prev.top
|
||||
@@ -75,7 +74,6 @@ Panel
|
||||
margin-left: 3
|
||||
width: 34
|
||||
height: 20
|
||||
@onCheckChange: Market.updateCurrentItems()
|
||||
|
||||
MarketComboBox
|
||||
id: slotComboBox
|
||||
@@ -90,7 +88,7 @@ Panel
|
||||
|
||||
MarketButtonBox
|
||||
id: filterDepot
|
||||
checked: false
|
||||
&default: false
|
||||
!text: tr('Show Depot Only')
|
||||
!tooltip: tr('Show your depot items only')
|
||||
anchors.top: prev.bottom
|
||||
@@ -99,7 +97,6 @@ Panel
|
||||
margin-top: 6
|
||||
margin-right: 3
|
||||
margin-left: 3
|
||||
@onCheckChange: Market.updateCurrentItems()
|
||||
|
||||
Panel
|
||||
id: itemsContainer
|
||||
@@ -152,11 +149,10 @@ Panel
|
||||
|
||||
MarketButtonBox
|
||||
id: filterSearchAll
|
||||
checked: false
|
||||
&default: true
|
||||
!text: tr('All')
|
||||
!tooltip: tr('Search all items')
|
||||
anchors.verticalCenter: prev.verticalCenter
|
||||
anchors.left: prev.right
|
||||
anchors.right: itemsContainer.right
|
||||
margin-left: 3
|
||||
@onCheckChange: Market.updateCurrentItems()
|
||||
|
@@ -1,11 +1,12 @@
|
||||
DetailsTableRow < TableRow
|
||||
font: verdana-11px-monochrome
|
||||
background-color: alpha
|
||||
focusable: true
|
||||
color: #cccccc
|
||||
height: 45
|
||||
focusable: false
|
||||
padding: 2
|
||||
even-background-color: alpha
|
||||
odd-background-color: alpha
|
||||
|
||||
DetailsTableColumn < TableColumn
|
||||
font: verdana-11px-monochrome
|
||||
|
@@ -1,35 +1,27 @@
|
||||
OfferTableRow < TableRow
|
||||
font: verdana-11px-monochrome
|
||||
background-color: alpha
|
||||
focusable: true
|
||||
color: #cccccc
|
||||
height: 15
|
||||
|
||||
$focus:
|
||||
background-color: #294f6d
|
||||
color: #ffffff
|
||||
|
||||
OfferTableColumn < TableColumn
|
||||
font: verdana-11px-monochrome
|
||||
background-color: alpha
|
||||
text-offset: 5 0
|
||||
color: #cccccc
|
||||
width: 80
|
||||
focusable: false
|
||||
|
||||
OfferTableWarningColumn < OfferTableColumn
|
||||
color: #e03d3d
|
||||
|
||||
OfferTableHeaderRow < TableHeaderRow
|
||||
font: verdana-11px-monochrome
|
||||
focusable: false
|
||||
color: #cccccc
|
||||
height: 20
|
||||
|
||||
OfferTableHeaderColumn < TableHeaderColumn
|
||||
OfferTableHeaderColumn < SortableTableHeaderColumn
|
||||
font: verdana-11px-monochrome
|
||||
background-color: alpha
|
||||
text-offset: 2 0
|
||||
color: #cccccc
|
||||
width: 80
|
||||
focusable: true
|
||||
|
||||
$focus:
|
||||
background-color: #294f6d
|
||||
@@ -74,8 +66,26 @@ Panel
|
||||
table-data: sellingTableData
|
||||
row-style: OfferTableRow
|
||||
column-style: OfferTableColumn
|
||||
header-row-style: OfferTableHeaderRow
|
||||
header-column-style: OfferTableHeaderColumn
|
||||
header-column-style: false
|
||||
header-row-style: false
|
||||
|
||||
OfferTableHeaderRow
|
||||
id: header
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Buyer Name')
|
||||
width: 100
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Amount')
|
||||
width: 60
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Total Price')
|
||||
width: 90
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Piece Price')
|
||||
width: 80
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Auction End')
|
||||
width: 120
|
||||
|
||||
TableData
|
||||
id: sellingTableData
|
||||
@@ -129,8 +139,26 @@ Panel
|
||||
table-data: buyingTableData
|
||||
row-style: OfferTableRow
|
||||
column-style: OfferTableColumn
|
||||
header-row-style: OfferTableHeaderRow
|
||||
header-column-style: OfferTableHeaderColumn
|
||||
header-column-style: false
|
||||
header-row-style: false
|
||||
|
||||
OfferTableHeaderRow
|
||||
id: header
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Seller Name')
|
||||
width: 100
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Amount')
|
||||
width: 60
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Total Price')
|
||||
width: 90
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Piece Price')
|
||||
width: 80
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Auction End')
|
||||
width: 120
|
||||
|
||||
TableData
|
||||
id: buyingTableData
|
||||
|
@@ -1,6 +1,5 @@
|
||||
StatsTableRow < TableRow
|
||||
font: verdana-11px-monochrome
|
||||
background-color: alpha
|
||||
focusable: true
|
||||
color: #cccccc
|
||||
height: 20
|
||||
@@ -9,7 +8,7 @@ StatsTableRow < TableRow
|
||||
StatsTableColumn < TableColumn
|
||||
font: verdana-11px-monochrome
|
||||
background-color: alpha
|
||||
text-offset: 5 0
|
||||
text-offset: 5 3
|
||||
color: #cccccc
|
||||
width: 110
|
||||
focusable: false
|
||||
|
@@ -1,9 +1,176 @@
|
||||
OfferTableRow < TableRow
|
||||
font: verdana-11px-monochrome
|
||||
color: #cccccc
|
||||
height: 15
|
||||
|
||||
OfferTableColumn < TableColumn
|
||||
font: verdana-11px-monochrome
|
||||
background-color: alpha
|
||||
text-offset: 5 0
|
||||
color: #cccccc
|
||||
width: 80
|
||||
|
||||
OfferTableWarningColumn < OfferTableColumn
|
||||
color: #e03d3d
|
||||
|
||||
OfferTableHeaderRow < TableHeaderRow
|
||||
font: verdana-11px-monochrome
|
||||
color: #cccccc
|
||||
height: 20
|
||||
|
||||
OfferTableHeaderColumn < SortableTableHeaderColumn
|
||||
font: verdana-11px-monochrome
|
||||
text-offset: 2 0
|
||||
color: #cccccc
|
||||
|
||||
$focus:
|
||||
background-color: #294f6d
|
||||
color: #ffffff
|
||||
|
||||
Panel
|
||||
background-color: #22283399
|
||||
margin: 1
|
||||
|
||||
Button
|
||||
id: sellCancelButton
|
||||
!text: tr('Cancel')
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: next.bottom
|
||||
margin-right: 6
|
||||
width: 80
|
||||
enabled: false
|
||||
|
||||
Label
|
||||
!text: tr('Current Offers')
|
||||
!text: tr('Sell Offers')
|
||||
font: verdana-11px-rounded
|
||||
text-offset: 0 2
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
margin-left: 10
|
||||
margin-top: 44
|
||||
margin-left: 6
|
||||
|
||||
Table
|
||||
id: mySellingTable
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: prev.left
|
||||
anchors.right: parent.right
|
||||
height: 150
|
||||
margin-top: 5
|
||||
margin-bottom: 5
|
||||
margin-right: 6
|
||||
padding: 1
|
||||
focusable: false
|
||||
background-color: #222833
|
||||
border-width: 1
|
||||
border-color: #191f27
|
||||
table-data: mySellingTableData
|
||||
row-style: OfferTableRow
|
||||
column-style: OfferTableColumn
|
||||
header-column-style: false
|
||||
header-row-style: false
|
||||
|
||||
OfferTableHeaderRow
|
||||
id: header
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Item Name')
|
||||
width: 160
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Total Price')
|
||||
width: 125
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Piece Price')
|
||||
width: 125
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Amount')
|
||||
width: 110
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Auction End')
|
||||
width: 110
|
||||
|
||||
TableData
|
||||
id: mySellingTableData
|
||||
anchors.bottom: mySellingTable.bottom
|
||||
anchors.left: mySellingTable.left
|
||||
anchors.right: mySellingTable.right
|
||||
margin-top: 2
|
||||
vertical-scrollbar: mySellingTableScrollBar
|
||||
|
||||
VerticalScrollBar
|
||||
id: mySellingTableScrollBar
|
||||
anchors.top: mySellingTable.top
|
||||
anchors.bottom: mySellingTable.bottom
|
||||
anchors.right: mySellingTable.right
|
||||
step: 28
|
||||
pixels-scroll: true
|
||||
|
||||
Button
|
||||
id: buyCancelButton
|
||||
!text: tr('Cancel')
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 5
|
||||
margin-right: 6
|
||||
width: 80
|
||||
enabled: false
|
||||
|
||||
Label
|
||||
!text: tr('Buy Offers')
|
||||
font: verdana-11px-rounded
|
||||
text-offset: 0 2
|
||||
anchors.top: prev.top
|
||||
anchors.left: parent.left
|
||||
margin-top: 9
|
||||
margin-left: 6
|
||||
|
||||
Table
|
||||
id: myBuyingTable
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: prev.left
|
||||
anchors.right: parent.right
|
||||
margin-top: 5
|
||||
margin-bottom: 5
|
||||
margin-right: 6
|
||||
height: 150
|
||||
padding: 1
|
||||
focusable: false
|
||||
background-color: #222833
|
||||
border-width: 1
|
||||
border-color: #191f27
|
||||
table-data: myBuyingTableData
|
||||
row-style: OfferTableRow
|
||||
column-style: OfferTableColumn
|
||||
header-column-style: false
|
||||
header-row-style: false
|
||||
|
||||
OfferTableHeaderRow
|
||||
id: header
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Item Name')
|
||||
width: 160
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Total Price')
|
||||
width: 125
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Piece Price')
|
||||
width: 125
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Amount')
|
||||
width: 110
|
||||
OfferTableHeaderColumn
|
||||
!text: tr('Auction End')
|
||||
width: 110
|
||||
|
||||
TableData
|
||||
id: myBuyingTableData
|
||||
anchors.bottom: myBuyingTable.bottom
|
||||
anchors.left: myBuyingTable.left
|
||||
anchors.right: myBuyingTable.right
|
||||
vertical-scrollbar: myBuyingTableScrollBar
|
||||
|
||||
VerticalScrollBar
|
||||
id: myBuyingTableScrollBar
|
||||
anchors.top: myBuyingTable.top
|
||||
anchors.bottom: myBuyingTable.bottom
|
||||
anchors.right: myBuyingTable.right
|
||||
step: 28
|
||||
pixels-scroll: true
|
||||
|
@@ -35,16 +35,12 @@ function onModalDialog(id, title, message, buttons, enterButton, escapeButton, c
|
||||
local messageLabel = modalDialog:getChildById('messageLabel')
|
||||
local choiceList = modalDialog:getChildById('choiceList')
|
||||
local choiceScrollbar = modalDialog:getChildById('choiceScrollBar')
|
||||
local buttonList = modalDialog:getChildById('buttonList')
|
||||
local buttonsPanel = modalDialog:getChildById('buttonsPanel')
|
||||
|
||||
modalDialog:setText(title)
|
||||
messageLabel:setText(message)
|
||||
|
||||
local horizontalPadding = modalDialog:getPaddingLeft() + modalDialog:getPaddingRight()
|
||||
modalDialog:setWidth(math.min(modalDialog.maximumWidth, math.max(messageLabel:getWidth(), modalDialog.minimumWidth)))
|
||||
messageLabel:setWidth(math.min(modalDialog.maximumWidth, math.max(messageLabel:getWidth(), modalDialog.minimumWidth)) - horizontalPadding)
|
||||
|
||||
local labelHeight = nil
|
||||
local labelHeight
|
||||
for i = 1, #choices do
|
||||
local choiceId = choices[i][1]
|
||||
local choiceName = choices[i][2]
|
||||
@@ -59,11 +55,12 @@ function onModalDialog(id, title, message, buttons, enterButton, escapeButton, c
|
||||
end
|
||||
choiceList:focusNextChild()
|
||||
|
||||
local buttonsWidth = 0
|
||||
for i = 1, #buttons do
|
||||
local buttonId = buttons[i][1]
|
||||
local buttonText = buttons[i][2]
|
||||
|
||||
local button = g_ui.createWidget('ModalButton', buttonList)
|
||||
local button = g_ui.createWidget('ModalButton', buttonsPanel)
|
||||
button:setText(buttonText)
|
||||
button.onClick = function(self)
|
||||
local focusedChoice = choiceList:getFocusedChild()
|
||||
@@ -74,6 +71,7 @@ function onModalDialog(id, title, message, buttons, enterButton, escapeButton, c
|
||||
g_game.answerModalDialog(id, buttonId, choice)
|
||||
destroyDialog()
|
||||
end
|
||||
buttonsWidth = buttonsWidth + button:getWidth() + button:getMarginLeft() + button:getMarginRight()
|
||||
end
|
||||
|
||||
local additionalHeight = 0
|
||||
@@ -84,11 +82,13 @@ function onModalDialog(id, title, message, buttons, enterButton, escapeButton, c
|
||||
additionalHeight = math.min(modalDialog.maximumChoices, math.max(modalDialog.minimumChoices, #choices)) * labelHeight
|
||||
additionalHeight = additionalHeight + choiceList:getPaddingTop() + choiceList:getPaddingBottom()
|
||||
end
|
||||
modalDialog:setHeight(modalDialog:getHeight() + additionalHeight)
|
||||
|
||||
addEvent(function()
|
||||
modalDialog:setHeight(modalDialog:getHeight() + messageLabel:getHeight() - 14)
|
||||
end)
|
||||
local horizontalPadding = modalDialog:getPaddingLeft() + modalDialog:getPaddingRight()
|
||||
buttonsWidth = buttonsWidth + horizontalPadding
|
||||
|
||||
modalDialog:setWidth(math.min(modalDialog.maximumWidth, math.max(buttonsWidth, messageLabel:getWidth(), modalDialog.minimumWidth)))
|
||||
messageLabel:setWidth(math.min(modalDialog.maximumWidth, math.max(buttonsWidth, messageLabel:getWidth(), modalDialog.minimumWidth)) - horizontalPadding)
|
||||
modalDialog:setHeight(modalDialog:getHeight() + additionalHeight + messageLabel:getHeight() - 8)
|
||||
|
||||
local enterFunc = function()
|
||||
local focusedChoice = choiceList:getFocusedChild()
|
||||
|
@@ -5,7 +5,7 @@ ChoiceListLabel < Label
|
||||
focusable: true
|
||||
|
||||
$focus:
|
||||
background-color: #ffffff22
|
||||
background-color: #00000055
|
||||
color: #ffffff
|
||||
|
||||
ChoiceList < TextList
|
||||
@@ -14,7 +14,6 @@ ChoiceList < TextList
|
||||
anchors.fill: parent
|
||||
anchors.top: prev.bottom
|
||||
anchors.bottom: next.top
|
||||
padding: 1
|
||||
margin-top: 4
|
||||
margin-bottom: 10
|
||||
focusable: false
|
||||
@@ -30,14 +29,19 @@ ChoiceScrollBar < VerticalScrollBar
|
||||
visible: false
|
||||
|
||||
ModalButton < Button
|
||||
width: 60
|
||||
margin: 2
|
||||
text-auto-resize: true
|
||||
margin-top: 2
|
||||
margin-bottom: 2
|
||||
margin-left: 2
|
||||
|
||||
$pressed:
|
||||
text-offset: 0 0
|
||||
|
||||
ModalDialog < MainWindow
|
||||
id: modalDialog
|
||||
size: 280 97
|
||||
&minimumWidth: 200
|
||||
&maximumWidth: 500
|
||||
&maximumWidth: 600
|
||||
&minimumChoices: 4
|
||||
&maximumChoices: 10
|
||||
|
||||
@@ -57,7 +61,7 @@ ModalDialog < MainWindow
|
||||
anchors.bottom: next.top
|
||||
|
||||
Panel
|
||||
id: buttonList
|
||||
id: buttonsPanel
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
@@ -177,6 +177,7 @@ function itemPopup(self, mousePosition, mouseButton)
|
||||
|
||||
if mouseButton == MouseRightButton then
|
||||
local menu = g_ui.createWidget('PopupMenu')
|
||||
menu:setGameMenu(true)
|
||||
menu:addOption(tr('Look'), function() return g_game.inspectNpcTrade(self:getItem()) end)
|
||||
menu:display(mousePosition)
|
||||
return true
|
||||
|
@@ -25,13 +25,17 @@ mountCreature = nil
|
||||
currentMount = 1
|
||||
|
||||
function init()
|
||||
connect(g_game, { onOpenOutfitWindow = create,
|
||||
onGameEnd = destroy })
|
||||
connect(g_game, {
|
||||
onOpenOutfitWindow = create,
|
||||
onGameEnd = destroy
|
||||
})
|
||||
end
|
||||
|
||||
function terminate()
|
||||
disconnect(g_game, { onOpenOutfitWindow = create,
|
||||
onGameEnd = destroy })
|
||||
disconnect(g_game, {
|
||||
onOpenOutfitWindow = create,
|
||||
onGameEnd = destroy
|
||||
})
|
||||
destroy()
|
||||
end
|
||||
|
||||
@@ -300,17 +304,11 @@ function updateOutfit()
|
||||
addon.widget:setChecked(false)
|
||||
addon.widget:setEnabled(false)
|
||||
end
|
||||
outfit.addons = 0
|
||||
|
||||
if availableAddons > 0 then
|
||||
for _, i in pairs(ADDON_SETS[availableAddons]) do
|
||||
addons[i].widget:setEnabled(true)
|
||||
end
|
||||
end
|
||||
|
||||
outfit.addons = 0
|
||||
for i = 1, #prevAddons do
|
||||
local addon = prevAddons[i]
|
||||
if addon and addons[i].widget:isEnabled() then
|
||||
addons[i].widget:setChecked(true)
|
||||
end
|
||||
end
|
||||
|
@@ -1,10 +1,11 @@
|
||||
DeathWindow < MainWindow
|
||||
id: deathWindow
|
||||
!text: tr('You are dead')
|
||||
size: 350 155
|
||||
&baseWidth: 350
|
||||
&baseHeight: 15
|
||||
|
||||
Label
|
||||
!text: tr('Alas! Brave adventurer, you have met a sad fate.\nBut do not despair, for the gods will bring you back\ninto this world in exchange for a small sacrifice\n\nSimply click on Ok to resume your journeys!')
|
||||
id: labelText
|
||||
width: 550
|
||||
height: 140
|
||||
anchors.left: parent.left
|
||||
|
@@ -1,5 +1,11 @@
|
||||
deathWindow = nil
|
||||
|
||||
local deathTexts = {
|
||||
regular = {text = 'Alas! Brave adventurer, you have met a sad fate.\nBut do not despair, for the gods will bring you back\ninto this world in exchange for a small sacrifice\n\nSimply click on Ok to resume your journeys!', height = 140, width = 0},
|
||||
unfair = {text = 'Alas! Brave adventurer, you have met a sad fate.\nBut do not despair, for the gods will bring you back\ninto this world in exchange for a small sacrifice\n\nThis death penalty has been reduced by %i%%\nbecause it was an unfair fight.\n\nSimply click on Ok to resume your journeys!', height = 185, width = 0},
|
||||
blessed = {text = 'Alas! Brave adventurer, you have met a sad fate.\nBut do not despair, for the gods will bring you back into this world\n\nThis death penalty has been reduced by 100%\nbecause you are blessed with the Adventurer\'s Blessing\n\nSimply click on Ok to resume your journeys!', height = 170, width = 90}
|
||||
}
|
||||
|
||||
function init()
|
||||
g_ui.importStyle('deathwindow')
|
||||
|
||||
@@ -21,9 +27,9 @@ function reset()
|
||||
end
|
||||
end
|
||||
|
||||
function display()
|
||||
function display(deathType, penalty)
|
||||
displayDeadMessage()
|
||||
openWindow()
|
||||
openWindow(deathType, penalty)
|
||||
end
|
||||
|
||||
function displayDeadMessage()
|
||||
@@ -33,12 +39,31 @@ function displayDeadMessage()
|
||||
modules.game_textmessage.displayGameMessage(tr('You are dead.'))
|
||||
end
|
||||
|
||||
function openWindow()
|
||||
function openWindow(deathType, penalty)
|
||||
if deathWindow then
|
||||
deathWindow:destroy()
|
||||
return
|
||||
end
|
||||
|
||||
deathWindow = g_ui.createWidget('DeathWindow', rootWidget)
|
||||
|
||||
local textLabel = deathWindow:getChildById('labelText')
|
||||
if deathType == DeathType.Regular then
|
||||
if penalty == 100 then
|
||||
textLabel:setText(deathTexts.regular.text)
|
||||
deathWindow:setHeight(deathWindow.baseHeight + deathTexts.regular.height)
|
||||
deathWindow:setWidth(deathWindow.baseWidth + deathTexts.regular.width)
|
||||
else
|
||||
textLabel:setText(string.format(deathTexts.unfair.text, 100 - penalty))
|
||||
deathWindow:setHeight(deathWindow.baseHeight + deathTexts.unfair.height)
|
||||
deathWindow:setWidth(deathWindow.baseWidth + deathTexts.unfair.width)
|
||||
end
|
||||
elseif deathType == DeathType.Blessed then
|
||||
textLabel:setText(deathTexts.blessed.text)
|
||||
deathWindow:setHeight(deathWindow.baseHeight + deathTexts.blessed.height)
|
||||
deathWindow:setWidth(deathWindow.baseWidth + deathTexts.blessed.width)
|
||||
end
|
||||
|
||||
local okButton = deathWindow:getChildById('buttonOk')
|
||||
local cancelButton = deathWindow:getChildById('buttonCancel')
|
||||
|
||||
|
@@ -44,7 +44,6 @@ MessageTypes = {
|
||||
[MessageModes.Party] = MessageSettings.centerGreen,
|
||||
[MessageModes.PartyManagement] = MessageSettings.centerWhite,
|
||||
[MessageModes.TutorialHint] = MessageSettings.centerWhite,
|
||||
[MessageModes.Market] = MessageSettings.centerWhite,
|
||||
[MessageModes.BeyondLast] = MessageSettings.centerWhite,
|
||||
[MessageModes.Report] = MessageSettings.consoleRed,
|
||||
[MessageModes.HotkeyUse] = MessageSettings.centerGreen,
|
||||
@@ -55,13 +54,19 @@ MessageTypes = {
|
||||
messagesPanel = nil
|
||||
|
||||
function init()
|
||||
connect(g_game, 'onTextMessage', displayMessage)
|
||||
for messageMode, _ in pairs(MessageTypes) do
|
||||
registerMessageMode(messageMode, displayMessage)
|
||||
end
|
||||
|
||||
connect(g_game, 'onGameEnd', clearMessages)
|
||||
messagesPanel = g_ui.loadUI('textmessage', modules.game_interface.getRootPanel())
|
||||
end
|
||||
|
||||
function terminate()
|
||||
disconnect(g_game, 'onTextMessage', displayMessage)
|
||||
for messageMode, _ in pairs(MessageTypes) do
|
||||
unregisterMessageMode(messageMode, displayMessage)
|
||||
end
|
||||
|
||||
disconnect(g_game, 'onGameEnd', clearMessages)
|
||||
clearMessages()
|
||||
messagesPanel:destroy()
|
||||
@@ -75,9 +80,7 @@ function displayMessage(mode, text)
|
||||
if not g_game.isOnline() then return end
|
||||
|
||||
local msgtype = MessageTypes[mode]
|
||||
|
||||
if not msgtype then
|
||||
perror('unhandled onTextMessage message mode ' .. mode .. ': ' .. text)
|
||||
return
|
||||
end
|
||||
|
||||
|
@@ -8,6 +8,7 @@ TextMessageLabel < UILabel
|
||||
|
||||
Panel
|
||||
anchors.fill: gameMapPanel
|
||||
anchors.bottom: gameBottomPanel.top
|
||||
focusable: false
|
||||
|
||||
Panel
|
||||
|
148
modules/game_unjustifiedpoints/unjustifiedpoints.lua
Normal file
@@ -0,0 +1,148 @@
|
||||
unjustifiedPointsWindow = nil
|
||||
unjustifiedPointsButton = nil
|
||||
contentsPanel = nil
|
||||
|
||||
openPvpSituationsLabel = nil
|
||||
currentSkullWidget = nil
|
||||
skullTimeLabel = nil
|
||||
|
||||
dayProgressBar = nil
|
||||
weekProgressBar = nil
|
||||
monthProgressBar = nil
|
||||
|
||||
daySkullWidget = nil
|
||||
weekSkullWidget = nil
|
||||
monthSkullWidget = nil
|
||||
|
||||
function init()
|
||||
connect(g_game, { onGameStart = online,
|
||||
onUnjustifiedPointsChange = onUnjustifiedPointsChange,
|
||||
onOpenPvpSituationsChange = onOpenPvpSituationsChange })
|
||||
connect(LocalPlayer, { onSkullChange = onSkullChange } )
|
||||
|
||||
unjustifiedPointsButton = modules.client_topmenu.addRightGameToggleButton('unjustifiedPointsButton',
|
||||
tr('Unjustified Points'), '/images/topbuttons/unjustifiedpoints', toggle)
|
||||
unjustifiedPointsButton:setOn(true)
|
||||
unjustifiedPointsButton:hide()
|
||||
|
||||
unjustifiedPointsWindow = g_ui.loadUI('unjustifiedpoints', modules.game_interface.getRightPanel())
|
||||
unjustifiedPointsWindow:disableResize()
|
||||
unjustifiedPointsWindow:setup()
|
||||
|
||||
contentsPanel = unjustifiedPointsWindow:getChildById('contentsPanel')
|
||||
|
||||
openPvpSituationsLabel = contentsPanel:getChildById('openPvpSituationsLabel')
|
||||
currentSkullWidget = contentsPanel:getChildById('currentSkullWidget')
|
||||
skullTimeLabel = contentsPanel:getChildById('skullTimeLabel')
|
||||
|
||||
dayProgressBar = contentsPanel:getChildById('dayProgressBar')
|
||||
weekProgressBar = contentsPanel:getChildById('weekProgressBar')
|
||||
monthProgressBar = contentsPanel:getChildById('monthProgressBar')
|
||||
daySkullWidget = contentsPanel:getChildById('daySkullWidget')
|
||||
weekSkullWidget = contentsPanel:getChildById('weekSkullWidget')
|
||||
monthSkullWidget = contentsPanel:getChildById('monthSkullWidget')
|
||||
|
||||
if g_game.isOnline() then
|
||||
online()
|
||||
end
|
||||
end
|
||||
|
||||
function terminate()
|
||||
disconnect(g_game, { onGameStart = online,
|
||||
onUnjustifiedPointsChange = onUnjustifiedPointsChange,
|
||||
onOpenPvpSituationsChange = onOpenPvpSituationsChange })
|
||||
disconnect(LocalPlayer, { onSkullChange = onSkullChange } )
|
||||
|
||||
unjustifiedPointsWindow:destroy()
|
||||
unjustifiedPointsButton:destroy()
|
||||
end
|
||||
|
||||
function onMiniWindowClose()
|
||||
unjustifiedPointsButton:setOn(false)
|
||||
end
|
||||
|
||||
function toggle()
|
||||
if unjustifiedPointsButton:isOn() then
|
||||
unjustifiedPointsWindow:close()
|
||||
unjustifiedPointsButton:setOn(false)
|
||||
else
|
||||
unjustifiedPointsWindow:open()
|
||||
unjustifiedPointsButton:setOn(true)
|
||||
end
|
||||
end
|
||||
|
||||
function online()
|
||||
if g_game.getFeature(GameUnjustifiedPoints) then
|
||||
unjustifiedPointsButton:show()
|
||||
else
|
||||
unjustifiedPointsButton:hide()
|
||||
unjustifiedPointsWindow:close()
|
||||
end
|
||||
|
||||
refresh()
|
||||
end
|
||||
|
||||
function refresh()
|
||||
local localPlayer = g_game.getLocalPlayer()
|
||||
|
||||
local unjustifiedPoints = g_game.getUnjustifiedPoints()
|
||||
onUnjustifiedPointsChange(unjustifiedPoints)
|
||||
|
||||
onSkullChange(localPlayer, localPlayer:getSkull())
|
||||
onOpenPvpSituationsChange(g_game.getOpenPvpSituations())
|
||||
end
|
||||
|
||||
function onSkullChange(localPlayer, skull)
|
||||
if not localPlayer:isLocalPlayer() then return end
|
||||
|
||||
if skull == SkullRed or skull == SkullBlack then
|
||||
currentSkullWidget:setIcon(getSkullImagePath(skull))
|
||||
currentSkullWidget:setTooltip('Remaining skull time')
|
||||
else
|
||||
currentSkullWidget:setIcon('')
|
||||
currentSkullWidget:setTooltip('You have no skull')
|
||||
end
|
||||
|
||||
daySkullWidget:setIcon(getSkullImagePath(getNextSkullId(skull)))
|
||||
weekSkullWidget:setIcon(getSkullImagePath(getNextSkullId(skull)))
|
||||
monthSkullWidget:setIcon(getSkullImagePath(getNextSkullId(skull)))
|
||||
end
|
||||
|
||||
function onOpenPvpSituationsChange(amount)
|
||||
openPvpSituationsLabel:setText(amount)
|
||||
end
|
||||
|
||||
local function getColorByKills(kills)
|
||||
if kills < 2 then
|
||||
return 'red'
|
||||
elseif kills < 3 then
|
||||
return 'yellow'
|
||||
end
|
||||
|
||||
return 'green'
|
||||
end
|
||||
|
||||
function onUnjustifiedPointsChange(unjustifiedPoints)
|
||||
if unjustifiedPoints.skullTime == 0 then
|
||||
skullTimeLabel:setText('No skull')
|
||||
skullTimeLabel:setTooltip('You have no skull')
|
||||
else
|
||||
skullTimeLabel:setText(unjustifiedPoints.skullTime .. ' days')
|
||||
skullTimeLabel:setTooltip('Remaining skull time')
|
||||
end
|
||||
|
||||
dayProgressBar:setValue(unjustifiedPoints.killsDay, 0, 100)
|
||||
dayProgressBar:setBackgroundColor(getColorByKills(unjustifiedPoints.killsDayRemaining))
|
||||
dayProgressBar:setTooltip(string.format('Unjustified points gained during the last 24 hours.\n%i kill%s left.', unjustifiedPoints.killsDayRemaining, (unjustifiedPoints.killsDayRemaining == 1 and '' or 's')))
|
||||
dayProgressBar:setText(string.format('%i kill%s left', unjustifiedPoints.killsDayRemaining, (unjustifiedPoints.killsDayRemaining == 1 and '' or 's')))
|
||||
|
||||
weekProgressBar:setValue(unjustifiedPoints.killsWeek, 0, 100)
|
||||
weekProgressBar:setBackgroundColor(getColorByKills(unjustifiedPoints.killsWeekRemaining))
|
||||
weekProgressBar:setTooltip(string.format('Unjustified points gained during the last 7 days.\n%i kill%s left.', unjustifiedPoints.killsWeekRemaining, (unjustifiedPoints.killsWeekRemaining == 1 and '' or 's')))
|
||||
weekProgressBar:setText(string.format('%i kill%s left', unjustifiedPoints.killsWeekRemaining, (unjustifiedPoints.killsWeekRemaining == 1 and '' or 's')))
|
||||
|
||||
monthProgressBar:setValue(unjustifiedPoints.killsMonth, 0, 100)
|
||||
monthProgressBar:setBackgroundColor(getColorByKills(unjustifiedPoints.killsMonthRemaining))
|
||||
monthProgressBar:setTooltip(string.format('Unjustified points gained during the last 30 days.\n%i kill%s left.', unjustifiedPoints.killsMonthRemaining, (unjustifiedPoints.killsMonthRemaining == 1 and '' or 's')))
|
||||
monthProgressBar:setText(string.format('%i kill%s left', unjustifiedPoints.killsMonthRemaining, (unjustifiedPoints.killsMonthRemaining == 1 and '' or 's')))
|
||||
end
|
8
modules/game_unjustifiedpoints/unjustifiedpoints.otmod
Normal file
@@ -0,0 +1,8 @@
|
||||
Module
|
||||
name: game_unjustifiedpoints
|
||||
description: View unjustified points
|
||||
author: Summ
|
||||
sandboxed: true
|
||||
scripts: [ unjustifiedpoints ]
|
||||
@onLoad: init()
|
||||
@onUnload: terminate()
|
80
modules/game_unjustifiedpoints/unjustifiedpoints.otui
Normal file
@@ -0,0 +1,80 @@
|
||||
SkullProgressBar < ProgressBar
|
||||
height: 13
|
||||
margin: 4 18 0 10
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
SkullWidget < UIWidget
|
||||
size: 13 13
|
||||
margin-right: 2
|
||||
anchors.right: parent.right
|
||||
image-source: /images/game/skull_socket
|
||||
|
||||
MiniWindow
|
||||
id: unjustifiedPointsWindow
|
||||
!text: tr('Unjustified Points')
|
||||
height: 114
|
||||
icon: /images/topbuttons/unjustifiedpoints
|
||||
@onClose: modules.game_unjustifiedpoints.onMiniWindowClose()
|
||||
&save: true
|
||||
|
||||
MiniWindowContents
|
||||
Label
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
!text: tr('Open PvP')
|
||||
!tooltip: tr('Open PvP Situations')
|
||||
phantom: false
|
||||
margin-top: 2
|
||||
margin-left: 10
|
||||
|
||||
Label
|
||||
id: openPvpSituationsLabel
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: parent.left
|
||||
font: verdana-11px-rounded
|
||||
margin-left: 12
|
||||
phantom: false
|
||||
|
||||
Label
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
!text: tr('Skull Time')
|
||||
margin-top: 2
|
||||
margin-right: 10
|
||||
|
||||
SkullWidget
|
||||
id: currentSkullWidget
|
||||
anchors.top: prev.bottom
|
||||
margin-right: 10
|
||||
|
||||
Label
|
||||
id: skullTimeLabel
|
||||
anchors.top: prev.top
|
||||
anchors.right: prev.left
|
||||
font: verdana-11px-rounded
|
||||
margin-right: 6
|
||||
phantom: false
|
||||
|
||||
SkullProgressBar
|
||||
id: dayProgressBar
|
||||
margin-top: 10
|
||||
|
||||
SkullWidget
|
||||
id: daySkullWidget
|
||||
anchors.top: prev.top
|
||||
|
||||
SkullProgressBar
|
||||
id: weekProgressBar
|
||||
|
||||
SkullWidget
|
||||
id: weekSkullWidget
|
||||
anchors.top: prev.top
|
||||
|
||||
SkullProgressBar
|
||||
id: monthProgressBar
|
||||
|
||||
SkullWidget
|
||||
id: monthSkullWidget
|
||||
anchors.top: prev.top
|
@@ -92,7 +92,9 @@ function onMiniWindowClose()
|
||||
end
|
||||
|
||||
function createAddWindow()
|
||||
addVipWindow = g_ui.displayUI('addvip')
|
||||
if not addVipWindow then
|
||||
addVipWindow = g_ui.displayUI('addvip')
|
||||
end
|
||||
end
|
||||
|
||||
function createEditWindow(widget)
|
||||
@@ -343,6 +345,7 @@ function onVipListMousePress(widget, mousePos, mouseButton)
|
||||
local vipList = vipWindow:getChildById('contentsPanel')
|
||||
|
||||
local menu = g_ui.createWidget('PopupMenu')
|
||||
menu:setGameMenu(true)
|
||||
menu:addOption(tr('Add new VIP'), function() createAddWindow() end)
|
||||
|
||||
menu:addSeparator()
|
||||
@@ -375,6 +378,7 @@ function onVipListLabelMousePress(widget, mousePos, mouseButton)
|
||||
local vipList = vipWindow:getChildById('contentsPanel')
|
||||
|
||||
local menu = g_ui.createWidget('PopupMenu')
|
||||
menu:setGameMenu(true)
|
||||
menu:addOption(tr('Send Message'), function() g_game.openPrivateChannel(widget:getText()) end)
|
||||
menu:addOption(tr('Add new VIP'), function() createAddWindow() end)
|
||||
menu:addOption(tr('Edit %s', widget:getText()), function() if widget then createEditWindow(widget) end end)
|
||||
|
@@ -120,6 +120,17 @@ GameSpritesAlphaChannel = 56
|
||||
GamePremiumExpiration = 57
|
||||
GameBrowseField = 58
|
||||
GameEnhancedAnimations = 59
|
||||
GameOGLInformation = 60
|
||||
GameMessageSizeCheck = 61
|
||||
GamePreviewState = 62
|
||||
GameLoginPacketEncryption = 63
|
||||
GameClientVersion = 64
|
||||
GameContentRevision = 65
|
||||
GameExperienceBonus = 66
|
||||
GameAuthenticator = 67
|
||||
GameUnjustifiedPoints = 68
|
||||
GameSessionKey = 69
|
||||
GameDeathType = 70
|
||||
|
||||
TextColors = {
|
||||
red = '#f55e5e', --'#c83200'
|
||||
@@ -184,7 +195,9 @@ MessageModes = {
|
||||
RVRChannel = 46,
|
||||
RVRAnswer = 47,
|
||||
RVRContinue = 48,
|
||||
Last = 49,
|
||||
GameHighlight = 49,
|
||||
NpcFromStartBlock = 50,
|
||||
Last = 51,
|
||||
Invalid = 255,
|
||||
}
|
||||
|
||||
@@ -201,7 +214,7 @@ CIPSOFT_RSA = "1321277432058722840622950990822933849527763264961655079678763618"
|
||||
"88792221429527047321331896351555606801473202394175817"
|
||||
|
||||
-- set to the latest Tibia.pic signature to make otclient compatible with official tibia
|
||||
PIC_SIGNATURE = 0x53208400
|
||||
PIC_SIGNATURE = 0x542100C1
|
||||
|
||||
OsTypes = {
|
||||
Linux = 1,
|
||||
@@ -244,4 +257,25 @@ ExtendedIds = {
|
||||
NeedsUpdate = 7
|
||||
}
|
||||
|
||||
PreviewState = {
|
||||
Default = 0,
|
||||
Inactive = 1,
|
||||
Active = 2
|
||||
}
|
||||
|
||||
Blessings = {
|
||||
None = 0,
|
||||
Adventurer = 1,
|
||||
SpiritualShielding = 2,
|
||||
EmbraceOfTibia = 4,
|
||||
FireOfSuns = 8,
|
||||
WisdomOfSolitude = 16,
|
||||
SparkOfPhoenix = 32
|
||||
}
|
||||
|
||||
DeathType = {
|
||||
Regular = 0,
|
||||
Blessed = 1
|
||||
}
|
||||
|
||||
-- @}
|
||||
|
@@ -35,6 +35,13 @@ NpcIconTradeQuest = 4
|
||||
|
||||
-- @}
|
||||
|
||||
function getNextSkullId(skullId)
|
||||
if skullId == SkullRed or skullId == SkullBlack then
|
||||
return SkullBlack
|
||||
end
|
||||
return SkullRed
|
||||
end
|
||||
|
||||
function getSkullImagePath(skullId)
|
||||
local path
|
||||
if skullId == SkullYellow then
|
||||
|
@@ -1,7 +1,5 @@
|
||||
local currentRsa
|
||||
|
||||
function g_game.getRsa()
|
||||
return currentRsa
|
||||
return G.currentRsa
|
||||
end
|
||||
|
||||
function g_game.findPlayerItem(itemId, subType)
|
||||
@@ -19,7 +17,7 @@ function g_game.findPlayerItem(itemId, subType)
|
||||
end
|
||||
|
||||
function g_game.chooseRsa(host)
|
||||
if currentRsa ~= CIPSOFT_RSA and currentRsa ~= OTSERV_RSA then return end
|
||||
if G.currentRsa ~= CIPSOFT_RSA and G.currentRsa ~= OTSERV_RSA then return end
|
||||
if host:ends('.tibia.com') or host:ends('.cipsoft.com') then
|
||||
g_game.setRsa(CIPSOFT_RSA)
|
||||
|
||||
@@ -29,57 +27,64 @@ function g_game.chooseRsa(host)
|
||||
g_game.setCustomOs(OsTypes.Linux)
|
||||
end
|
||||
else
|
||||
if currentRsa == CIPSOFT_RSA then
|
||||
if G.currentRsa == CIPSOFT_RSA then
|
||||
g_game.setCustomOs(-1)
|
||||
end
|
||||
g_game.setRsa(OTSERV_RSA)
|
||||
end
|
||||
|
||||
-- Hack fix to resolve some 760 login issues
|
||||
if g_game.getClientVersion() <= 760 then
|
||||
g_game.setCustomOs(2)
|
||||
end
|
||||
end
|
||||
|
||||
function g_game.setRsa(rsa, e)
|
||||
e = e or '65537'
|
||||
g_crypt.rsaSetPublicKey(rsa, e)
|
||||
currentRsa = rsa
|
||||
G.currentRsa = rsa
|
||||
end
|
||||
|
||||
function g_game.isOfficialTibia()
|
||||
return currentRsa == CIPSOFT_RSA
|
||||
return G.currentRsa == CIPSOFT_RSA
|
||||
end
|
||||
|
||||
function g_game.getSupportedClients()
|
||||
return {
|
||||
740, 741, 750, 760, 770, 772,
|
||||
740, 741, 750, 760, 770, 772,
|
||||
780, 781, 782, 790, 792,
|
||||
|
||||
800, 810, 811, 820, 821, 822,
|
||||
830, 831, 840, 842, 850, 853,
|
||||
854, 855, 857, 860, 861, 862,
|
||||
800, 810, 811, 820, 821, 822,
|
||||
830, 831, 840, 842, 850, 853,
|
||||
854, 855, 857, 860, 861, 862,
|
||||
870, 871,
|
||||
|
||||
900, 910, 920, 931, 940, 943,
|
||||
944, 951, 952, 953, 954, 960,
|
||||
961, 963, 970, 971, 972, 973,
|
||||
980, 981, 982, 983, 984, 985,
|
||||
900, 910, 920, 931, 940, 943,
|
||||
944, 951, 952, 953, 954, 960,
|
||||
961, 963, 970, 971, 972, 973,
|
||||
980, 981, 982, 983, 984, 985,
|
||||
986,
|
||||
|
||||
1000, 1001, 1002, 1010, 1011,
|
||||
1012, 1013, 1020, 1021, 1022,
|
||||
1030, 1031, 1032, 1033, 1034,
|
||||
1035, 1036, 1037, 1038, 1039,
|
||||
1000, 1001, 1002, 1010, 1011,
|
||||
1012, 1013, 1020, 1021, 1022,
|
||||
1030, 1031, 1032, 1033, 1034,
|
||||
1035, 1036, 1037, 1038, 1039,
|
||||
1040, 1041, 1050, 1051, 1052,
|
||||
1053, 1054, 1055, 1056, 1057,
|
||||
1058, 1059, 1060, 1061
|
||||
1058, 1059, 1060, 1061, 1062,
|
||||
1063, 1064, 1070, 1071, 1072,
|
||||
1073, 1074, 1075, 1076
|
||||
}
|
||||
end
|
||||
|
||||
-- The client version and protocol version where
|
||||
-- unsynchronized for some releases, not sure if this
|
||||
-- unsynchronized for some releases, not sure if this
|
||||
-- will be the normal standard.
|
||||
|
||||
-- Client Version: Publicly given version when
|
||||
-- Client Version: Publicly given version when
|
||||
-- downloading Cipsoft client.
|
||||
|
||||
-- Protocol Version: Previously was the same as
|
||||
-- Protocol Version: Previously was the same as
|
||||
-- the client version, but was unsychronized in some
|
||||
-- releases, now it needs to be verified and added here
|
||||
-- if it does not match the client version.
|
||||
@@ -87,7 +92,7 @@ end
|
||||
-- Reason for defining both: The server now requires a
|
||||
-- Client version and Protocol version from the client.
|
||||
|
||||
-- Important: Use getClientVersion for specific protocol
|
||||
-- Important: Use getClientVersion for specific protocol
|
||||
-- features to ensure we are using the proper version.
|
||||
|
||||
function g_game.getClientProtocolVersion(client)
|
||||
@@ -105,4 +110,6 @@ function g_game.getClientProtocolVersion(client)
|
||||
return clients[client] or client
|
||||
end
|
||||
|
||||
g_game.setRsa(OTSERV_RSA)
|
||||
if not G.currentRsa then
|
||||
g_game.setRsa(OTSERV_RSA)
|
||||
end
|
||||
|
@@ -19,6 +19,7 @@ Module
|
||||
dofile 'creature'
|
||||
dofile 'player'
|
||||
dofile 'market'
|
||||
dofile 'textmessages'
|
||||
dofile 'thing'
|
||||
dofile 'spells'
|
||||
|
||||
|