PageRenderTime 55ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/CompoundPart.cpp

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