Gesche 9 months ago
parent
commit
7fc3229610

+ 31
- 0
Dockerfile View File

@@ -0,0 +1,31 @@
1
+from ubuntu:latest
2
+
3
+WORKDIR /app
4
+
5
+RUN apt-get update; apt-get install -y \
6
+  build-essential \
7
+  cmake \
8
+  git-core \
9
+  libboost-all-dev \
10
+  libglew-dev \
11
+  liblua5.1-0-dev \
12
+  libopenal-dev \
13
+  libphysfs-dev \
14
+  libssl-dev \
15
+  libvorbis-dev \
16
+  zlib1g-dev
17
+
18
+RUN apt-get install -y \
19
+  libncurses5-dev \
20
+  mercurial; \
21
+  hg clone -r stable-2.0 http://hg.icculus.org/icculus/physfs/; \
22
+    cd physfs; \
23
+    mkdir build && cd build && cmake .. && make && make install; \
24
+    mv /usr/local/lib/libphysfs.a /usr/lib/x86_64-linux-gnu/.
25
+
26
+ADD . /app
27
+
28
+# Build application
29
+RUN mkdir -p build && cd build && cmake .. && make -j$(grep -c ^process /proc/cpuinfo); 
30
+
31
+CMD cd build; ./otclient 

+ 9
- 0
README.md View File

@@ -37,6 +37,15 @@ In short, if you need to compile OTClient, follow these tutorials:
37 37
 * [Compiling on Linux](https://github.com/edubart/otclient/wiki/Compiling-on-Linux)
38 38
 * [Compiling on OS X](https://github.com/edubart/otclient/wiki/Compiling-on-Mac-OS-X)
39 39
 
40
+### Build and run with Docker
41
+
42
+To build and run the client:
43
+```
44
+./build.sh
45
+./run.sh
46
+```
47
+
48
+The build step should be run just when something on implementation changes.
40 49
 
41 50
 ### Need help?
42 51
 

+ 3
- 0
build.sh View File

@@ -0,0 +1,3 @@
1
+#!/usr/bin/env bash
2
+
3
+docker build -t edubart/otclient .

+ 5
- 0
modules/corelib/keyboard.lua View File

@@ -26,6 +26,11 @@ local function retranslateKeyComboDesc(keyComboDesc)
26 26
   if keyComboDesc == nil then
27 27
     error('Unable to translate key combo \'' .. keyComboDesc .. '\'')
28 28
   end
29
+
30
+  if type(keyComboDesc) == 'number' then
31
+    keyComboDesc = tostring(keyComboDesc)
32
+  end
33
+
29 34
   local keyCombo = {}
30 35
   for i,currentKeyDesc in ipairs(keyComboDesc:split('+')) do
31 36
     for keyCode, keyDesc in pairs(KeyCodeDescs) do

+ 4
- 3
modules/corelib/ui/uicombobox.lua View File

@@ -19,13 +19,14 @@ function UIComboBox:clearOptions()
19 19
   self:clearText()
20 20
 end
21 21
 
22
-function UIComboBox:getOption(text)
23
-  if not self.options then return nil end
22
+function UIComboBox:isOption(text)
23
+  if not self.options then return false end
24 24
   for i,v in ipairs(self.options) do
25 25
     if v.text == text then
26
-      return nil
26
+      return true
27 27
     end
28 28
   end
29
+  return false
29 30
 end
30 31
 
31 32
 function UIComboBox:setOption(text, dontSignal)

+ 5
- 0
modules/corelib/ui/uimovabletabbar.lua View File

@@ -385,6 +385,11 @@ function UIMoveableTabBar:selectTab(tab)
385 385
   tab:setOn(false)
386 386
   tab.blinking = false
387 387
 
388
+  if tab.blinkEvent then
389
+    removeEvent(tab.blinkEvent)
390
+    tab.blinkEvent = nil
391
+  end
392
+
388 393
   local parent = tab:getParent()
389 394
   parent:focusChild(tab, MouseFocusReason)
390 395
   updateNavigation(self)

+ 23
- 3
modules/corelib/ui/uiscrollbar.lua View File

@@ -126,8 +126,8 @@ end
126 126
 function UIScrollBar:onSetup()
127 127
   self.setupDone = true
128 128
   local sliderButton = self:getChildById('sliderButton')
129
-  g_mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:decrement() end, 300)
130
-  g_mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:increment() end, 300)
129
+  g_mouse.bindAutoPress(self:getChildById('decrementButton'), function() self:onDecrement() end, 300)
130
+  g_mouse.bindAutoPress(self:getChildById('incrementButton'), function() self:onIncrement() end, 300)
131 131
   g_mouse.bindPressMove(sliderButton, function(mousePos, mouseMoved) parseSliderPos(self, sliderButton, mousePos, mouseMoved) end)
