Advertisement
Guest User

Untitled

a guest
Aug 18th, 2019
113
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 31.48 KB | None | 0 0
  1. #include "creature.h"
  2. #include "thingtypemanager.h"
  3. #include "localplayer.h"
  4. #include "map.h"
  5. #include "tile.h"
  6. #include "item.h"
  7. #include "game.h"
  8. #include "effect.h"
  9. #include "luavaluecasts.h"
  10. #include "lightview.h"
  11.  
  12. #include <framework/graphics/graphics.h>
  13. #include <framework/core/eventdispatcher.h>
  14. #include <framework/core/clock.h>
  15.  
  16. #include <framework/graphics/paintershaderprogram.h>
  17. #include <framework/graphics/ogl/painterogl2_shadersources.h>
  18. #include <framework/graphics/texturemanager.h>
  19. #include <framework/graphics/framebuffermanager.h>
  20. #include "spritemanager.h"
  21.  
  22. Creature::Creature() : Thing()
  23. {
  24. m_id = 0;
  25. m_healthPercent = 100;
  26. m_speed = 210;
  27. m_direction = Otc::South;
  28. m_walkAnimationPhase = 0;
  29. m_walkedPixels = 0;
  30. m_walkTurnDirection = Otc::InvalidDirection;
  31. m_skull = Otc::SkullNone;
  32. m_shield = Otc::ShieldNone;
  33. m_emblem = Otc::EmblemNone;
  34. m_type = Proto::CreatureTypeUnknown;
  35. m_icon = Otc::NpcIconNone;
  36. m_lastStepDirection = Otc::InvalidDirection;
  37. m_nameCache.setFont(g_fonts.getFont("verdana-11px-rounded"));
  38. m_nameCache.setAlign(Fw::AlignTopCenter);
  39. m_footStep = 0;
  40. m_speedFormula.fill(-1);
  41. m_outfitColor = Color::white;
  42. }
  43.  
  44. void Creature::draw(const Point& dest, float scaleFactor, bool animate, LightView *lightView)
  45. {
  46. if(!canBeSeen())
  47. return;
  48.  
  49. Point animationOffset = animate ? m_walkOffset : Point(0,0);
  50.  
  51. if(m_showTimedSquare && animate) {
  52. g_painter->setColor(m_timedSquareColor);
  53. g_painter->drawBoundingRect(Rect(dest + (animationOffset - getDisplacement() + 2)*scaleFactor, Size(28, 28)*scaleFactor), std::max<int>((int)(2*scaleFactor), 1));
  54. g_painter->setColor(Color::white);
  55. }
  56.  
  57. if(m_showStaticSquare && animate) {
  58. g_painter->setColor(m_staticSquareColor);
  59. g_painter->drawBoundingRect(Rect(dest + (animationOffset - getDisplacement())*scaleFactor, Size(Otc::TILE_PIXELS, Otc::TILE_PIXELS)*scaleFactor), std::max<int>((int)(2*scaleFactor), 1));
  60. g_painter->setColor(Color::white);
  61. }
  62.  
  63. internalDrawOutfit(dest + animationOffset * scaleFactor, scaleFactor, animate, animate, m_direction);
  64. m_footStepDrawn = true;
  65.  
  66. if(lightView) {
  67. Light light = rawGetThingType()->getLight();
  68. if(m_light.intensity != light.intensity || m_light.color != light.color)
  69. light = m_light;
  70.  
  71. // local player always have a minimum light in complete darkness
  72. if(isLocalPlayer() && (g_map.getLight().intensity < 64 || m_position.z > Otc::SEA_FLOOR)) {
  73. light.intensity = std::max<uint8>(light.intensity, 3);
  74. if(light.color == 0 || light.color > 215)
  75. light.color = 215;
  76. }
  77.  
  78. if(light.intensity > 0)
  79. lightView->addLightSource(dest + (animationOffset + Point(16,16)) * scaleFactor, scaleFactor, light);
  80. }
  81. }
  82.  
  83. void Creature::internalDrawOutfit(Point dest, float scaleFactor, bool animateWalk, bool animateIdle, Otc::Direction direction, LightView *lightView)
  84. {
  85. g_painter->setColor(m_outfitColor);
  86.  
  87. // outfit is a real creature
  88. if(m_outfit.getCategory() == ThingCategoryCreature) {
  89. int animationPhase = animateWalk ? m_walkAnimationPhase : 0;
  90.  
  91. if(isAnimateAlways() && animateIdle) {
  92. int ticksPerFrame = 1000 / getAnimationPhases();
  93. animationPhase = (g_clock.millis() % (ticksPerFrame * getAnimationPhases())) / ticksPerFrame;
  94. }
  95.  
  96. // xPattern => creature direction
  97. int xPattern;
  98. if(direction == Otc::NorthEast || direction == Otc::SouthEast)
  99. xPattern = Otc::East;
  100. else if(direction == Otc::NorthWest || direction == Otc::SouthWest)
  101. xPattern = Otc::West;
  102. else
  103. xPattern = direction;
  104.  
  105. int zPattern = 0;
  106. if(m_outfit.getMount() != 0) {
  107. auto datType = g_things.rawGetThingType(m_outfit.getMount(), ThingCategoryCreature);
  108. dest -= datType->getDisplacement() * scaleFactor;
  109. datType->draw(dest, scaleFactor, 0, xPattern, 0, 0, animationPhase, lightView);
  110. dest += getDisplacement() * scaleFactor;
  111. zPattern = std::min<int>(1, getNumPatternZ() - 1);
  112. }
  113.  
  114. PointF jumpOffset = m_jumpOffset * scaleFactor;
  115. dest -= Point(stdext::round(jumpOffset.x), stdext::round(jumpOffset.y));
  116.  
  117. // yPattern => creature addon
  118. for(int yPattern = 0; yPattern < getNumPatternY(); yPattern++) {
  119.  
  120. // continue if we dont have this addon
  121. if(yPattern > 0 && !(m_outfit.getAddons() & (1 << (yPattern-1))))
  122. continue;
  123.  
  124. auto datType = rawGetThingType();
  125. datType->draw(dest, scaleFactor, 0, xPattern, yPattern, zPattern, animationPhase, yPattern == 0 ? lightView : nullptr);
  126.  
  127. if(getLayers() > 1) {
  128. Color oldColor = g_painter->getColor();
  129. Painter::CompositionMode oldComposition = g_painter->getCompositionMode();
  130. g_painter->setCompositionMode(Painter::CompositionMode_Multiply);
  131. g_painter->setColor(m_outfit.getHeadColor());
  132. datType->draw(dest, scaleFactor, SpriteMaskYellow, xPattern, yPattern, zPattern, animationPhase);
  133. g_painter->setColor(m_outfit.getBodyColor());
  134. datType->draw(dest, scaleFactor, SpriteMaskRed, xPattern, yPattern, zPattern, animationPhase);
  135. g_painter->setColor(m_outfit.getLegsColor());
  136. datType->draw(dest, scaleFactor, SpriteMaskGreen, xPattern, yPattern, zPattern, animationPhase);
  137. g_painter->setColor(m_outfit.getFeetColor());
  138. datType->draw(dest, scaleFactor, SpriteMaskBlue, xPattern, yPattern, zPattern, animationPhase);
  139. g_painter->setColor(oldColor);
  140. g_painter->setCompositionMode(oldComposition);
  141. }
  142. }
  143. // outfit is a creature imitating an item or the invisible effect
  144. } else {
  145. ThingType *type = g_things.rawGetThingType(m_outfit.getAuxId(), m_outfit.getCategory());
  146.  
  147. int animationPhase = 0;
  148. int animationPhases = type->getAnimationPhases();
  149. int animateTicks = Otc::ITEM_TICKS_PER_FRAME;
  150.  
  151. // when creature is an effect we cant render the first and last animation phase,
  152. // instead we should loop in the phases between
  153. if(m_outfit.getCategory() == ThingCategoryEffect) {
  154. animationPhases = std::max<int>(1, animationPhases-2);
  155. animateTicks = Otc::INVISIBLE_TICKS_PER_FRAME;
  156. }
  157.  
  158. if(animationPhases > 1) {
  159. if(animateIdle)
  160. animationPhase = (g_clock.millis() % (animateTicks * animationPhases)) / animateTicks;
  161. else
  162. animationPhase = animationPhases-1;
  163. }
  164.  
  165. if(m_outfit.getCategory() == ThingCategoryEffect)
  166. animationPhase = std::min<int>(animationPhase+1, animationPhases);
  167.  
  168. type->draw(dest - (getDisplacement() * scaleFactor), scaleFactor, 0, 0, 0, 0, animationPhase, lightView);
  169. }
  170.  
  171. g_painter->resetColor();
  172. }
  173.  
  174. void Creature::drawOutfit(const Rect& destRect, bool resize)
  175. {
  176. int exactSize;
  177. if(m_outfit.getCategory() == ThingCategoryCreature)
  178. exactSize = getExactSize();
  179. else
  180. exactSize = g_things.rawGetThingType(m_outfit.getAuxId(), m_outfit.getCategory())->getExactSize();
  181.  
  182. int frameSize;
  183. if(!resize)
  184. frameSize = std::max<int>(exactSize * 0.75f, 2 * Otc::TILE_PIXELS * 0.75f);
  185. else if(!(frameSize = exactSize))
  186. return;
  187.  
  188. if(g_graphics.canUseFBO()) {
  189. const FrameBufferPtr& outfitBuffer = g_framebuffers.getTemporaryFrameBuffer();
  190. outfitBuffer->resize(Size(frameSize, frameSize));
  191. outfitBuffer->bind();
  192. g_painter->setAlphaWriting(true);
  193. g_painter->clear(Color::alpha);
  194. internalDrawOutfit(Point(frameSize - Otc::TILE_PIXELS, frameSize - Otc::TILE_PIXELS) + getDisplacement(), 1, false, true, Otc::South);
  195. outfitBuffer->release();
  196. outfitBuffer->draw(destRect, Rect(0,0,frameSize,frameSize));
  197. } else {
  198. float scaleFactor = destRect.width() / (float)frameSize;
  199. Point dest = destRect.bottomRight() - (Point(Otc::TILE_PIXELS,Otc::TILE_PIXELS) - getDisplacement()) * scaleFactor;
  200. internalDrawOutfit(dest, scaleFactor, false, true, Otc::South);
  201. }
  202. }
  203.  
  204. void Creature::drawInformation(const Point& point, bool useGray, const Rect& parentRect, int drawFlags)
  205. {
  206. if(m_healthPercent < 1) // creature is dead
  207. return;
  208.  
  209. Color fillColor = Color(96, 96, 96);
  210.  
  211. if(!useGray)
  212. fillColor = m_informationColor;
  213.  
  214. // calculate main rects
  215. Rect backgroundRect = Rect(point.x-(13.5), point.y, 27, 4);
  216. backgroundRect.bind(parentRect);
  217.  
  218. Size nameSize = m_nameCache.getTextSize();
  219. Rect textRect = Rect(point.x - nameSize.width() / 2.0, point.y-12, nameSize);
  220. textRect.bind(parentRect);
  221.  
  222. // distance them
  223. uint32 offset = 12;
  224. if(isLocalPlayer()) {
  225. offset *= 2;
  226. }
  227.  
  228. if(textRect.top() == parentRect.top())
  229. backgroundRect.moveTop(textRect.top() + offset);
  230. if(backgroundRect.bottom() == parentRect.bottom())
  231. textRect.moveTop(backgroundRect.top() - offset);
  232.  
  233. // health rect is based on background rect, so no worries
  234. Rect healthRect = backgroundRect.expanded(-1);
  235. healthRect.setWidth((m_healthPercent / 100.0) * 25);
  236.  
  237. // draw
  238. //g_game.getFeature(Otc::GameBlueNpcNameColor) &&
  239. if(isNpc() && m_healthPercent == 100 && !useGray)
  240. if (getName() == "The Oracle") {
  241. fillColor = Color(0xff, 0xEA, 0x00);
  242. } else {
  243. fillColor = Color(0x00, 0x64, 0xf8);
  244. }
  245.  
  246. if(drawFlags & Otc::DrawBars) {
  247. g_painter->setColor(Color::black);
  248. g_painter->drawFilledRect(backgroundRect);
  249.  
  250. g_painter->setColor(fillColor);
  251. g_painter->drawFilledRect(healthRect);
  252.  
  253. if(drawFlags & Otc::DrawManaBar && isLocalPlayer()) {
  254. LocalPlayerPtr player = g_game.getLocalPlayer();
  255. if(player) {
  256. backgroundRect.moveTop(backgroundRect.bottom());
  257.  
  258. g_painter->setColor(Color::black);
  259. g_painter->drawFilledRect(backgroundRect);
  260.  
  261. Rect manaRect = backgroundRect.expanded(-1);
  262. double maxMana = player->getMaxMana();
  263. if(maxMana == 0) {
  264. manaRect.setWidth(25);
  265. } else {
  266. manaRect.setWidth(player->getMana() / (maxMana * 1.0) * 25);
  267. }
  268.  
  269. g_painter->setColor(Color::blue);
  270. g_painter->drawFilledRect(manaRect);
  271. }
  272. }
  273. }
  274.  
  275. if(drawFlags & Otc::DrawNames) {
  276. if(g_painter->getColor() != fillColor)
  277. g_painter->setColor(fillColor);
  278. m_nameCache.draw(textRect);
  279. }
  280.  
  281. if(m_skull != Otc::SkullNone && m_skullTexture) {
  282. g_painter->setColor(Color::white);
  283. Rect skullRect = Rect(backgroundRect.x() + 13.5 + 12, backgroundRect.y() + 5, m_skullTexture->getSize());
  284. g_painter->drawTexturedRect(skullRect, m_skullTexture);
  285. }
  286. if(m_shield != Otc::ShieldNone && m_shieldTexture && m_showShieldTexture) {
  287. g_painter->setColor(Color::white);
  288. Rect shieldRect = Rect(backgroundRect.x() + 13.5, backgroundRect.y() + 5, m_shieldTexture->getSize());
  289. g_painter->drawTexturedRect(shieldRect, m_shieldTexture);
  290. }
  291. if(m_emblem != Otc::EmblemNone && m_emblemTexture) {
  292. g_painter->setColor(Color::white);
  293. Rect emblemRect = Rect(backgroundRect.x() + 13.5 + 12, backgroundRect.y() + 16, m_emblemTexture->getSize());
  294. g_painter->drawTexturedRect(emblemRect, m_emblemTexture);
  295. }
  296. if(m_type != Proto::CreatureTypeUnknown && m_typeTexture) {
  297. g_painter->setColor(Color::white);
  298. Rect typeRect = Rect(backgroundRect.x() + 13.5 + 12 + 12, backgroundRect.y() + 16, m_typeTexture->getSize());
  299. g_painter->drawTexturedRect(typeRect, m_typeTexture);
  300. }
  301. if(m_icon != Otc::NpcIconNone && m_iconTexture) {
  302. g_painter->setColor(Color::white);
  303. Rect iconRect = Rect(backgroundRect.x() + 13.5 + 12, backgroundRect.y() + 5, m_iconTexture->getSize());
  304. g_painter->drawTexturedRect(iconRect, m_iconTexture);
  305. }
  306. }
  307.  
  308. void Creature::turn(Otc::Direction direction)
  309. {
  310. // if is not walking change the direction right away
  311. if(!m_walking)
  312. setDirection(direction);
  313. // schedules to set the new direction when walk ends
  314. else
  315. m_walkTurnDirection = direction;
  316. }
  317.  
  318. void Creature::walk(const Position& oldPos, const Position& newPos)
  319. {
  320. if(oldPos == newPos)
  321. return;
  322.  
  323. // get walk direction
  324. m_lastStepDirection = oldPos.getDirectionFromPosition(newPos);
  325. m_lastStepFromPosition = oldPos;
  326. m_lastStepToPosition = newPos;
  327.  
  328. // set current walking direction
  329. setDirection(m_lastStepDirection);
  330.  
  331. // starts counting walk
  332. m_walking = true;
  333. m_walkTimer.restart();
  334. m_walkedPixels = 0;
  335.  
  336. if(m_walkFinishAnimEvent) {
  337. m_walkFinishAnimEvent->cancel();
  338. m_walkFinishAnimEvent = nullptr;
  339. }
  340.  
  341. // no direction need to be changed when the walk ends
  342. m_walkTurnDirection = Otc::InvalidDirection;
  343.  
  344. // starts updating walk
  345. nextWalkUpdate();
  346. }
  347.  
  348. void Creature::stopWalk()
  349. {
  350. if(!m_walking)
  351. return;
  352.  
  353. // stops the walk right away
  354. terminateWalk();
  355. }
  356.  
  357. void Creature::jump(int height, int duration)
  358. {
  359. if(!m_jumpOffset.isNull())
  360. return;
  361.  
  362. m_jumpTimer.restart();
  363. m_jumpHeight = height;
  364. m_jumpDuration = duration;
  365.  
  366. updateJump();
  367. }
  368.  
  369. void Creature::updateJump()
  370. {
  371. int t = m_jumpTimer.ticksElapsed();
  372. double a = -4 * m_jumpHeight / (m_jumpDuration * m_jumpDuration);
  373. double b = +4 * m_jumpHeight / (m_jumpDuration);
  374.  
  375. double height = a*t*t + b*t;
  376. int roundHeight = stdext::round(height);
  377. int halfJumpDuration = m_jumpDuration / 2;
  378.  
  379. // schedules next update
  380. if(m_jumpTimer.ticksElapsed() < m_jumpDuration) {
  381. m_jumpOffset = PointF(height, height);
  382.  
  383. int diff = 0;
  384. if(m_jumpTimer.ticksElapsed() < halfJumpDuration)
  385. diff = 1;
  386. else if(m_jumpTimer.ticksElapsed() > halfJumpDuration)
  387. diff = -1;
  388.  
  389. int nextT, i = 1;
  390. do {
  391. nextT = stdext::round((-b + std::sqrt(std::max<double>(b*b + 4*a*(roundHeight+diff*i), 0.0)) * diff) / (2*a));
  392. ++i;
  393.  
  394. if(nextT < halfJumpDuration)
  395. diff = 1;
  396. else if(nextT > halfJumpDuration)
  397. diff = -1;
  398. } while(nextT - m_jumpTimer.ticksElapsed() == 0 && i < 3);
  399.  
  400. auto self = static_self_cast<Creature>();
  401. g_dispatcher.scheduleEvent([self] {
  402. self->updateJump();
  403. }, nextT - m_jumpTimer.ticksElapsed());
  404. }
  405. else
  406. m_jumpOffset = PointF(0, 0);
  407. }
  408.  
  409. void Creature::onPositionChange(const Position& newPos, const Position& oldPos)
  410. {
  411. callLuaField("onPositionChange", newPos, oldPos);
  412. }
  413.  
  414. void Creature::onAppear()
  415. {
  416. // cancel any disappear event
  417. if(m_disappearEvent) {
  418. m_disappearEvent->cancel();
  419. m_disappearEvent = nullptr;
  420. }
  421.  
  422. // creature appeared the first time or wasn't seen for a long time
  423. if(m_removed) {
  424. stopWalk();
  425. m_removed = false;
  426. callLuaField("onAppear");
  427. // walk
  428. } else if(m_oldPosition != m_position && m_oldPosition.isInRange(m_position,1,1) && m_allowAppearWalk) {
  429. m_allowAppearWalk = false;
  430. walk(m_oldPosition, m_position);
  431. callLuaField("onWalk", m_oldPosition, m_position);
  432. // teleport
  433. } else if(m_oldPosition != m_position) {
  434. stopWalk();
  435. callLuaField("onDisappear");
  436. callLuaField("onAppear");
  437. } // else turn
  438. }
  439.  
  440. void Creature::onDisappear()
  441. {
  442. if(m_disappearEvent)
  443. m_disappearEvent->cancel();
  444.  
  445. m_oldPosition = m_position;
  446.  
  447. // a pair onDisappear and onAppear events are fired even when creatures walks or turns,
  448. // so we must filter
  449. auto self = static_self_cast<Creature>();
  450. m_disappearEvent = g_dispatcher.addEvent([self] {
  451. self->m_removed = true;
  452. self->stopWalk();
  453.  
  454. self->callLuaField("onDisappear");
  455.  
  456. // invalidate this creature position
  457. if(!self->isLocalPlayer())
  458. self->setPosition(Position());
  459. self->m_oldPosition = Position();
  460. self->m_disappearEvent = nullptr;
  461. });
  462. }
  463.  
  464. void Creature::onDeath()
  465. {
  466. callLuaField("onDeath");
  467. }
  468.  
  469. void Creature::updateWalkAnimation(int totalPixelsWalked)
  470. {
  471. // update outfit animation
  472. if(m_outfit.getCategory() != ThingCategoryCreature)
  473. return;
  474.  
  475. int footAnimPhases = getAnimationPhases() - 1;
  476. int footDelay = getStepDuration(true) / 3;
  477. // Since mount is a different outfit we need to get the mount animation phases
  478. if(m_outfit.getMount() != 0) {
  479. ThingType *type = g_things.rawGetThingType(m_outfit.getMount(), m_outfit.getCategory());
  480. footAnimPhases = type->getAnimationPhases() - 1;
  481. }
  482. if(footAnimPhases == 0)
  483. m_walkAnimationPhase = 0;
  484. else if(m_footStepDrawn && m_footTimer.ticksElapsed() >= footDelay && totalPixelsWalked < 32) {
  485. m_footStep++;
  486. m_walkAnimationPhase = 1 + (m_footStep % footAnimPhases);
  487. m_footStepDrawn = false;
  488. m_footTimer.restart();
  489. } else if(m_walkAnimationPhase == 0 && totalPixelsWalked < 32) {
  490. m_walkAnimationPhase = 1 + (m_footStep % footAnimPhases);
  491. }
  492.  
  493. if(totalPixelsWalked == 32 && !m_walkFinishAnimEvent) {
  494. auto self = static_self_cast<Creature>();
  495. m_walkFinishAnimEvent = g_dispatcher.scheduleEvent([self] {
  496. if(!self->m_walking || self->m_walkTimer.ticksElapsed() >= self->getStepDuration(true))
  497. self->m_walkAnimationPhase = 0;
  498. self->m_walkFinishAnimEvent = nullptr;
  499. }, std::min<int>(footDelay, 200));
  500. }
  501.  
  502. }
  503.  
  504. void Creature::updateWalkOffset(int totalPixelsWalked)
  505. {
  506. m_walkOffset = Point(0,0);
  507. if(m_direction == Otc::North || m_direction == Otc::NorthEast || m_direction == Otc::NorthWest)
  508. m_walkOffset.y = 32 - totalPixelsWalked;
  509. else if(m_direction == Otc::South || m_direction == Otc::SouthEast || m_direction == Otc::SouthWest)
  510. m_walkOffset.y = totalPixelsWalked - 32;
  511.  
  512. if(m_direction == Otc::East || m_direction == Otc::NorthEast || m_direction == Otc::SouthEast)
  513. m_walkOffset.x = totalPixelsWalked - 32;
  514. else if(m_direction == Otc::West || m_direction == Otc::NorthWest || m_direction == Otc::SouthWest)
  515. m_walkOffset.x = 32 - totalPixelsWalked;
  516. }
  517.  
  518. void Creature::updateWalkingTile()
  519. {
  520. // determine new walking tile
  521. TilePtr newWalkingTile;
  522. Rect virtualCreatureRect(Otc::TILE_PIXELS + (m_walkOffset.x - getDisplacementX()),
  523. Otc::TILE_PIXELS + (m_walkOffset.y - getDisplacementY()),
  524. Otc::TILE_PIXELS, Otc::TILE_PIXELS);
  525. for(int xi = -1; xi <= 1 && !newWalkingTile; ++xi) {
  526. for(int yi = -1; yi <= 1 && !newWalkingTile; ++yi) {
  527. Rect virtualTileRect((xi+1)*Otc::TILE_PIXELS, (yi+1)*Otc::TILE_PIXELS, Otc::TILE_PIXELS, Otc::TILE_PIXELS);
  528.  
  529. // only render creatures where bottom right is inside tile rect
  530. if(virtualTileRect.contains(virtualCreatureRect.bottomRight())) {
  531. newWalkingTile = g_map.getOrCreateTile(m_position.translated(xi, yi, 0));
  532. }
  533. }
  534. }
  535.  
  536. if(newWalkingTile != m_walkingTile) {
  537. if(m_walkingTile)
  538. m_walkingTile->removeWalkingCreature(static_self_cast<Creature>());
  539. if(newWalkingTile) {
  540. newWalkingTile->addWalkingCreature(static_self_cast<Creature>());
  541.  
  542. // recache visible tiles in map views
  543. if(newWalkingTile->isEmpty())
  544. g_map.notificateTileUpdate(newWalkingTile->getPosition());
  545. }
  546. m_walkingTile = newWalkingTile;
  547. }
  548. }
  549.  
  550. void Creature::nextWalkUpdate()
  551. {
  552. // remove any previous scheduled walk updates
  553. if(m_walkUpdateEvent)
  554. m_walkUpdateEvent->cancel();
  555.  
  556. // do the update
  557. updateWalk();
  558.  
  559. // schedules next update
  560. if(m_walking) {
  561. auto self = static_self_cast<Creature>();
  562. m_walkUpdateEvent = g_dispatcher.scheduleEvent([self] {
  563. self->m_walkUpdateEvent = nullptr;
  564. self->nextWalkUpdate();
  565. }, getStepDuration() / 32);
  566. }
  567. }
  568.  
  569. void Creature::updateWalk()
  570. {
  571. float walkTicksPerPixel = getStepDuration(true) / 32;
  572. int totalPixelsWalked = std::min<int>(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f);
  573.  
  574. // needed for paralyze effect
  575. m_walkedPixels = std::max<int>(m_walkedPixels, totalPixelsWalked);
  576.  
  577. // update walk animation and offsets
  578. updateWalkAnimation(totalPixelsWalked);
  579. updateWalkOffset(m_walkedPixels);
  580. updateWalkingTile();
  581.  
  582. // terminate walk
  583. if(m_walking && m_walkTimer.ticksElapsed() >= getStepDuration())
  584. terminateWalk();
  585. }
  586.  
  587. void Creature::terminateWalk()
  588. {
  589. // remove any scheduled walk update
  590. if(m_walkUpdateEvent) {
  591. m_walkUpdateEvent->cancel();
  592. m_walkUpdateEvent = nullptr;
  593. }
  594.  
  595. // now the walk has ended, do any scheduled turn
  596. if(m_walkTurnDirection != Otc::InvalidDirection) {
  597. setDirection(m_walkTurnDirection);
  598. m_walkTurnDirection = Otc::InvalidDirection;
  599. }
  600.  
  601. if(m_walkingTile) {
  602. m_walkingTile->removeWalkingCreature(static_self_cast<Creature>());
  603. m_walkingTile = nullptr;
  604. }
  605.  
  606. m_walking = false;
  607. m_walkedPixels = 0;
  608.  
  609. // reset walk animation states
  610. m_walkOffset = Point(0,0);
  611. m_walkAnimationPhase = 0;
  612. }
  613.  
  614. void Creature::setName(const std::string& name)
  615. {
  616. m_nameCache.setText(name);
  617. m_name = name;
  618. }
  619.  
  620. void Creature::setHealthPercent(uint8 healthPercent)
  621. {
  622. if(healthPercent > 92)
  623. m_informationColor = Color(0x00, 0xBC, 0x00);
  624. else if(healthPercent > 60)
  625. m_informationColor = Color(0x50, 0xA1, 0x50);
  626. else if(healthPercent > 30)
  627. m_informationColor = Color(0xA1, 0xA1, 0x00);
  628. else if(healthPercent > 8)
  629. m_informationColor = Color(0xBF, 0x0A, 0x0A);
  630. else if(healthPercent > 3)
  631. m_informationColor = Color(0x91, 0x0F, 0x0F);
  632. else
  633. m_informationColor = Color(0x85, 0x0C, 0x0C);
  634.  
  635. m_healthPercent = healthPercent;
  636. callLuaField("onHealthPercentChange", healthPercent);
  637.  
  638. if(healthPercent <= 0)
  639. onDeath();
  640. }
  641.  
  642. void Creature::setDirection(Otc::Direction direction)
  643. {
  644. assert(direction != Otc::InvalidDirection);
  645. m_direction = direction;
  646. }
  647.  
  648. void Creature::setOutfit(const Outfit& outfit)
  649. {
  650. Outfit oldOutfit = m_outfit;
  651. if(outfit.getCategory() != ThingCategoryCreature) {
  652. if(!g_things.isValidDatId(outfit.getAuxId(), outfit.getCategory()))
  653. return;
  654. m_outfit.setAuxId(outfit.getAuxId());
  655. m_outfit.setCategory(outfit.getCategory());
  656. } else {
  657. if(outfit.getId() > 0 && !g_things.isValidDatId(outfit.getId(), ThingCategoryCreature))
  658. return;
  659. m_outfit = outfit;
  660. }
  661. m_walkAnimationPhase = 0; // might happen when player is walking and outfit is changed.
  662.  
  663. callLuaField("onOutfitChange", m_outfit, oldOutfit);
  664. }
  665.  
  666. void Creature::setOutfitColor(const Color& color, int duration)
  667. {
  668. if(m_outfitColorUpdateEvent) {
  669. m_outfitColorUpdateEvent->cancel();
  670. m_outfitColorUpdateEvent = nullptr;
  671. }
  672.  
  673. if(duration > 0) {
  674. Color delta = (color - m_outfitColor) / (float)duration;
  675. m_outfitColorTimer.restart();
  676. updateOutfitColor(m_outfitColor, color, delta, duration);
  677. }
  678. else
  679. m_outfitColor = color;
  680. }
  681.  
  682. void Creature::updateOutfitColor(Color color, Color finalColor, Color delta, int duration)
  683. {
  684. if(m_outfitColorTimer.ticksElapsed() < duration) {
  685. m_outfitColor = color + delta * m_outfitColorTimer.ticksElapsed();
  686.  
  687. auto self = static_self_cast<Creature>();
  688. m_outfitColorUpdateEvent = g_dispatcher.scheduleEvent([=] {
  689. self->updateOutfitColor(color, finalColor, delta, duration);
  690. }, 100);
  691. }
  692. else {
  693. m_outfitColor = finalColor;
  694. }
  695. }
  696.  
  697. void Creature::setSpeed(uint16 speed)
  698. {
  699. uint16 oldSpeed = m_speed;
  700. m_speed = speed;
  701.  
  702. // speed can change while walking (utani hur, paralyze, etc..)
  703. if(m_walking)
  704. nextWalkUpdate();
  705.  
  706. callLuaField("onSpeedChange", m_speed, oldSpeed);
  707. }
  708.  
  709. void Creature::setBaseSpeed(double baseSpeed)
  710. {
  711. if(m_baseSpeed != baseSpeed) {
  712. double oldBaseSpeed = m_baseSpeed;
  713. m_baseSpeed = baseSpeed;
  714.  
  715. callLuaField("onBaseSpeedChange", baseSpeed, oldBaseSpeed);
  716. }
  717. }
  718.  
  719. void Creature::setSkull(uint8 skull)
  720. {
  721. m_skull = skull;
  722. callLuaField("onSkullChange", m_skull);
  723. }
  724.  
  725. void Creature::setShield(uint8 shield)
  726. {
  727. m_shield = shield;
  728. callLuaField("onShieldChange", m_shield);
  729. }
  730.  
  731. void Creature::setEmblem(uint8 emblem)
  732. {
  733. m_emblem = emblem;
  734. callLuaField("onEmblemChange", m_emblem);
  735. }
  736.  
  737. void Creature::setType(uint8 type)
  738. {
  739. m_type = type;
  740. callLuaField("onTypeChange", m_type);
  741. }
  742.  
  743. void Creature::setIcon(uint8 icon)
  744. {
  745. m_icon = icon;
  746. callLuaField("onIconChange", m_icon);
  747. }
  748.  
  749. void Creature::setSkullTexture(const std::string& filename)
  750. {
  751. m_skullTexture = g_textures.getTexture(filename);
  752. }
  753.  
  754. void Creature::setShieldTexture(const std::string& filename, bool blink)
  755. {
  756. m_shieldTexture = g_textures.getTexture(filename);
  757. m_showShieldTexture = true;
  758.  
  759. if(blink && !m_shieldBlink) {
  760. auto self = static_self_cast<Creature>();
  761. g_dispatcher.scheduleEvent([self]() {
  762. self->updateShield();
  763. }, SHIELD_BLINK_TICKS);
  764. }
  765.  
  766. m_shieldBlink = blink;
  767. }
  768.  
  769. void Creature::setEmblemTexture(const std::string& filename)
  770. {
  771. m_emblemTexture = g_textures.getTexture(filename);
  772. }
  773.  
  774. void Creature::setTypeTexture(const std::string& filename)
  775. {
  776. m_typeTexture = g_textures.getTexture(filename);
  777. }
  778.  
  779. void Creature::setIconTexture(const std::string& filename)
  780. {
  781. m_iconTexture = g_textures.getTexture(filename);
  782. }
  783.  
  784. void Creature::setSpeedFormula(double speedA, double speedB, double speedC)
  785. {
  786. m_speedFormula[Otc::SpeedFormulaA] = speedA;
  787. m_speedFormula[Otc::SpeedFormulaB] = speedB;
  788. m_speedFormula[Otc::SpeedFormulaC] = speedC;
  789. }
  790.  
  791. bool Creature::hasSpeedFormula()
  792. {
  793. return m_speedFormula[Otc::SpeedFormulaA] != -1 && m_speedFormula[Otc::SpeedFormulaB] != -1
  794. && m_speedFormula[Otc::SpeedFormulaC] != -1;
  795. }
  796.  
  797. void Creature::addTimedSquare(uint8 color)
  798. {
  799. m_showTimedSquare = true;
  800. m_timedSquareColor = Color::from8bit(color);
  801.  
  802. // schedule removal
  803. auto self = static_self_cast<Creature>();
  804. g_dispatcher.scheduleEvent([self]() {
  805. self->removeTimedSquare();
  806. }, VOLATILE_SQUARE_DURATION);
  807. }
  808.  
  809. void Creature::updateShield()
  810. {
  811. m_showShieldTexture = !m_showShieldTexture;
  812.  
  813. if(m_shield != Otc::ShieldNone && m_shieldBlink) {
  814. auto self = static_self_cast<Creature>();
  815. g_dispatcher.scheduleEvent([self]() {
  816. self->updateShield();
  817. }, SHIELD_BLINK_TICKS);
  818. }
  819. else if(!m_shieldBlink)
  820. m_showShieldTexture = true;
  821. }
  822.  
  823. Point Creature::getDrawOffset()
  824. {
  825. Point drawOffset;
  826. if(m_walking) {
  827. if(m_walkingTile)
  828. drawOffset -= Point(1,1) * m_walkingTile->getDrawElevation();
  829. drawOffset += m_walkOffset;
  830. } else {
  831. const TilePtr& tile = getTile();
  832. if(tile)
  833. drawOffset -= Point(1,1) * tile->getDrawElevation();
  834. }
  835. return drawOffset;
  836. }
  837.  
  838. int Creature::getStepDuration(bool ignoreDiagonal, Otc::Direction dir)
  839. {
  840. int speed = m_speed;
  841. if(speed < 1)
  842. return 0;
  843.  
  844. if(g_game.getFeature(Otc::GameNewSpeedLaw))
  845. speed *= 2;
  846.  
  847. int groundSpeed = 0;
  848. Position tilePos;
  849.  
  850. if(dir == Otc::InvalidDirection)
  851. tilePos = m_lastStepToPosition;
  852. else
  853. tilePos = m_position.translatedToDirection(dir);
  854.  
  855. if(!tilePos.isValid())
  856. tilePos = m_position;
  857. const TilePtr& tile = g_map.getTile(tilePos);
  858. if(tile) {
  859. groundSpeed = tile->getGroundSpeed();
  860. if(groundSpeed == 0)
  861. groundSpeed = 150;
  862. }
  863.  
  864. int interval = 1000;
  865. if(groundSpeed > 0 && speed > 0)
  866. interval = 1000 * groundSpeed;
  867.  
  868. if(g_game.getFeature(Otc::GameNewSpeedLaw) && hasSpeedFormula()) {
  869. int formulatedSpeed = 1;
  870. if(speed > -m_speedFormula[Otc::SpeedFormulaB]) {
  871. formulatedSpeed = std::max<int>(1, (int)floor((m_speedFormula[Otc::SpeedFormulaA] * log((speed / 2)
  872. + m_speedFormula[Otc::SpeedFormulaB]) + m_speedFormula[Otc::SpeedFormulaC]) + 0.5));
  873. }
  874. interval = std::floor(interval / (double)formulatedSpeed);
  875. }
  876. else
  877. interval /= speed;
  878.  
  879. if(g_game.getClientVersion() >= 900)
  880. interval = (interval / g_game.getServerBeat()) * g_game.getServerBeat();
  881.  
  882. float factor = 3;
  883. if(g_game.getClientVersion() <= 810)
  884. factor = 2;
  885.  
  886. interval = std::max<int>(interval, g_game.getServerBeat());
  887.  
  888. if(!ignoreDiagonal && (m_lastStepDirection == Otc::NorthWest || m_lastStepDirection == Otc::NorthEast ||
  889. m_lastStepDirection == Otc::SouthWest || m_lastStepDirection == Otc::SouthEast))
  890. interval *= factor;
  891.  
  892. return interval;
  893. }
  894.  
  895. Point Creature::getDisplacement()
  896. {
  897. if(m_outfit.getCategory() == ThingCategoryEffect)
  898. return Point(8, 8);
  899. else if(m_outfit.getCategory() == ThingCategoryItem)
  900. return Point(0, 0);
  901. return Thing::getDisplacement();
  902. }
  903.  
  904. int Creature::getDisplacementX()
  905. {
  906. if(m_outfit.getCategory() == ThingCategoryEffect)
  907. return 8;
  908. else if(m_outfit.getCategory() == ThingCategoryItem)
  909. return 0;
  910.  
  911. if(m_outfit.getMount() != 0) {
  912. auto datType = g_things.rawGetThingType(m_outfit.getMount(), ThingCategoryCreature);
  913. return datType->getDisplacementX();
  914. }
  915.  
  916. return Thing::getDisplacementX();
  917. }
  918.  
  919. int Creature::getDisplacementY()
  920. {
  921. if(m_outfit.getCategory() == ThingCategoryEffect)
  922. return 8;
  923. else if(m_outfit.getCategory() == ThingCategoryItem)
  924. return 0;
  925.  
  926. if(m_outfit.getMount() != 0) {
  927. auto datType = g_things.rawGetThingType(m_outfit.getMount(), ThingCategoryCreature);
  928. return datType->getDisplacementY();
  929. }
  930.  
  931. return Thing::getDisplacementY();
  932. }
  933.  
  934. int Creature::getExactSize(int layer, int xPattern, int yPattern, int zPattern, int animationPhase)
  935. {
  936. int exactSize = 0;
  937.  
  938. animationPhase = 0;
  939. xPattern = Otc::South;
  940.  
  941. zPattern = 0;
  942. if(m_outfit.getMount() != 0)
  943. zPattern = 1;
  944.  
  945. for(yPattern = 0; yPattern < getNumPatternY(); yPattern++) {
  946. if(yPattern > 0 && !(m_outfit.getAddons() & (1 << (yPattern-1))))
  947. continue;
  948.  
  949. for(layer = 0; layer < getLayers(); ++layer)
  950. exactSize = std::max<int>(exactSize, Thing::getExactSize(layer, xPattern, yPattern, zPattern, animationPhase));
  951. }
  952.  
  953. return exactSize;
  954. }
  955.  
  956. const ThingTypePtr& Creature::getThingType()
  957. {
  958. return g_things.getThingType(m_outfit.getId(), ThingCategoryCreature);
  959. }
  960.  
  961. ThingType* Creature::rawGetThingType()
  962. {
  963. return g_things.rawGetThingType(m_outfit.getId(), ThingCategoryCreature);
  964. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement