block.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /* MegaZeux
  2. *
  3. * Copyright (C) 1996 Greg Janson
  4. * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
  5. * Copyright (C) 2017 Alice Rowan <petrifiedrowan@gmail.com>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License as
  9. * published by the Free Software Foundation; either version 2 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include "block.h"
  22. #include "data.h"
  23. #include "idarray.h"
  24. #include "idput.h"
  25. #include "robot.h"
  26. #include "world_struct.h"
  27. static inline int duplicate_storage_object(struct world *mzx_world,
  28. struct board *dest_board, struct board *src_board, int src_id, int src_param)
  29. {
  30. if(is_robot(src_id))
  31. {
  32. struct robot *src_robot = src_board->robot_list[src_param];
  33. return duplicate_robot(mzx_world, dest_board, src_robot, 0, 0, true);
  34. }
  35. else
  36. if(is_signscroll(src_id))
  37. {
  38. struct scroll *src_scroll = src_board->scroll_list[src_param];
  39. return duplicate_scroll(dest_board, src_scroll);
  40. }
  41. else
  42. if(src_id == SENSOR)
  43. {
  44. struct sensor *src_sensor = src_board->sensor_list[src_param];
  45. return duplicate_sensor(dest_board, src_sensor);
  46. }
  47. return src_param;
  48. }
  49. static inline void copy_board_to_board_buffer(struct world *mzx_world,
  50. struct board *src_board, int src_offset, struct board *dest_board,
  51. int block_width, int block_height, char *buffer_id, char *buffer_color,
  52. char *buffer_param, char *buffer_under_id, char *buffer_under_color,
  53. char *buffer_under_param)
  54. {
  55. char *level_id = src_board->level_id;
  56. char *level_param = src_board->level_param;
  57. char *level_color = src_board->level_color;
  58. char *level_under_id = src_board->level_under_id;
  59. char *level_under_param = src_board->level_under_param;
  60. char *level_under_color = src_board->level_under_color;
  61. int src_width = src_board->board_width;
  62. int src_skip = src_width - block_width;
  63. int buffer_offset = 0;
  64. enum thing src_id;
  65. enum thing src_under_id;
  66. int src_param;
  67. int src_under_param;
  68. int i, i2;
  69. for(i = 0; i < block_height; i++)
  70. {
  71. for(i2 = 0; i2 < block_width; i2++)
  72. {
  73. src_id = (enum thing)level_id[src_offset];
  74. src_param = level_param[src_offset];
  75. src_under_id = (enum thing)level_under_id[src_offset];
  76. src_under_param = level_under_param[src_offset];
  77. // Storage objects need to be copied to the destination board
  78. if(!is_storageless(src_id))
  79. src_param = duplicate_storage_object(mzx_world, dest_board,
  80. src_board, src_id, src_param);
  81. // Storage objects can also exist under, unfortunately.
  82. if(!is_storageless(src_under_id))
  83. src_under_param = duplicate_storage_object(mzx_world, dest_board,
  84. src_board, src_under_id, src_under_param);
  85. // Copy to the buffer
  86. if(src_param != -1)
  87. {
  88. buffer_id[buffer_offset] = src_id;
  89. buffer_param[buffer_offset] = src_param;
  90. buffer_color[buffer_offset] = level_color[src_offset];
  91. buffer_under_id[buffer_offset] = src_under_id;
  92. buffer_under_param[buffer_offset] = src_under_param;
  93. buffer_under_color[buffer_offset] = level_under_color[src_offset];
  94. }
  95. else
  96. // If the storage object failed to allocate, copy under data instead
  97. if(src_under_param != -1)
  98. {
  99. buffer_id[buffer_offset] = src_under_id;
  100. buffer_param[buffer_offset] = src_under_param;
  101. buffer_color[buffer_offset] = level_under_color[src_offset];
  102. buffer_under_id[buffer_offset] = SPACE;
  103. buffer_under_param[buffer_offset] = 0;
  104. buffer_under_color[buffer_offset] = 7;
  105. }
  106. // No under either? Just put a space...
  107. else
  108. {
  109. buffer_id[buffer_offset] = SPACE;
  110. buffer_param[buffer_offset] = 0;
  111. buffer_color[buffer_offset] = 7;
  112. buffer_under_id[buffer_offset] = SPACE;
  113. buffer_under_param[buffer_offset] = 0;
  114. buffer_under_color[buffer_offset] = 7;
  115. }
  116. src_offset++;
  117. buffer_offset++;
  118. }
  119. src_offset += src_skip;
  120. }
  121. }
  122. static void clear_storage_object(struct board *dest_board, int id, int param)
  123. {
  124. if(id == SENSOR)
  125. {
  126. clear_sensor_id(dest_board, param);
  127. }
  128. else
  129. if(is_signscroll(id))
  130. {
  131. clear_scroll_id(dest_board, param);
  132. }
  133. else
  134. if(is_robot(id))
  135. {
  136. clear_robot_id(dest_board, param);
  137. }
  138. }
  139. static inline void copy_board_buffer_to_board(
  140. struct board *dest_board, int dest_offset, int block_width, int block_height,
  141. char *buffer_id, char *buffer_color, char *buffer_param, char *buffer_under_id,
  142. char *buffer_under_color, char *buffer_under_param)
  143. {
  144. char *level_id = dest_board->level_id;
  145. char *level_param = dest_board->level_param;
  146. char *level_color = dest_board->level_color;
  147. char *level_under_id = dest_board->level_under_id;
  148. char *level_under_param = dest_board->level_under_param;
  149. char *level_under_color = dest_board->level_under_color;
  150. int dest_width = dest_board->board_width;
  151. int dest_skip = dest_width - block_width;
  152. int buffer_offset = 0;
  153. enum thing buffer_id_cur;
  154. enum thing dest_id;
  155. int dest_param;
  156. int i, i2;
  157. for(i = 0; i < block_height; i++)
  158. {
  159. for(i2 = 0; i2 < block_width; i2++)
  160. {
  161. dest_id = (enum thing)level_id[dest_offset];
  162. buffer_id_cur = (enum thing)buffer_id[buffer_offset];
  163. if(dest_id != PLAYER)
  164. {
  165. dest_param = level_param[dest_offset];
  166. // Storage objects need to be deleted from the destination board
  167. if(!is_storageless(dest_id))
  168. clear_storage_object(dest_board, dest_id, dest_param);
  169. if(is_robot(buffer_id_cur))
  170. {
  171. struct robot *cur_robot =
  172. dest_board->robot_list[(int)buffer_param[buffer_offset]];
  173. cur_robot->xpos = dest_offset % dest_width;
  174. cur_robot->ypos = dest_offset / dest_width;
  175. }
  176. // Copy off of the buffer onto the board
  177. if(buffer_id_cur != PLAYER)
  178. {
  179. level_id[dest_offset] = buffer_id_cur;
  180. level_param[dest_offset] = buffer_param[buffer_offset];
  181. level_color[dest_offset] = buffer_color[buffer_offset];
  182. level_under_id[dest_offset] = buffer_under_id[buffer_offset];
  183. level_under_param[dest_offset] = buffer_under_param[buffer_offset];
  184. level_under_color[dest_offset] = buffer_under_color[buffer_offset];
  185. }
  186. // Unless the buffer contains a player, in which case, copy under data
  187. else
  188. {
  189. level_id[dest_offset] = buffer_under_id[buffer_offset];
  190. level_param[dest_offset] = buffer_under_param[buffer_offset];
  191. level_color[dest_offset] = buffer_under_color[buffer_offset];
  192. }
  193. }
  194. else
  195. {
  196. // This can't be placed on top of the player. Free any storage
  197. // objects so this isn't leaking storage object IDs.
  198. if(!is_storageless(buffer_id_cur))
  199. {
  200. clear_storage_object(dest_board, buffer_id_cur,
  201. buffer_param[buffer_offset]);
  202. }
  203. // Also make sure there isn't a storage object under in the extremely
  204. // unlikely event one got there.
  205. if(!is_storageless(buffer_under_id[buffer_offset]))
  206. {
  207. clear_storage_object(dest_board, buffer_under_id[buffer_offset],
  208. buffer_under_param[buffer_offset]);
  209. }
  210. }
  211. dest_offset++;
  212. buffer_offset++;
  213. }
  214. dest_offset += dest_skip;
  215. }
  216. }
  217. void copy_board_to_board(struct world *mzx_world,
  218. struct board *src_board, int src_offset,
  219. struct board *dest_board, int dest_offset,
  220. int block_width, int block_height)
  221. {
  222. // While we could not use buffering if the boards are different, this
  223. // is a bit more complex than layer copying, so use buffers anyway.
  224. // The different boards case only affects the editor anyway.
  225. char *buffer_id = cmalloc(block_width * block_height);
  226. char *buffer_color = cmalloc(block_width * block_height);
  227. char *buffer_param = cmalloc(block_width * block_height);
  228. char *buffer_under_id = cmalloc(block_width * block_height);
  229. char *buffer_under_color = cmalloc(block_width * block_height);
  230. char *buffer_under_param = cmalloc(block_width * block_height);
  231. copy_board_to_board_buffer(mzx_world,
  232. src_board, src_offset, dest_board, block_width, block_height,
  233. buffer_id, buffer_color, buffer_param, buffer_under_id,
  234. buffer_under_color, buffer_under_param);
  235. copy_board_buffer_to_board(
  236. dest_board, dest_offset, block_width, block_height,
  237. buffer_id, buffer_color, buffer_param, buffer_under_id,
  238. buffer_under_color, buffer_under_param);
  239. free(buffer_id);
  240. free(buffer_color);
  241. free(buffer_param);
  242. free(buffer_under_id);
  243. free(buffer_under_color);
  244. free(buffer_under_param);
  245. }
  246. static void copy_layer_to_layer_buffered(
  247. char *src_char, char *src_color, int src_width, int src_offset,
  248. char *dest_char, char *dest_color, int dest_width, int dest_offset,
  249. int block_width, int block_height)
  250. {
  251. char *buffer_char = cmalloc(block_width * block_height);
  252. char *buffer_color = cmalloc(block_width * block_height);
  253. int buffer_offset = 0;
  254. int src_skip = src_width - block_width;
  255. int dest_skip = dest_width - block_width;
  256. // Copying to the same layer; don't worry about char 32
  257. int i, i2;
  258. // Copy layer to buffer
  259. for(i = 0; i < block_height; i++)
  260. {
  261. for(i2 = 0; i2 < block_width; i2++)
  262. {
  263. buffer_char[buffer_offset] = src_char[src_offset];
  264. buffer_color[buffer_offset] = src_color[src_offset];
  265. src_offset++;
  266. buffer_offset++;
  267. }
  268. src_offset += src_skip;
  269. }
  270. buffer_offset = 0;
  271. // Copy buffer back to layer
  272. for(i = 0; i < block_height; i++)
  273. {
  274. for(i2 = 0; i2 < block_width; i2++)
  275. {
  276. dest_char[dest_offset] = buffer_char[buffer_offset];
  277. dest_color[dest_offset] = buffer_color[buffer_offset];
  278. dest_offset++;
  279. buffer_offset++;
  280. }
  281. dest_offset += dest_skip;
  282. }
  283. free(buffer_char);
  284. free(buffer_color);
  285. }
  286. static void copy_layer_to_layer_direct(
  287. char *src_char, char *src_color, int src_width, int src_offset,
  288. char *dest_char, char *dest_color, int dest_width, int dest_offset,
  289. int block_width, int block_height)
  290. {
  291. int src_skip = src_width - block_width;
  292. int dest_skip = dest_width - block_width;
  293. // Copying to a different layer; track char 32s
  294. int src_char_cur;
  295. int i, i2;
  296. for(i = 0; i < block_height; i++)
  297. {
  298. for(i2 = 0; i2 < block_width; i2++)
  299. {
  300. src_char_cur = src_char[src_offset];
  301. if(src_char_cur != 32)
  302. {
  303. dest_char[dest_offset] = src_char_cur;
  304. dest_color[dest_offset] = src_color[src_offset];
  305. }
  306. src_offset++;
  307. dest_offset++;
  308. }
  309. src_offset += src_skip;
  310. dest_offset += dest_skip;
  311. }
  312. }
  313. void copy_layer_to_layer(
  314. char *src_char, char *src_color, int src_width, int src_offset,
  315. char *dest_char, char *dest_color, int dest_width, int dest_offset,
  316. int block_width, int block_height)
  317. {
  318. // Same pointer? Use a buffer
  319. if(src_char == dest_char || src_color == dest_color)
  320. {
  321. copy_layer_to_layer_buffered(
  322. src_char, src_color, src_width, src_offset,
  323. dest_char, dest_color, dest_width, dest_offset,
  324. block_width, block_height);
  325. }
  326. // Otherwise, it's safe to just copy direct
  327. else
  328. {
  329. copy_layer_to_layer_direct(
  330. src_char, src_color, src_width, src_offset,
  331. dest_char, dest_color, dest_width, dest_offset,
  332. block_width, block_height);
  333. }
  334. }
  335. void copy_board_to_layer(
  336. struct board *src_board, int src_offset,
  337. char *dest_char, char *dest_color, int dest_width, int dest_offset,
  338. int block_width, int block_height)
  339. {
  340. int src_width = src_board->board_width;
  341. int src_skip = src_width - block_width;
  342. int dest_skip = dest_width - block_width;
  343. int src_char;
  344. int i, i2;
  345. for(i = 0; i < block_height; i++)
  346. {
  347. for(i2 = 0; i2 < block_width; i2++)
  348. {
  349. src_char = get_id_char(src_board, src_offset);
  350. if(src_char != 32)
  351. {
  352. dest_char[dest_offset] = src_char;
  353. dest_color[dest_offset] = get_id_color(src_board, src_offset);
  354. }
  355. src_offset++;
  356. dest_offset++;
  357. }
  358. src_offset += src_skip;
  359. dest_offset += dest_skip;
  360. }
  361. }
  362. void copy_layer_to_board(
  363. char *src_char, char *src_color, int src_width, int src_offset,
  364. struct board *dest_board, int dest_offset,
  365. int block_width, int block_height, enum thing convert_id)
  366. {
  367. char *level_id = dest_board->level_id;
  368. char *level_param = dest_board->level_param;
  369. char *level_color = dest_board->level_color;
  370. int dest_width = dest_board->board_width;
  371. int src_skip = src_width - block_width;
  372. int dest_skip = dest_width - block_width;
  373. enum thing dest_id;
  374. char src_char_cur;
  375. int i, i2;
  376. for(i = 0; i < block_height; i++)
  377. {
  378. for(i2 = 0; i2 < block_width; i2++)
  379. {
  380. src_char_cur = src_char[src_offset];
  381. dest_id = level_id[dest_offset];
  382. if(src_char_cur != 32 && dest_id != PLAYER)
  383. {
  384. // Storage objects need to be deleted from the destination board
  385. if(!is_storageless(dest_id))
  386. clear_storage_object(dest_board, dest_id, level_param[dest_offset]);
  387. level_id[dest_offset] = (char)convert_id;
  388. level_param[dest_offset] = src_char_cur;
  389. level_color[dest_offset] = src_color[src_offset];
  390. }
  391. src_offset++;
  392. dest_offset++;
  393. }
  394. src_offset += src_skip;
  395. dest_offset += dest_skip;
  396. }
  397. }
  398. #ifdef CONFIG_EDITOR
  399. // Place the player on the board. Intended for editor operations that move the
  400. // player but need to preserve the thing under the player. Only use after the
  401. // old player has been removed; THIS FUNCTION WILL NOT REMOVE IT.
  402. void copy_replace_player(struct world *mzx_world, int x, int y)
  403. {
  404. struct board *cur_board = mzx_world->current_board;
  405. enum thing src_id;
  406. int offset;
  407. if(x >= cur_board->board_width)
  408. x = cur_board->board_width - 1;
  409. if(y >= cur_board->board_height)
  410. y = cur_board->board_height - 1;
  411. offset = x + (y * cur_board->board_width);
  412. src_id = (enum thing)cur_board->level_id[offset];
  413. if(is_robot(src_id) || is_signscroll(src_id))
  414. clear_storage_object(cur_board, src_id, cur_board->level_param[offset]);
  415. id_place(mzx_world, x, y, PLAYER, 0, 0);
  416. mzx_world->player_x = x;
  417. mzx_world->player_y = y;
  418. }
  419. // This goes here so the block buffer monstrosity can be inlined.
  420. void move_board_block(struct world *mzx_world,
  421. struct board *src_board, int src_x, int src_y,
  422. struct board *dest_board, int dest_x, int dest_y,
  423. int block_width, int block_height,
  424. int clear_width, int clear_height)
  425. {
  426. char *buffer_id = cmalloc(block_width * block_height);
  427. char *buffer_color = cmalloc(block_width * block_height);
  428. char *buffer_param = cmalloc(block_width * block_height);
  429. char *buffer_under_id = cmalloc(block_width * block_height);
  430. char *buffer_under_color = cmalloc(block_width * block_height);
  431. char *buffer_under_param = cmalloc(block_width * block_height);
  432. // The source board needs to be cleared, so set that up too.
  433. // This is pretty much copied from clear_board_block, but I think
  434. // that's a fair sacrifice.
  435. // The section of the source board that gets cleared can also be
  436. // larger than the block being copied due to bounds clipping.
  437. char *level_id = src_board->level_id;
  438. char *level_param = src_board->level_param;
  439. char *level_color = src_board->level_color;
  440. char *level_under_id = src_board->level_under_id;
  441. char *level_under_param = src_board->level_under_param;
  442. char *level_under_color = src_board->level_under_color;
  443. int src_width = src_board->board_width;
  444. int src_skip = src_width - clear_width;
  445. enum thing src_id;
  446. int src_param;
  447. int i, i2;
  448. int src_offset = src_x + (src_y * src_width);
  449. int dest_offset = dest_x + (dest_y * dest_board->board_width);
  450. boolean replace_player = false;
  451. int player_x;
  452. int player_y;
  453. // Work around to move the player
  454. if((mzx_world->player_x >= src_x) &&
  455. (mzx_world->player_y >= src_y) &&
  456. (mzx_world->player_x < (src_x + clear_width)) &&
  457. (mzx_world->player_y < (src_y + clear_height)) &&
  458. (src_board == dest_board))
  459. {
  460. player_x = mzx_world->player_x - src_x + dest_x;
  461. player_y = mzx_world->player_y - src_y + dest_y;
  462. replace_player = true;
  463. id_remove_top(mzx_world, mzx_world->player_x, mzx_world->player_y);
  464. }
  465. copy_board_to_board_buffer(mzx_world,
  466. src_board, src_offset, dest_board, block_width, block_height,
  467. buffer_id, buffer_color, buffer_param, buffer_under_id,
  468. buffer_under_color, buffer_under_param);
  469. for(i = 0; i < clear_height; i++)
  470. {
  471. for(i2 = 0; i2 < clear_width; i2++)
  472. {
  473. src_id = (enum thing)level_id[src_offset];
  474. if(src_id != PLAYER)
  475. {
  476. src_param = level_param[src_offset];
  477. if(!is_storageless(src_id))
  478. clear_storage_object(src_board, src_id, src_param);
  479. level_id[src_offset] = (char)SPACE;
  480. level_param[src_offset] = 0;
  481. level_color[src_offset] = 7;
  482. }
  483. else
  484. if(!is_storageless(level_under_id[src_offset]))
  485. clear_storage_object(src_board, level_under_id[src_offset],
  486. level_under_param[src_offset]);
  487. level_under_id[src_offset] = (char)SPACE;
  488. level_under_param[src_offset] = 0;
  489. level_under_color[src_offset] = 7;
  490. src_offset++;
  491. }
  492. src_offset += src_skip;
  493. }
  494. copy_board_buffer_to_board(
  495. dest_board, dest_offset, block_width, block_height,
  496. buffer_id, buffer_color, buffer_param, buffer_under_id,
  497. buffer_under_color, buffer_under_param);
  498. if(replace_player)
  499. copy_replace_player(mzx_world, player_x, player_y);
  500. free(buffer_id);
  501. free(buffer_color);
  502. free(buffer_param);
  503. free(buffer_under_id);
  504. free(buffer_under_color);
  505. free(buffer_under_param);
  506. }
  507. #endif //CONFIG_EDITOR