gcsx_layer.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /* GCSx
  2. ** LAYER.CPP
  3. **
  4. ** Layer storage format and layer-related functions
  5. ** Doesn't include any editor-only functionality
  6. */
  7. /*****************************************************************************
  8. ** Copyright (C) 2003-2006 Janson
  9. **
  10. ** This program is free software; you can redistribute it and/or modify
  11. ** it under the terms of the GNU General Public License as published by
  12. ** the Free Software Foundation; either version 2 of the License, or
  13. ** (at your option) any later version.
  14. **
  15. ** This program is distributed in the hope that it will be useful,
  16. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. ** GNU General Public License for more details.
  19. **
  20. ** You should have received a copy of the GNU General Public License
  21. ** along with this program; if not, write to the Free Software
  22. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  23. *****************************************************************************/
  24. #include "all.h"
  25. Layer::Layer(World* myWorld, Scene* myScene, int myId) : name(blankString), nameL(blankString), spawns() { start_func
  26. if (myId) assert(myWorld);
  27. assert(myScene);
  28. world = myWorld;
  29. scene = myScene;
  30. cached = 0;
  31. layerType = LAYER_EMPTY;
  32. xSize = ySize = LAYER_TILE_DEFAULT_SIZE;
  33. tilesetId = 0;
  34. tileset = NULL;
  35. tileData = NULL;
  36. effectsData = NULL;
  37. extendedData = NULL;
  38. usesEffects = 0;
  39. usesExtended = 0;
  40. lockCount = 0;
  41. lockCountPlay = 0;
  42. id = myId;
  43. assert(LAYER_TILE_FLIP == TextureMap::TEXTURE_FLIP);
  44. assert(LAYER_TILE_MIRROR == TextureMap::TEXTURE_MIRROR);
  45. assert(LAYER_TILE_ROTATE == TextureMap::TEXTURE_ROTATE);
  46. }
  47. Layer::~Layer() { start_func
  48. if (tileset) {
  49. for (int pos = 0; pos < lockCount; ++pos) {
  50. tileset->markUnlock();
  51. }
  52. for (int pos = 0; pos < lockCountPlay; ++pos) {
  53. tileset->markUnlockPlay();
  54. }
  55. }
  56. delete[] tileData;
  57. delete[] effectsData;
  58. delete[] extendedData;
  59. vector<Spawn*>::iterator end = spawns.end();
  60. for (vector<Spawn*>::iterator pos = spawns.begin(); pos != end; ++pos) {
  61. assert(*pos);
  62. delete *pos;
  63. }
  64. }
  65. int Layer::getXSize() const { start_func
  66. // @TODO: Valid for image/font layers, but shouldn't be called at this time
  67. assert(layerType == LAYER_TILE);
  68. return xSize;
  69. }
  70. int Layer::getYSize() const { start_func
  71. // @TODO: Valid for image/font layers, but shouldn't be called at this time
  72. assert(layerType == LAYER_TILE);
  73. return ySize;
  74. }
  75. void Layer::loadHeaderNonSpawns(FileRead* file) throw_File { start_func
  76. assert(file);
  77. assert(world);
  78. if (file->getVersion() > 1) {
  79. throw FileException("Unsupported layer version %d", file->getVersion());
  80. }
  81. file->readStr(name);
  82. nameL = name;
  83. toLower(nameL);
  84. layerType = (LayerType)file->readInt();
  85. id = file->readInt();
  86. switch (layerType) {
  87. case LAYER_EMPTY:
  88. break;
  89. case LAYER_TILE:
  90. xSize = file->readInt();
  91. ySize = file->readInt();
  92. tilesetId = file->readInt();
  93. usesExtended = file->readInt();
  94. usesEffects = file->readInt();
  95. tileset = world->findTileSet(tilesetId);
  96. if ((xSize > MAX_LAYERTILESIZE) || (xSize < 0) ||
  97. (ySize > MAX_LAYERTILESIZE) || (ySize < 0) ||
  98. (usesEffects < 0) || (usesEffects > 1) ||
  99. (usesExtended < 0) || (usesExtended > 1) ||
  100. (!tileset) || (!id)) {
  101. throw FileException("Corrupted layer header");
  102. }
  103. break;
  104. case LAYER_IMAGE:
  105. case LAYER_FONT:
  106. // @TODO: error checking
  107. xSize = file->readInt();
  108. ySize = file->readInt();
  109. tilesetId = 0;
  110. usesEffects = 0;
  111. usesExtended = 0;
  112. tileset = NULL;
  113. break;
  114. default:
  115. throw FileException("Corrupted layer header");
  116. }
  117. }
  118. void Layer::loadHeader(FileRead* file) throw_File { start_func
  119. loadHeaderNonSpawns(file);
  120. int numSpawns = file->readInt();
  121. if (numSpawns < 0) throw FileException("Corrupted layer header");
  122. assert(spawns.empty());
  123. for (; numSpawns > 0; --numSpawns) {
  124. Spawn* newSpawn = new Spawn();
  125. try {
  126. newSpawn->load(file, world);
  127. }
  128. catch (...) {
  129. delete newSpawn;
  130. throw;
  131. }
  132. spawns.push_back(newSpawn);
  133. }
  134. cached = 1;
  135. }
  136. void Layer::loadContent(FileRead* file) throw_File { start_func
  137. // We don't delete 'file' if we throw because we're only
  138. // supposed to be called this from Scene
  139. assert(file);
  140. assert(file->getVersion() <= 1);
  141. switch (layerType) {
  142. case LAYER_EMPTY:
  143. case LAYER_IMAGE: // @TODO:
  144. case LAYER_FONT: // @TODO:
  145. break;
  146. case LAYER_TILE:
  147. assert(!tileData);
  148. assert(!effectsData);
  149. assert(!extendedData);
  150. tileData = new Uint32[xSize * ySize];
  151. file->readIntBulk(tileData, xSize * ySize);
  152. if (usesExtended) {
  153. extendedData = new Uint32[xSize * ySize];
  154. file->readIntBulk(extendedData, xSize * ySize);
  155. }
  156. if (usesEffects) {
  157. effectsData = new Uint32[xSize * ySize];
  158. file->readIntBulk(effectsData, xSize * ySize);
  159. }
  160. break;
  161. default:
  162. assert(0);
  163. }
  164. cached = 0;
  165. }
  166. void Layer::cacheLoad() { start_func
  167. // Should never be called
  168. assert(0);
  169. }
  170. void Layer::recacheContent() { start_func
  171. assert(!cached);
  172. assert(!lockCount && !lockCountPlay);
  173. delete[] tileData;
  174. tileData = NULL;
  175. delete[] effectsData;
  176. effectsData = NULL;
  177. delete[] extendedData;
  178. extendedData = NULL;
  179. cached = 1;
  180. }
  181. int Layer::doLock(int play) throw_File { start_func
  182. switch (layerType) {
  183. case LAYER_EMPTY:
  184. case LAYER_IMAGE:
  185. case LAYER_FONT: // @TODO:
  186. break;
  187. case LAYER_TILE:
  188. if (tileset) {
  189. if (play) tileset->markLockPlay();
  190. else tileset->markLock();
  191. }
  192. break;
  193. default:
  194. assert(0);
  195. }
  196. if ((!lockCount) && (!lockCountPlay)) {
  197. SpriteOrder::iterator end = spritesByOrder.end();
  198. for (SpriteOrder::iterator pos = spritesByOrder.begin(); pos != end; ++pos) {
  199. (*pos)->setActive();
  200. }
  201. }
  202. if (play) ++lockCountPlay;
  203. else ++lockCount;
  204. return lockCount + lockCountPlay;
  205. }
  206. int Layer::doUnlock(int play) { start_func
  207. if (play) --lockCountPlay;
  208. else --lockCount;
  209. assert(lockCount >= 0);
  210. assert(lockCountPlay >= 0);
  211. if ((!lockCount) && (!lockCountPlay)) {
  212. SpriteOrder::iterator end = spritesByOrder.end();
  213. for (SpriteOrder::iterator pos = spritesByOrder.begin(); pos != end; ++pos) {
  214. (*pos)->setInactive();
  215. }
  216. }
  217. switch (layerType) {
  218. case LAYER_EMPTY:
  219. case LAYER_IMAGE:
  220. case LAYER_FONT: // @TODO:
  221. break;
  222. case LAYER_TILE:
  223. if (tileset) {
  224. if (play) tileset->markUnlockPlay();
  225. else tileset->markUnlock();
  226. }
  227. break;
  228. default:
  229. assert(0);
  230. }
  231. return lockCount + lockCountPlay;
  232. }
  233. int Layer::markLock() throw_File { start_func
  234. return doLock(0);
  235. }
  236. int Layer::markUnlock() { start_func
  237. return doUnlock(0);
  238. }
  239. int Layer::markLockPlay() throw_File { start_func
  240. return doLock(1);
  241. }
  242. int Layer::markUnlockPlay() { start_func
  243. return doUnlock(1);
  244. }
  245. int Layer::isContentCached() const { start_func
  246. return cached;
  247. }
  248. int Layer::isLocked() const { start_func
  249. return lockCount + lockCountPlay;
  250. }
  251. void Layer::draw(int viewX, int viewY) { start_func
  252. assert(lockCountPlay > 0);
  253. switch (layerType) {
  254. case LAYER_EMPTY:
  255. case LAYER_IMAGE:
  256. case LAYER_FONT: // @TODO:
  257. break;
  258. case LAYER_TILE:
  259. drawTile(viewX, viewY);
  260. break;
  261. default:
  262. assert(0);
  263. }
  264. drawSprites(viewX, viewY);
  265. }
  266. void Layer::drawSprites(int viewX, int viewY) { start_func
  267. SpriteOrder::iterator end = spritesByOrder.end();
  268. for (SpriteOrder::iterator pos = spritesByOrder.begin(); pos != end; ++pos) {
  269. (*pos)->draw(viewX, viewY);
  270. }
  271. }
  272. void Layer::drawTile(int viewX, int viewY) { start_func
  273. if (tileset) {
  274. const TextureMap* texm = tileset->getTexture();
  275. assert(texm);
  276. int tw = tileset->getWidth();
  277. int th = tileset->getHeight();
  278. int tc = tileset->getCount();
  279. int wrap = 1;
  280. int startX, y, startXTile, startYTile, maxX, maxY, pitch;
  281. int lastColor = -1;
  282. int lastAlpha = -1;
  283. // @TODO: possible optimization knowing entire layer is one color/alpha/orientation
  284. if (wrap) {
  285. // Tile in upper-left corner, or nearest-to but starting offscreen
  286. startXTile = (viewX - ((viewX < 0) ? tw - 1 : 0)) / tw;
  287. startYTile = (viewY - ((viewY < 0) ? th - 1 : 0)) / th;
  288. // Pixel coordinate of that tile
  289. startX = startXTile * tw - viewX;
  290. y = startYTile * th - viewY;
  291. // Adjust to correct wrapped tile
  292. startXTile = startXTile % xSize;
  293. if (startXTile < 0) startXTile += xSize;
  294. startYTile = startYTile % ySize;
  295. if (startYTile < 0) startYTile += ySize;
  296. // Farthest right/down *pixel* we draw
  297. // This is an inner bound, not an outer- we draw this pixel, but not the next
  298. maxX = screenWidth - 1;
  299. maxY = screenHeight - 1;
  300. // End-of-line pitch
  301. pitch = (maxX - startX + tw) / tw;
  302. pitch = pitch % xSize;
  303. pitch = xSize - pitch;
  304. if (pitch <= startXTile) pitch += xSize;
  305. }
  306. else {
  307. // True starting point, based on viewpoint
  308. startX = -viewX;
  309. y = -viewY;
  310. // Which tile is closest to upper left corner
  311. startXTile = -(startX / tw);
  312. startYTile = -(y / th);
  313. // Behavior at edge
  314. if (startXTile < 0) startXTile = 0;
  315. if (startYTile < 0) startYTile = 0;
  316. // Adjust starting point to closest tile to upper left
  317. startX += startXTile * tw;
  318. y += startYTile * th;
  319. // Farthest right/down *pixel* we draw- bounded to size
  320. // This is an inner bound, not an outer- we draw this pixel, but not the next
  321. maxX = min(screenWidth - 1, startX + tw * (xSize - startXTile) - 1);
  322. maxY = min(screenHeight - 1, y + th * (ySize - startYTile) - 1);
  323. // (skip display if nothing to show)
  324. if ((maxX < 0) || (maxY < 0) ||
  325. (startX > maxX) || (y > maxY)) return;
  326. // End-of-line pitch
  327. pitch = (maxX - startX + tw) / tw;
  328. pitch = xSize - pitch;
  329. }
  330. // Starting point
  331. Uint32* tiles = tileData + startXTile + startYTile * xSize;
  332. int x = startX;
  333. // (these only matter if wrapping)
  334. int xt = startXTile;
  335. int yt = startYTile;
  336. // Extended data
  337. Uint32* ext = NULL;
  338. if (usesExtended) ext = extendedData + startXTile + startYTile * xSize;
  339. // Optimize loop based on possible settings
  340. // Compiler will optimize-out clauses/conditionals this way
  341. #define drawLayerLoop(doWrap, doExt) \
  342. while (1) { \
  343. int data = *tiles++; \
  344. int alpha; \
  345. if (ext) alpha = *ext++; \
  346. int tile = data & LAYER_TILE_INDEX; \
  347. if ((tile) && (tile <= tc)) { \
  348. int color = data & LAYER_TILE_COLOR; \
  349. if ((color != lastColor) || ((ext) && (alpha != lastAlpha))) { \
  350. lastColor = color; \
  351. if (ext) { \
  352. lastAlpha = alpha; \
  353. glColor4f((color & LAYER_TILE_COLOR_R) / (float)LAYER_TILE_COLOR_R, \
  354. (color & LAYER_TILE_COLOR_G) / (float)LAYER_TILE_COLOR_G, \
  355. (color & LAYER_TILE_COLOR_B) / (float)LAYER_TILE_COLOR_B, \
  356. alpha / (float)LAYER_EXT_ALPHA \
  357. ); \
  358. } \
  359. else { \
  360. glColor3f((color & LAYER_TILE_COLOR_R) / (float)LAYER_TILE_COLOR_R, \
  361. (color & LAYER_TILE_COLOR_G) / (float)LAYER_TILE_COLOR_G, \
  362. (color & LAYER_TILE_COLOR_B) / (float)LAYER_TILE_COLOR_B \
  363. ); \
  364. } \
  365. } \
  366. texm->draw(tile, x, y, data & LAYER_TILE_ORIENTATION); \
  367. } \
  368. x += tw; \
  369. if (doWrap) { \
  370. xt = (xt + 1) % xSize; \
  371. if (xt == 0) { \
  372. tiles -= xSize; \
  373. if (ext) ext -= xSize; \
  374. } \
  375. } \
  376. if (x > maxX) { \
  377. y += th; \
  378. if (y > maxY) break; \
  379. x = startX; \
  380. tiles += pitch; \
  381. if (ext) ext += pitch; \
  382. if (doWrap) { \
  383. xt = startXTile; \
  384. yt = (yt + 1) % ySize; \
  385. if (yt == 0) { \
  386. tiles -= ySize * xSize; \
  387. if (ext) ext -= ySize * xSize; \
  388. } \
  389. } \
  390. } \
  391. }
  392. if (wrap) {
  393. if (usesExtended) {
  394. drawLayerLoop(1, 1);
  395. }
  396. else {
  397. drawLayerLoop(1, 0);
  398. }
  399. }
  400. else {
  401. if (usesExtended) {
  402. drawLayerLoop(0, 1);
  403. }
  404. else {
  405. drawLayerLoop(0, 0);
  406. }
  407. }
  408. }
  409. }
  410. void Layer::spawnLayer() { start_func
  411. vector<Spawn*>::iterator end = spawns.end();
  412. WorldPlay* wp = dynamic_cast<WorldPlay*>(world);
  413. assert(wp);
  414. for (vector<Spawn*>::iterator pos = spawns.begin(); pos != end; ++pos) {
  415. assert(*pos);
  416. (*pos)->generate(this, wp);
  417. }
  418. }
  419. void Layer::indexSprite(Sprite* addSprite) { start_func
  420. assert(addSprite);
  421. WorldPlay* wp = dynamic_cast<WorldPlay*>(world);
  422. assert(wp);
  423. wp->indexSprite(addSprite);
  424. orderSprite(addSprite);
  425. if (lockCount || lockCountPlay) addSprite->setActive();
  426. }
  427. void Layer::deindexSprite(Sprite* remSprite) { start_func
  428. assert(remSprite);
  429. WorldPlay* wp = dynamic_cast<WorldPlay*>(world);
  430. assert(wp);
  431. if (lockCount || lockCountPlay) remSprite->setInactive();
  432. wp->deindexSprite(remSprite);
  433. deorderSprite(remSprite);
  434. }
  435. void Layer::orderSprite(Sprite* addSprite) { start_func
  436. assert(addSprite);
  437. assert(spritesByOrder.find(addSprite) == spritesByOrder.end());
  438. spritesByOrder.insert(addSprite);
  439. }
  440. void Layer::deorderSprite(Sprite* remSprite) { start_func
  441. assert(remSprite);
  442. spritesByOrder.erase(remSprite);
  443. }