Client Upload Part 2

RD Core / Commit #5

Commit Client Upload Part 2

Alex Alex committed 19/10/2026 00:06 main Patch
1,244 files +271,215 -0
A RD_Client/src/client/spritemanager.cpp +518-0 View file
@@ -0,0 +1,518 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "spritemanager.h"
24+#include "game.h"
25+#include <framework/core/resourcemanager.h>
26+#include <framework/core/filestream.h>
27+#include <framework/graphics/image.h>
28+#include <framework/graphics/atlas.h>
29+#include <framework/util/crypt.h>
30+#include <framework/util/pngunpacker.h>
31+
32+SpriteManager g_sprites;
33+
34+SpriteManager::SpriteManager()
35+{
36+ m_spritesCount = 0;
37+ m_signature = 0;
38+}
39+
40+void SpriteManager::terminate()
41+{
42+ unload();
43+}
44+
45+bool SpriteManager::loadSpr(std::string file)
46+{
47+ m_spritesCount = 0;
48+ m_signature = 0;
49+ m_loaded = false;
50+ m_sprites.clear();
51+ m_spritesFile = nullptr;
52+
53+ auto cwmFile = g_resources.guessFilePath(file, "cwm");
54+ if (g_resources.fileExists(cwmFile)) {
55+ m_isHdMod = true;
56+ return loadCwmSpr(cwmFile);
57+ }
58+
59+ auto sprFile = g_resources.guessFilePath(file, "spr");
60+ if (g_resources.fileExists(sprFile)) {
61+ return loadCasualSpr(sprFile);
62+ }
63+
64+ return false;
65+}
66+
67+#ifdef WITH_ENCRYPTION
68+
69+void SpriteManager::saveSpr(std::string fileName)
70+{
71+ if (!m_loaded)
72+ stdext::throw_exception("failed to save, spr is not loaded");
73+ if (!m_spritesFile)
74+ stdext::throw_exception("not allowed");
75+
76+ try {
77+ FileStreamPtr fin = g_resources.createFile(fileName);
78+ if (!fin)
79+ stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName));
80+
81+ fin->addU32(m_signature);
82+ if (g_game.getFeature(Otc::GameSpritesU32))
83+ fin->addU32(m_spritesCount);
84+ else
85+ fin->addU16(m_spritesCount);
86+
87+ uint32 offset = fin->tell();
88+ uint32 spriteAddress = offset + 4 * m_spritesCount;
89+ for (int i = 1; i <= m_spritesCount; i++)
90+ fin->addU32(0);
91+
92+ for (int i = 1; i <= m_spritesCount; i++) {
93+ m_spritesFile->seek((i - 1) * 4 + m_spritesOffset);
94+ uint32 fromAdress = m_spritesFile->getU32();
95+ if (fromAdress != 0) {
96+ fin->seek(offset + (i - 1) * 4);
97+ fin->addU32(spriteAddress);
98+ fin->seek(spriteAddress);
99+
100+ m_spritesFile->seek(fromAdress);
101+ fin->addU8(m_spritesFile->getU8());
102+ fin->addU8(m_spritesFile->getU8());
103+ fin->addU8(m_spritesFile->getU8());
104+
105+ uint16 dataSize = m_spritesFile->getU16();
106+ fin->addU16(dataSize);
107+ std::vector<char> spriteData(m_spriteSize * m_spriteSize);
108+ m_spritesFile->read(spriteData.data(), dataSize);
109+ fin->write(spriteData.data(), dataSize);
110+
111+ spriteAddress = fin->tell();
112+ }
113+ //TODO: Check for overwritten sprites.
114+ }
115+
116+ fin->flush();
117+ fin->close();
118+ } catch (std::exception& e) {
119+ g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what()));
120+ }
121+}
122+
123+void SpriteManager::saveSpr64(std::string fileName)
124+{
125+ if (!m_loaded)
126+ stdext::throw_exception("failed to save, spr is not loaded");
127+ if (!m_spritesFile || m_spriteSize != 32)
128+ stdext::throw_exception("not allowed");
129+
130+ try {
131+ FileStreamPtr fin = g_resources.createFile(fileName);
132+ if (!fin)
133+ stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName));
134+
135+ fin->addU32(m_signature);
136+ if (g_game.getFeature(Otc::GameSpritesU32))
137+ fin->addU32(m_spritesCount);
138+ else
139+ fin->addU16(m_spritesCount);
140+
141+ uint32 offset = fin->tell();
142+ for (int i = 1; i <= m_spritesCount; i++)
143+ fin->addU32(0);
144+
145+ for (int i = 1; i <= m_spritesCount; i++) {
146+ ImagePtr sprite = getSpriteImage(i);
147+ if (!sprite) {
148+ continue;
149+ }
150+ sprite = sprite->upscale();
151+
152+ uint32 spriteAddress = fin->tell();
153+ fin->seek(offset + (i - 1) * 4);
154+ fin->addU32(spriteAddress);
155+ fin->seek(spriteAddress);
156+
157+ fin->addU8(0xff);
158+ fin->addU8(0x00);
159+ fin->addU8(0xff);
160+
161+ uint8_t* pixels = sprite->getPixelData();
162+ int pixelCount = sprite->getPixelCount() * 4;
163+ std::vector<uint8_t> buffer(pixelCount + 1024, 0);
164+ int bufferPos = 0;
165+
166+ int skipedPixels = 0;
167+ for (int i = 0; i < pixelCount; ) {
168+ int transparent = 0, colored = 0;
169+ for (int j = i; j < pixelCount; j += 4) {
170+ if (pixels[j + 3] == 0x00) {
171+ if (colored != 0) break;
172+ transparent += 1;
173+ } else {
174+ colored += 1;
175+ }
176+ }
177+
178+ *(uint16_t*)(buffer.data() + bufferPos) = transparent;
179+ bufferPos += 2;
180+ *(uint16_t*)(buffer.data() + bufferPos) = colored;
181+ bufferPos += 2;
182+
183+ i += transparent * 4;
184+
185+ for (int c = 0; c < colored; ++c) {
186+ buffer[bufferPos++] = pixels[i];
187+ buffer[bufferPos++] = pixels[i + 1];
188+ buffer[bufferPos++] = pixels[i + 2];
189+ i += 4;
190+ }
191+ }
192+
193+ fin->addU16(bufferPos);
194+ fin->write(buffer.data(), bufferPos);
195+ }
196+
197+ fin->flush();
198+ fin->close();
199+ } catch (std::exception& e) {
200+ g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what()));
201+ }
202+}
203+
204+void SpriteManager::encryptSprites(std::string fileName)
205+{
206+ if (!m_loaded)
207+ stdext::throw_exception("failed to save, spr is not loaded");
208+
209+ try {
210+ FileStreamPtr fin = g_resources.createFile(fileName);
211+ if (!fin)
212+ stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName));
213+
214+ const char otcv8Signature[] = "OTV8";
215+ fin->addU32(*((uint32_t*)otcv8Signature));
216+ fin->addU32(m_signature);
217+ fin->addU32(m_spritesCount);
218+
219+ for (int i = 1; i <= m_spritesCount; i++) {
220+ ImagePtr sprite = getSpriteImage(i);
221+ if (!sprite) {
222+ fin->addU16(0);
223+ continue;
224+ }
225+ uint8_t* pixels = sprite->getPixelData();
226+ int pixelCount = sprite->getPixelCount() * 4;
227+ std::vector<uint8_t> buffer(pixelCount + 1024, 0);
228+ int bufferPos = 0;
229+
230+ bool hasAlpha = false;
231+ for (int i = 3; i < pixelCount; i += 4) {
232+ if (pixels[i] != 0x00 && pixels[i] != 0xFF) {
233+ hasAlpha = true;
234+ break;
235+ }
236+ }
237+
238+ buffer[bufferPos++] = (hasAlpha ? 1 : 0);
239+ int skipedPixels = 0;
240+ for (int i = 0; i < pixelCount; ) {
241+ int transparent = 0, colored = 0;
242+ for (int j = i; j < pixelCount; j += 4) {
243+ if (pixels[j + 3] == 0x00) {
244+ if (colored != 0) break;
245+ transparent += 1;
246+ } else {
247+ colored += 1;
248+ }
249+ }
250+
251+ *(uint16_t*)(buffer.data() + bufferPos) = transparent;
252+ bufferPos += 2;
253+ *(uint16_t*)(buffer.data() + bufferPos) = colored;
254+ bufferPos += 2;
255+
256+ i += transparent * 4;
257+
258+ for (int c = 0; c < colored; ++c) {
259+ buffer[bufferPos++] = pixels[i];
260+ buffer[bufferPos++] = pixels[i + 1];
261+ buffer[bufferPos++] = pixels[i + 2];
262+ if (hasAlpha) {
263+ buffer[bufferPos++] = pixels[i + 3];
264+ }
265+ i += 4;
266+ }
267+ }
268+
269+ g_crypt.bencrypt(buffer.data(), bufferPos, (uint64_t)m_signature + i);
270+ fin->addU16(bufferPos);
271+ fin->write(buffer.data(), bufferPos);
272+ }
273+
274+ fin->flush();
275+ fin->close();
276+ }
277+ catch (std::exception& e) {
278+ g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what()));
279+ }
280+}
281+
282+void SpriteManager::dumpSprites(std::string dir)
283+{
284+ if (dir.empty()) {
285+ g_logger.error("Empty dir for sprites dump");
286+ return;
287+ }
288+ g_resources.makeDir(dir);
289+ for (int i = 1; i <= m_spritesCount; i++) {
290+ auto img = getSpriteImage(i);
291+ if (!img) continue;
292+ img->savePNG(dir + "/" + std::to_string(i) + ".png");
293+ }
294+}
295+
296+#endif
297+
298+void SpriteManager::unload()
299+{
300+ m_spritesCount = 0;
301+ m_signature = 0;
302+ m_spritesFile = nullptr;
303+ m_sprites.clear();
304+}
305+
306+ImagePtr SpriteManager::getSpriteImage(int id)
307+{
308+ if (m_isHdMod) {
309+ return getSpriteImageHd(id);
310+ }
311+ else {
312+ return getSpriteImageCasual(id);
313+ }
314+}
315+
316+bool SpriteManager::loadCasualSpr(std::string file)
317+{
318+ m_spriteSize = 32u;
319+ try {
320+ file = g_resources.guessFilePath(file, "spr");
321+
322+ m_spritesFile = g_resources.openFile(file, true);
323+
324+ m_signature = m_spritesFile->getU32();
325+ if (m_signature == *((uint32_t*)"OTV8")) {
326+ m_signature = m_spritesFile->getU32();
327+ m_spritesCount = m_spritesFile->getU32();
328+ m_sprites.resize(m_spritesCount + 1);
329+ for (int i = 1; i <= m_spritesCount; ++i) {
330+ int bufferSize = m_spritesFile->getU16();
331+ if (bufferSize == 0) continue;
332+ m_sprites[i].resize(bufferSize + 1);
333+ m_sprites[i][0] = 0;
334+ m_spritesFile->read(m_sprites[i].data() + 1, bufferSize);
335+ }
336+ m_spritesFile = nullptr;
337+ }
338+ else {
339+ m_spritesCount = g_game.getFeature(Otc::GameSpritesU32) ? m_spritesFile->getU32() : m_spritesFile->getU16();
340+ m_spritesOffset = m_spritesFile->tell();
341+ }
342+ m_loaded = true;
343+ g_lua.callGlobalField("g_sprites", "onLoadSpr", file);
344+ return true;
345+ }
346+ catch (std::exception& e) {
347+ m_spritesFile = nullptr;
348+ m_sprites.clear();
349+ g_logger.error(stdext::format("Failed to load sprites from '%s': %s", file, e.what()));
350+ return false;
351+ }
352+}
353+
354+bool SpriteManager::loadCwmSpr(std::string file)
355+{
356+ try {
357+ auto inFilePath = g_resources.guessFilePath(file, "cwm");
358+ auto spritesFile = g_resources.openFile(inFilePath, g_game.getFeature(Otc::GameDontCacheFiles));
359+
360+ uint8_t version = spritesFile->getU8();
361+ if (version != 0x01) {
362+ g_logger.error(stdext::format("Invalid CWM file version - %s", file));
363+ return false;
364+ }
365+
366+ m_spriteSize = spritesFile->getU16();
367+ m_cachedData = std::move(PngUnpacker::unpack(spritesFile));
368+ m_spritesCount = m_cachedData.size();
369+
370+ if (m_spritesCount == 0) {
371+ g_logger.error(stdext::format("Failed to load sprites from '%s' - no sprites", file));
372+ return false;
373+ }
374+
375+ m_loaded = true;
376+ return true;
377+ }
378+ catch (stdext::exception& e) {
379+ g_logger.error(stdext::format("Failed to load sprites from '%s': %s", file, e.what()));
380+ return false;
381+ }
382+
383+ return false;
384+}
385+
386+ImagePtr SpriteManager::getSpriteImageCasual(int id)
387+{
388+ try {
389+ int spriteDataSize = m_spriteSize * m_spriteSize * 4;
390+
391+ if (!m_sprites.empty()) {
392+ if (id >= (int)m_sprites.size())
393+ return nullptr;
394+ auto& buffer = m_sprites[id];
395+ if (buffer.size() < 5)
396+ return nullptr;
397+ if (buffer[0] == 0) {
398+ buffer[0] = 1;
399+ g_crypt.bdecrypt(buffer.data() + 1, buffer.size() - 1, (uint64_t)m_signature + id);
400+ }
401+
402+ if (buffer[1] > 1) {
403+ stdext::throw_exception("Invalid sprite encryption");
404+ }
405+
406+ bool hasAlpha = (buffer[1] == 1);
407+
408+ ImagePtr image(new Image(Size(m_spriteSize, m_spriteSize)));
409+ uint8* pixels = image->getPixelData();
410+ int writePos = 0;
411+
412+ size_t bufferPos = 2;
413+ while (bufferPos != buffer.size()) {
414+ uint16_t transparentPixels = *(uint16_t*)(&buffer[bufferPos]);
415+ bufferPos += 2;
416+ uint16_t coloredPixels = *(uint16_t*)(&buffer[bufferPos]);
417+ bufferPos += 2;
418+
419+ writePos += transparentPixels * 4;
420+ for (int i = 0; i < coloredPixels; ++i) {
421+ pixels[writePos++] = buffer[bufferPos++];
422+ pixels[writePos++] = buffer[bufferPos++];
423+ pixels[writePos++] = buffer[bufferPos++];
424+ if (hasAlpha) {
425+ pixels[writePos] = buffer[bufferPos++];
426+ }
427+ else {
428+ pixels[writePos] = 0xFF;
429+ }
430+ writePos += 1;
431+ }
432+ }
433+
434+ return image;
435+ }
436+
437+ if (id == 0 || !m_spritesFile)
438+ return nullptr;
439+
440+ m_spritesFile->seek(((id - 1) * 4) + m_spritesOffset);
441+
442+ uint32 spriteAddress = m_spritesFile->getU32();
443+
444+ // no sprite? return an empty texture
445+ if (spriteAddress == 0)
446+ return nullptr;
447+
448+ m_spritesFile->seek(spriteAddress);
449+
450+ // color key
451+ m_spritesFile->getU8();
452+ m_spritesFile->getU8();
453+ m_spritesFile->getU8();
454+
455+ uint16 pixelDataSize = m_spritesFile->getU16();
456+
457+ ImagePtr image(new Image(Size(m_spriteSize, m_spriteSize)));
458+
459+ uint8* pixels = image->getPixelData();
460+ int writePos = 0;
461+ bool useAlpha = g_game.getFeature(Otc::GameSpritesAlphaChannel);
462+
463+ std::vector<uint8> data(pixelDataSize);
464+ if (pixelDataSize > 0)
465+ m_spritesFile->read(data.data(), pixelDataSize);
466+
467+ // decompress pixels
468+ size_t read = 0;
469+ while (read + 4 <= data.size() && writePos < spriteDataSize) {
470+ uint16 transparentPixels = data[read] | (data[read + 1] << 8);
471+ uint16 coloredPixels = data[read + 2] | (data[read + 3] << 8);
472+ read += 4;
473+
474+ writePos += transparentPixels * 4;
475+ if (writePos >= spriteDataSize)
476+ break;
477+
478+ if (useAlpha) {
479+ size_t count = std::min<size_t>({ static_cast<size_t>(coloredPixels) * 4, static_cast<size_t>(spriteDataSize - writePos), data.size() - read });
480+ memcpy(&pixels[writePos], &data[read], count);
481+ writePos += coloredPixels * 4;
482+ read += static_cast<size_t>(coloredPixels) * 4;
483+ }
484+ else {
485+ for (int i = 0; i < coloredPixels && writePos < spriteDataSize && read + 3 <= data.size(); i++) {
486+ pixels[writePos + 0] = data[read];
487+ pixels[writePos + 1] = data[read + 1];
488+ pixels[writePos + 2] = data[read + 2];
489+ pixels[writePos + 3] = 0xFF;
490+ writePos += 4;
491+ read += 3;
492+ }
493+ }
494+ }
495+
496+ return image;
497+ }
498+ catch (stdext::exception& e) {
499+ g_logger.error(stdext::format("Failed to get sprite id %d: %s", id, e.what()));
500+ return nullptr;
501+ }
502+}
503+
504+ImagePtr SpriteManager::getSpriteImageHd(int id)
505+{
506+ if (id == 0 || !m_loaded)
507+ return nullptr;
508+
509+ if (m_cachedData.find(id) == m_cachedData.end())
510+ {
511+ return nullptr;
512+ }
513+
514+ try {
515+ return Image::loadPNG(m_cachedData[id].data(), m_cachedData[id].size());
516+ } catch (...) {}
517+ return nullptr;
518+}
A RD_Client/src/client/spritemanager.h +77-0 View file
@@ -0,0 +1,77 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef SPRITEMANAGER_H
24+#define SPRITEMANAGER_H
25+
26+#include "const.h"
27+#include <framework/core/declarations.h>
28+#include <framework/graphics/declarations.h>
29+
30+//@bindsingleton g_sprites
31+class SpriteManager
32+{
33+public:
34+ SpriteManager();
35+
36+ void terminate();
37+
38+ bool loadSpr(std::string file);
39+ void unload();
40+
41+#ifdef WITH_ENCRYPTION
42+ void saveSpr(std::string fileName);
43+ void saveSpr64(std::string fileName);
44+ void encryptSprites(std::string fileName);
45+ void dumpSprites(std::string dir);
46+#endif
47+
48+ uint32 getSignature() { return m_signature; }
49+ int getSpritesCount() { return m_spritesCount; }
50+
51+ ImagePtr getSpriteImage(int id);
52+ bool isLoaded() { return m_loaded; }
53+
54+ int spriteSize() { return m_spriteSize; }
55+ float getOffsetFactor() const { return static_cast<float>(m_spriteSize) / 32.0f; }
56+ bool isHdMod() const { return m_isHdMod; }
57+
58+private:
59+ bool loadCasualSpr(std::string file);
60+ bool loadCwmSpr(std::string file);
61+
62+ ImagePtr getSpriteImageCasual(int id);
63+ ImagePtr getSpriteImageHd(int id);
64+ bool m_loaded = false;
65+ bool m_isHdMod = false;
66+ uint32 m_signature;
67+ int m_spritesCount;
68+ int m_spritesOffset;
69+ int m_spriteSize;
70+ FileStreamPtr m_spritesFile;
71+ std::vector<std::vector<uint8_t>> m_sprites;
72+ std::unordered_map<uint32, std::string> m_cachedData;
73+};
74+
75+extern SpriteManager g_sprites;
76+
77+#endif
A RD_Client/src/client/statictext.cpp +169-0 View file
@@ -0,0 +1,169 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "statictext.h"
24+#include "map.h"
25+#include "spritemanager.h"
26+#include <framework/core/clock.h>
27+#include <framework/core/eventdispatcher.h>
28+#include <framework/graphics/graphics.h>
29+#include <framework/graphics/fontmanager.h>
30+
31+StaticText::StaticText()
32+{
33+ m_mode = Otc::MessageNone;
34+ m_color = Color::white;
35+ m_cachedText.setFont(g_fonts.getFont("verdana-11px-rounded"));
36+ m_cachedText.setAlign(Fw::AlignCenter);
37+}
38+
39+void StaticText::drawText(const Point& dest, const Rect& parentRect)
40+{
41+ Size textSize = m_cachedText.getTextSize();
42+ Rect rect = Rect(dest - Point(textSize.width() / 2, textSize.height()) + Point(20, 5), textSize);
43+ Rect boundRect = rect;
44+ boundRect.bind(parentRect);
45+
46+ // draw only if the real center is not too far from the parent center, or its a yell
47+ //if(g_map.isAwareOfPosition(m_position) || isYell()) {
48+ m_cachedText.draw(boundRect, m_color);
49+ //}
50+}
51+
52+void StaticText::setFont(const std::string& fontName)
53+{
54+ m_cachedText.setFont(g_fonts.getFont(fontName));
55+}
56+
57+void StaticText::setText(const std::string& text)
58+{
59+ m_cachedText.setText(text);
60+}
61+
62+bool StaticText::addMessage(const std::string& name, Otc::MessageMode mode, const std::string& text)
63+{
64+ return addColoredMessage(name, mode, { text, "" });
65+}
66+
67+bool StaticText::addColoredMessage(const std::string& name, Otc::MessageMode mode, const std::vector<std::string>& texts)
68+{
69+ if (texts.empty() || texts.size() % 2 != 0)
70+ return false;
71+ //TODO: this could be moved to lua
72+ // first message
73+ if (m_messages.size() == 0) {
74+ m_name = name;
75+ m_mode = mode;
76+ }
77+ // check if we can really own the message
78+ else if (m_name != name || m_mode != mode) {
79+ return false;
80+ }
81+ // too many messages
82+ else if (m_messages.size() > 10) {
83+ m_messages.pop_front();
84+ m_updateEvent->cancel();
85+ m_updateEvent = nullptr;
86+ }
87+
88+ size_t len = 0;
89+ for (size_t i = 0; i < texts.size(); i += 2) {
90+ len += texts[i].length();
91+ }
92+
93+ int delay = std::max<int>(Otc::STATIC_DURATION_PER_CHARACTER * len, Otc::MIN_STATIC_TEXT_DURATION);
94+ if (isYell())
95+ delay *= 2;
96+
97+ m_messages.push_back(StaticTextMessage{ texts, g_clock.millis() + delay });
98+ compose();
99+
100+ if (!m_updateEvent)
101+ scheduleUpdate();
102+ return true;
103+}
104+
105+void StaticText::update()
106+{
107+ m_messages.pop_front();
108+ if(m_messages.empty()) {
109+ // schedule removal
110+ auto self = asStaticText();
111+ g_dispatcher.addEvent([self]() { g_map.removeThing(self); });
112+ } else {
113+ compose();
114+ scheduleUpdate();
115+ }
116+}
117+
118+void StaticText::scheduleUpdate()
119+{
120+ int delay = std::max<int>(m_messages.front().time - g_clock.millis(), 0);
121+
122+ auto self = asStaticText();
123+ m_updateEvent = g_dispatcher.scheduleEvent([self]() {
124+ self->m_updateEvent = nullptr;
125+ self->update();
126+ }, delay);
127+}
128+
129+void StaticText::compose()
130+{
131+ //TODO: this could be moved to lua
132+ std::vector<std::string> texts;
133+
134+ if(m_mode == Otc::MessageSay) {
135+ texts.push_back(m_name + " says:\n");
136+ texts.push_back("#EFEF00");
137+ m_color = Color(239, 239, 0);
138+ } else if(m_mode == Otc::MessageWhisper) {
139+ texts.push_back(m_name + " whispers:\n");
140+ texts.push_back("#EFEF00");
141+ m_color = Color(239, 239, 0);
142+ } else if(m_mode == Otc::MessageYell) {
143+ texts.push_back(m_name + " yells:\n");
144+ texts.push_back("#EFEF00");
145+ m_color = Color(239, 239, 0);
146+ } else if(m_mode == Otc::MessageMonsterSay || m_mode == Otc::MessageMonsterYell || m_mode == Otc::MessageSpell
147+ || m_mode == Otc::MessageBarkLow || m_mode == Otc::MessageBarkLoud) {
148+ m_color = Color(254, 101, 0);
149+ } else if(m_mode == Otc::MessageNpcFrom || m_mode == Otc::MessageNpcFromStartBlock) {
150+ texts.push_back(m_name + " says:\n");
151+ texts.push_back("#5FF7F7");
152+ m_color = Color(95, 247, 247);
153+ } else {
154+ g_logger.warning(stdext::format("Unknown speak type: %d", m_mode));
155+ }
156+
157+ for(uint i = 0; i < m_messages.size(); ++i) {
158+ for (size_t j = 0; j < m_messages[i].texts.size() - 1; j += 2) {
159+ texts.push_back(m_messages[i].texts[j]);
160+ texts.push_back(m_messages[i].texts[j + 1].empty() ? m_color.toHex() : m_messages[i].texts[j + 1]);
161+ }
162+ if (texts.size() >= 2 && i < m_messages.size() - 1) {
163+ texts[texts.size() - 2] += "\n";
164+ }
165+ }
166+
167+ m_cachedText.setColoredText(texts);
168+ m_cachedText.wrapText(275);
169+}
A RD_Client/src/client/statictext.h +77-0 View file
@@ -0,0 +1,77 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef STATICTEXT_H
24+#define STATICTEXT_H
25+
26+#include "thing.h"
27+#include <framework/graphics/cachedtext.h>
28+#include <framework/core/timer.h>
29+
30+struct StaticTextMessage {
31+ std::vector<std::string> texts;
32+ ticks_t time;
33+};
34+
35+// @bindclass
36+class StaticText : public Thing
37+{
38+public:
39+ StaticText();
40+
41+ void drawText(const Point& dest, const Rect& parentRect);
42+
43+ std::string getName() { return m_name; }
44+ std::string getText() { return m_cachedText.getText(); }
45+ Otc::MessageMode getMessageMode() { return m_mode; }
46+ std::vector<std::string> getFirstMessage() { return m_messages[0].texts; }
47+
48+ bool isYell() { return m_mode == Otc::MessageYell || m_mode == Otc::MessageMonsterYell || m_mode == Otc::MessageBarkLoud; }
49+
50+ void setText(const std::string& text);
51+ void setFont(const std::string& fontName);
52+ bool addMessage(const std::string& name, Otc::MessageMode mode, const std::string& text);
53+ bool addColoredMessage(const std::string& name, Otc::MessageMode mode, const std::vector<std::string>& texts);
54+ StaticTextPtr asStaticText() { return static_self_cast<StaticText>(); }
55+ bool isStaticText() { return true; }
56+
57+ void setColor(const Color& color) { m_color = color; }
58+ Color getColor() { return m_color; }
59+
60+ CachedText& getCachedText() { return m_cachedText; }
61+ bool hasText() { return m_cachedText.hasText(); }
62+
63+private:
64+ void update();
65+ void scheduleUpdate();
66+ void compose();
67+
68+ stdext::boolean<false> m_yell;
69+ std::deque<StaticTextMessage> m_messages;
70+ std::string m_name;
71+ Otc::MessageMode m_mode;
72+ Color m_color;
73+ CachedText m_cachedText;
74+ ScheduledEventPtr m_updateEvent;
75+};
76+
77+#endif
A RD_Client/src/client/thing.cpp +114-0 View file
@@ -0,0 +1,114 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "thing.h"
24+#include "spritemanager.h"
25+#include "thingtypemanager.h"
26+#include <framework/graphics/graphics.h>
27+#include "map.h"
28+#include "tile.h"
29+#include "game.h"
30+#include <framework/util/stats.h>
31+
32+Thing::Thing() :
33+ m_datId(0)
34+{
35+ g_stats.addThing();
36+}
37+
38+Thing::~Thing()
39+{
40+ g_stats.removeThing();
41+}
42+
43+void Thing::setPosition(const Position& position)
44+{
45+ if(m_position == position)
46+ return;
47+
48+ Position oldPos = m_position;
49+ m_position = position;
50+ onPositionChange(position, oldPos);
51+}
52+
53+int Thing::getStackPriority()
54+{
55+ // bug fix for old versions
56+ if (g_game.getClientVersion() <= 800 && isSplash()) {
57+ return 1;
58+ }
59+ if(isGround())
60+ return 0;
61+ else if(isGroundBorder())
62+ return 1;
63+ else if(isOnBottom())
64+ return 2;
65+ else if(isOnTop())
66+ return 3;
67+ else if(isCreature())
68+ return 4;
69+ else // common items
70+ return 5;
71+}
72+
73+const TilePtr& Thing::getTile()
74+{
75+ return g_map.getTile(m_position);
76+}
77+
78+ContainerPtr Thing::getParentContainer()
79+{
80+ if(m_position.x == 0xffff && m_position.y & 0x40) {
81+ int containerId = m_position.y ^ 0x40;
82+ return g_game.getContainer(containerId);
83+ }
84+ return nullptr;
85+}
86+
87+int Thing::getStackPos()
88+{
89+ if(m_position.x == 65535 && isItem()) // is inside a container
90+ return m_position.z;
91+ else if(const TilePtr& tile = getTile())
92+ return tile->getThingStackPos(static_self_cast<Thing>());
93+ else {
94+ g_logger.traceError("got a thing with invalid stackpos");
95+ return -1;
96+ }
97+}
98+
99+const ThingTypePtr& Thing::getThingType()
100+{
101+ return g_things.getNullThingType();
102+}
103+
104+ThingType* Thing::rawGetThingType()
105+{
106+ return g_things.getNullThingType().get();
107+}
108+
109+Color Thing::updatedMarkedColor() {
110+ if (!m_marked)
111+ return Color::white;
112+ m_markedColor.setAlpha(0.1f + std::abs(500 - g_clock.millis() % 1000) / 1000.0f); // 0.1-0.6
113+ return m_markedColor;
114+}
A RD_Client/src/client/thing.h +157-0 View file
@@ -0,0 +1,157 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef THING_H
24+#define THING_H
25+
26+#include "declarations.h"
27+#include "thingtype.h"
28+#include "thingtypemanager.h"
29+#include <framework/luaengine/luaobject.h>
30+#include <framework/graphics/drawqueue.h>
31+
32+// @bindclass
33+#pragma pack(push,1) // disable memory alignment
34+class Thing : public LuaObject
35+{
36+public:
37+ Thing();
38+ virtual ~Thing();
39+
40+ virtual void draw(const Point& dest, bool animate = true, LightView* lightView = nullptr) { }
41+
42+ virtual void setId(uint32 id) { }
43+ void setPosition(const Position& position);
44+
45+ virtual uint32 getId() { return 0; }
46+ Position getPosition() { return m_position; }
47+ int getStackPriority();
48+ virtual const TilePtr& getTile();
49+ ContainerPtr getParentContainer();
50+ int getStackPos();
51+
52+ void setMarked(const std::string& color) {
53+ if (color.empty()) {
54+ m_marked = false;
55+ return;
56+ }
57+ m_marked = true;
58+ m_markedColor = Color(color);
59+ }
60+ Color updatedMarkedColor();
61+
62+ virtual bool isItem() { return false; }
63+ virtual bool isEffect() { return false; }
64+ virtual bool isMissile() { return false; }
65+ virtual bool isCreature() { return false; }
66+ virtual bool isNpc() { return false; }
67+ virtual bool isMonster() { return false; }
68+ virtual bool isPlayer() { return false; }
69+ virtual bool isLocalPlayer() { return false; }
70+ virtual bool isAnimatedText() { return false; }
71+ virtual bool isStaticText() { return false; }
72+
73+ // type shortcuts
74+ virtual const ThingTypePtr& getThingType();
75+ virtual ThingType *rawGetThingType();
76+ Size getSize() { return rawGetThingType()->getSize(); }
77+ int getWidth() { return rawGetThingType()->getWidth(); }
78+ int getHeight() { return rawGetThingType()->getHeight(); }
79+ virtual Point getDisplacement() { return rawGetThingType()->getDisplacement(); }
80+ virtual int getDisplacementX() { return rawGetThingType()->getDisplacementX(); }
81+ virtual int getDisplacementY() { return rawGetThingType()->getDisplacementY(); }
82+ virtual int getExactSize(int layer, int xPattern, int yPattern, int zPattern, int animationPhase) { return rawGetThingType()->getExactSize(layer, xPattern, yPattern, zPattern, animationPhase); }
83+ int getLayers() { return rawGetThingType()->getLayers(); }
84+ int getNumPatternX() { return rawGetThingType()->getNumPatternX(); }
85+ int getNumPatternY() { return rawGetThingType()->getNumPatternY(); }
86+ int getNumPatternZ() { return rawGetThingType()->getNumPatternZ(); }
87+ int getAnimationPhases() { return rawGetThingType()->getAnimationPhases(); }
88+ AnimatorPtr getAnimator() { return rawGetThingType()->getAnimator(); }
89+ AnimatorPtr getIdleAnimator() { return rawGetThingType()->getIdleAnimator(); }
90+ int getGroundSpeed() { return rawGetThingType()->getGroundSpeed(); }
91+ int getMaxTextLength() { return rawGetThingType()->getMaxTextLength(); }
92+ Light getLight() { return rawGetThingType()->getLight(); }
93+ int getMinimapColor() { return rawGetThingType()->getMinimapColor(); }
94+ int getLensHelp() { return rawGetThingType()->getLensHelp(); }
95+ int getClothSlot() { return rawGetThingType()->getClothSlot(); }
96+ int getElevation() { return rawGetThingType()->getElevation(); }
97+ bool isGround() { return rawGetThingType()->isGround(); }
98+ bool isGroundBorder() { return rawGetThingType()->isGroundBorder(); }
99+ bool isOnBottom() { return rawGetThingType()->isOnBottom(); }
100+ bool isOnTop() { return rawGetThingType()->isOnTop(); }
101+ bool isContainer() { return rawGetThingType()->isContainer(); }
102+ bool isStackable() { return rawGetThingType()->isStackable(); }
103+ bool isForceUse() { return rawGetThingType()->isForceUse(); }
104+ bool isMultiUse() { return rawGetThingType()->isMultiUse(); }
105+ bool isWritable() { return rawGetThingType()->isWritable(); }
106+ bool isChargeable() { return rawGetThingType()->isChargeable(); }
107+ bool isWritableOnce() { return rawGetThingType()->isWritableOnce(); }
108+ bool isFluidContainer() { return rawGetThingType()->isFluidContainer(); }
109+ bool isSplash() { return rawGetThingType()->isSplash(); }
110+ bool isNotWalkable() { return rawGetThingType()->isNotWalkable(); }
111+ bool isNotMoveable() { return rawGetThingType()->isNotMoveable(); }
112+ bool blockProjectile() { return rawGetThingType()->blockProjectile(); }
113+ bool isNotPathable() { return rawGetThingType()->isNotPathable(); }
114+ bool isPickupable() { return rawGetThingType()->isPickupable(); }
115+ bool isHangable() { return rawGetThingType()->isHangable(); }
116+ bool isHookSouth() { return rawGetThingType()->isHookSouth(); }
117+ bool isHookEast() { return rawGetThingType()->isHookEast(); }
118+ bool isRotateable() { return rawGetThingType()->isRotateable(); }
119+ bool hasLight() { return rawGetThingType()->hasLight(); }
120+ bool isDontHide() { return rawGetThingType()->isDontHide(); }
121+ bool isTranslucent() { return rawGetThingType()->isTranslucent(); }
122+ bool hasDisplacement() { return rawGetThingType()->hasDisplacement(); }
123+ bool hasElevation() { return rawGetThingType()->hasElevation(); }
124+ bool isLyingCorpse() { return rawGetThingType()->isLyingCorpse(); }
125+ bool isAnimateAlways() { return rawGetThingType()->isAnimateAlways(); }
126+ bool hasMiniMapColor() { return rawGetThingType()->hasMiniMapColor(); }
127+ bool hasLensHelp() { return rawGetThingType()->hasLensHelp(); }
128+ bool isFullGround() { return rawGetThingType()->isFullGround(); }
129+ bool isIgnoreLook() { return rawGetThingType()->isIgnoreLook(); }
130+ bool isCloth() { return rawGetThingType()->isCloth(); }
131+ bool isMarketable() { return rawGetThingType()->isMarketable(); }
132+ bool isUsable() { return rawGetThingType()->isUsable(); }
133+ bool isWrapable() { return rawGetThingType()->isWrapable(); }
134+ bool isUnwrapable() { return rawGetThingType()->isUnwrapable(); }
135+ bool isTopEffect() { return rawGetThingType()->isTopEffect(); }
136+ MarketData getMarketData() { return rawGetThingType()->getMarketData(); }
137+
138+ void hide() { m_hidden = true; }
139+ void show() { m_hidden = false; }
140+ void setHidden(bool value) { m_hidden = value; }
141+ bool isHidden() { return m_hidden; }
142+
143+ virtual void onPositionChange(const Position& newPos, const Position& oldPos) { }
144+ virtual void onAppear() { }
145+ virtual void onDisappear() { }
146+
147+protected:
148+ Position m_position;
149+ uint16 m_datId;
150+ bool m_marked = false;
151+ bool m_hidden = false;
152+ Color m_markedColor;
153+};
154+#pragma pack(pop)
155+
156+#endif
157+
A RD_Client/src/client/thingstype.h +67-0 View file
@@ -0,0 +1,67 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef DATMANAGER_H
24+#define DATMANAGER_H
25+
26+#include <framework/global.h>
27+#include <framework/core/declarations.h>
28+#include "thingtype.h"
29+
30+//@bindsingleton g_thingsType
31+class ThingsType
32+{
33+public:
34+
35+ enum Categories {
36+ Item = 0,
37+ Creature,
38+ Effect,
39+ Missile,
40+ LastCategory
41+ };
42+
43+ bool load(const std::string& file);
44+ void unload();
45+
46+ bool parseThingType(const FileStreamPtr& fin, ThingType& thingType);
47+
48+ ThingType *getEmptyThingType() { return &m_emptyThingType; }
49+ ThingType *getThingType(uint16 id, Categories category);
50+
51+ uint32 getSignature() { return m_signature; }
52+ bool isLoaded() { return m_loaded; }
53+
54+ uint16 getFirstItemId() { return 100; }
55+ uint16 getMaxItemid() { return m_things[Item].size() + 99; }
56+ bool isValidItemId(int id) { return id >= getFirstItemId() && id <= getMaxItemid(); }
57+
58+private:
59+ uint32 m_signature;
60+ stdext::boolean<false> m_loaded;
61+ ThingTypeList m_things[LastCategory];
62+ static ThingType m_emptyThingType;
63+};
64+
65+extern ThingsType g_thingsType;
66+
67+#endif
Top