No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

thingtype.cpp 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  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. #include "thingtype.h"
  23. #include "spritemanager.h"
  24. #include "game.h"
  25. #include "lightview.h"
  26. #include <framework/graphics/graphics.h>
  27. #include <framework/graphics/texture.h>
  28. #include <framework/graphics/image.h>
  29. #include <framework/graphics/texturemanager.h>
  30. #include <framework/core/filestream.h>
  31. #include <framework/otml/otml.h>
  32. ThingType::ThingType()
  33. {
  34. m_category = ThingInvalidCategory;
  35. m_id = 0;
  36. m_null = true;
  37. m_exactSize = 0;
  38. m_realSize = 0;
  39. m_animator = nullptr;
  40. m_numPatternX = m_numPatternY = m_numPatternZ = 0;
  41. m_animationPhases = 0;
  42. m_layers = 0;
  43. m_elevation = 0;
  44. m_opacity = 1.0f;
  45. }
  46. void ThingType::serialize(const FileStreamPtr& fin)
  47. {
  48. for(int i = 0; i < ThingLastAttr; ++i) {
  49. if(!hasAttr((ThingAttr)i))
  50. continue;
  51. int attr = i;
  52. if(g_game.getClientVersion() >= 780) {
  53. if(attr == ThingAttrChargeable)
  54. attr = ThingAttrWritable;
  55. else if(attr >= ThingAttrWritable)
  56. attr += 1;
  57. } else if(g_game.getClientVersion() >= 1000) {
  58. if(attr == ThingAttrNoMoveAnimation)
  59. attr = 16;
  60. else if(attr >= ThingAttrPickupable)
  61. attr += 1;
  62. }
  63. fin->addU8(attr);
  64. switch(attr) {
  65. case ThingAttrDisplacement: {
  66. fin->addU16(m_displacement.x);
  67. fin->addU16(m_displacement.y);
  68. break;
  69. }
  70. case ThingAttrLight: {
  71. Light light = m_attribs.get<Light>(attr);
  72. fin->addU16(light.intensity);
  73. fin->addU16(light.color);
  74. break;
  75. }
  76. case ThingAttrMarket: {
  77. MarketData market = m_attribs.get<MarketData>(attr);
  78. fin->addU16(market.category);
  79. fin->addU16(market.tradeAs);
  80. fin->addU16(market.showAs);
  81. fin->addString(market.name);
  82. fin->addU16(market.restrictVocation);
  83. fin->addU16(market.requiredLevel);
  84. break;
  85. }
  86. case ThingAttrUsable:
  87. case ThingAttrElevation:
  88. case ThingAttrGround:
  89. case ThingAttrWritable:
  90. case ThingAttrWritableOnce:
  91. case ThingAttrMinimapColor:
  92. case ThingAttrCloth:
  93. case ThingAttrLensHelp:
  94. fin->addU16(m_attribs.get<uint16>(attr));
  95. break;
  96. default:
  97. break;
  98. };
  99. }
  100. fin->addU8(ThingLastAttr);
  101. fin->addU8(m_size.width());
  102. fin->addU8(m_size.height());
  103. if(m_size.width() > 1 || m_size.height() > 1)
  104. fin->addU8(m_realSize);
  105. fin->addU8(m_layers);
  106. fin->addU8(m_numPatternX);
  107. fin->addU8(m_numPatternY);
  108. fin->addU8(m_numPatternZ);
  109. fin->addU8(m_animationPhases);
  110. if(g_game.getFeature(Otc::GameEnhancedAnimations)) {
  111. if(m_animationPhases > 1 && m_animator != nullptr) {
  112. m_animator->serialize(fin);
  113. }
  114. }
  115. for(uint i = 0; i < m_spritesIndex.size(); i++) {
  116. if(g_game.getFeature(Otc::GameSpritesU32))
  117. fin->addU32(m_spritesIndex[i]);
  118. else
  119. fin->addU16(m_spritesIndex[i]);
  120. }
  121. }
  122. void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileStreamPtr& fin)
  123. {
  124. m_null = false;
  125. m_id = clientId;
  126. m_category = category;
  127. int count = 0, attr = -1;
  128. bool done = false;
  129. for(int i = 0 ; i < ThingLastAttr;++i) {
  130. count++;
  131. attr = fin->getU8();
  132. if(attr == ThingLastAttr) {
  133. done = true;
  134. break;
  135. }
  136. if(g_game.getClientVersion() >= 1000) {
  137. /* In 10.10+ all attributes from 16 and up were
  138. * incremented by 1 to make space for 16 as
  139. * "No Movement Animation" flag.
  140. */
  141. if(attr == 16)
  142. attr = ThingAttrNoMoveAnimation;
  143. else if(attr > 16)
  144. attr -= 1;
  145. } else if(g_game.getClientVersion() >= 860) {
  146. /* Default attribute values follow
  147. * the format of 8.6-9.86.
  148. * Therefore no changes here.
  149. */
  150. } else if(g_game.getClientVersion() >= 780) {
  151. /* In 7.80-8.54 all attributes from 8 and higher were
  152. * incremented by 1 to make space for 8 as
  153. * "Item Charges" flag.
  154. */
  155. if(attr == 8) {
  156. m_attribs.set(ThingAttrChargeable, true);
  157. continue;
  158. } else if(attr > 8)
  159. attr -= 1;
  160. } else if(g_game.getClientVersion() >= 755) {
  161. /* In 7.55-7.72 attributes 23 is "Floor Change". */
  162. if(attr == 23)
  163. attr = ThingAttrFloorChange;
  164. } else if(g_game.getClientVersion() >= 740) {
  165. /* In 7.4-7.5 attribute "Ground Border" did not exist
  166. * attributes 1-15 have to be adjusted.
  167. * Several other changes in the format.
  168. */
  169. if(attr > 0 && attr <= 15)
  170. attr += 1;
  171. else if(attr == 16)
  172. attr = ThingAttrLight;
  173. else if(attr == 17)
  174. attr = ThingAttrFloorChange;
  175. else if(attr == 18)
  176. attr = ThingAttrFullGround;
  177. else if(attr == 19)
  178. attr = ThingAttrElevation;
  179. else if(attr == 20)
  180. attr = ThingAttrDisplacement;
  181. else if(attr == 22)
  182. attr = ThingAttrMinimapColor;
  183. else if(attr == 23)
  184. attr = ThingAttrRotateable;
  185. else if(attr == 24)
  186. attr = ThingAttrLyingCorpse;
  187. else if(attr == 25)
  188. attr = ThingAttrHangable;
  189. else if(attr == 26)
  190. attr = ThingAttrHookSouth;
  191. else if(attr == 27)
  192. attr = ThingAttrHookEast;
  193. else if(attr == 28)
  194. attr = ThingAttrAnimateAlways;
  195. /* "Multi Use" and "Force Use" are swapped */
  196. if(attr == ThingAttrMultiUse)
  197. attr = ThingAttrForceUse;
  198. else if(attr == ThingAttrForceUse)
  199. attr = ThingAttrMultiUse;
  200. }
  201. switch(attr) {
  202. case ThingAttrDisplacement: {
  203. if(g_game.getClientVersion() >= 755) {
  204. m_displacement.x = fin->getU16();
  205. m_displacement.y = fin->getU16();
  206. } else {
  207. m_displacement.x = 8;
  208. m_displacement.y = 8;
  209. }
  210. m_attribs.set(attr, true);
  211. break;
  212. }
  213. case ThingAttrLight: {
  214. Light light;
  215. light.intensity = fin->getU16();
  216. light.color = fin->getU16();
  217. m_attribs.set(attr, light);
  218. break;
  219. }
  220. case ThingAttrMarket: {
  221. MarketData market;
  222. market.category = fin->getU16();
  223. market.tradeAs = fin->getU16();
  224. market.showAs = fin->getU16();
  225. market.name = fin->getString();
  226. market.restrictVocation = fin->getU16();
  227. market.requiredLevel = fin->getU16();
  228. m_attribs.set(attr, market);
  229. break;
  230. }
  231. case ThingAttrElevation: {
  232. m_elevation = fin->getU16();
  233. m_attribs.set(attr, m_elevation);
  234. break;
  235. }
  236. case ThingAttrUsable:
  237. case ThingAttrGround:
  238. case ThingAttrWritable:
  239. case ThingAttrWritableOnce:
  240. case ThingAttrMinimapColor:
  241. case ThingAttrCloth:
  242. case ThingAttrLensHelp:
  243. m_attribs.set(attr, fin->getU16());
  244. break;
  245. default:
  246. m_attribs.set(attr, true);
  247. break;
  248. };
  249. }
  250. if(!done)
  251. stdext::throw_exception(stdext::format("corrupt data (id: %d, category: %d, count: %d, lastAttr: %d)",
  252. m_id, m_category, count, attr));
  253. bool hasFrameGroups = (category == ThingCategoryCreature && g_game.getFeature(Otc::GameIdleAnimations));
  254. uint8 groupCount = hasFrameGroups ? fin->getU8() : 1;
  255. m_animationPhases = 0;
  256. int totalSpritesCount = 0;
  257. for(int i = 0; i < groupCount; ++i) {
  258. uint8 frameGroupType = FrameGroupDefault;
  259. if(hasFrameGroups)
  260. frameGroupType = fin->getU8();
  261. uint8 width = fin->getU8();
  262. uint8 height = fin->getU8();
  263. m_size = Size(width, height);
  264. if(width > 1 || height > 1) {
  265. m_realSize = fin->getU8();
  266. m_exactSize = std::min<int>(m_realSize, std::max<int>(width * 32, height * 32));
  267. }
  268. else
  269. m_exactSize = 32;
  270. m_layers = fin->getU8();
  271. m_numPatternX = fin->getU8();
  272. m_numPatternY = fin->getU8();
  273. if(g_game.getClientVersion() >= 755)
  274. m_numPatternZ = fin->getU8();
  275. else
  276. m_numPatternZ = 1;
  277. int groupAnimationsPhases = fin->getU8();
  278. m_animationPhases += groupAnimationsPhases;
  279. if(groupAnimationsPhases > 1 && g_game.getFeature(Otc::GameEnhancedAnimations)) {
  280. m_animator = AnimatorPtr(new Animator);
  281. m_animator->unserialize(groupAnimationsPhases, fin);
  282. }
  283. int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * groupAnimationsPhases;
  284. if((totalSpritesCount+totalSprites) > 4096)
  285. stdext::throw_exception("a thing type has more than 4096 sprites");
  286. m_spritesIndex.resize((totalSpritesCount+totalSprites));
  287. for(int i = totalSpritesCount; i < (totalSpritesCount+totalSprites); i++)
  288. m_spritesIndex[i] = g_game.getFeature(Otc::GameSpritesU32) ? fin->getU32() : fin->getU16();
  289. totalSpritesCount += totalSprites;
  290. }
  291. m_textures.resize(m_animationPhases);
  292. m_texturesFramesRects.resize(m_animationPhases);
  293. m_texturesFramesOriginRects.resize(m_animationPhases);
  294. m_texturesFramesOffsets.resize(m_animationPhases);
  295. }
  296. void ThingType::exportImage(std::string fileName)
  297. {
  298. if(m_null)
  299. stdext::throw_exception("cannot export null thingtype");
  300. if(m_spritesIndex.size() == 0)
  301. stdext::throw_exception("cannot export thingtype without sprites");
  302. ImagePtr image(new Image(Size(32 * m_size.width() * m_layers * m_numPatternX, 32 * m_size.height() * m_animationPhases * m_numPatternY * m_numPatternZ)));
  303. for(int z = 0; z < m_numPatternZ; ++z) {
  304. for(int y = 0; y < m_numPatternY; ++y) {
  305. for(int x = 0; x < m_numPatternX; ++x) {
  306. for(int l = 0; l < m_layers; ++l) {
  307. for(int a = 0; a < m_animationPhases; ++a) {
  308. for(int w = 0; w < m_size.width(); ++w) {
  309. for(int h = 0; h < m_size.height(); ++h) {
  310. image->blit(Point(32 * (m_size.width() - w - 1 + m_size.width() * x + m_size.width() * m_numPatternX * l),
  311. 32 * (m_size.height() - h - 1 + m_size.height() * y + m_size.height() * m_numPatternY * a + m_size.height() * m_numPatternY * m_animationPhases * z)),
  312. g_sprites.getSpriteImage(m_spritesIndex[getSpriteIndex(w, h, l, x, y, z, a)]));
  313. }
  314. }
  315. }
  316. }
  317. }
  318. }
  319. }
  320. image->savePNG(fileName);
  321. }
  322. void ThingType::unserializeOtml(const OTMLNodePtr& node)
  323. {
  324. for(const OTMLNodePtr& node2 : node->children()) {
  325. if(node2->tag() == "opacity")
  326. m_opacity = node2->value<float>();
  327. else if(node2->tag() == "notprewalkable")
  328. m_attribs.set(ThingAttrNotPreWalkable, node2->value<bool>());
  329. else if(node2->tag() == "image")
  330. m_customImage = node2->value();
  331. else if(node2->tag() == "full-ground") {
  332. if(node2->value<bool>())
  333. m_attribs.set(ThingAttrFullGround, true);
  334. else
  335. m_attribs.remove(ThingAttrFullGround);
  336. }
  337. }
  338. }
  339. void ThingType::draw(const Point& dest, float scaleFactor, int layer, int xPattern, int yPattern, int zPattern, int animationPhase, LightView *lightView)
  340. {
  341. if(m_null)
  342. return;
  343. if(animationPhase >= m_animationPhases)
  344. return;
  345. const TexturePtr& texture = getTexture(animationPhase); // texture might not exists, neither its rects.
  346. if(!texture)
  347. return;
  348. uint frameIndex = getTextureIndex(layer, xPattern, yPattern, zPattern);
  349. if(frameIndex >= m_texturesFramesRects[animationPhase].size())
  350. return;
  351. Point textureOffset;
  352. Rect textureRect;
  353. if(scaleFactor != 1.0f) {
  354. textureRect = m_texturesFramesOriginRects[animationPhase][frameIndex];
  355. } else {
  356. textureOffset = m_texturesFramesOffsets[animationPhase][frameIndex];
  357. textureRect = m_texturesFramesRects[animationPhase][frameIndex];
  358. }
  359. Rect screenRect(dest + (textureOffset - m_displacement - (m_size.toPoint() - Point(1, 1)) * 32) * scaleFactor,
  360. textureRect.size() * scaleFactor);
  361. bool useOpacity = m_opacity < 1.0f;
  362. if(useOpacity)
  363. g_painter->setColor(Color(1.0f,1.0f,1.0f,m_opacity));
  364. g_painter->drawTexturedRect(screenRect, texture, textureRect);
  365. if(useOpacity)
  366. g_painter->setColor(Color::white);
  367. if(lightView && hasLight()) {
  368. Light light = getLight();
  369. if(light.intensity > 0)
  370. lightView->addLightSource(screenRect.center(), scaleFactor, light);
  371. }
  372. }
  373. const TexturePtr& ThingType::getTexture(int animationPhase)
  374. {
  375. TexturePtr& animationPhaseTexture = m_textures[animationPhase];
  376. if(!animationPhaseTexture) {
  377. bool useCustomImage = false;
  378. if(animationPhase == 0 && !m_customImage.empty())
  379. useCustomImage = true;
  380. // we don't need layers in common items, they will be pre-drawn
  381. int textureLayers = 1;
  382. int numLayers = m_layers;
  383. if(m_category == ThingCategoryCreature && numLayers >= 2) {
  384. // 5 layers: outfit base, red mask, green mask, blue mask, yellow mask
  385. textureLayers = 5;
  386. numLayers = 5;
  387. }
  388. int indexSize = textureLayers * m_numPatternX * m_numPatternY * m_numPatternZ;
  389. Size textureSize = getBestTextureDimension(m_size.width(), m_size.height(), indexSize);
  390. ImagePtr fullImage;
  391. if(useCustomImage)
  392. fullImage = Image::load(m_customImage);
  393. else
  394. fullImage = ImagePtr(new Image(textureSize * Otc::TILE_PIXELS));
  395. m_texturesFramesRects[animationPhase].resize(indexSize);
  396. m_texturesFramesOriginRects[animationPhase].resize(indexSize);
  397. m_texturesFramesOffsets[animationPhase].resize(indexSize);
  398. for(int z = 0; z < m_numPatternZ; ++z) {
  399. for(int y = 0; y < m_numPatternY; ++y) {
  400. for(int x = 0; x < m_numPatternX; ++x) {
  401. for(int l = 0; l < numLayers; ++l) {
  402. bool spriteMask = (m_category == ThingCategoryCreature && l > 0);
  403. int frameIndex = getTextureIndex(l % textureLayers, x, y, z);
  404. Point framePos = Point(frameIndex % (textureSize.width() / m_size.width()) * m_size.width(),
  405. frameIndex / (textureSize.width() / m_size.width()) * m_size.height()) * Otc::TILE_PIXELS;
  406. if(!useCustomImage) {
  407. for(int h = 0; h < m_size.height(); ++h) {
  408. for(int w = 0; w < m_size.width(); ++w) {
  409. uint spriteIndex = getSpriteIndex(w, h, spriteMask ? 1 : l, x, y, z, animationPhase);
  410. ImagePtr spriteImage = g_sprites.getSpriteImage(m_spritesIndex[spriteIndex]);
  411. if(spriteImage) {
  412. if(spriteMask) {
  413. static Color maskColors[] = { Color::red, Color::green, Color::blue, Color::yellow };
  414. spriteImage->overwriteMask(maskColors[l - 1]);
  415. }
  416. Point spritePos = Point(m_size.width() - w - 1,
  417. m_size.height() - h - 1) * Otc::TILE_PIXELS;
  418. fullImage->blit(framePos + spritePos, spriteImage);
  419. }
  420. }
  421. }
  422. }
  423. Rect drawRect(framePos + Point(m_size.width(), m_size.height()) * Otc::TILE_PIXELS - Point(1,1), framePos);
  424. for(int x = framePos.x; x < framePos.x + m_size.width() * Otc::TILE_PIXELS; ++x) {
  425. for(int y = framePos.y; y < framePos.y + m_size.height() * Otc::TILE_PIXELS; ++y) {
  426. uint8 *p = fullImage->getPixel(x,y);
  427. if(p[3] != 0x00) {
  428. drawRect.setTop (std::min<int>(y, (int)drawRect.top()));
  429. drawRect.setLeft (std::min<int>(x, (int)drawRect.left()));
  430. drawRect.setBottom(std::max<int>(y, (int)drawRect.bottom()));
  431. drawRect.setRight (std::max<int>(x, (int)drawRect.right()));
  432. }
  433. }
  434. }
  435. m_texturesFramesRects[animationPhase][frameIndex] = drawRect;
  436. m_texturesFramesOriginRects[animationPhase][frameIndex] = Rect(framePos, Size(m_size.width(), m_size.height()) * Otc::TILE_PIXELS);
  437. m_texturesFramesOffsets[animationPhase][frameIndex] = drawRect.topLeft() - framePos;
  438. }
  439. }
  440. }
  441. }
  442. animationPhaseTexture = TexturePtr(new Texture(fullImage, true));
  443. animationPhaseTexture->setSmooth(true);
  444. }
  445. return animationPhaseTexture;
  446. }
  447. Size ThingType::getBestTextureDimension(int w, int h, int count)
  448. {
  449. const int MAX = 32;
  450. int k = 1;
  451. while(k < w)
  452. k<<=1;
  453. w = k;
  454. k = 1;
  455. while(k < h)
  456. k<<=1;
  457. h = k;
  458. int numSprites = w*h*count;
  459. assert(numSprites <= MAX*MAX);
  460. assert(w <= MAX);
  461. assert(h <= MAX);
  462. Size bestDimension = Size(MAX, MAX);
  463. for(int i=w;i<=MAX;i<<=1) {
  464. for(int j=h;j<=MAX;j<<=1) {
  465. Size candidateDimension = Size(i, j);
  466. if(candidateDimension.area() < numSprites)
  467. continue;
  468. if((candidateDimension.area() < bestDimension.area()) ||
  469. (candidateDimension.area() == bestDimension.area() && candidateDimension.width() + candidateDimension.height() < bestDimension.width() + bestDimension.height()))
  470. bestDimension = candidateDimension;
  471. }
  472. }
  473. return bestDimension;
  474. }
  475. uint ThingType::getSpriteIndex(int w, int h, int l, int x, int y, int z, int a) {
  476. uint index =
  477. ((((((a % m_animationPhases)
  478. * m_numPatternZ + z)
  479. * m_numPatternY + y)
  480. * m_numPatternX + x)
  481. * m_layers + l)
  482. * m_size.height() + h)
  483. * m_size.width() + w;
  484. assert(index < m_spritesIndex.size());
  485. return index;
  486. }
  487. uint ThingType::getTextureIndex(int l, int x, int y, int z) {
  488. return ((l * m_numPatternZ + z)
  489. * m_numPatternY + y)
  490. * m_numPatternX + x;
  491. }
  492. int ThingType::getExactSize(int layer, int xPattern, int yPattern, int zPattern, int animationPhase)
  493. {
  494. if(m_null)
  495. return 0;
  496. getTexture(animationPhase); // we must calculate it anyway.
  497. int frameIndex = getTextureIndex(layer, xPattern, yPattern, zPattern);
  498. Size size = m_texturesFramesOriginRects[animationPhase][frameIndex].size() - m_texturesFramesOffsets[animationPhase][frameIndex].toSize();
  499. return std::max<int>(size.width(), size.height());
  500. }
  501. void ThingType::setPathable(bool var)
  502. {
  503. if(var == true)
  504. m_attribs.remove(ThingAttrNotPathable);
  505. else
  506. m_attribs.set(ThingAttrNotPathable, true);
  507. }