Merge branch 'master' of https://github.com/edubart/otclient
This commit is contained in:
commit
7fc3229610
|
@ -0,0 +1,31 @@
|
|||
from ubuntu:latest
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update; apt-get install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
git-core \
|
||||
libboost-all-dev \
|
||||
libglew-dev \
|
||||
liblua5.1-0-dev \
|
||||
libopenal-dev \
|
||||
libphysfs-dev \
|
||||
libssl-dev \
|
||||
libvorbis-dev \
|
||||
zlib1g-dev
|
||||
|
||||
RUN apt-get install -y \
|
||||
libncurses5-dev \
|
||||
mercurial; \
|
||||
hg clone -r stable-2.0 http://hg.icculus.org/icculus/physfs/; \
|
||||
cd physfs; \
|
||||
mkdir build && cd build && cmake .. && make && make install; \
|
||||
mv /usr/local/lib/libphysfs.a /usr/lib/x86_64-linux-gnu/.
|
||||
|
||||
ADD . /app
|
||||
|
||||
# Build application
|
||||
RUN mkdir -p build && cd build && cmake .. && make -j$(grep -c ^process /proc/cpuinfo);
|
||||
|
||||
CMD cd build; ./otclient
|
|
@ -37,6 +37,15 @@ In short, if you need to compile OTClient, follow these tutorials:
|
|||
* [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)
|
||||
|
||||
### Build and run with Docker
|
||||
|
||||
To build and run the client:
|
||||
```
|
||||
./build.sh
|
||||
./run.sh
|
||||
```
|
||||
|
||||
The build step should be run just when something on implementation changes.
|
||||
|
||||
### Need help?
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
docker build -t edubart/otclient .
|
|
@ -26,6 +26,11 @@ local function retranslateKeyComboDesc(keyComboDesc)
|
|||
if keyComboDesc == nil then
|
||||
error('Unable to translate key combo \'' .. keyComboDesc .. '\'')
|
||||
end
|
||||
|
||||
if type(keyComboDesc) == 'number' then
|
||||
keyComboDesc = tostring(keyComboDesc)
|
||||
end
|
||||
|
||||
local keyCombo = {}
|
||||
for i,currentKeyDesc in ipairs(keyComboDesc:split('+')) do
|
||||
for keyCode, keyDesc in pairs(KeyCodeDescs) do
|
||||
|
|
|
@ -19,13 +19,14 @@ function UIComboBox:clearOptions()
|
|||
self:clearText()
|
||||
end
|
||||
|
||||
function UIComboBox:getOption(text)
|
||||
if not self.options then return nil end
|
||||
function UIComboBox:isOption(text)
|
||||
if not self.options then return false end
|
||||
for i,v in ipairs(self.options) do
|
||||
if v.text == text then
|
||||
return nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function UIComboBox:setOption(text, dontSignal)
|
||||
|
|
|
@ -385,6 +385,11 @@ function UIMoveableTabBar:selectTab(tab)
|
|||
tab:setOn(false)
|
||||
tab.blinking = false
|
||||
|
||||
if tab.blinkEvent then
|
||||
removeEvent(tab.blinkEvent)
|
||||
tab.blinkEvent = nil
|
||||
end
|
||||
|
||||
local parent = tab:getParent()
|
||||
parent:focusChild(tab, MouseFocusReason)
|
||||
updateNavigation(self)
|
||||
|
|
|
@ -126,8 +126,8 @@ end
|
|||
function UIScrollBar:onSetup()
|
||||
self.setupDone = true
|
||||
local sliderButton = self:getChildById('sliderButton')
|
||||
g_mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:decrement() end, 300)
|
||||
g_mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:increment() end, 300)
|
||||
g_mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:onDecrement() end, 300)
|
||||
g_mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:onIncrement() end, 300)
|
||||
g_mouse.bindPressMove(sliderButton, function(mousePos, mouseMoved) parseSliderPos(self, sliderButton, mousePos, mouseMoved) end)
|
||||
g_mouse.bindPress(sliderButton, function(mousePos, mouseButton) parseSliderPress(self, sliderButton, mousePos, mouseButton) end)
|
||||
|
||||
|
@ -158,6 +158,26 @@ function UIScrollBar:onStyleApply(styleName, styleNode)
|
|||
end
|
||||
end
|
||||
|
||||
function UIScrollBar:onDecrement()
|
||||
if g_keyboard.isCtrlPressed() then
|
||||
self:decrement(self.value)
|
||||
elseif g_keyboard.isShiftPressed() then
|
||||
self:decrement(10)
|
||||
else
|
||||
self:decrement()
|
||||
end
|
||||
end
|
||||
|
||||
function UIScrollBar:onIncrement()
|
||||
if g_keyboard.isCtrlPressed() then
|
||||
self:increment(self.maximum)
|
||||
elseif g_keyboard.isShiftPressed() then
|
||||
self:increment(10)
|
||||
else
|
||||
self:increment()
|
||||
end
|
||||
end
|
||||
|
||||
function UIScrollBar:decrement(count)
|
||||
count = count or self.step
|
||||
self:setValue(self.value - count)
|
||||
|
|
|
@ -52,6 +52,13 @@ SayModes = {
|
|||
[3] = { speakTypeDesc = 'yell', icon = '/images/game/console/yell' }
|
||||
}
|
||||
|
||||
ChannelEventFormats = {
|
||||
[ChannelEvent.Join] = '%s joined the channel.',
|
||||
[ChannelEvent.Leave] = '%s left the channel.',
|
||||
[ChannelEvent.Invite] = '%s has been invited to the channel.',
|
||||
[ChannelEvent.Exclude] = '%s has been removed from the channel.',
|
||||
}
|
||||
|
||||
MAX_HISTORY = 500
|
||||
MAX_LINES = 100
|
||||
HELP_CHANNEL = 9
|
||||
|
@ -98,7 +105,8 @@ function init()
|
|||
onRuleViolationCancel = onRuleViolationCancel,
|
||||
onRuleViolationLock = onRuleViolationLock,
|
||||
onGameStart = online,
|
||||
onGameEnd = offline
|
||||
onGameEnd = offline,
|
||||
onChannelEvent = onChannelEvent,
|
||||
})
|
||||
|
||||
consolePanel = g_ui.loadUI('console', modules.game_interface.getBottomPanel())
|
||||
|
@ -240,7 +248,8 @@ function terminate()
|
|||
onRuleViolationCancel = onRuleViolationCancel,
|
||||
onRuleViolationLock = onRuleViolationLock,
|
||||
onGameStart = online,
|
||||
onGameEnd = offline
|
||||
onGameEnd = offline,
|
||||
onChannelEvent = onChannelEvent,
|
||||
})
|
||||
|
||||
if g_game.isOnline() then clear() end
|
||||
|
@ -1437,3 +1446,19 @@ function offline()
|
|||
end
|
||||
clear()
|
||||
end
|
||||
|
||||
function onChannelEvent(channelId, name, type)
|
||||
local fmt = ChannelEventFormats[type]
|
||||
if not fmt then
|
||||
print(('Unknown channel event type (%d).'):format(type))
|
||||
return
|
||||
end
|
||||
|
||||
local channel = channels[channelId]
|
||||
if channel then
|
||||
local tab = getTab(channel)
|
||||
if tab then
|
||||
addTabText(fmt:format(name), SpeakTypesSettings.channelOrange, tab)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -120,7 +120,7 @@ function setSkillTooltip(id, value)
|
|||
widget:setTooltip(value)
|
||||
end
|
||||
|
||||
function setSkillPercent(id, percent, tooltip)
|
||||
function setSkillPercent(id, percent, tooltip, color)
|
||||
local skill = skillsWindow:recursiveGetChildById(id)
|
||||
local widget = skill:getChildById('percent')
|
||||
if widget then
|
||||
|
@ -129,6 +129,10 @@ function setSkillPercent(id, percent, tooltip)
|
|||
if tooltip then
|
||||
widget:setTooltip(tooltip)
|
||||
end
|
||||
|
||||
if color then
|
||||
widget:setBackgroundColor(color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -333,10 +337,34 @@ function onStaminaChange(localPlayer, stamina)
|
|||
if minutes < 10 then
|
||||
minutes = '0' .. minutes
|
||||
end
|
||||
local percent = math.floor(100 * stamina / (42 * 60)) -- max is 42 hours
|
||||
local percent = math.floor(100 * stamina / (42 * 60)) -- max is 42 hours --TODO not in all client versions
|
||||
|
||||
setSkillValue('stamina', hours .. ":" .. minutes)
|
||||
setSkillPercent('stamina', percent, tr('You have %s percent', percent))
|
||||
|
||||
--TODO not all client versions have premium time
|
||||
if stamina > 2400 and g_game.getClientVersion() >= 1038 and localPlayer:isPremium() then
|
||||
local text = tr("You have %s hours and %s minutes left", hours, minutes) .. '\n' ..
|
||||
tr("Now you will gain 50%% more experience")
|
||||
setSkillPercent('stamina', percent, text, 'green')
|
||||
elseif stamina > 2400 and g_game.getClientVersion() >= 1038 and not localPlayer:isPremium() then
|
||||
local text = tr("You have %s hours and %s minutes left", hours, minutes) .. '\n' ..
|
||||
tr("You will not gain 50%% more experience because you aren't premium player, now you receive only 1x experience points")
|
||||
setSkillPercent('stamina', percent, text, '#89F013')
|
||||
elseif stamina > 2400 and g_game.getClientVersion() < 1038 then
|
||||
local text = tr("You have %s hours and %s minutes left", hours, minutes) .. '\n' ..
|
||||
tr("If you are premium player, you will gain 50%% more experience")
|
||||
setSkillPercent('stamina', percent, text, 'green')
|
||||
elseif stamina <= 2400 and stamina > 840 then
|
||||
setSkillPercent('stamina', percent, tr("You have %s hours and %s minutes left", hours, minutes), 'orange')
|
||||
elseif stamina <= 840 and stamina > 0 then
|
||||
local text = tr("You have %s hours and %s minutes left", hours, minutes) .. "\n" ..
|
||||
tr("You gain only 50%% experience and you don't may gain loot from monsters")
|
||||
setSkillPercent('stamina', percent, text, 'red')
|
||||
elseif stamina == 0 then
|
||||
local text = tr("You have %s hours and %s minutes left", hours, minutes) .. "\n" ..
|
||||
tr("You don't may receive experience and loot from monsters")
|
||||
setSkillPercent('stamina', percent, text, 'black')
|
||||
end
|
||||
end
|
||||
|
||||
function onOfflineTrainingChange(localPlayer, offlineTrainingTime)
|
||||
|
|
|
@ -8,7 +8,7 @@ TextMessageLabel < UILabel
|
|||
|
||||
Panel
|
||||
anchors.fill: gameMapPanel
|
||||
anchors.bottom: gameBottomPanel.top
|
||||
anchors.bottom: gameMapPanel.bottom
|
||||
focusable: false
|
||||
|
||||
Panel
|
||||
|
|
|
@ -333,4 +333,11 @@ SubscriptionStatus = {
|
|||
Premium = 1,
|
||||
}
|
||||
|
||||
ChannelEvent = {
|
||||
Join = 0,
|
||||
Leave = 1,
|
||||
Invite = 2,
|
||||
Exclude = 3,
|
||||
}
|
||||
|
||||
-- @}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
SE_enforcing=`getenforce` || true
|
||||
|
||||
sudo setenforce Permissive || true
|
||||
|
||||
# Enable any host to connect on X Org
|
||||
xhost +
|
||||
|
||||
docker run -ti --rm \
|
||||
-e DISPLAY \
|
||||
-v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
--device /dev/dri \
|
||||
edubart/otclient
|
||||
|
||||
# Enable any host to connect on X Org
|
||||
xhost -
|
||||
|
||||
sudo setenforce $SE_enforcing || true
|
|
@ -123,6 +123,24 @@ int Animator::getPhase()
|
|||
return m_phase;
|
||||
}
|
||||
|
||||
int Animator::getPhaseAt(ticks_t time)
|
||||
{
|
||||
int index = 0;
|
||||
ticks_t total = 0;
|
||||
|
||||
for(const auto &pair: m_phaseDurations) {
|
||||
total += std::get<1>(pair);
|
||||
|
||||
if (time < total) {
|
||||
return index;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
return std::min<int>(index, m_animationPhases - 1);
|
||||
}
|
||||
|
||||
int Animator::getStartPhase()
|
||||
{
|
||||
if(m_startPhase > -1)
|
||||
|
@ -197,3 +215,13 @@ void Animator::calculateSynchronous()
|
|||
}
|
||||
m_lastPhaseTicks = ticks;
|
||||
}
|
||||
|
||||
ticks_t Animator::getTotalDuration()
|
||||
{
|
||||
ticks_t time = 0;
|
||||
for (const auto &pair: m_phaseDurations) {
|
||||
time += std::get<1>(pair);
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
|
|
@ -50,12 +50,15 @@ public:
|
|||
|
||||
void setPhase(int phase);
|
||||
int getPhase();
|
||||
int getPhaseAt(ticks_t time);
|
||||
|
||||
int getStartPhase();
|
||||
int getAnimationPhases() { return m_animationPhases; }
|
||||
bool isAsync() { return m_async; }
|
||||
bool isComplete() { return m_isComplete; }
|
||||
|
||||
ticks_t getTotalDuration();
|
||||
|
||||
void resetAnimation();
|
||||
|
||||
private:
|
||||
|
|
|
@ -405,7 +405,7 @@ void Creature::updateJump()
|
|||
|
||||
int nextT, i = 1;
|
||||
do {
|
||||
nextT = stdext::round((-b + std::sqrt(std::max<int>(b*b + 4*a*(roundHeight+diff*i), 0.0)) * diff) / (2*a));
|
||||
nextT = stdext::round((-b + std::sqrt(std::max<double>(b*b + 4*a*(roundHeight+diff*i), 0.0)) * diff) / (2*a));
|
||||
++i;
|
||||
|
||||
if(nextT < halfJumpDuration)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "effect.h"
|
||||
#include "map.h"
|
||||
#include "game.h"
|
||||
#include <framework/core/eventdispatcher.h>
|
||||
|
||||
void Effect::drawEffect(const Point& dest, float scaleFactor, bool animate, int offsetX, int offsetY, LightView *lightView)
|
||||
|
@ -30,8 +31,20 @@ void Effect::drawEffect(const Point& dest, float scaleFactor, bool animate, int
|
|||
return;
|
||||
|
||||
int animationPhase = 0;
|
||||
if(animate)
|
||||
animationPhase = std::min<int>((int)(m_animationTimer.ticksElapsed() / m_phaseDuration), getAnimationPhases() - 1);
|
||||
if(animate) {
|
||||
if(g_game.getFeature(Otc::GameEnhancedAnimations)) {
|
||||
// This requires a separate getPhaseAt method as using getPhase would make all magic effects use the same phase regardless of their appearance time
|
||||
animationPhase = rawGetThingType()->getAnimator()->getPhaseAt(m_animationTimer.ticksElapsed());
|
||||
} else {
|
||||
// hack to fix some animation phases duration, currently there is no better solution
|
||||
int ticks = EFFECT_TICKS_PER_FRAME;
|
||||
if (m_id == 33) {
|
||||
ticks <<= 2;
|
||||
}
|
||||
|
||||
animationPhase = std::min<int>((int)(m_animationTimer.ticksElapsed() / ticks), getAnimationPhases() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int xPattern = offsetX % getNumPatternX();
|
||||
if(xPattern < 0)
|
||||
|
@ -47,15 +60,24 @@ void Effect::drawEffect(const Point& dest, float scaleFactor, bool animate, int
|
|||
void Effect::onAppear()
|
||||
{
|
||||
m_animationTimer.restart();
|
||||
m_phaseDuration = EFFECT_TICKS_PER_FRAME;
|
||||
|
||||
int duration = 0;
|
||||
if(g_game.getFeature(Otc::GameEnhancedAnimations)) {
|
||||
duration = getThingType()->getAnimator()->getTotalDuration();
|
||||
} else {
|
||||
duration = EFFECT_TICKS_PER_FRAME;
|
||||
|
||||
// hack to fix some animation phases duration, currently there is no better solution
|
||||
if(m_id == 33)
|
||||
m_phaseDuration <<= 2;
|
||||
if(m_id == 33) {
|
||||
duration <<= 2;
|
||||
}
|
||||
|
||||
duration *= getAnimationPhases();
|
||||
}
|
||||
|
||||
// schedule removal
|
||||
auto self = asEffect();
|
||||
g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, m_phaseDuration * getAnimationPhases());
|
||||
g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, duration);
|
||||
}
|
||||
|
||||
void Effect::setId(uint32 id)
|
||||
|
|
|
@ -51,7 +51,6 @@ protected:
|
|||
|
||||
private:
|
||||
Timer m_animationTimer;
|
||||
uint m_phaseDuration;
|
||||
uint16 m_id;
|
||||
};
|
||||
|
||||
|
|
|
@ -1903,9 +1903,11 @@ void ProtocolGame::parseQuestLine(const InputMessagePtr& msg)
|
|||
|
||||
void ProtocolGame::parseChannelEvent(const InputMessagePtr& msg)
|
||||
{
|
||||
msg->getU16(); // channel id
|
||||
g_game.formatCreatureName(msg->getString()); // player name
|
||||
msg->getU8(); // event type
|
||||
uint16 channelId = msg->getU16();
|
||||
std::string name = g_game.formatCreatureName(msg->getString());
|
||||
uint8 type = msg->getU8();
|
||||
|
||||
g_lua.callGlobalField("g_game", "onChannelEvent", channelId, name, type);
|
||||
}
|
||||
|
||||
void ProtocolGame::parseItemInfo(const InputMessagePtr& msg)
|
||||
|
|
|
@ -29,7 +29,19 @@
|
|||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable:4091) // warning C4091: 'typedef ': ignored on left of '' when no variable is declared
|
||||
#include <imagehlp.h>
|
||||
#pragma warning (pop)
|
||||
|
||||
#else
|
||||
|
||||
#include <imagehlp.h>
|
||||
|
||||
#endif
|
||||
|
||||
const char *getExceptionName(DWORD exceptionCode)
|
||||
{
|
||||
|
|
|
@ -23,13 +23,21 @@
|
|||
#include "demangle.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable:4091) // warning C4091: 'typedef ': ignored on left of '' when no variable is declared
|
||||
#include <dbghelp.h>
|
||||
#pragma warning (pop)
|
||||
|
||||
#else
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#endif
|
||||
|
||||
namespace stdext {
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
BUILD_TYPE="RelWithDebInfo";
|
||||
BUILD_COMMIT="devel";
|
||||
BUILD_REVISION="0";
|
||||
VERSION="0.6.3";
|
||||
VERSION="0.6.6";
|
||||
AB
|
||||
</PREPROCESSOR_DEFS>
|
||||
|
||||
|
@ -85,8 +85,8 @@
|
|||
</OTCLIENT_LIBDEPS>
|
||||
|
||||
<OTCLIENT_LIBDEPS_D>
|
||||
glew32.lib;
|
||||
zlib.lib;
|
||||
glew32d.lib;
|
||||
zlibd.lib;
|
||||
libeay32.lib;
|
||||
physfs.lib;
|
||||
openal32.lib;
|
||||
|
|
Loading…
Reference in New Issue