gcsx_layeredit.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. /* GCSx
  2. ** LAYEREDIT.CPP
  3. **
  4. ** Layer editor-only functionality
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. #include "all.h"
  24. LayerEdit::LayerEdit(WorldEdit* myWorld, Scene* myScene, int myId) : Layer(myWorld, myScene, myId) { start_func
  25. if (myId) assert(myWorld);
  26. if (myId) {
  27. // Find an unused "Untitled" name
  28. string findName("layer");
  29. int pos = 1;
  30. while (myScene->findLayer(findName) >= 0) {
  31. findName = "layer ";
  32. findName += intToStr(++pos);
  33. }
  34. findName[0] = toupper(findName[0]);
  35. name = findName;
  36. headerModified = contentModified = 1;
  37. }
  38. else headerModified = contentModified = 0;
  39. nameL = name;
  40. toLower(nameL);
  41. disassociated = 0;
  42. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_CREATE, this, 0, 0);
  43. }
  44. LayerEdit::~LayerEdit() { start_func
  45. // @TODO: doesn't appear to be needed; no way to delete accidentally
  46. // if (!disassociated) desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_DELETE, this, 0, 0);
  47. }
  48. void LayerEdit::setInfo(WorldEdit* myWorld, int newId) { start_func
  49. world = myWorld;
  50. id = newId;
  51. }
  52. void LayerEdit::disassociate() throw_File { start_func
  53. // Ensure not cached- our exception point
  54. scene->cacheLoad();
  55. // Notify objects
  56. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_DELETE, this, 0, 0);
  57. disassociated = 1;
  58. }
  59. void LayerEdit::reassociate() { start_func
  60. assert(disassociated);
  61. // Notify objects
  62. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_CREATE, this, 0, 0);
  63. // Ensure written to file
  64. headerModified = contentModified = 1;
  65. disassociated = 0;
  66. }
  67. SpawnEdit* LayerEdit::getSpawn(int pos) { start_func
  68. assert(pos >= 0);
  69. assert(pos < (int)spawns.size());
  70. return dynamic_cast<SpawnEdit*>(spawns[pos]);
  71. }
  72. void LayerEdit::blitSpawns(int pX, int pY, int pW, int pH, int offsetX, int offsetY, SDL_Surface* dest, const set<int>* selected, int dim) { start_func
  73. vector<Spawn*>::iterator end = spawns.end();
  74. for (vector<Spawn*>::iterator pos = spawns.begin(); pos != end; ++pos) {
  75. SpawnEdit* spawn = dynamic_cast<SpawnEdit*>(*pos);
  76. int sel = 0;
  77. if (selected) sel = selected->count(spawn->getId());
  78. spawn->blit(offsetX, offsetY, dest, pX, pY, pW, pH, dim, sel);
  79. }
  80. }
  81. void LayerEdit::blit(int pX, int pY, int pW, int pH, int offsetX, int offsetY, SDL_Surface* dest, int dim) { start_func
  82. if ((layerType == LAYER_TILE) && (tileset)) {
  83. blitTileLayer(dynamic_cast<TileSetEdit*>(tileset), tileData, usesExtended ? extendedData : NULL, xSize, ySize, pX, pY, pW, pH, offsetX, offsetY, dest, dim);
  84. }
  85. }
  86. // doesn't get passed or support effects layer
  87. void LayerEdit::blitTileLayer(const TileSetEdit* tileset, const Uint32* data, const Uint32* dataExt, int width, int height, int pX, int pY, int pW, int pH, int offsetX, int offsetY, SDL_Surface* dest, int dim, const Uint32* selData, const Uint32* selDataExt, int selXOffs, int selYOffs, int selWidth) { start_func
  88. if (pX < 0) {
  89. pW += pX;
  90. pX = 0;
  91. }
  92. if (pY < 0) {
  93. pH += pY;
  94. pY = 0;
  95. }
  96. if (pW <= 0) return;
  97. if (pH <= 0) return;
  98. int tileWidth = tileset->getWidth();
  99. int tileHeight = tileset->getHeight();
  100. // Determine first/last rows to show
  101. int tY = pY / tileHeight;
  102. int bottom = (pY + pH - 1) / tileHeight;
  103. if (bottom >= height) bottom = height - 1;
  104. if (tY > bottom) return;
  105. // Determine first/last tiles of each row we need to show
  106. int leftmost = pX / tileWidth;
  107. int rightmost = (pX + pW - 1) / tileWidth;
  108. if (rightmost >= width) rightmost = width - 1;
  109. if (leftmost > rightmost) return;
  110. // Determine the starting x/y pixel
  111. pX = offsetX + leftmost * tileWidth;
  112. pY = offsetY + tY * tileHeight;
  113. data = data + leftmost + tY * width;
  114. if (dataExt) dataExt = dataExt + leftmost + tY * width;
  115. if (selData) {
  116. selData = selData + leftmost - selXOffs + (tY - selYOffs) * width;
  117. if (selDataExt) selDataExt = selDataExt + leftmost - selXOffs + (tY - selYOffs) * width;
  118. }
  119. int pitch = width - rightmost + leftmost - 1;
  120. for (; tY <= bottom; ++tY) {
  121. for (int tX = leftmost; tX <= rightmost; ++tX) {
  122. // Draw tile
  123. // @TODO: animate?, collision?
  124. Uint32 dat = *data;
  125. Uint32 ext = LAYER_EXT_DEFAULT;
  126. if (dataExt) ext = *dataExt;
  127. // Apply floating selection if present, and within proper range
  128. if (selData) {
  129. if ((tX < width + selXOffs) && (tX >= selXOffs) && (tY < height + selYOffs) && (tY >= selYOffs)) {
  130. if (*selData & LAYER_TILE_INDEX) {
  131. dat = *selData;
  132. if (selDataExt) ext = *selDataExt;
  133. }
  134. }
  135. }
  136. int tile = dat & LAYER_TILE_INDEX;
  137. if (tile) {
  138. int alpha = ext & LAYER_EXT_ALPHA;
  139. alpha = alpha >> LAYER_EXT_ALPHA_SHIFT;
  140. if (dim) alpha = alpha / 2 + 1;
  141. tileset->blitTileFx(tile, dest, pX, pY, alpha,
  142. dat & LAYER_TILE_MIRROR,
  143. dat & LAYER_TILE_FLIP,
  144. dat & LAYER_TILE_ROTATE,
  145. (dat & LAYER_TILE_COLOR) >> LAYER_TILE_COLOR_SHIFT);
  146. }
  147. pX += tileWidth;
  148. ++data;
  149. if (dataExt) ++dataExt;
  150. if (selData) ++selData;
  151. if (selDataExt) ++selDataExt;
  152. }
  153. pX = offsetX + leftmost * tileWidth;
  154. pY += tileHeight;
  155. data += pitch;
  156. if (dataExt) dataExt += pitch;
  157. if (selData) selData += pitch;
  158. if (selDataExt) selDataExt += pitch;
  159. }
  160. }
  161. void LayerEdit::setHeaderModified() { start_func
  162. getSceneEdit()->setHeaderModified();
  163. headerModified = 1;
  164. }
  165. void LayerEdit::setContentModified() { start_func
  166. getSceneEdit()->setContentModified();
  167. contentModified = 1;
  168. }
  169. void LayerEdit::loadLayer(Uint32* dest, Uint32* destExt, Uint32* destFx, int destPitch, const Rect& rect) const { start_func
  170. assert(layerType == LAYER_TILE);
  171. assert(rect.x >= 0);
  172. assert(rect.y >= 0);
  173. assert(rect.x + rect.w <= xSize);
  174. assert(rect.y + rect.h <= ySize);
  175. assert(dest);
  176. assert(destPitch >= rect.w);
  177. assert(!cached);
  178. matrixCopy(tileData + rect.x + rect.y * xSize, dest,
  179. rect.w * 4, rect.h, xSize * 4, destPitch * 4);
  180. if ((destExt) && (usesExtended)) {
  181. matrixCopy(extendedData + rect.x + rect.y * xSize, destExt,
  182. rect.w * 4, rect.h, xSize * 4, destPitch * 4);
  183. }
  184. if ((destFx) && (usesEffects)) {
  185. matrixCopy(effectsData + rect.x + rect.y * xSize, destFx,
  186. rect.w * 4, rect.h, xSize * 4, destPitch * 4);
  187. }
  188. }
  189. void LayerEdit::saveLayer(const Uint32* source, const Uint32* sourceExt, const Uint32* sourceFx, int sourcePitch, const Rect& rect, Window* exWin) { start_func
  190. assert(layerType == LAYER_TILE);
  191. assert(rect.x >= 0);
  192. assert(rect.y >= 0);
  193. assert(rect.x + rect.w <= xSize);
  194. assert(rect.y + rect.h <= ySize);
  195. assert(source);
  196. assert(sourcePitch >= rect.w);
  197. assert(!cached);
  198. matrixCopy(source + rect.x + rect.y * sourcePitch, tileData,
  199. rect.w * 4, rect.h, sourcePitch * 4, xSize * 4);
  200. if ((sourceExt) && (usesExtended)) {
  201. matrixCopy(sourceExt + rect.x + rect.y * sourcePitch, extendedData,
  202. rect.w * 4, rect.h, sourcePitch * 4, xSize * 4);
  203. }
  204. if ((sourceFx) && (usesEffects)) {
  205. matrixCopy(sourceFx + rect.x + rect.y * sourcePitch, effectsData,
  206. rect.w * 4, rect.h, sourcePitch * 4, xSize * 4);
  207. }
  208. setContentModified();
  209. // Send out update event
  210. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_TILES, this, 0, 0, exWin);
  211. }
  212. void LayerEdit::swapLayer(Uint32* data, Uint32* dataExt, Uint32* dataFx, int dataPitch, const Rect& rect) { start_func
  213. assert(layerType == LAYER_TILE);
  214. assert(rect.x >= 0);
  215. assert(rect.y >= 0);
  216. assert(rect.x + rect.w <= xSize);
  217. assert(rect.y + rect.h <= ySize);
  218. assert(data || dataFx || dataExt);
  219. assert(dataPitch >= rect.w);
  220. assert(!cached);
  221. int lineSize = rect.w * 4;
  222. Uint32* ourData = NULL;
  223. Uint32* ourDataExt = NULL;
  224. Uint32* ourDataFx = NULL;
  225. if (data) ourData = tileData + rect.x + rect.y * xSize;
  226. if ((dataExt) && (usesExtended)) ourDataExt = extendedData + rect.x + rect.y * xSize;
  227. if ((dataFx) && (usesEffects)) ourDataFx = effectsData + rect.x + rect.y * xSize;
  228. // (optimization)
  229. if ((!ourData) && (!ourDataExt) && (!ourDataFx)) return;
  230. Uint32* temp = new Uint32[rect.w];
  231. for (int pos = rect.h; pos > 0; --pos) {
  232. if (ourData) {
  233. memcpy(temp, data, lineSize);
  234. memcpy(data, ourData, lineSize);
  235. memcpy(ourData, temp, lineSize);
  236. ourData += xSize;
  237. data += dataPitch;
  238. }
  239. if (ourDataExt) {
  240. memcpy(temp, dataExt, lineSize);
  241. memcpy(dataExt, ourDataExt, lineSize);
  242. memcpy(ourDataExt, temp, lineSize);
  243. ourDataExt += xSize;
  244. dataExt += dataPitch;
  245. }
  246. if (ourDataFx) {
  247. memcpy(temp, dataFx, lineSize);
  248. memcpy(dataFx, ourDataFx, lineSize);
  249. memcpy(ourDataFx, temp, lineSize);
  250. ourDataFx += xSize;
  251. dataFx += dataPitch;
  252. }
  253. }
  254. setContentModified();
  255. // Send out update event
  256. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_TILES, this, 0, 0);
  257. delete[] temp;
  258. }
  259. void LayerEdit::setName(const std::string& newName, Window* srcWin, Window* exWin) throw_Undo { start_func
  260. if (name != newName) {
  261. setHeaderModified();
  262. if (world) getWorldEdit()->undo.storeUndoName(UndoBuffer::UNDO_LAYERNAME, id, name, newName, srcWin);
  263. name = newName;
  264. nameL = newName;
  265. toLower(nameL);
  266. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_NAME, this, 0, 0, exWin);
  267. }
  268. }
  269. void LayerEdit::setType(LayerType newType, Window* srcWin, Window* exWin) throw_Undo { start_func
  270. assert(!cached);
  271. if (newType != layerType) {
  272. setHeaderModified();
  273. setContentModified();
  274. if (world) {
  275. getWorldEdit()->undo.preUndoBlock();
  276. // @TODO: This won't store the destroyed tileset/effects/collision settings!
  277. getWorldEdit()->undo.storeUndoLayerType(id, layerType, srcWin);
  278. }
  279. layerType = newType;
  280. if (layerType != LAYER_TILE) {
  281. if (tileset) {
  282. for (int pos = 0; pos < lockCount; ++pos) {
  283. tileset->markUnlock();
  284. }
  285. }
  286. tilesetId = 0;
  287. tileset = NULL;
  288. usesEffects = 0;
  289. usesExtended = 0;
  290. }
  291. // Send this first so items note the changed type before noting sizes
  292. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_TYPE, this, 0, 0, exWin);
  293. // setSize() handles most of the work for us.
  294. // If throws undo, the previous storeUndoLayerType will undo the above changes
  295. // @TODO: don't call this if undoing- undo will call it in a separate block
  296. // Purposely don't pass on exWin as this is a "cascaded" change that may not be expected
  297. setSize(xSize, ySize, srcWin);
  298. if (world) getWorldEdit()->undo.postUndoBlock();
  299. }
  300. }
  301. void LayerEdit::setEffects(int newUsesEffects, Window* srcWin, Window* exWin) throw_Undo { start_func
  302. assert(!cached);
  303. assert(layerType == LAYER_TILE);
  304. usesEffects = usesEffects ? 1 : 0;
  305. newUsesEffects = newUsesEffects ? 1 : 0;
  306. if (usesEffects != newUsesEffects) {
  307. setHeaderModified();
  308. setContentModified();
  309. if (world) {
  310. getWorldEdit()->undo.preUndoBlock();
  311. getWorldEdit()->undo.storeUndoLayerFx(id, usesEffects, srcWin);
  312. }
  313. usesEffects = newUsesEffects;
  314. // setSize() handles most of the work for us.
  315. // @TODO: don't call this if undoing- undo will call it in a separate block
  316. // Purposely don't pass on exWin as this is a "cascaded" change that may not be expected
  317. setSize(xSize, ySize, srcWin);
  318. if (world) getWorldEdit()->undo.postUndoBlock();
  319. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_USESFX, this, 0, 0, exWin);
  320. }
  321. }
  322. void LayerEdit::setExtended(int newUsesExtended, Window* srcWin, Window* exWin) throw_Undo { start_func
  323. assert(!cached);
  324. assert(layerType == LAYER_TILE);
  325. usesExtended = usesExtended ? 1 : 0;
  326. newUsesExtended = newUsesExtended ? 1 : 0;
  327. if (usesExtended != newUsesExtended) {
  328. setHeaderModified();
  329. setContentModified();
  330. if (world) {
  331. getWorldEdit()->undo.preUndoBlock();
  332. getWorldEdit()->undo.storeUndoLayerExt(id, usesExtended, srcWin);
  333. }
  334. usesExtended = newUsesExtended;
  335. // setSize() handles most of the work for us.
  336. // @TODO: don't call this if undoing- undo will call it in a separate block
  337. // Purposely don't pass on exWin as this is a "cascaded" change that may not be expected
  338. setSize(xSize, ySize, srcWin);
  339. if (world) getWorldEdit()->undo.postUndoBlock();
  340. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_USESEXT, this, 0, 0, exWin);
  341. }
  342. }
  343. void LayerEdit::setSize(int newX, int newY, Window* srcWin, Window* exWin, Uint32** undo1, Uint32** undo2, Uint32** undo3) throw_Undo { start_func
  344. assert(!cached);
  345. assert(newX >= 1);
  346. assert(newY >= 1);
  347. Uint32* newTileData = NULL;
  348. Uint32* newExtendedData = NULL;
  349. Uint32* newEffectsData = NULL;
  350. setHeaderModified();
  351. setContentModified();
  352. // Undo method?
  353. if (undo1) {
  354. if (layerType == LAYER_TILE) {
  355. swap(*undo1, tileData);
  356. swap(*undo3, extendedData);
  357. swap(*undo2, effectsData);
  358. }
  359. }
  360. else {
  361. try {
  362. // @TODO: Allocate space for FONT and IMAGE layers
  363. // Reallocate
  364. if (layerType == LAYER_TILE) {
  365. assert(newX <= MAX_LAYERTILESIZE);
  366. assert(newY <= MAX_LAYERTILESIZE);
  367. // Don't do anything unless size/effects status changes OR we have a NULL ptr currently
  368. if ((newX != xSize) || (newY != ySize) || (!tileData) ||
  369. ((usesEffects) && (!effectsData)) || ((!usesEffects) && (effectsData)) ||
  370. ((usesExtended) && (!extendedData)) || ((!usesExtended) && (extendedData))) {
  371. newTileData = new Uint32[newX * newY];
  372. memSet32(newTileData, LAYER_TILE_DEFAULT, newX * newY);
  373. if (tileData != NULL) matrixCopy(tileData, newTileData, min(newX, xSize) * 4, min(newY, ySize), xSize * 4, newX * 4);
  374. if (usesExtended) {
  375. newExtendedData = new Uint32[newX * newY];
  376. memSet32(newExtendedData, LAYER_EXT_DEFAULT, newX * newY);
  377. if (extendedData != NULL) matrixCopy(extendedData, newExtendedData, min(newX, xSize) * 4, min(newY, ySize), xSize * 4, newX * 4);
  378. }
  379. if (usesEffects) {
  380. newEffectsData = new Uint32[newX * newY];
  381. memSet32(newEffectsData, LAYER_FX_DEFAULT, newX * newY);
  382. if (effectsData != NULL) matrixCopy(effectsData, newEffectsData, min(newX, xSize) * 4, min(newY, ySize), xSize * 4, newX * 4);
  383. }
  384. // Store undo (instead of deletion) - exception point
  385. if (world) {
  386. getWorldEdit()->undo.storeUndoLayerSize(id, xSize, ySize, tileData, extendedData, effectsData, srcWin);
  387. }
  388. else {
  389. delete[] tileData;
  390. delete[] extendedData;
  391. delete[] effectsData;
  392. }
  393. tileData = newTileData;
  394. extendedData = newExtendedData;
  395. effectsData = newEffectsData;
  396. newTileData = NULL;
  397. newExtendedData = NULL;
  398. newEffectsData = NULL;
  399. }
  400. }
  401. }
  402. catch (...) {
  403. delete[] newTileData;
  404. delete[] newExtendedData;
  405. delete[] newEffectsData;
  406. throw;
  407. }
  408. }
  409. int modified = 0;
  410. if (xSize != newX) modified |= OBJMOD_WIDTH;
  411. if (ySize != newY) modified |= OBJMOD_HEIGHT;
  412. xSize = newX;
  413. ySize = newY;
  414. if (modified) desktop->broadcastObjChange(OBJ_LAYER | modified, this, 0, 0, exWin);
  415. }
  416. void LayerEdit::setTileset(int newTilesetId, Window* srcWin, Window* exWin) throw_File throw_Undo { start_func
  417. assert(!cached);
  418. assert(layerType == LAYER_TILE);
  419. assert(world);
  420. if (tilesetId != newTilesetId) {
  421. TileSet* newTileset = NULL;
  422. setHeaderModified();
  423. // This is our (undo) exception point
  424. if (world) getWorldEdit()->undo.storeUndoLayerTileSet(id, tilesetId, srcWin);
  425. // We have to allow tileset 0 for undo system when deleting a layer
  426. if (newTilesetId) {
  427. // We handle the new set first in case it file-excepts, we can go backwards
  428. newTileset = world->findTileSet(newTilesetId);
  429. assert(newTileset);
  430. try {
  431. for (int pos = 0; pos < lockCount; ++pos) {
  432. // This is our (file) exception point
  433. newTileset->markLock();
  434. }
  435. }
  436. catch (...) {
  437. if (world) getWorldEdit()->undo.cancelLastUndo();
  438. throw;
  439. }
  440. }
  441. if (tileset) {
  442. for (int pos = 0; pos < lockCount; ++pos) {
  443. tileset->markUnlock();
  444. }
  445. }
  446. tileset = newTileset;
  447. tilesetId = newTilesetId;
  448. // Don't broadcast if no tileset; presumably layer is about to be deleted anyways
  449. if (tileset) {
  450. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_TILESET, this, 0, 0, exWin);
  451. // Scan data and set any tiles that are > number of tiles in tileset to 0
  452. // @TODO: Make this an option, with a popup warning
  453. clearTilesAbove(tileset->getCount());
  454. }
  455. }
  456. }
  457. void LayerEdit::clearTilesAbove(int code) { start_func
  458. assert(!cached);
  459. assert(layerType == LAYER_TILE);
  460. int setmod = 0;
  461. if (tileData) {
  462. for (int pos = xSize * ySize - 1; pos >= 0; --pos) {
  463. if ((int)(tileData[pos] & LAYER_TILE_INDEX) > code) {
  464. if (!setmod) setmod = 1;
  465. tileData[pos] = LAYER_TILE_DEFAULT;
  466. if (extendedData) extendedData[pos] = LAYER_EXT_DEFAULT;
  467. // (keep effects)
  468. }
  469. }
  470. }
  471. if (setmod) {
  472. setContentModified();
  473. // Purposely no exWin as this is a "cascaded" change that may not be expected
  474. desktop->broadcastObjChange(OBJ_LAYER | OBJMOD_TILES, this, 0, 0);
  475. }
  476. }
  477. void LayerEdit::addSpawn(SpawnEdit* newSpawn, Window* srcWin, Window* exWin) throw_Undo throw_File { start_func
  478. assert(!cached);
  479. assert(newSpawn);
  480. // Exception points
  481. if (lockCount) newSpawn->markLock();
  482. try {
  483. if (world) getWorldEdit()->undo.storeUndoSpawnCreate(newSpawn->getId(), id, srcWin);
  484. }
  485. catch (...) {
  486. if (lockCount) newSpawn->markUnlock();
  487. throw;
  488. }
  489. setHeaderModified();
  490. spawns.push_back(newSpawn);
  491. if (world) getWorldEdit()->indexSpawn(newSpawn);
  492. desktop->broadcastObjChange(OBJ_SPAWN | OBJMOD_CREATE, newSpawn, 0, 0, exWin);
  493. }
  494. void LayerEdit::deleteSpawn(SpawnEdit* delSpawn, Window* srcWin, Window* exWin) throw_Undo { start_func
  495. assert(!cached);
  496. assert(delSpawn);
  497. vector<Spawn*>::iterator end = spawns.end();
  498. for (vector<Spawn*>::iterator pos = spawns.begin(); pos != end; ++pos) {
  499. if (*pos == delSpawn) {
  500. // Exception point
  501. int delObj = 1;
  502. if (world) delObj = !getWorldEdit()->undo.storeUndoSpawnDelete(delSpawn->getId(), id, delSpawn, srcWin);
  503. if (lockCount) delSpawn->markUnlock();
  504. setHeaderModified();
  505. spawns.erase(pos);
  506. if (world) getWorldEdit()->deindexSpawn(delSpawn);
  507. desktop->broadcastObjChange(OBJ_SPAWN | OBJMOD_DELETE, delSpawn, 0, 0, exWin);
  508. if (delObj) delete delSpawn;
  509. return;
  510. }
  511. }
  512. }
  513. int LayerEdit::propertiesDialog(int isNew, LayerType defaultType, Window* srcWin, Window* exWin) { start_func
  514. int newTileset = tilesetId;
  515. LayerType newType = layerType;
  516. string newName = name;
  517. int newWidth = xSize;
  518. int newHeight = ySize;
  519. int newExtended = usesExtended;
  520. int newEffects = usesEffects;
  521. // @TODO: allowing type change once created causes errors with scene edit windows
  522. int allowTypeChange = 0;
  523. if (isNew) {
  524. newType = defaultType;
  525. allowTypeChange = 1;
  526. }
  527. if (LayerPropertiesDialog::create()->run(allowTypeChange, &newType, &newName, &newWidth, &newHeight, &newTileset, &newExtended, &newEffects, this)) {
  528. try {
  529. scene->markLock();
  530. }
  531. catch (FileException& e) {
  532. guiErrorBox(string(e.details), errorTitleFile);
  533. return 0;
  534. }
  535. // Change to new properties
  536. try {
  537. if (world) getWorldEdit()->undo.preUndoBlock();
  538. setName(newName, srcWin, exWin);
  539. setType(newType, srcWin, exWin);
  540. setSize(newWidth, newHeight, srcWin, exWin);
  541. if (newType == LAYER_TILE) {
  542. setExtended(newExtended, srcWin, exWin);
  543. setEffects(newEffects, srcWin, exWin);
  544. try {
  545. setTileset(newTileset, srcWin, exWin);
  546. }
  547. catch (FileException& e) {
  548. if (world) getWorldEdit()->undo.cancelUndoBlock();
  549. scene->markUnlock();
  550. guiErrorBox(string(e.details), errorTitleFile);
  551. return 0;
  552. }
  553. }
  554. if (world) getWorldEdit()->undo.postUndoBlock();
  555. }
  556. catch (UndoException& e) {
  557. scene->markUnlock();
  558. return 0;
  559. }
  560. scene->markUnlock();
  561. return 1;
  562. }
  563. return 0;
  564. }
  565. int LayerEdit::isHeaderModified() const { start_func
  566. return headerModified;
  567. }
  568. int LayerEdit::isContentModified() const { start_func
  569. return contentModified;
  570. }
  571. void LayerEdit::saveSuccess() { start_func
  572. headerModified = 0;
  573. contentModified = 0;
  574. }
  575. void LayerEdit::loadHeader(FileRead* file) throw_File { start_func
  576. loadHeaderNonSpawns(file);
  577. int numSpawns = file->readInt();
  578. if (numSpawns < 0) throw FileException("Corrupted layer header");
  579. assert(spawns.empty());
  580. for (; numSpawns > 0; --numSpawns) {
  581. SpawnEdit* newSpawn = new SpawnEdit(getWorldEdit(), this);
  582. try {
  583. newSpawn->load(file, world);
  584. }
  585. catch (...) {
  586. delete newSpawn;
  587. throw;
  588. }
  589. spawns.push_back(newSpawn);
  590. }
  591. cached = 1;
  592. }
  593. Uint32 LayerEdit::saveHeader(FileWrite* file) throw_File { start_func
  594. file->writeStr(name);
  595. file->writeInt(layerType);
  596. file->writeInt(id);
  597. switch (layerType) {
  598. case LAYER_EMPTY:
  599. break;
  600. case LAYER_IMAGE:
  601. case LAYER_FONT:
  602. file->writeInt(xSize);
  603. file->writeInt(ySize);
  604. break;
  605. case LAYER_TILE:
  606. file->writeInt(xSize);
  607. file->writeInt(ySize);
  608. file->writeInt(tilesetId);
  609. file->writeInt(usesExtended);
  610. file->writeInt(usesEffects);
  611. break;
  612. default:
  613. assert(0);
  614. }
  615. // Spawns for all layer types
  616. vector<Spawn*>::iterator end = spawns.end();
  617. file->writeInt(spawns.size());
  618. for (vector<Spawn*>::iterator pos = spawns.begin(); pos != end; ++pos) {
  619. dynamic_cast<SpawnEdit*>(*pos)->save(file);
  620. }
  621. return 1;
  622. }
  623. void LayerEdit::saveContent(FileWrite* file) throw_File { start_func
  624. switch (layerType) {
  625. case LAYER_EMPTY:
  626. case LAYER_IMAGE: // @TODO:
  627. case LAYER_FONT: // @TODO:
  628. break;
  629. case LAYER_TILE:
  630. assert(tileData);
  631. file->writeIntBulk(tileData, xSize * ySize);
  632. if (usesExtended) {
  633. assert(extendedData);
  634. file->writeIntBulk(extendedData, xSize * ySize);
  635. }
  636. if (usesEffects) {
  637. assert(effectsData);
  638. file->writeIntBulk(effectsData, xSize * ySize);
  639. }
  640. break;
  641. default:
  642. assert(0);
  643. }
  644. }
  645. void LayerEdit::cachedContent(FileRead* file, int oldData) { start_func
  646. // Should never be called
  647. assert(0);
  648. }
  649. int LayerEdit::doLock(int play) throw_File { start_func
  650. if (lockCount == 0) {
  651. vector<Spawn*>::iterator end = spawns.end();
  652. vector<Spawn*>::iterator pos = spawns.begin();
  653. try {
  654. for (; pos != end; ++pos) {
  655. assert(*pos);
  656. SpawnEdit* edit = dynamic_cast<SpawnEdit*>(*pos);
  657. edit->markLock();
  658. }
  659. // Do this in here so exception unlocks spawns
  660. return Layer::doLock(play);
  661. }
  662. catch (...) {
  663. // (unlock those that were successful)
  664. for (vector<Spawn*>::iterator pos2 = spawns.begin(); pos2 != pos; ++pos2) {
  665. SpawnEdit* edit = dynamic_cast<SpawnEdit*>(*pos2);
  666. edit->markUnlock();
  667. }
  668. throw;
  669. }
  670. }
  671. return Layer::doLock(play);
  672. }
  673. int LayerEdit::doUnlock(int play) { start_func
  674. if (lockCount == 1) {
  675. vector<Spawn*>::iterator end = spawns.end();
  676. for (vector<Spawn*>::iterator pos = spawns.begin(); pos != end; ++pos) {
  677. assert(*pos);
  678. SpawnEdit* edit = dynamic_cast<SpawnEdit*>(*pos);
  679. edit->markUnlock();
  680. }
  681. }
  682. return Layer::doUnlock(play);
  683. }