132 132
   g_mouse.bindPress(sliderButton, function(mousePos, mouseButton) parseSliderPress(self, sliderButton, mousePos, mouseButton) end)
133 133
 
@@ -158,6 +158,26 @@ function UIScrollBar:onStyleApply(styleName, styleNode)
158 158
   end
159 159
 end
160 160
 
161
+function UIScrollBar:onDecrement()
162
+  if g_keyboard.isCtrlPressed() then
163
+    self:decrement(self.value)
164
+  elseif g_keyboard.isShiftPressed() then
165
+    self:decrement(10)
166
+  else
167
+    self:decrement()
168
+  end
169
+end
170
+
171
+function UIScrollBar:onIncrement()
172
+  if g_keyboard.isCtrlPressed() then
173
+    self:increment(self.maximum)
174
+  elseif g_keyboard.isShiftPressed() then
175
+    self:increment(10)
176
+  else
177
+    self:increment()
178
+  end
179
+end
180
+
161 181
 function UIScrollBar:decrement(count)
162 182
   count = count or self.step
163 183
   self:setValue(self.value - count)
@@ -264,4 +284,4 @@ function UIScrollBar:getStep() return self.step end
264 284
 function UIScrollBar:getOrientation() return self.orientation end
265 285
 function UIScrollBar:getShowValue() return self.showValue end
266 286
 function UIScrollBar:getSymbol() return self.symbol end
267
-function UIScrollBar:getMouseScroll() return self.mouseScroll end
287
+function UIScrollBar:getMouseScroll() return self.mouseScroll end

+ 27
- 2
modules/game_console/console.lua View File

@@ -52,6 +52,13 @@ SayModes = {
52 52
   [3] = { speakTypeDesc = 'yell', icon = '/images/game/console/yell' }
53 53
 }
54 54
 
55
+ChannelEventFormats = {
56
+  [ChannelEvent.Join] = '%s joined the channel.',
57
+  [ChannelEvent.Leave] = '%s left the channel.',
58
+  [ChannelEvent.Invite] = '%s has been invited to the channel.',
59
+  [ChannelEvent.Exclude] = '%s has been removed from the channel.',
60
+}
61
+
55 62
 MAX_HISTORY = 500
56 63
 MAX_LINES = 100
57 64
 HELP_CHANNEL = 9
@@ -98,7 +105,8 @@ function init()
98 105
     onRuleViolationCancel = onRuleViolationCancel,
99 106
     onRuleViolationLock = onRuleViolationLock,
100 107
     onGameStart = online,
101
-    onGameEnd = offline
108
+    onGameEnd = offline,
109
+    onChannelEvent = onChannelEvent,
102 110
   })
103 111
 
104 112
   consolePanel = g_ui.loadUI('console', modules.game_interface.getBottomPanel())
@@ -240,7 +248,8 @@ function terminate()
240 248
     onRuleViolationCancel = onRuleViolationCancel,
241 249
     onRuleViolationLock = onRuleViolationLock,
242 250
     onGameStart = online,
