PageRenderTime 77ms CodeModel.GetById 47ms RepoModel.GetById 0ms app.codeStats 1ms

/src/CompoundPart.cpp

https://github.com/bdonlan/openc2e
C++ | 686 lines | 514 code | 111 blank | 61 comment | 153 complexity | ace486cb5a0b371193313d05a7fc251a MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*
  2. * CompoundPart.cpp
  3. * openc2e
  4. *
  5. * Created by Alyssa Milburn on Tue May 25 2004.
  6. * Copyright (c) 2004-2008 Alyssa Milburn. All rights reserved.
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU Lesser General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * Lesser General Public License for more details.
  17. *
  18. */
  19. #include "CompoundPart.h"
  20. #include "CameraPart.h"
  21. #include "Camera.h"
  22. #include "World.h"
  23. #include "Engine.h"
  24. #include "creaturesImage.h"
  25. #include "Backend.h"
  26. #include "Agent.h"
  27. bool partzorder::operator ()(const CompoundPart *s1, const CompoundPart *s2) const {
  28. // TODO: unsure about all of this, needs a check (but seems to work)
  29. if (s1->getParent()->getZOrder() == s2->getParent()->getZOrder()) {
  30. // part 0 is often at the same plane as other parts..
  31. // TODO: is this correct fix? I suspect not, we should be sorting by reaction order, not id.
  32. if (s1->getParent() == s2->getParent()) {
  33. return s1->id > s2->id;
  34. } else
  35. return s1->getZOrder() > s2->getZOrder();
  36. }
  37. // TODO: we shouldn't be checking engine.bmprenderer for this, but it's a cheap/easy way to check for seamonkeys
  38. if (engine.bmprenderer)
  39. return (int)s1->getParent()->getZOrder() > (int)s2->getParent()->getZOrder();
  40. else
  41. return s1->getParent()->getZOrder() > s2->getParent()->getZOrder();
  42. }
  43. shared_ptr<creaturesImage> TextEntryPart::caretsprite;
  44. void CompoundPart::render(Surface *renderer, int xoffset, int yoffset) {
  45. if (parent->visible) {
  46. partRender(renderer, xoffset + (int)parent->x, yoffset + (int)parent->y);
  47. if (parent->displaycore /*&& (id == 0)*/) {
  48. // TODO: tsk, this should be drawn along with the other craziness on the line plane, i expect
  49. int xoff = xoffset + (int)parent->x + x;
  50. int yoff = yoffset + (int)parent->y + y;
  51. renderer->renderLine(xoff + (getWidth() / 2), yoff, xoff + getWidth(), yoff + (getHeight() / 2), 0xFF0000CC);
  52. renderer->renderLine(xoff + getWidth(), yoff + (getHeight() / 2), xoff + (getWidth() / 2), yoff + getHeight(), 0xFF0000CC);
  53. renderer->renderLine(xoff + (getWidth() / 2), yoff + getHeight(), xoff, yoff + (getHeight() / 2), 0xFF0000CC);
  54. renderer->renderLine(xoff, yoff + (getHeight() / 2), xoff + (getWidth() / 2), yoff, 0xFF0000CC);
  55. }
  56. }
  57. }
  58. bool CompoundPart::showOnRemoteCameras() {
  59. return !parent->camerashy();
  60. }
  61. void SpritePart::partRender(Surface *renderer, int xoffset, int yoffset) {
  62. // TODO: we need a nicer way to handle such errors
  63. if (getCurrentSprite() >= getSprite()->numframes()) {
  64. if (engine.version == 2) {
  65. // hack for invalid poses - use the last sprite in the file (as real C2 does)
  66. spriteno = getSprite()->numframes() - 1;
  67. } else {
  68. std::string err = boost::str(boost::format("pose to be rendered %d (firstimg %d, base %d) was past end of sprite file '%s' (%d sprites)") %
  69. pose % firstimg % base % getSprite()->getName() % getSprite()->numframes());
  70. parent->unhandledException(err, false);
  71. return;
  72. }
  73. }
  74. assert(getCurrentSprite() < getSprite()->numframes());
  75. renderer->render(getSprite(), getCurrentSprite(), xoffset + x, yoffset + y, has_alpha, alpha, draw_mirrored);
  76. }
  77. void SpritePart::setFrameNo(unsigned int f) {
  78. assert(f < animation.size());
  79. frameno = f;
  80. }
  81. void SpritePart::setPose(unsigned int p) {
  82. if (firstimg + base + p >= getSprite()->numframes()) {
  83. if (engine.version == 2) {
  84. // hack for invalid poses - use the last sprite in the file (as real C2 does)
  85. spriteno = getSprite()->numframes() - 1;
  86. } else {
  87. // TODO: mention anim frame if animation is non-empty
  88. std::string err = boost::str(boost::format("new pose %d (firstimg %d, base %d) was past end of sprite file '%s' (%d sprites)") %
  89. p % firstimg % base % getSprite()->getName() % getSprite()->numframes());
  90. parent->unhandledException(err, false);
  91. return;
  92. }
  93. } else {
  94. spriteno = firstimg + base + p;
  95. }
  96. pose = p;
  97. }
  98. void SpritePart::setBase(unsigned int b) {
  99. base = b;
  100. }
  101. bool SpritePart::transparentAt(unsigned int x, unsigned int y) {
  102. return getSprite()->transparentAt(getCurrentSprite(), x, y);
  103. }
  104. void CompoundPart::gainFocus() {
  105. assert(false);
  106. }
  107. void CompoundPart::loseFocus() {
  108. throw creaturesException("impossible loseFocus() call");
  109. }
  110. void CompoundPart::handleKey(char c) {
  111. throw creaturesException("impossible handleKey() call");
  112. }
  113. void CompoundPart::handleSpecialKey(char c) {
  114. throw creaturesException("impossible handleSpecialKey() call");
  115. }
  116. int CompoundPart::handleClick(float clickx, float clicky) {
  117. return parent->handleClick(clickx + x + parent->x, clicky + y + parent->y);
  118. }
  119. CompoundPart::CompoundPart(Agent *p, unsigned int _id, int _x, int _y, int _z) : parent(p), zorder(_z), id(_id) {
  120. addZOrder();
  121. x = _x;
  122. y = _y;
  123. has_alpha = false;
  124. }
  125. CompoundPart::~CompoundPart() {
  126. world.zorder.erase(zorder_iter);
  127. }
  128. SpritePart::SpritePart(Agent *p, unsigned int _id, std::string spritefile, unsigned int fimg,
  129. int _x, int _y, unsigned int _z) : AnimatablePart(p, _id, _x, _y, _z) {
  130. origsprite = sprite = world.gallery.getImage(spritefile);
  131. firstimg = fimg;
  132. caos_assert(sprite);
  133. pose = 0;
  134. base = 0;
  135. spriteno = firstimg;
  136. is_transparent = (engine.version > 2);
  137. framerate = 1;
  138. framedelay = 0;
  139. draw_mirrored = false;
  140. if (sprite->numframes() <= firstimg) {
  141. if (world.gametype == "cv") {
  142. // Creatures Village allows you to create sprites with crazy invalid data, do the same as it does
  143. // (obviously the firstimg data is invalid so all attempts to change the pose/etc will fail, same as in CV)
  144. spriteno = 0;
  145. } else if (engine.bmprenderer) {
  146. // BLCK hasn't been called yet, so we can't check validity yet
  147. } else {
  148. throw caosException(boost::str(boost::format("Failed to create sprite part: first sprite %d is beyond %d sprite(s) in file") % firstimg % sprite->numframes()));
  149. }
  150. }
  151. }
  152. SpritePart::~SpritePart() {
  153. }
  154. #include "images/bmpImage.h"
  155. void SpritePart::changeSprite(std::string spritefile, unsigned int fimg) {
  156. shared_ptr<creaturesImage> spr = world.gallery.getImage(spritefile);
  157. caos_assert(spr);
  158. base = 0; // TODO: should we preserve base?
  159. // TODO: this is a hack for the bmprenderer, is it really a good idea?
  160. if (engine.bmprenderer) {
  161. bmpImage *origimg = dynamic_cast<bmpImage *>(sprite.get());
  162. bmpImage *newimg = dynamic_cast<bmpImage *>(spr.get());
  163. if (origimg && newimg && origimg->numframes() > 0) newimg->setBlockSize(origimg->width(0), origimg->height(0));
  164. }
  165. caos_assert(spr->numframes() > fimg);
  166. firstimg = fimg;
  167. // TODO: should we preserve tint?
  168. origsprite = sprite = spr;
  169. setPose(pose); // TODO: we need to preserve pose, but shouldn't we do some sanity checking?
  170. }
  171. void SpritePart::changeSprite(shared_ptr<creaturesImage> spr) {
  172. caos_assert(spr);
  173. // TODO: should we preserve tint?
  174. base = 0; // TODO: should we preserve base?
  175. origsprite = sprite = spr;
  176. setPose(pose); // TODO: we need to preserve pose, but shouldn't we do some sanity checking?
  177. }
  178. unsigned int SpritePart::getWidth() {
  179. return sprite->width(getCurrentSprite());
  180. }
  181. unsigned int SpritePart::getHeight() {
  182. return sprite->height(getCurrentSprite());
  183. }
  184. unsigned int CompoundPart::getZOrder() const {
  185. return parent->getZOrder() + zorder;
  186. }
  187. void CompoundPart::zapZOrder() {
  188. renderable::zapZOrder();
  189. world.zorder.erase(zorder_iter);
  190. }
  191. void CompoundPart::addZOrder() {
  192. renderable::addZOrder();
  193. zorder_iter = world.zorder.insert(this);
  194. }
  195. void SpritePart::tint(unsigned char r, unsigned char g, unsigned char b, unsigned char rotation, unsigned char swap) {
  196. sprite = origsprite->mutableCopy();
  197. sprite->tint(r, g, b, rotation, swap);
  198. }
  199. DullPart::DullPart(Agent *p, unsigned int _id, std::string spritefile, unsigned int fimg, int _x, int _y,
  200. unsigned int _z) : SpritePart(p, _id, spritefile, fimg, _x, _y, _z) {
  201. }
  202. ButtonPart::ButtonPart(Agent *p, unsigned int _id, std::string spritefile, unsigned int fimg, int _x, int _y,
  203. unsigned int _z, const bytestring_t &animhover, int msgid, int option) : SpritePart(p, _id, spritefile, fimg, _x, _y, _z) {
  204. messageid = msgid;
  205. hitopaquepixelsonly = (option == 1);
  206. hoveranimation = animhover;
  207. }
  208. unsigned int calculateScriptId(unsigned int message_id); // from caosVM_agent.cpp, TODO: move into shared file
  209. int ButtonPart::handleClick(float x, float y) {
  210. return calculateScriptId(messageid);
  211. }
  212. void ButtonPart::mouseIn() {
  213. // TODO: what if ANIM is called during mouse hover?
  214. if (hoveranimation.size()) {
  215. oldanim = animation;
  216. animation = hoveranimation;
  217. setFrameNo(0);
  218. }
  219. }
  220. void ButtonPart::mouseOut() {
  221. // TODO: what if ANIM is called during mouse hover?
  222. if (hoveranimation.size()) {
  223. animation = oldanim;
  224. if (animation.size())
  225. setFrameNo(0);
  226. }
  227. }
  228. TextPart::TextPart(Agent *p, unsigned int _id, std::string spritefile, unsigned int fimg, int _x, int _y, unsigned int _z, std::string fontsprite)
  229. : SpritePart(p, _id, spritefile, fimg, _x, _y, _z) {
  230. textsprite = world.gallery.getImage(fontsprite);
  231. caos_assert(textsprite);
  232. caos_assert(textsprite->numframes() == 224);
  233. leftmargin = 8; topmargin = 8; rightmargin = 8; bottommargin = 8;
  234. linespacing = 0; charspacing = 0;
  235. horz_align = leftalign; vert_align = top;
  236. currpage = 0;
  237. recalculateData(); // ie, insert a blank first page
  238. }
  239. TextPart::~TextPart() {
  240. }
  241. void TextPart::addTint(std::string tintinfo) {
  242. // add a tint, starting at text.size(), using the data in tintinfo
  243. // TODO: there's some caching to be done here, but tinting is rather rare, so..
  244. unsigned short r = 128, g = 128, b = 128, rot = 128, swap = 128;
  245. int where = 0;
  246. std::string cur;
  247. for (unsigned int i = 0; i <= tintinfo.size(); i++) {
  248. if (i == tintinfo.size() || tintinfo[i] == ' ') {
  249. unsigned short val = atoi(cur.c_str());
  250. if (val <= 256) {
  251. switch (where) {
  252. case 0: r = val; break;
  253. case 1: g = val; break;
  254. case 2: b = val; break;
  255. case 3: rot = val; break;
  256. case 4: swap = val; break;
  257. }
  258. } // TODO: else explode();
  259. where++;
  260. cur = "";
  261. if (where > 4) break;
  262. } else cur += tintinfo[i];
  263. }
  264. texttintinfo t;
  265. t.offset = text.size();
  266. if (!(r == 128 && g == 128 && b == 128 && rot == 128 && swap == 128)) {
  267. t.sprite = textsprite->mutableCopy();
  268. t.sprite->tint(r, g, b, rot, swap);
  269. } else t.sprite = textsprite;
  270. tints.push_back(t);
  271. }
  272. void TextPart::setText(std::string t) {
  273. text.clear();
  274. tints.clear();
  275. // parse and remove the <tint> tagging
  276. for (unsigned int i = 0; i < t.size(); i++) {
  277. if ((t[i] == '<') && (t.size() > i+4))
  278. if ((t[i + 1] == 't') && (t[i + 2] == 'i') && (t[i + 3] == 'n') && (t[i + 4] == 't')) {
  279. i += 5;
  280. std::string tintinfo;
  281. if (t[i] == ' ') i++; // skip initial space, if any
  282. for (; i < t.size(); i++) {
  283. if (t[i] == '>')
  284. break;
  285. tintinfo += t[i];
  286. }
  287. addTint(tintinfo);
  288. continue;
  289. }
  290. text += t[i];
  291. }
  292. recalculateData();
  293. }
  294. void TextEntryPart::setText(std::string t) {
  295. TextPart::setText(t);
  296. // place caret at the end of the text
  297. caretpos = text.size();
  298. }
  299. unsigned int calculateScriptId(unsigned int message_id); // from caosVM_agent.cpp, TODO: move into shared file
  300. int TextEntryPart::handleClick(float clickx, float clicky) {
  301. world.setFocus(this);
  302. return -1; // TODO: this shouldn't be passed onto the parent agent?
  303. }
  304. void TextEntryPart::handleKey(char c) {
  305. text.insert(caretpos, 1, c);
  306. caretpos++;
  307. recalculateData();
  308. }
  309. void TextEntryPart::handleSpecialKey(char c) {
  310. switch (c) {
  311. case 8: // backspace
  312. if (caretpos == 0) return;
  313. text.erase(text.begin() + (caretpos - 1));
  314. caretpos--;
  315. break;
  316. case 13: // return
  317. // TODO: check if we should do this or a newline
  318. parent->queueScript(calculateScriptId(messageid), 0); // TODO: is a null FROM correct?
  319. return;
  320. case 37: // left
  321. if (caretpos == 0) return;
  322. caretpos--;
  323. return;
  324. case 39: // right
  325. if (caretpos == text.size()) return;
  326. caretpos++;
  327. return;
  328. case 38: // up
  329. case 40: // down
  330. return;
  331. case 46: // delete
  332. if ((text.size() == 0) || (caretpos >= text.size()))
  333. return;
  334. text.erase(text.begin() + caretpos);
  335. break;
  336. default:
  337. return;
  338. }
  339. assert(caretpos <= text.size());
  340. recalculateData();
  341. }
  342. void TextPart::setFormat(int left, int top, int right, int bottom, int line, int _char, horizontalalign horza, verticalalign verta, bool lastpagescroll) {
  343. leftmargin = left;
  344. topmargin = top;
  345. rightmargin = right;
  346. bottommargin = bottom;
  347. linespacing = line;
  348. charspacing = _char;
  349. horz_align = horza;
  350. vert_align = verta;
  351. last_page_scroll = lastpagescroll;
  352. recalculateData();
  353. }
  354. unsigned int TextPart::calculateWordWidth(std::string word) {
  355. unsigned int x = 0;
  356. for (unsigned int i = 0; i < word.size(); i++) {
  357. if (word[i] < 32) continue; // TODO: replace with space or similar?
  358. int spriteid = word[i] - 32;
  359. x += textsprite->width(spriteid);
  360. if (i != 0) x += charspacing;
  361. }
  362. return x;
  363. }
  364. /*
  365. * Recalculate the data used for rendering the text part.
  366. */
  367. void TextPart::recalculateData() {
  368. linedata currentdata;
  369. lines.clear();
  370. pages.clear();
  371. pageheights.clear();
  372. pages.push_back(0);
  373. if (text.size() == 0) {
  374. pageheights.push_back(0);
  375. lines.push_back(currentdata); // blank line, so caret is rendered in TextEntryParts
  376. return;
  377. }
  378. unsigned int textwidth = getWidth() - leftmargin - rightmargin;
  379. unsigned int textheight = getHeight() - topmargin - bottommargin;
  380. unsigned int currenty = 0, usedheight = 0;
  381. unsigned int i = 0;
  382. while (i < text.size()) {
  383. bool newline = false;
  384. unsigned int startoffset = i;
  385. // first, retrieve a word from the text
  386. std::string word;
  387. for (; i < text.size(); i++) {
  388. if ((text[i] == ' ') || (text[i] == '\n')) {
  389. if (text[i] == '\n') newline = true;
  390. i++;
  391. break;
  392. }
  393. word += text[i];
  394. }
  395. // next, work out whether it fits
  396. unsigned int wordlen = calculateWordWidth(word);
  397. unsigned int spacelen = textsprite->width(0) + charspacing;
  398. unsigned int possiblelen = wordlen;
  399. if (currentdata.text.size() > 0)
  400. possiblelen = wordlen + spacelen;
  401. // TODO: set usedheight as appropriate/needed
  402. usedheight = textsprite->height(0);
  403. if (currentdata.width + possiblelen <= textwidth) {
  404. // the rest of the word fits on the current line, so that's okay.
  405. // add a space if we're not the first word on this line
  406. if (currentdata.text.size() > 0) word = std::string(" ") + word;
  407. currentdata.text += word;
  408. currentdata.width += possiblelen;
  409. } else if (wordlen <= textwidth) {
  410. // the rest of the word doesn't fit on the current line, but does on the next line.
  411. if (currenty + usedheight > textheight) {
  412. pageheights.push_back(currenty);
  413. pages.push_back(lines.size());
  414. currenty = 0;
  415. } else currenty += usedheight + linespacing + 1;
  416. currentdata.text += " "; // TODO: HACK THINK ABOUT THIS
  417. lines.push_back(currentdata);
  418. currentdata.reset(startoffset);
  419. currentdata.text += word;
  420. currentdata.width += wordlen;
  421. } else {
  422. // TODO: word is too wide to fit on a single line
  423. // we should output as much as possible and then go backwards
  424. }
  425. // we force a newline here if necessary (ie, if the last char is '\n', except not in the last case)
  426. if ((i < text.size()) && (newline)) {
  427. if (currenty + usedheight > textheight) {
  428. pageheights.push_back(currenty);
  429. pages.push_back(lines.size());
  430. currenty = 0;
  431. } else currenty += usedheight + linespacing + 1;
  432. lines.push_back(currentdata);
  433. currentdata.reset(i);
  434. }
  435. }
  436. if (currentdata.text.size() > 0) {
  437. currenty += usedheight;
  438. if (text[text.size() - 1] == ' ') currentdata.text += " "; // TODO: HACK THINK ABOUT THIS
  439. lines.push_back(currentdata);
  440. }
  441. pageheights.push_back(currenty);
  442. }
  443. void TextPart::partRender(Surface *renderer, int xoffset, int yoffset, TextEntryPart *caretdata) {
  444. SpritePart::partRender(renderer, xoffset, yoffset);
  445. unsigned int xoff = xoffset + x + leftmargin;
  446. unsigned int yoff = yoffset + y + topmargin;
  447. unsigned int textwidth = getWidth() - leftmargin - rightmargin;
  448. unsigned int textheight = getHeight() - topmargin - bottommargin;
  449. unsigned int currenty = 0, usedheight = 0;
  450. if (vert_align == bottom)
  451. currenty = textheight - pageheights[currpage];
  452. else if (vert_align == middle)
  453. currenty = (textheight - pageheights[currpage]) / 2;
  454. unsigned int startline = pages[currpage];
  455. unsigned int endline = (currpage + 1 < pages.size() ? pages[currpage + 1] : lines.size());
  456. shared_ptr<creaturesImage> sprite = textsprite; unsigned int currtint = 0;
  457. for (unsigned int i = startline; i < endline; i++) {
  458. unsigned int currentx = 0, somex = xoff;
  459. if (horz_align == rightalign)
  460. somex = somex + (textwidth - lines[i].width);
  461. else if (horz_align == centeralign)
  462. somex = somex + ((textwidth - lines[i].width) / 2);
  463. for (unsigned int x = 0; x < lines[i].text.size(); x++) {
  464. if (currtint < tints.size() && tints[currtint].offset == lines[i].offset + x) {
  465. sprite = tints[currtint].sprite;
  466. currtint++;
  467. }
  468. if (lines[i].text[x] < 32) continue; // TODO: replace with space or similar?
  469. int spriteid = lines[i].text[x] - 32;
  470. renderer->render(sprite, spriteid, somex + currentx, yoff + currenty, has_alpha, alpha);
  471. if ((caretdata) && (caretdata->caretpos == lines[i].offset + x))
  472. caretdata->renderCaret(renderer, somex + currentx, yoff + currenty);
  473. currentx += textsprite->width(spriteid) + charspacing;
  474. }
  475. if ((caretdata) && (caretdata->caretpos == lines[i].offset + lines[i].text.size()))
  476. caretdata->renderCaret(renderer, somex + currentx, yoff + currenty);
  477. currenty += textsprite->height(0) + linespacing + 1;
  478. }
  479. }
  480. FixedTextPart::FixedTextPart(Agent *p, unsigned int _id, std::string spritefile, unsigned int fimg, int _x, int _y,
  481. unsigned int _z, std::string fontsprite) : TextPart(p, _id, spritefile, fimg, _x, _y, _z, fontsprite) {
  482. // nothing, hopefully.. :)
  483. }
  484. TextEntryPart::TextEntryPart(Agent *p, unsigned int _id, std::string spritefile, unsigned int fimg, int _x, int _y,
  485. unsigned int _z, unsigned int msgid, std::string fontsprite) : TextPart(p, _id, spritefile, fimg, _x, _y, _z, fontsprite) {
  486. // TODO: hm, this never gets freed..
  487. if (!caretsprite) { caretsprite = world.gallery.getImage("cursor"); caos_assert(caretsprite); }
  488. caretpose = 0;
  489. caretpos = 0;
  490. focused = false;
  491. messageid = msgid;
  492. }
  493. void TextEntryPart::partRender(Surface *renderer, int xoffset, int yoffset) {
  494. TextPart::partRender(renderer, xoffset, yoffset, (focused ? this : 0));
  495. }
  496. void TextEntryPart::renderCaret(Surface *renderer, int xoffset, int yoffset) {
  497. // TODO: fudge xoffset/yoffset as required
  498. renderer->render(caretsprite, caretpose, xoffset, yoffset, has_alpha, alpha);
  499. }
  500. void TextEntryPart::tick() {
  501. SpritePart::tick();
  502. if (focused) {
  503. caretpose++;
  504. if (caretpose == caretsprite->numframes())
  505. caretpose = 0;
  506. }
  507. }
  508. void SpritePart::tick() {
  509. if (!animation.empty()) {
  510. if (framerate > 1) {
  511. framedelay++;
  512. if (framedelay == (unsigned int)framerate + 1)
  513. framedelay = 0;
  514. }
  515. }
  516. if (framedelay == 0)
  517. updateAnimation();
  518. }
  519. void AnimatablePart::updateAnimation() {
  520. if (animation.empty()) return;
  521. if (frameno == animation.size()) return;
  522. assert(frameno < animation.size());
  523. if (animation[frameno] == 255) {
  524. if (frameno == (animation.size() - 1)) {
  525. frameno = 0;
  526. } else {
  527. // if we're not at the end, we ought to have at least one more item
  528. assert(frameno + 1 < animation.size());
  529. frameno = animation[frameno + 1];
  530. if (frameno >= animation.size()) {
  531. // this is an internal error because it should have been validated at pose-setting time
  532. std::string err = boost::str(boost::format("internal animation error: tried looping back to frame %d but that is beyond animation size %d") %
  533. (int)frameno % (int)animation.size());
  534. parent->unhandledException(err, false);
  535. animation.clear();
  536. return;
  537. }
  538. }
  539. }
  540. setPose(animation[frameno]);
  541. frameno++;
  542. }
  543. CameraPart::CameraPart(Agent *p, unsigned int _id, std::string spritefile, unsigned int fimg, int _x, int _y,
  544. unsigned int _z, unsigned int view_width, unsigned int view_height, unsigned int camera_width, unsigned int camera_height)
  545. : SpritePart(p, _id, spritefile, fimg, _x, _y, _z) {
  546. viewwidth = view_width;
  547. viewheight = view_height;
  548. camerawidth = camera_width;
  549. cameraheight = camera_height;
  550. camera = shared_ptr<Camera>(new PartCamera(this));
  551. }
  552. void CameraPart::partRender(class Surface *renderer, int xoffset, int yoffset) {
  553. // TODO: hack to stop us rendering cameras inside cameras. better way?
  554. if (renderer == engine.backend->getMainSurface()) {
  555. // make sure we're onscreen before bothering to do any work..
  556. if (xoffset + x + (int)camerawidth >= 0 && yoffset + y + (int)cameraheight >= 0 &&
  557. xoffset + x < (int)renderer->getWidth() && yoffset + y < (int)renderer->getHeight()) {
  558. Surface *surface = engine.backend->newSurface(viewwidth, viewheight);
  559. assert(surface); // TODO: good behaviour?
  560. world.drawWorld(camera.get(), surface);
  561. renderer->blitSurface(surface, xoffset + x, yoffset + y, camerawidth, cameraheight);
  562. engine.backend->freeSurface(surface);
  563. }
  564. }
  565. SpritePart::partRender(renderer, xoffset, yoffset);
  566. }
  567. void CameraPart::tick() {
  568. SpritePart::tick();
  569. camera->tick();
  570. }
  571. GraphPart::GraphPart(Agent *p, unsigned int _id, std::string spritefile, unsigned int fimg, int _x, int _y,
  572. unsigned int _z, unsigned int novalues) : SpritePart(p, _id, spritefile, fimg, _x, _y, _z) {
  573. // TODO: store novalues
  574. }
  575. /* vim: set noet: */