Add Battle Animations module - #352
Conversation
…euristics for finding animation count. Attempt to optimise
…animations may need it
|
Still missing one feature: patching the game code to accept higher battle animation IDs than usual. Oops |
tolmar
left a comment
There was a problem hiding this comment.
Looks good!
I read through everything and only had a few comments.
Note that I just read through everything, I did not build or test it myself.
…ated per-animation
|
OK, after some discussion on the discord server, I've changed the way we're dumping so that tilesets are no longer duplicated when decompiling and deduplicated when compiling. This makes it easier for external editors to handle sharing tilesets across animations. I'll update the PR description to reflect this |
PhoenixBound
left a comment
There was a problem hiding this comment.
Sorry for the flood, some of these are nitpicky. I also haven't tested anything out in its current form, this is just from reading the code
The existing/old ROM thing being optional is an appreciated change already. Re frame count being hidden: as discussed on Discord, I can see the case for restricting dumping of unused frames to a tool outside of CoilSnake, so 👍 to that.
Thanks for working on this!
| text_frames = raw.split("\n\n")[:-1] # Last split is the trailing gap | ||
|
|
||
| for text_frame in text_frames: | ||
| arrangement = EbOneByteTileArrangement(32, 32) | ||
| arrangement.arrangement = [[EbOneByteTileArrangementItem(int(x, 16)) for x in y.split()] for y in text_frame.split("\n")] | ||
| self.arrangements.append(arrangement) | ||
| self.frame_count += 1 |
There was a problem hiding this comment.
Since this is reading from a file provided by the user, this should probably have a bit of up-front validation to limit surprises:
| text_frames = raw.split("\n\n")[:-1] # Last split is the trailing gap | |
| for text_frame in text_frames: | |
| arrangement = EbOneByteTileArrangement(32, 32) | |
| arrangement.arrangement = [[EbOneByteTileArrangementItem(int(x, 16)) for x in y.split()] for y in text_frame.split("\n")] | |
| self.arrangements.append(arrangement) | |
| self.frame_count += 1 | |
| text_frames = raw.split("\n\n") | |
| if text_frames and not text_frames[-1]: | |
| # The last split is from the trailing \n\n gap | |
| text_frames.pop() | |
| if not text_frames: | |
| raise CoilSnakeUserError("Battle animation has no frames") | |
| for i, text_frame in enumerate(text_frames): | |
| arrangement = EbOneByteTileArrangement(32, 32) | |
| rows = text_frame.split("\n") | |
| if len(rows) != arrangement.height: | |
| raise CoilSnakeUserError("Frame {} has {} rows of tiles (expected {})".format(i, len(rows), arrangement.height) | |
| for j, row in enumerate(rows): | |
| tiles = row.split() | |
| if len(tiles) != arrangement.width: | |
| raise CoilSnakeUserError("Frame {} row {} has {} tiles (expected {})".format(i, j, len(tiles), arrangement.width) | |
| for k, tile in enumerate(tiles): | |
| arrangement.arrangement[j][k] = EbOneByteTileArrangementItem(int(tile, 16)) | |
| self.arrangements.append(arrangement) | |
| self.frame_count += 1 |
There was a problem hiding this comment.
Applied though I haven't tested messing with the file to see if these errors get fielded correctly
| if confirm != "yes": | ||
| return | ||
|
|
||
| has_old = tkinter.messagebox.askyesnocancel("Do you have expanded battle animations?", |
There was a problem hiding this comment.
Not going to request a specific change with the "old ROM" stuff, this is all fine for now. But in the future, maybe it would be neat if the logic of asking for an existing compiled ROM was behind a callback in upgrade_project, because upgrade_project can load the project and check its version.
This introduces the Battle Animations module, which is responsible for decompiling+recompiling data related to battle animations (aka PSI animations). To support dumping edited data from the current usual method of editing battle animations, which requires direct edits to a project's base ROM plus a CCScript repointing of the data, the project upgrade process now also optionally accepts an existing compiled ROM either as a prompt in the GUI or the new CLI flag
--existing_rom/-e.Battle animations are dumped into the new
BattleAnimationsdirectory, which includes thebattle_animations.ymlconfiguration file, theTilesetssubdirectory which includes grayscale PNGs of each of the tilesets, and theArrangementssubdirectory which includes the individual frame tilemaps for every animation. Like the vanilla game, tilesets compiled into the ROM do not contain extraneous tiles.Brand-new animations can be added by creating a new
.maparrangement file with the appropriate number filename and adding a corresponding entry tobattle_animations.yml. If there are more animations than vanilla, then a small code patch will be applied so that the function at C3F981 can process them. Additional battle animation IDs start at 55 (56 in CCScript) due to battle animations and the HDMA-based enemy animations sharing the same ID space.