243
-    onGameEnd = offline
251
+    onGameEnd = offline,
252
+    onChannelEvent = onChannelEvent,
244 253
   })
245 254
 
246 255
   if g_game.isOnline() then clear() end
@@ -1437,3 +1446,19 @@ function offline()
1437 1446
   end
1438 1447
   clear()
1439 1448
 end
1449
+
1450
+function onChannelEvent(channelId, name, type)
1451
+  local fmt = ChannelEventFormats[type]
1452
+  if not fmt then
1453
+    print(('Unknown channel event type (%d).'):format(type))
1454
+    return
1455
+  end
1456
+
1457
+  local channel = channels[channelId]
1458
+  if channel then
1459
+    local tab = getTab(channel)
1460
+    if tab then
1461
+      addTabText(fmt:format(name), SpeakTypesSettings.channelOrange, tab)
1462
+    end
1463
+  end
1464
+end

+ 31
- 3
modules/game_skills/skills.lua View File

@@ -120,7 +120,7 @@ function setSkillTooltip(id, value)
120 120
   widget:setTooltip(value)
121 121
 end
122 122
 
123
-function setSkillPercent(id, percent, tooltip)
123
+function setSkillPercent(id, percent, tooltip, color)
124 124
   local skill = skillsWindow:recursiveGetChildById(id)
125 125
   local widget = skill:getChildById('percent')
126 126
   if widget then
@@ -129,6 +129,10 @@ function setSkillPercent(id, percent, tooltip)
129 129
     if tooltip then
130 130
       widget:setTooltip(tooltip)
131 131
     end
132
+
133
+    if color then
134
+    	widget:setBackgroundColor(color)
135
+    end
132 136
   end
133 137
 end
134 138
 
@@ -333,10 +337,34 @@ function onStaminaChange(localPlayer, stamina)
333 337
   if minutes < 10 then
334 338
     minutes = '0' .. minutes
335 339
   end
336
-  local percent = math.floor(100 * stamina / (42 * 60)) -- max is 42 hours
340
+  local percent = math.floor(100 * stamina / (42 * 60)) -- max is 42 hours --TODO not in all client versions
337 341
 
338 342
   setSkillValue('stamina', hours .. ":" .. minutes)
339
-  setSkillPercent('stamina', percent, tr('You have %s percent', percent))
343
+
344
+  --TODO not all client versions have premium time
345
+  if stamina > 2400 and g_game.getClientVersion() >= 1038 and localPlayer:isPremium() then
346
+  	local text = tr("You have %s hours and %s minutes left", hours, minutes) .. '\n' ..
347
+		tr("Now you will gain 50%% more experience")
348
+		setSkillPercent('stamina', percent, text, 'green')
349
+	elseif stamina > 2400 and g_game.getClientVersion() >= 1038 and not localPlayer:isPremium() then
350
+		local text = tr("You have %s hours and %s minutes left", hours, minutes) .. '\n' ..
351
+		tr("You will not gain 50%% more experience because you aren't premium player, now you receive only 1x experience points")
352
+		setSkillPercent('stamina', percent, text, '#89F013')
353
+	elseif stamina > 2400 and g_game.getClientVersion() < 1038 then
354
+		local text = tr("You have %s hours and %s minutes left", hours, minutes) .. '\n' ..
355
+		tr("If you are premium player, you will gain 50%% more experience")
356
+		setSkillPercent('stamina', percent, text, 'green')
357
+	elseif stamina <= 2400 and stamina > 840 then
358
+		setSkillPercent('stamina', percent, tr("You have %s hours and %s minutes left", hours, minutes), 'orange')
359
+	elseif stamina <= 840 and stamina > 0 then
360
+		local text = tr("You have %s hours and %s minutes left", hours, minutes) .. "\n" ..
361
+		tr("You gain only 50%% experience and you don't may gain loot from monsters")
362
+		setSkillPercent('stamina', percent, text, 'red')
363
+	elseif stamina == 0 then
364
+		local text = tr("You have %s hours and %s minutes left", hours, minutes) .. "\n" ..
365
+		tr("You don't may receive experience and loot from monsters")
366
+		setSkillPercent('stamina', percent, text, 'black')
367
+	end
340 368
 end
341 369
 
342 370
 function onOfflineTrainingChange(localPlayer, offlineTrainingTime)

+ 1
- 1
modules/game_textmessage/textmessage.otui View File

@@ -8,7 +8,7 @@ TextMessageLabel < UILabel
8 8
 
9 9
 Panel
10 10
   anchors.fill: gameMapPanel
11
-  anchors.bottom: gameBottomPanel.top
11
+  anchors.bottom: gameMapPanel.bottom
12 12
   focusable: false
13 13
 
14 14
   Panel

+ 7
- 0
modules/gamelib/const.lua View File

@@ -333,4 +333,11 @@ SubscriptionStatus = {
333 333
   Premium = 1,
334 334
 }
335 335
 
336
+ChannelEvent = {
337
+  Join = 0,
338
+  Leave = 1,
339
+  Invite = 2,
340
+  Exclude = 3,
341
+}
342
+
336 343
 -- @}

+ 19
- 0
run.sh View File

@@ -0,0 +1,19 @@
1
+#!/usr/bin/env bash
2
+
3
+SE_enforcing=`getenforce` || true
4
+
5
+sudo setenforce Permissive || true
6
+
7
+# Enable any host to connect on X Org
8
+xhost +
9
+
10
+docker run -ti --rm \
11
+       -e DISPLAY \
12
+       -v /tmp/.X11-unix:/tmp/.X11-unix \
13
+       --device /dev/dri \
14
+       edubart/otclient 
15
+
16
+# Enable any host to connect on X Org
17
+xhost -
18
+
19
+sudo setenforce $SE_enforcing || true

+ 30
- 2
src/client/animator.cpp View File

@@ -123,6 +123,24 @@ int Animator::getPhase()
123 123
     return m_phase;
124 124
 }
125 125
 
126
+int Animator::getPhaseAt(ticks_t time)
127
+{
128
+    int index = 0;
129
+    ticks_t total = 0;
130
+
131
+    for(const auto &pair: m_phaseDurations) {
132
+        total += std::get<1>(pair);
133
+
134
+        if (time < total) {
135
+            return index;
136
+        }
137
+
138
+        ++index;
139
+    }
140
+
141
+    return std::min<int>(index, m_animationPhases - 1);
142
+}
143
+
126 144
 int Animator::getStartPhase()
127 145
 {
128 146
     if(m_startPhase > -1)
@@ -157,7 +175,7 @@ int Animator::getLoopPhase()
157 175
 
158 176
     if(m_loopCount == 0)
159 177
         return 0;
160
-        
178
+
161 179
     if(m_currentLoop < (m_loopCount - 1)) {
162 180
         m_currentLoop++;
163 181
         return 0;
@@ -182,7 +200,7 @@ void Animator::calculateSynchronous()
182 200
     int totalDuration = 0;
183 201
     for(int i = 0; i < m_animationPhases; i++)
184 202
         totalDuration += getPhaseDuration(i);
185
-    
203
+
186 204
     ticks_t ticks = g_clock.millis();
187 205
     int elapsedTicks = (int)(ticks % totalDuration);
188 206
     int totalTime = 0;
@@ -197,3 +215,13 @@ void Animator::calculateSynchronous()
197 215
     }
198 216
     m_lastPhaseTicks = ticks;
199 217
 }
218
+
219
+ticks_t Animator::getTotalDuration()
220
+{
221
+    ticks_t time = 0;
222
+    for (const auto &pair: m_phaseDurations) {
223
+        time += std::get<1>(pair);
224
+    }
225
+
226
+    return time;
227
+}

+ 3
- 0
src/client/animator.h View File

@@ -50,12 +50,15 @@ public:
50 50
 
51 51
     void setPhase(int phase);
52 52
     int getPhase();
