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.

tile.cpp 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  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 "tile.h"
  23. #include "item.h"
  24. #include "thingtypemanager.h"
  25. #include "map.h"
  26. #include "game.h"
  27. #include "localplayer.h"
  28. #include "effect.h"
  29. #include "protocolgame.h"
  30. #include "lightview.h"
  31. #include <framework/graphics/fontmanager.h>
  32. Tile::Tile(const Position& position) :
  33. m_position(position),
  34. m_drawElevation(0),
  35. m_minimapColor(0),
  36. m_flags(0)
  37. {
  38. }
  39. void Tile::draw(const Point& dest, float scaleFactor, int drawFlags, LightView *lightView)
  40. {
  41. bool animate = drawFlags & Otc::DrawAnimations;
  42. /* Flags to be checked for. */
  43. static const tileflags_t flags[] = {
  44. TILESTATE_HOUSE,
  45. TILESTATE_PROTECTIONZONE,
  46. TILESTATE_OPTIONALZONE,
  47. TILESTATE_HARDCOREZONE,
  48. TILESTATE_REFRESH,
  49. TILESTATE_NOLOGOUT,
  50. TILESTATE_LAST
  51. };
  52. // first bottom items
  53. if(drawFlags & (Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawOnBottom)) {
  54. m_drawElevation = 0;
  55. for(const ThingPtr& thing : m_things) {
  56. if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom())
  57. break;
  58. bool restore = false;
  59. if(g_map.showZones() && thing->isGround()) {
  60. for(unsigned int i = 0; i < sizeof(flags) / sizeof(tileflags_t); ++i) {
  61. tileflags_t flag = flags[i];
  62. if(hasFlag(flag) && g_map.showZone(flag)) {
  63. g_painter->setOpacity(g_map.getZoneOpacity());
  64. g_painter->setColor(g_map.getZoneColor(flag));
  65. restore = true;
  66. break;
  67. }
  68. }
  69. }
  70. if(m_selected)
  71. g_painter->setColor(Color::teal);
  72. if((thing->isGround() && drawFlags & Otc::DrawGround) ||
  73. (thing->isGroundBorder() && drawFlags & Otc::DrawGroundBorders) ||
  74. (thing->isOnBottom() && drawFlags & Otc::DrawOnBottom)) {
  75. thing->draw(dest - m_drawElevation*scaleFactor, scaleFactor, animate, lightView);
  76. if(restore) {
  77. g_painter->resetOpacity();
  78. g_painter->resetColor();
  79. }
  80. }
  81. if(m_selected)
  82. g_painter->resetColor();
  83. m_drawElevation += thing->getElevation();
  84. if(m_drawElevation > Otc::MAX_ELEVATION)
  85. m_drawElevation = Otc::MAX_ELEVATION;
  86. }
  87. }
  88. int redrawPreviousTopW = 0;
  89. int redrawPreviousTopH = 0;
  90. if(drawFlags & Otc::DrawItems) {
  91. // now common items in reverse order
  92. for(auto it = m_things.rbegin(); it != m_things.rend(); ++it) {
  93. const ThingPtr& thing = *it;
  94. if(thing->isOnTop() || thing->isOnBottom() || thing->isGroundBorder() || thing->isGround() || thing->isCreature())
  95. break;
  96. thing->draw(dest - m_drawElevation*scaleFactor, scaleFactor, animate, lightView);
  97. if(thing->isLyingCorpse()) {
  98. redrawPreviousTopW = std::max<int>(thing->getWidth(), redrawPreviousTopW);
  99. redrawPreviousTopH = std::max<int>(thing->getHeight(), redrawPreviousTopH);
  100. }
  101. m_drawElevation += thing->getElevation();
  102. if(m_drawElevation > Otc::MAX_ELEVATION)
  103. m_drawElevation = Otc::MAX_ELEVATION;
  104. }
  105. }
  106. // after we render 2x2 lying corpses, we must redraw previous creatures/ontop above them
  107. if(redrawPreviousTopH > 0 || redrawPreviousTopW > 0) {
  108. int topRedrawFlags = drawFlags & (Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawOnTop | Otc::DrawAnimations);
  109. if(topRedrawFlags) {
  110. for(int x=-redrawPreviousTopW;x<=0;++x) {
  111. for(int y=-redrawPreviousTopH;y<=0;++y) {
  112. if(x == 0 && y == 0)
  113. continue;
  114. const TilePtr& tile = g_map.getTile(m_position.translated(x,y));
  115. if(tile)
  116. tile->draw(dest + Point(x*Otc::TILE_PIXELS, y*Otc::TILE_PIXELS)*scaleFactor, scaleFactor, topRedrawFlags);
  117. }
  118. }
  119. }
  120. }
  121. // creatures
  122. if(drawFlags & Otc::DrawCreatures) {
  123. if(animate) {
  124. for(const CreaturePtr& creature : m_walkingCreatures) {
  125. creature->draw(Point(dest.x + ((creature->getPosition().x - m_position.x)*Otc::TILE_PIXELS - m_drawElevation)*scaleFactor,
  126. dest.y + ((creature->getPosition().y - m_position.y)*Otc::TILE_PIXELS - m_drawElevation)*scaleFactor), scaleFactor, animate, lightView);
  127. }
  128. }
  129. for(auto it = m_things.rbegin(); it != m_things.rend(); ++it) {
  130. const ThingPtr& thing = *it;
  131. if(!thing->isCreature())
  132. continue;
  133. CreaturePtr creature = thing->static_self_cast<Creature>();
  134. if(creature && (!creature->isWalking() || !animate))
  135. creature->draw(dest - m_drawElevation*scaleFactor, scaleFactor, animate, lightView);
  136. }
  137. }
  138. // effects
  139. if(drawFlags & Otc::DrawEffects)
  140. for(const EffectPtr& effect : m_effects)
  141. effect->drawEffect(dest - m_drawElevation*scaleFactor, scaleFactor, animate, m_position.x - g_map.getCentralPosition().x, m_position.y - g_map.getCentralPosition().y, lightView);
  142. // top items
  143. if(drawFlags & Otc::DrawOnTop)
  144. for(const ThingPtr& thing : m_things)
  145. if(thing->isOnTop())
  146. thing->draw(dest, scaleFactor, animate, lightView);
  147. // draw translucent light (for tiles beneath holes)
  148. if(hasTranslucentLight() && lightView) {
  149. Light light;
  150. light.intensity = 1;
  151. lightView->addLightSource(dest + Point(16,16) * scaleFactor, scaleFactor, light);
  152. }
  153. }
  154. void Tile::clean()
  155. {
  156. while(!m_things.empty())
  157. removeThing(m_things.front());
  158. }
  159. void Tile::addWalkingCreature(const CreaturePtr& creature)
  160. {
  161. m_walkingCreatures.push_back(creature);
  162. }
  163. void Tile::removeWalkingCreature(const CreaturePtr& creature)
  164. {
  165. auto it = std::find(m_walkingCreatures.begin(), m_walkingCreatures.end(), creature);
  166. if(it != m_walkingCreatures.end())
  167. m_walkingCreatures.erase(it);
  168. }
  169. void Tile::addThing(const ThingPtr& thing, int stackPos)
  170. {
  171. if(!thing)
  172. return;
  173. if(thing->isEffect()) {
  174. if(thing->isTopEffect())
  175. m_effects.insert(m_effects.begin(), thing->static_self_cast<Effect>());
  176. else
  177. m_effects.push_back(thing->static_self_cast<Effect>());
  178. } else {
  179. // priority 854
  180. // 0 - ground, --> -->
  181. // 1 - ground borders --> -->
  182. // 2 - bottom (walls), --> -->
  183. // 3 - on top (doors) --> -->
  184. // 4 - creatures, from top to bottom <-- -->
  185. // 5 - items, from top to bottom <-- <--
  186. if(stackPos < 0 || stackPos == 255) {
  187. int priority = thing->getStackPriority();
  188. // -1 or 255 => auto detect position
  189. // -2 => append
  190. bool append;
  191. if(stackPos == -2)
  192. append = true;
  193. else {
  194. append = (priority <= 3);
  195. // newer protocols does not store creatures in reverse order
  196. if(g_game.getClientVersion() >= 854 && priority == 4)
  197. append = !append;
  198. }
  199. for(stackPos = 0; stackPos < (int)m_things.size(); ++stackPos) {
  200. int otherPriority = m_things[stackPos]->getStackPriority();
  201. if((append && otherPriority > priority) || (!append && otherPriority >= priority))
  202. break;
  203. }
  204. } else if(stackPos > (int)m_things.size())
  205. stackPos = m_things.size();
  206. m_things.insert(m_things.begin() + stackPos, thing);
  207. if(m_things.size() > MAX_THINGS)
  208. removeThing(m_things[MAX_THINGS]);
  209. /*
  210. // check stack priorities
  211. // this code exists to find stackpos bugs faster
  212. int lastPriority = 0;
  213. for(const ThingPtr& thing : m_things) {
  214. int priority = thing->getStackPriority();
  215. assert(lastPriority <= priority);
  216. lastPriority = priority;
  217. }
  218. */
  219. }
  220. thing->setPosition(m_position);
  221. thing->onAppear();
  222. if(thing->isTranslucent())
  223. checkTranslucentLight();
  224. }
  225. bool Tile::removeThing(ThingPtr thing)
  226. {
  227. if(!thing)
  228. return false;
  229. bool removed = false;
  230. if(thing->isEffect()) {
  231. EffectPtr effect = thing->static_self_cast<Effect>();
  232. auto it = std::find(m_effects.begin(), m_effects.end(), effect);
  233. if(it != m_effects.end()) {
  234. m_effects.erase(it);
  235. removed = true;
  236. }
  237. } else {
  238. auto it = std::find(m_things.begin(), m_things.end(), thing);
  239. if(it != m_things.end()) {
  240. m_things.erase(it);
  241. removed = true;
  242. }
  243. }
  244. thing->onDisappear();
  245. if(thing->isTranslucent())
  246. checkTranslucentLight();
  247. return removed;
  248. }
  249. ThingPtr Tile::getThing(int stackPos)
  250. {
  251. if(stackPos >= 0 && stackPos < (int)m_things.size())
  252. return m_things[stackPos];
  253. return nullptr;
  254. }
  255. EffectPtr Tile::getEffect(uint16 id)
  256. {
  257. for(const EffectPtr& effect : m_effects)
  258. if(effect->getId() == id)
  259. return effect;
  260. return nullptr;
  261. }
  262. bool Tile::hasThing(const ThingPtr& thing)
  263. {
  264. return std::find(m_things.begin(), m_things.end(), thing) != m_things.end();
  265. }
  266. int Tile::getThingStackPos(const ThingPtr& thing)
  267. {
  268. for(uint stackpos = 0; stackpos < m_things.size(); ++stackpos)
  269. if(thing == m_things[stackpos])
  270. return stackpos;
  271. return -1;
  272. }
  273. ThingPtr Tile::getTopThing()
  274. {
  275. if(isEmpty())
  276. return nullptr;
  277. for(const ThingPtr& thing : m_things)
  278. if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop() && !thing->isCreature())
  279. return thing;
  280. return m_things[m_things.size() - 1];
  281. }
  282. std::vector<ItemPtr> Tile::getItems()
  283. {
  284. std::vector<ItemPtr> items;
  285. for(const ThingPtr& thing : m_things) {
  286. if(!thing->isItem())
  287. continue;
  288. ItemPtr item = thing->static_self_cast<Item>();
  289. items.push_back(item);
  290. }
  291. return items;
  292. }
  293. std::vector<CreaturePtr> Tile::getCreatures()
  294. {
  295. std::vector<CreaturePtr> creatures;
  296. for(const ThingPtr& thing : m_things) {
  297. if(thing->isCreature())
  298. creatures.push_back(thing->static_self_cast<Creature>());
  299. }
  300. return creatures;
  301. }
  302. ItemPtr Tile::getGround()
  303. {
  304. ThingPtr firstObject = getThing(0);
  305. if(!firstObject)
  306. return nullptr;
  307. if(firstObject->isGround() && firstObject->isItem())
  308. return firstObject->static_self_cast<Item>();
  309. return nullptr;
  310. }
  311. int Tile::getGroundSpeed()
  312. {
  313. int groundSpeed = 100;
  314. if(ItemPtr ground = getGround())
  315. groundSpeed = ground->getGroundSpeed();
  316. return groundSpeed;
  317. }
  318. uint8 Tile::getMinimapColorByte()
  319. {
  320. uint8 color = 255; // alpha
  321. if(m_minimapColor != 0)
  322. return m_minimapColor;
  323. for(const ThingPtr& thing : m_things) {
  324. if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop())
  325. break;
  326. uint8 c = thing->getMinimapColor();
  327. if(c != 0)
  328. color = c;
  329. }
  330. return color;
  331. }
  332. ThingPtr Tile::getTopLookThing()
  333. {
  334. if(isEmpty())
  335. return nullptr;
  336. for(uint i = 0; i < m_things.size(); ++i) {
  337. ThingPtr thing = m_things[i];
  338. if(!thing->isIgnoreLook() && (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop()))
  339. return thing;
  340. }
  341. return m_things[0];
  342. }
  343. ThingPtr Tile::getTopUseThing()
  344. {
  345. if(isEmpty())
  346. return nullptr;
  347. for(uint i = 0; i < m_things.size(); ++i) {
  348. ThingPtr thing = m_things[i];
  349. if (thing->isForceUse() || (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop() && !thing->isCreature() && !thing->isSplash()))
  350. return thing;
  351. }
  352. for(uint i = 0; i < m_things.size(); ++i) {
  353. ThingPtr thing = m_things[i];
  354. if (!thing->isGround() && !thing->isGroundBorder() && !thing->isCreature() && !thing->isSplash())
  355. return thing;
  356. }
  357. return m_things[0];
  358. }
  359. CreaturePtr Tile::getTopCreature()
  360. {
  361. CreaturePtr creature;
  362. for(uint i = 0; i < m_things.size(); ++i) {
  363. ThingPtr thing = m_things[i];
  364. if(thing->isLocalPlayer()) // return local player if there is no other creature
  365. creature = thing->static_self_cast<Creature>();
  366. else if(thing->isCreature() && !thing->isLocalPlayer())
  367. return thing->static_self_cast<Creature>();
  368. }
  369. if(!creature && !m_walkingCreatures.empty())
  370. creature = m_walkingCreatures.back();
  371. // check for walking creatures in tiles around
  372. if(!creature) {
  373. for(int xi=-1;xi<=1;++xi) {
  374. for(int yi=-1;yi<=1;++yi) {
  375. Position pos = m_position.translated(xi, yi);
  376. if(pos == m_position)
  377. continue;
  378. const TilePtr& tile = g_map.getTile(pos);
  379. if(tile) {
  380. for(const CreaturePtr& c : tile->getCreatures()) {
  381. if(c->isWalking() && c->getLastStepFromPosition() == m_position && c->getStepProgress() < 0.75f) {
  382. creature = c;
  383. }
  384. }
  385. }
  386. }
  387. }
  388. }
  389. return creature;
  390. }
  391. ThingPtr Tile::getTopMoveThing()
  392. {
  393. if(isEmpty())
  394. return nullptr;
  395. for(uint i = 0; i < m_things.size(); ++i) {
  396. ThingPtr thing = m_things[i];
  397. if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop() && !thing->isCreature()) {
  398. if(i > 0 && thing->isNotMoveable())
  399. return m_things[i-1];
  400. return thing;
  401. }
  402. }
  403. for(const ThingPtr& thing : m_things) {
  404. if(thing->isCreature())
  405. return thing;
  406. }
  407. return m_things[0];
  408. }
  409. ThingPtr Tile::getTopMultiUseThing()
  410. {
  411. if(isEmpty())
  412. return nullptr;
  413. if(CreaturePtr topCreature = getTopCreature())
  414. return topCreature;
  415. for(uint i = 0; i < m_things.size(); ++i) {
  416. ThingPtr thing = m_things[i];
  417. if(thing->isForceUse())
  418. return thing;
  419. }
  420. for(uint i = 0; i < m_things.size(); ++i) {
  421. ThingPtr thing = m_things[i];
  422. if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop()) {
  423. if(i > 0 && thing->isSplash())
  424. return m_things[i-1];
  425. return thing;
  426. }
  427. }
  428. for(uint i = 0; i < m_things.size(); ++i) {
  429. ThingPtr thing = m_things[i];
  430. if(!thing->isGround() && !thing->isOnTop())
  431. return thing;
  432. }
  433. return m_things[0];
  434. }
  435. bool Tile::isWalkable(bool ignoreCreatures)
  436. {
  437. if(!getGround())
  438. return false;
  439. for(const ThingPtr& thing : m_things) {
  440. if(thing->isNotWalkable())
  441. return false;
  442. if(!ignoreCreatures) {
  443. if(thing->isCreature()) {
  444. CreaturePtr creature = thing->static_self_cast<Creature>();
  445. if(!creature->isPassable() && creature->canBeSeen())
  446. return false;
  447. }
  448. }
  449. }
  450. return true;
  451. }
  452. bool Tile::isPathable()
  453. {
  454. for(const ThingPtr& thing : m_things)
  455. if(thing->isNotPathable())
  456. return false;
  457. return true;
  458. }
  459. bool Tile::isFullGround()
  460. {
  461. ItemPtr ground = getGround();
  462. if(ground && ground->isFullGround())
  463. return true;
  464. return false;
  465. }
  466. bool Tile::isFullyOpaque()
  467. {
  468. ThingPtr firstObject = getThing(0);
  469. return firstObject && firstObject->isFullGround();
  470. }
  471. bool Tile::isSingleDimension()
  472. {
  473. if(!m_walkingCreatures.empty())
  474. return false;
  475. for(const ThingPtr& thing : m_things)
  476. if(thing->getHeight() != 1 || thing->getWidth() != 1)
  477. return false;
  478. return true;
  479. }
  480. bool Tile::isLookPossible()
  481. {
  482. for(const ThingPtr& thing : m_things)
  483. if(thing->blockProjectile())
  484. return false;
  485. return true;
  486. }
  487. bool Tile::isClickable()
  488. {
  489. bool hasGround = false;
  490. bool hasOnBottom = false;
  491. bool hasIgnoreLook = false;
  492. for(const ThingPtr& thing : m_things) {
  493. if(thing->isGround())
  494. hasGround = true;
  495. if(thing->isOnBottom())
  496. hasOnBottom = true;
  497. if((hasGround || hasOnBottom) && !hasIgnoreLook)
  498. return true;
  499. }
  500. return false;
  501. }
  502. bool Tile::isEmpty()
  503. {
  504. return m_things.size() == 0;
  505. }
  506. bool Tile::isDrawable()
  507. {
  508. return !m_things.empty() || !m_walkingCreatures.empty() || !m_effects.empty();
  509. }
  510. bool Tile::mustHookEast()
  511. {
  512. for(const ThingPtr& thing : m_things)
  513. if(thing->isHookEast())
  514. return true;
  515. return false;
  516. }
  517. bool Tile::mustHookSouth()
  518. {
  519. for(const ThingPtr& thing : m_things)
  520. if(thing->isHookSouth())
  521. return true;
  522. return false;
  523. }
  524. bool Tile::hasCreature()
  525. {
  526. for(const ThingPtr& thing : m_things)
  527. if(thing->isCreature())
  528. return true;
  529. return false;
  530. }
  531. bool Tile::limitsFloorsView(bool isFreeView)
  532. {
  533. // ground and walls limits the view
  534. ThingPtr firstThing = getThing(0);
  535. if(isFreeView) {
  536. if(firstThing && !firstThing->isDontHide() && (firstThing->isGround() || firstThing->isOnBottom()))
  537. return true;
  538. } else if(firstThing && !firstThing->isDontHide() && (firstThing->isGround() || (firstThing->isOnBottom() && firstThing->blockProjectile())))
  539. return true;
  540. return false;
  541. }
  542. bool Tile::canErase()
  543. {
  544. return m_walkingCreatures.empty() && m_effects.empty() && m_things.empty() && m_flags == 0 && m_minimapColor == 0;
  545. }
  546. int Tile::getElevation() const
  547. {
  548. int elevation = 0;
  549. for(const ThingPtr& thing : m_things)
  550. if(thing->getElevation() > 0)
  551. elevation++;
  552. return elevation;
  553. }
  554. bool Tile::hasElevation(int elevation)
  555. {
  556. return getElevation() >= elevation;
  557. }
  558. void Tile::checkTranslucentLight()
  559. {
  560. if(m_position.z != Otc::SEA_FLOOR)
  561. return;
  562. Position downPos = m_position;
  563. if(!downPos.down())
  564. return;
  565. TilePtr tile = g_map.getOrCreateTile(downPos);
  566. if(!tile)
  567. return;
  568. bool translucent = false;
  569. for(const ThingPtr& thing : m_things) {
  570. if(thing->isTranslucent() || thing->hasLensHelp()) {
  571. translucent = true;
  572. break;
  573. }
  574. }
  575. if(translucent)
  576. tile->m_flags |= TILESTATE_TRANSLUECENT_LIGHT;
  577. else
  578. tile->m_flags &= ~TILESTATE_TRANSLUECENT_LIGHT;
  579. }
  580. /* vim: set ts=4 sw=4 et :*/