53
+    int getPhaseAt(ticks_t time);
53 54
 
54 55
     int getStartPhase();
55 56
     int getAnimationPhases() { return m_animationPhases; }
56 57
     bool isAsync() { return m_async; }
57 58
     bool isComplete() { return m_isComplete; }
58 59
 
60
+    ticks_t getTotalDuration();
61
+
59 62
     void resetAnimation();
60 63
 
61 64
 private:

+ 1
- 1
src/client/creature.cpp View File

@@ -405,7 +405,7 @@ void Creature::updateJump()
405 405
 
406 406
         int nextT, i = 1;
407 407
         do {
408
-            nextT = stdext::round((-b + std::sqrt(std::max<int>(b*b + 4*a*(roundHeight+diff*i), 0.0)) * diff) / (2*a));
408
+            nextT = stdext::round((-b + std::sqrt(std::max<double>(b*b + 4*a*(roundHeight+diff*i), 0.0)) * diff) / (2*a));
409 409
             ++i;
410 410
 
411 411
             if(nextT < halfJumpDuration)

+ 29
- 7
src/client/effect.cpp View File

@@ -22,6 +22,7 @@
22 22
 
23 23
 #include "effect.h"
24 24
 #include "map.h"
25
+#include "game.h"
25 26
 #include <framework/core/eventdispatcher.h>
26 27
 
27 28
 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
30 31
         return;
31 32
 
32 33
     int animationPhase = 0;
33
-    if(animate)
34
-        animationPhase = std::min<int>((int)(m_animationTimer.ticksElapsed() / m_phaseDuration), getAnimationPhases() - 1);
34
+    if(animate) {
35
+        if(g_game.getFeature(Otc::GameEnhancedAnimations)) {
36
+            // This requires a separate getPhaseAt method as using getPhase would make all magic effects use the same phase regardless of their appearance time
37
+            animationPhase = rawGetThingType()->getAnimator()->getPhaseAt(m_animationTimer.ticksElapsed());
38
+        } else {
39
+            // hack to fix some animation phases duration, currently there is no better solution
40
+            int ticks = EFFECT_TICKS_PER_FRAME;
41
+            if (m_id == 33) {
42
+                ticks <<= 2;
43
+            }
44
+
45
+            animationPhase = std::min<int>((int)(m_animationTimer.ticksElapsed() / ticks), getAnimationPhases() - 1);
46
+        }
47
+    }
35 48
 
36 49
     int xPattern = offsetX % getNumPatternX();
37 50
     if(xPattern < 0)
@@ -47,15 +60,24 @@ void Effect::drawEffect(const Point& dest, float scaleFactor, bool animate, int
47 60
 void Effect::onAppear()
48 61
 {
49 62
     m_animationTimer.restart();
50
-    m_phaseDuration = EFFECT_TICKS_PER_FRAME;
51 63
 
52
-    // hack to fix some animation phases duration, currently there is no better solution
53
-    if(m_id == 33)
54
-        m_phaseDuration <<= 2;
64
+    int duration = 0;
65
+    if(g_game.getFeature(Otc::GameEnhancedAnimations)) {
66
+        duration = getThingType()->getAnimator()->getTotalDuration();
67
+    } else {
68
+        duration = EFFECT_TICKS_PER_FRAME;
69
+
70
+        // hack to fix some animation phases duration, currently there is no better solution
71
+        if(m_id == 33) {
72
+            duration <<= 2;
73
+        }
74
+
75
+        duration *= getAnimationPhases();
76
+    }
55 77
 
56 78
     // schedule removal
57 79
     auto self = asEffect();
58
-    g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, m_phaseDuration * getAnimationPhases());
80
+    g_dispatcher.scheduleEvent([self]() { g_map.removeThing(self); }, duration);
59 81
 }
60 82
 
61 83
 void Effect::setId(uint32 id)

+ 0
- 1
src/client/effect.h View File

@@ -51,7 +51,6 @@ protected:
51 51
 
52 52
 private:
53 53
     Timer m_animationTimer;
54
-    uint m_phaseDuration;
55 54
     uint16 m_id;
56 55
 };
57 56
 

+ 6
- 4
src/client/protocolgameparse.cpp View File

@@ -1296,7 +1296,7 @@ void ProtocolGame::parsePremiumTrigger(const InputMessagePtr& msg)
1296 1296
     for(int i=0;i<triggerCount;++i) {
1297 1297
         triggers.push_back(msg->getU8());
1298 1298
     }
1299
-    
1299
+
1300 1300
     if(g_game.getClientVersion() <= 1096) {
1301 1301
         bool something = msg->getU8() == 1;
1302 1302
     }
@@ -1903,9 +1903,11 @@ void ProtocolGame::parseQuestLine(const InputMessagePtr& msg)
1903 1903
 
1904 1904
 void ProtocolGame::parseChannelEvent(const InputMessagePtr& msg)
1905 1905
 {
1906
-    msg->getU16(); // channel id
1907
-    g_game.formatCreatureName(msg->getString()); // player name
1908
-    msg->getU8(); // event type
1906
+    uint16 channelId = msg->getU16();
1907
+    std::string name = g_game.formatCreatureName(msg->getString());
1908
+    uint8 type = msg->getU8();
1909
+
1910
+    g_lua.callGlobalField("g_game", "onChannelEvent", channelId, name, type);
1909 1911
 }
1910 1912
 
1911 1913
 void ProtocolGame::parseItemInfo(const InputMessagePtr& msg)

+ 12
- 0
src/framework/platform/win32crashhandler.cpp View File

@@ -29,8 +29,20 @@
29 29
 #include <winsock2.h>
30 30
 #include <windows.h>
31 31
 #include <process.h>
32
+
33
+#ifdef _MSC_VER
34
+
35
+#pragma warning (push)
36
+#pragma warning (disable:4091) // warning C4091: 'typedef ': ignored on left of '' when no variable is declared
37
+#include <imagehlp.h>
38
+#pragma warning (pop)
39
+
40
+#else
41
+
32 42
 #include <imagehlp.h>
33 43
 
44
+#endif
45
+
34 46
 const char *getExceptionName(DWORD exceptionCode)
35 47
 {
36 48
     switch (exceptionCode) {

+ 8
- 0
src/framework/stdext/demangle.cpp View File

@@ -23,13 +23,21 @@
23 23
 #include "demangle.h"
24 24
 
25 25
 #ifdef _MSC_VER
26
+
26 27
 #include <winsock2.h>
27 28
 #include <windows.h>
29
+
30
+#pragma warning (push)
31
+#pragma warning (disable:4091) // warning C4091: 'typedef ': ignored on left of '' when no variable is declared
28 32
 #include <dbghelp.h>
33
+#pragma warning (pop)
34
+
29 35
 #else
36
+
30 37
 #include <cxxabi.h>
31 38
 #include <cstring>
32 39
 #include <cstdlib>
40
+
33 41
 #endif
34 42
 
35 43
 namespace stdext {

+ 3
- 3
vc14/settings.props View File

@@ -25,7 +25,7 @@
25 25
         BUILD_TYPE="RelWithDebInfo";
26 26
         BUILD_COMMIT="devel";
27 27
         BUILD_REVISION="0";
28
-        VERSION="0.6.3";
28
+        VERSION="0.6.6";
29 29
         AB
30 30
     </PREPROCESSOR_DEFS>
31 31
     
@@ -85,8 +85,8 @@
85 85
     </OTCLIENT_LIBDEPS>
86 86
     
87 87
     <OTCLIENT_LIBDEPS_D>
88
-        glew32.lib;
89
-        zlib.lib;
88
+        glew32d.lib;
89
+        zlibd.lib;
90 90
         libeay32.lib;
91 91
         physfs.lib;
92 92
         openal32.lib;

Loading…
Cancel
Save