├── .gitignore
├── LICENSE
├── MainWindow.cpp
├── MainWindow.h
├── MainWindow.ui
├── QDKEdit.cpp
├── QDKEdit.h
├── QTileEdit.cpp
├── QTileEdit.h
├── QTileSelector.cpp
├── QTileSelector.h
├── README.md
├── eDKit.pro
├── main.cpp
└── win32
└── eDKit.rar
/.gitignore:
--------------------------------------------------------------------------------
1 | # C++ objects and libs
2 |
3 | *.*~
4 | *.slo
5 | *.lo
6 | *.o
7 | *.a
8 | *.la
9 | *.lai
10 | *.so
11 | *.dll
12 | *.dylib
13 |
14 | # Qt-es
15 |
16 | *.pro.user
17 | *.pro.user.*
18 | moc_*.cpp
19 | qrc_*.cpp
20 | Makefile
21 | *-build-*
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/MainWindow.cpp:
--------------------------------------------------------------------------------
1 | #include "MainWindow.h"
2 | #include "ui_MainWindow.h"
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | MainWindow::MainWindow(QWidget *parent) :
14 | QMainWindow(parent),
15 | ui(new Ui::MainWindow)
16 | {
17 | ui->setupUi(this);
18 | ui->lvlEdit->setupTileSelector(ui->tileSelect, 2.0f, 256);
19 |
20 | ui->actionOpen_ROM->setShortcut(QKeySequence::Open);
21 | ui->actionSave_ROM->setShortcut(QKeySequence::Save);
22 | ui->actionUndo->setShortcut(Qt::CTRL + Qt::Key_U);
23 |
24 | connect(ui->lvlEdit, SIGNAL(dataChanged()), this, SLOT(updateText()));
25 | connect(ui->tabWidget, SIGNAL(currentChanged(int)), ui->lvlEdit, SLOT(toggleSpriteMode(int)));
26 | connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabsSwitched(int)));
27 | connect(ui->btnWrite, SIGNAL(clicked()), ui->lvlEdit, SLOT(saveLevel()));
28 | connect(ui->actionOpen_ROM, SIGNAL(triggered()), this, SLOT(loadROM()));
29 | connect(ui->actionSave_ROM, SIGNAL(triggered()), this, SLOT(SaveROM()));
30 | connect(ui->actionUndo, SIGNAL(triggered()), ui->lvlEdit, SLOT(undo()));
31 | connect(ui->actionEmpty_Level, SIGNAL(triggered()), ui->lvlEdit, SLOT(clearLevel()));
32 | connect(ui->actionExportLvl, SIGNAL(triggered()), this, SLOT(ExportLvl()));
33 | connect(ui->actionImportLvl, SIGNAL(triggered()), this, SLOT(ImportLvl()));
34 |
35 | connect(ui->lvlEdit, SIGNAL(musicChanged(int)), ui->cmbMusic, SLOT(setCurrentIndex(int)));
36 | connect(ui->lvlEdit, SIGNAL(paletteChanged(int)), ui->spbPalette, SLOT(setValue(int)));
37 | connect(ui->lvlEdit, SIGNAL(sizeChanged(int)), ui->cmbSize, SLOT(setCurrentIndex(int)));
38 | connect(ui->lvlEdit, SIGNAL(tilesetChanged(int)), ui->cmbTileset, SLOT(setCurrentIndex(int)));
39 | connect(ui->lvlEdit, SIGNAL(timeChanged(int)), ui->spbTime, SLOT(setValue(int)));
40 | connect(ui->lvlEdit, SIGNAL(dataChanged()), this, SLOT(enableSaveBtn()));
41 |
42 | connect(ui->spbLevel, SIGNAL(valueChanged(int)), this, SLOT(changeLevel(int)));
43 | connect(ui->cmbSize, SIGNAL(currentIndexChanged(int)), ui->lvlEdit, SLOT(changeSize(int)));
44 | connect(ui->cmbMusic, SIGNAL(currentIndexChanged(int)), ui->lvlEdit, SLOT(changeMusic(int)));
45 | connect(ui->cmbTileset, SIGNAL(currentIndexChanged(int)), ui->lvlEdit, SLOT(changeTileset(int)));
46 | connect(ui->spbPalette, SIGNAL(valueChanged(int)), ui->lvlEdit, SLOT(changePalette(int)));
47 | connect(ui->spbTime, SIGNAL(valueChanged(int)), ui->lvlEdit, SLOT(changeTime(int)));
48 |
49 | connect(ui->ckbTransparency, SIGNAL(clicked(bool)), ui->lvlEdit, SLOT(changeSpriteTransparency(bool)));
50 | connect(ui->lvlEdit, SIGNAL(spriteSelected(int)), this, SLOT(selectSprite(int)));
51 | connect(ui->lvlEdit, SIGNAL(spriteAdded(QString, int)), this, SLOT(addSprite(QString, int)));
52 | connect(ui->lvlEdit, SIGNAL(spriteRemoved(int)), this, SLOT(removeSprite(int)));
53 |
54 | connect(ui->lvlEdit, SIGNAL(switchAdded(QDKSwitch*)), this, SLOT(addSwitch(QDKSwitch*)));
55 | connect(ui->lvlEdit, SIGNAL(switchUpdated(int,QDKSwitch*)), this, SLOT(updateSwitch(int,QDKSwitch*)));
56 | connect(ui->lvlEdit, SIGNAL(switchRemoved(int)), this, SLOT(removeSwitch(int)));
57 |
58 | connect(ui->lvlEdit, SIGNAL(tilesVRAMchanged(int)), this, SLOT(updateVRAMtiles(int)));
59 | connect(ui->lvlEdit, SIGNAL(spriteVRAMchanged(int)), this, SLOT(updateVRAMsprites(int)));
60 |
61 | if ((qApp->arguments().size() > 1) && (QFile::exists(qApp->arguments().at(1))))
62 | {
63 | ui->lvlEdit->loadAllLevels(qApp->arguments().at(1));
64 | ui->lvlEdit->changeLevel(0);
65 | ui->lvlInfo->setPlainText(ui->lvlEdit->getLevelInfo());
66 | }
67 | else
68 | {
69 | ui->lvlEdit->loadAllLevels(BASE_ROM);
70 | ui->lvlEdit->changeLevel(0);
71 | ui->lvlInfo->setPlainText(ui->lvlEdit->getLevelInfo());
72 | }
73 | ui->tabWidget->setCurrentIndex(0);
74 | ui->spbLevel->setFocus();
75 |
76 | QMenu *newSpriteMenu = new QMenu(this);
77 | QDir dir("sprites/");
78 | QStringList list = dir.entryList(QStringList("*.png"), QDir::Files, QDir::Name);
79 |
80 | QAction *action;
81 | QPixmap *pix;
82 | QPixmap pixLB(32, 32);
83 | qreal ar;
84 |
85 | for (int i = 0; i < list.size(); i++)
86 | {
87 | pix = new QPixmap("sprites/" + list.at(i));
88 | pixLB.fill();
89 |
90 | ar = (qreal)pix->width() / (qreal)pix->height();
91 |
92 | if (ar >= 1.0f)
93 | {
94 | QPainter painter;
95 | painter.begin(&pixLB);
96 | painter.drawPixmap(0, 16 - (16.0f/ar), pix->scaled(QSize(32, 32), Qt::KeepAspectRatio));
97 | painter.end();
98 | }
99 | else
100 | {
101 | QPainter painter;
102 | painter.begin(&pixLB);
103 | painter.drawPixmap(16 - (16.0f*ar), 0, pix->scaled(QSize(32, 32), Qt::KeepAspectRatio));
104 | painter.end();
105 | }
106 | delete pix;
107 |
108 | action = new QAction(QIcon(pixLB), ui->lvlEdit->spriteNumToString(list.at(i).mid(7,2).toInt(0, 16)), newSpriteMenu);
109 | action->setStatusTip(list.at(i).mid(7,2));
110 | newSpriteMenu->addAction(action);
111 | if (list.at(i).contains("set", Qt::CaseInsensitive))
112 | i+=0x21;
113 | }
114 |
115 | ui->toolButton->setMenu(newSpriteMenu);
116 | connect(newSpriteMenu, SIGNAL(triggered(QAction*)), this, SLOT(addNewSprite(QAction*)));
117 |
118 | QShortcut* scSprites = new QShortcut(QKeySequence(Qt::Key_Delete), ui->lstSprites);
119 | connect(scSprites, SIGNAL(activated()), this, SLOT(removeSelectedSprite()));
120 |
121 | QShortcut* scSwitches = new QShortcut(QKeySequence(Qt::Key_Delete), ui->treSwitches);
122 | connect(scSwitches, SIGNAL(activated()), this, SLOT(delSwitchItem()));
123 |
124 | changeLevel(0);
125 | }
126 |
127 | void MainWindow::tabsSwitched(int index)
128 | {
129 | ui->lvlEdit->toggleSwitchMode((bool)(index > 1));
130 |
131 | if (index > 1)
132 | on_treSwitches_currentItemChanged(ui->treSwitches->currentItem(), NULL);
133 | }
134 |
135 | void MainWindow::removeSelectedSprite()
136 | {
137 | int i = ui->lstSprites->currentRow();
138 | if (i != -1)
139 | ui->lvlEdit->deleteSprite(i);
140 | }
141 |
142 | void MainWindow::selectSprite(int num)
143 | {
144 | ui->lstSprites->setCurrentRow(num);
145 | ui->grpSpriteProp->setEnabled(false);
146 | if (num == -1)
147 | return;
148 |
149 | int spriteID;
150 | int selSprite = ui->lvlEdit->getSelectedSprite(&spriteID);
151 | if (selSprite == -1)
152 | return;
153 |
154 | if ((spriteID != 0x7F) && (spriteID != 0x98) && (spriteID != 0x80) && (spriteID != 0x80)
155 | && (spriteID != 0x84) && (spriteID != 0x70) && (spriteID != 0x72) && (spriteID != 0x54)
156 | && (spriteID != 0xB8) && (spriteID != 0x6E) && (spriteID != 0x9A) && (spriteID != 0xCC))
157 | return;
158 |
159 | quint8 flag;
160 | ui->lvlEdit->getSpriteFlag(selSprite, &flag);
161 |
162 | ui->grpSpriteProp->setEnabled(true);
163 | ui->btnFlip->setEnabled(false);
164 | ui->spbSpeed->setEnabled(false);
165 | ui->cmbElevator->setEnabled(false);
166 | ui->spbRAW->setEnabled(false);
167 | ui->spbRAW->setValue(flag);
168 |
169 | //sprites that may get flipped
170 | if ((spriteID == 0x7F) || (spriteID == 0x98) || (spriteID == 0x80))
171 | {
172 | ui->btnFlip->setEnabled(true);
173 | ui->btnFlip->setText("Flip");
174 | ui->lblDirection->setText("Direction");
175 | }
176 |
177 | //sprites that may get flipped but should display another text ;)
178 | if (spriteID == 0x84)
179 | {
180 | ui->btnFlip->setEnabled(true);
181 | ui->lblDirection->setText("Direction");
182 | if (flag & 1)
183 | ui->btnFlip->setText("Left");
184 | else
185 | ui->btnFlip->setText("Right");
186 | }
187 |
188 | //sprites with walking speed property
189 | if ((spriteID == 0x80) || (spriteID == 0x98) || (spriteID == 0x84))
190 | {
191 | ui->spbSpeed->setEnabled(true);
192 | ui->spbSpeed->setValue(flag >> 1);
193 | }
194 |
195 | //elevator time between boards and speed
196 | //table @ 0x30F77
197 | //flag byte selects pair from table
198 | // 0x40 0x20 ; 0x60 0x20 ; 0x80 0x20 ; 0xA0 0x20
199 | // 0x40 0x40 ; 0x60 0x40 ; 0x80 0x40 ; 0xA0 0x40
200 | // 0x40 0x60 ; 0x60 0x60 ; 0x80 0x60 ; 0xA0 0x60
201 | // 0x40 0x80 ; 0x60 0x80 ; 0x80 0x80 ; 0xA0 0x80
202 | // 16 settings
203 | // default is 0x60 0x40 => 0x05
204 | //speed = (flag % 4) * 0x20 + 0x40;
205 | //time = (flag / 4) * 0x20 + 0x20;
206 |
207 | // first byte is speed; second byte time between new boards
208 | if ((spriteID == 0x70) || (spriteID == 0x72))
209 | {
210 | ui->cmbElevator->setEnabled(true);
211 | if ((flag >= 0) && (flag < 16))
212 | ui->cmbElevator->setCurrentIndex(flag);
213 | else
214 | ui->cmbElevator->setCurrentIndex(5);
215 | }
216 |
217 | //board speed
218 | if (spriteID == 0x54)
219 | {
220 | ui->btnFlip->setEnabled(true);
221 | ui->lblDirection->setText("Speed");
222 | if (!flag)
223 | ui->btnFlip->setText("Normal");
224 | else
225 | ui->btnFlip->setText("Slow");
226 | }
227 |
228 | if ((spriteID == 0xB8) || (spriteID == 0x6E) || (spriteID == 0x9A) || (spriteID == 0xCC))
229 | {
230 | ui->spbRAW->setEnabled(true);
231 | ui->spbRAW->setValue(flag);
232 | }
233 | }
234 |
235 | void MainWindow::addSprite(QString text, int id)
236 | {
237 | QString icon = QString("sprites/sprite_%1.png").arg(id, 2, 16, QChar('0'));
238 | if (!QFile::exists(icon))
239 | icon = QString("sprites/sprite_%1_set_00.png").arg(id, 2, 16, QChar('0'));
240 | QListWidgetItem *item = new QListWidgetItem(QIcon(icon), text);
241 | item->setToolTip(text);
242 | item->setStatusTip(QString("%1").arg(id, 2, 16, QChar('0')));
243 | ui->lstSprites->addItem(item);
244 | }
245 |
246 | void MainWindow::removeSprite(int index)
247 | {
248 | QListWidgetItem *tmp = ui->lstSprites->takeItem(index);
249 | delete tmp;
250 | }
251 |
252 |
253 | void MainWindow::changeLevel(int id)
254 | {
255 | if (ui->lvlEdit->isChanged())
256 | {
257 | QMessageBox::StandardButton result = QMessageBox::question(NULL, "Level data changed", "The level data has been changed. Save data?", QMessageBox::Yes | QMessageBox::No);
258 | if (result == QMessageBox::Yes) // save changes
259 | ui->lvlEdit->saveLevel();
260 | else if (result != QMessageBox::No) // discard changes
261 | qWarning() << "Unexpected return value form messagebox!";
262 | }
263 | ui->lvlEdit->changeLevel(id);
264 | ui->btnWrite->setEnabled(false);
265 | }
266 |
267 | void MainWindow::updateText()
268 | {
269 | ui->lvlInfo->setPlainText(ui->lvlEdit->getLevelInfo());
270 | }
271 |
272 | void MainWindow::loadROM()
273 | {
274 | QString file = QFileDialog::getOpenFileName(0, "Select Donkey Kong (GB) ROM", qApp->applicationDirPath(), "Donkey Kong (GB) ROM (*.gb)");
275 |
276 | if (!QFile::exists(file))
277 | return;
278 |
279 | ui->lvlEdit->loadAllLevels(file);
280 | ui->lvlEdit->changeLevel(0);
281 | ui->lvlInfo->setPlainText(ui->lvlEdit->getLevelInfo());
282 | }
283 |
284 | void MainWindow::SaveROM()
285 | {
286 | if (ui->lvlEdit->isChanged())
287 | {
288 | QMessageBox::StandardButton result = QMessageBox::question(NULL, "Level data changed", "The level data has been changed. Save data?", QMessageBox::Yes | QMessageBox::No);
289 | if (result == QMessageBox::Yes) // save changes
290 | ui->lvlEdit->saveLevel();
291 | else if (result != QMessageBox::No) // discard changes
292 | qWarning() << "Unexpected return value form messagebox!";
293 | }
294 |
295 | QString file = QFileDialog::getSaveFileName(0, "Select Donkey Kong (GB) ROM", qApp->applicationDirPath(), "Donkey Kong (GB) ROM (*.gb)");
296 |
297 | if (!QFile::exists(file))
298 | QFile::copy(qApp->applicationDirPath() + BASE_ROM, file);
299 |
300 | ui->lvlEdit->saveAllLevels(file);
301 | }
302 |
303 | void MainWindow::ExportLvl()
304 | {
305 | if (ui->lvlEdit->isChanged())
306 | {
307 | QMessageBox::StandardButton result = QMessageBox::question(NULL, "Level data changed", "The level data has been changed. Save data?", QMessageBox::Yes | QMessageBox::No);
308 | if (result == QMessageBox::Yes) // save changes
309 | ui->lvlEdit->saveLevel();
310 | else if (result != QMessageBox::No) // discard changes
311 | qWarning() << "Unexpected return value form messagebox!";
312 | }
313 |
314 | QString file = QFileDialog::getSaveFileName(0, "Export level data", qApp->applicationDirPath(), "eDKit level data (*.lvl)");
315 |
316 | if (!ui->lvlEdit->exportCurrentLevel(file))
317 | qWarning() << "Level export failed";
318 | }
319 |
320 | void MainWindow::ImportLvl()
321 | {
322 | QString file = QFileDialog::getOpenFileName(0, "Import level data", qApp->applicationDirPath(), "eDKit level data (*.lvl)");
323 |
324 | if (!QFile::exists(file))
325 | return;
326 | ui->lvlEdit->importLevel(file);
327 | }
328 |
329 | void MainWindow::addNewSprite(QAction *action)
330 | {
331 | ui->lvlEdit->addSprite(action->statusTip().toInt(0, 16));
332 | }
333 |
334 | MainWindow::~MainWindow()
335 | {
336 | delete ui;
337 | }
338 |
339 | void MainWindow::on_btnFlip_clicked()
340 | {
341 | int spriteID;
342 | int selSprite = ui->lvlEdit->getSelectedSprite(&spriteID);
343 | if (selSprite == -1)
344 | return;
345 |
346 | quint8 flag;
347 | ui->lvlEdit->getSpriteFlag(selSprite, &flag);
348 |
349 | // Mario
350 | if (spriteID == 0x7F)
351 | {
352 | if (flag == 0x00)
353 | flag = 0x01;
354 | else
355 | flag = 0x00;
356 | }
357 |
358 | //walking guys
359 | if ((spriteID == 0x80) || (spriteID == 0x98))
360 | {
361 | if ((flag & 1) == 0x01)
362 | flag ^= 1;
363 | else
364 | flag |= 1;
365 |
366 | }
367 |
368 | //sprites that may get flipped but should display another text ;)
369 | if (spriteID == 0x84)
370 | {
371 | if (flag & 1)
372 | {
373 | ui->btnFlip->setText("Right");
374 | flag ^= 1;
375 | }
376 | else
377 | {
378 | ui->btnFlip->setText("Left");
379 | flag |= 1;
380 | }
381 | }
382 |
383 | //board speed
384 | if (spriteID == 0x54)
385 | {
386 | if (flag)
387 | {
388 | flag = 0;
389 | ui->btnFlip->setText("Normal");
390 | }
391 | else
392 | {
393 | flag = 1;
394 | ui->btnFlip->setText("Slow");
395 | }
396 | }
397 |
398 | ui->lvlEdit->setSpriteFlag(selSprite, flag);
399 | ui->spbRAW->setValue(flag);
400 | }
401 |
402 | void MainWindow::on_spbSpeed_valueChanged(int arg1)
403 | {
404 | if ((arg1 < 0) || (arg1 > 127))
405 | return;
406 |
407 | int spriteID;
408 | int selSprite = ui->lvlEdit->getSelectedSprite(&spriteID);
409 | if (selSprite == -1)
410 | return;
411 |
412 | quint8 flag;
413 | ui->lvlEdit->getSpriteFlag(selSprite, &flag);
414 |
415 | //sprites with walking speed property
416 | if ((spriteID == 0x80) || (spriteID == 0x98) || (spriteID == 0x84))
417 | {
418 | flag = (arg1 * 2) | (flag & 1);
419 | ui->lvlEdit->setSpriteFlag(selSprite, flag);
420 | ui->spbRAW->setValue(flag);
421 | }
422 | }
423 |
424 | void MainWindow::on_cmbElevator_currentIndexChanged(int index)
425 | {
426 | if ((index < 0) || (index > 15))
427 | return;
428 |
429 | int spriteID;
430 | int selSprite = ui->lvlEdit->getSelectedSprite(&spriteID);
431 | if (selSprite == -1)
432 | return;
433 |
434 | // elevator
435 | if ((spriteID == 0x70) || (spriteID == 0x72))
436 | {
437 | ui->lvlEdit->setSpriteFlag(selSprite, index);
438 | ui->spbRAW->setValue(index);
439 | }
440 | }
441 |
442 | void MainWindow::on_spbRAW_valueChanged(int arg1)
443 | {
444 | if ((arg1 < 0) || (arg1 > 255))
445 | return;
446 |
447 | int spriteID;
448 | int selSprite = ui->lvlEdit->getSelectedSprite(&spriteID);
449 | if (selSprite == -1)
450 | return;
451 |
452 | // DKs
453 | if ((spriteID == 0xB8) || (spriteID == 0x6E) || (spriteID == 0x9A) || (spriteID == 0xCC))
454 | ui->lvlEdit->setSpriteFlag(selSprite, arg1);
455 | }
456 |
457 | void MainWindow::addSwitch(QDKSwitch *sw)
458 | {
459 | addSwitchAtPos(ui->treSwitches->topLevelItemCount(), sw);
460 | }
461 |
462 | void MainWindow::addSwitchAtPos(int i, QDKSwitch *sw)
463 | {
464 | QTreeWidgetItem *item = new QTreeWidgetItem();
465 | QTreeWidgetItem *child;
466 | QDKSwitchObject *obj;
467 |
468 | QString state;
469 | switch (sw->state)
470 | {
471 | case 1: state = "middle"; break;
472 | case 2: state = "right"; break;
473 | default: state = "left";
474 | }
475 |
476 | item->setText(0, QString("Switch %1 (%2) at %3x%4").arg(i).arg(state).arg(sw->x).arg(sw->y));
477 |
478 | for (int i = 0; i < sw->connectedTo.size(); i++)
479 | {
480 | child = new QTreeWidgetItem();
481 | obj = &sw->connectedTo[i];
482 | if (!obj->isSprite)
483 | child->setText(0, QString("Tile at %1x%2").arg(obj->x).arg(obj->y));
484 | else
485 | child->setText(0, QString("Sprite at %1x%2").arg(obj->x).arg(obj->y));
486 |
487 | item->addChild(child);
488 | }
489 |
490 | ui->treSwitches->insertTopLevelItem(i, item);
491 | ui->treSwitches->expandAll();
492 | }
493 |
494 | void MainWindow::updateSwitch(int i, QDKSwitch *sw)
495 | {
496 | QTreeWidgetItem *item = ui->treSwitches->takeTopLevelItem(i);
497 | if (!item)
498 | return;
499 |
500 | addSwitchAtPos(i, sw);
501 | }
502 |
503 | void MainWindow::removeSwitch(int i)
504 | {
505 | if ((i < 0) || (i > ui->treSwitches->topLevelItemCount()))
506 | return;
507 |
508 | QTreeWidgetItem *item = ui->treSwitches->takeTopLevelItem(i);
509 | delete item;
510 | }
511 |
512 | void MainWindow::on_treSwitches_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *)
513 | {
514 | if (!current)
515 | return;
516 |
517 | QTreeWidgetItem *parent;
518 | parent = current->parent();
519 | if (!parent)
520 | parent = current;
521 |
522 | bool ok;
523 | int i = parent->text(0).mid(7, 1).toInt(&ok);
524 | if (!ok)
525 | i = -1;
526 |
527 | ui->lvlEdit->selectSwitch(i);
528 | }
529 |
530 |
531 | void MainWindow::delSwitchItem()
532 | {
533 | QTreeWidgetItem *item = ui->treSwitches->currentItem();
534 | if (!item)
535 | return;
536 | if (!item->parent())
537 | ui->lvlEdit->deleteCurrentSwitch();
538 | else
539 | ui->lvlEdit->deleteSwitchObj(item->parent()->indexOfChild(item));
540 | }
541 |
542 | void MainWindow::enableSaveBtn()
543 | {
544 | ui->btnWrite->setEnabled(true);
545 | }
546 |
547 | void MainWindow::updateVRAMtiles(int tiles)
548 | {
549 | if (tiles <= 80)
550 | {
551 | ui->barVRAMtiles->setRange(0, 80);
552 | ui->barVRAMtiles->setValue(tiles);
553 | QPalette p = ui->barVRAMtiles->palette();
554 | p.setColor(QPalette::Highlight, Qt::green);
555 | ui->barVRAMtiles->setPalette(p);
556 | }
557 | else if (tiles < (256 - ui->barVRAMsprites->value()) + 80)
558 | {
559 | ui->barVRAMtiles->setRange(0, (256 - ui->barVRAMsprites->value()) + 80);
560 | ui->barVRAMtiles->setValue(tiles);
561 | QPalette p = ui->barVRAMtiles->palette();
562 | p.setColor(QPalette::Highlight, QColor(0xFF, 0xA0, 0x00));
563 | ui->barVRAMtiles->setPalette(p);
564 | }
565 | else
566 | {
567 | ui->barVRAMtiles->setRange(0, tiles);
568 | ui->barVRAMtiles->setValue(tiles);
569 | QPalette p = ui->barVRAMtiles->palette();
570 | p.setColor(QPalette::Highlight, Qt::red);
571 | ui->barVRAMtiles->setPalette(p);
572 | }
573 | }
574 |
575 | void MainWindow::updateVRAMsprites(int sprites)
576 | {
577 | if (sprites <= 256)
578 | {
579 | ui->barVRAMsprites->setRange(0, 256);
580 | ui->barVRAMsprites->setValue(sprites);
581 | QPalette p = ui->barVRAMsprites->palette();
582 | p.setColor(QPalette::Highlight, Qt::green);
583 | ui->barVRAMsprites->setPalette(p);
584 | }
585 | else
586 | {
587 | ui->barVRAMsprites->setRange(0, sprites);
588 | ui->barVRAMsprites->setValue(sprites);
589 | QPalette p = ui->barVRAMsprites->palette();
590 | p.setColor(QPalette::Highlight, Qt::red);
591 | ui->barVRAMsprites->setPalette(p);
592 | }
593 |
594 | if (ui->barVRAMtiles->value() > 80)
595 | updateVRAMtiles(ui->barVRAMtiles->value());
596 | }
597 |
--------------------------------------------------------------------------------
/MainWindow.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINWINDOW_H
2 | #define MAINWINDOW_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #define BASE_ROM "base.gb"
10 |
11 | struct QDKSwitch;
12 |
13 | namespace Ui {
14 | class MainWindow;
15 | }
16 |
17 | class MainWindow : public QMainWindow
18 | {
19 | Q_OBJECT
20 |
21 | public:
22 | explicit MainWindow(QWidget *parent = 0);
23 | ~MainWindow();
24 |
25 | private:
26 | // void spriteContextMenu(QListWidgetItem *item, QPoint globalPos);
27 | void addSwitchAtPos(int i, QDKSwitch *sw);
28 |
29 | private slots:
30 | void updateText();
31 |
32 | void changeLevel(int id);
33 | void loadROM();
34 | void SaveROM();
35 | void ExportLvl();
36 | void ImportLvl();
37 | void enableSaveBtn();
38 | void selectSprite(int num);
39 | void addSprite(QString text, int id);
40 | void removeSelectedSprite();
41 | void removeSprite(int index);
42 | void addNewSprite(QAction *action);
43 | void addSwitch(QDKSwitch *sw);
44 | void updateSwitch(int i, QDKSwitch *sw);
45 | void removeSwitch(int i);
46 | void tabsSwitched(int index);
47 |
48 | void on_btnFlip_clicked();
49 | void on_spbSpeed_valueChanged(int arg1);
50 | void on_cmbElevator_currentIndexChanged(int index);
51 | void on_spbRAW_valueChanged(int arg1);
52 | void on_treSwitches_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *);
53 | void delSwitchItem();
54 | void updateVRAMtiles(int tiles);
55 | void updateVRAMsprites(int sprites);
56 |
57 | private:
58 | Ui::MainWindow *ui;
59 | };
60 |
61 | #endif // MAINWINDOW_H
62 |
--------------------------------------------------------------------------------
/MainWindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 860
10 | 648
11 |
12 |
13 |
14 | eDKit alpha (c) 2014-15 by bailli
15 |
16 |
17 |
18 |
19 |
20 | 10
21 | 10
22 | 512
23 | 448
24 |
25 |
26 |
27 |
28 |
29 |
30 | 9
31 | 470
32 | 411
33 | 151
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 530
44 | 10
45 | 321
46 | 451
47 |
48 |
49 |
50 | QTabWidget::Rounded
51 |
52 |
53 | 0
54 |
55 |
56 | Qt::ElideNone
57 |
58 |
59 |
60 | &Tiles
61 |
62 |
63 |
64 |
65 | 7
66 | 18
67 | 301
68 | 386
69 |
70 |
71 |
72 |
73 |
74 |
75 | &Sprites
76 |
77 |
78 |
79 |
80 | 157
81 | 218
82 | 150
83 | 21
84 |
85 |
86 |
87 | Transparent sprites
88 |
89 |
90 | true
91 |
92 |
93 |
94 |
95 |
96 | 8
97 | 10
98 | 301
99 | 201
100 |
101 |
102 |
103 | Qt::MoveAction
104 |
105 |
106 |
107 |
108 |
109 | 8
110 | 217
111 | 126
112 | 24
113 |
114 |
115 |
116 | Add new sprite
117 |
118 |
119 | QToolButton::InstantPopup
120 |
121 |
122 | Qt::ToolButtonTextBesideIcon
123 |
124 |
125 | Qt::NoArrow
126 |
127 |
128 |
129 |
130 | false
131 |
132 |
133 |
134 | 8
135 | 250
136 | 289
137 | 161
138 |
139 |
140 |
141 | Sprite properties
142 |
143 |
144 | -
145 |
146 |
147 | Direction
148 |
149 |
150 |
151 | -
152 |
153 |
154 | false
155 |
156 |
157 | Flip/Speed
158 |
159 |
160 |
161 | -
162 |
163 |
164 | Qt::Horizontal
165 |
166 |
167 |
168 | 42
169 | 20
170 |
171 |
172 |
173 |
174 | -
175 |
176 |
177 | Walking Speed
178 |
179 |
180 |
181 | -
182 |
183 |
184 | false
185 |
186 |
187 | 127
188 |
189 |
190 |
191 | -
192 |
193 |
194 | Qt::Horizontal
195 |
196 |
197 |
198 | 42
199 | 20
200 |
201 |
202 |
203 |
204 | -
205 |
206 |
207 | Elevator Speed/Time
208 |
209 |
210 |
211 | -
212 |
213 |
214 | false
215 |
216 |
-
217 |
218 | Speed 1; Time 1
219 |
220 |
221 | -
222 |
223 | Speed 2; Time 1
224 |
225 |
226 | -
227 |
228 | Speed 3; Time 1
229 |
230 |
231 | -
232 |
233 | Speed 4; Time 1
234 |
235 |
236 | -
237 |
238 | Speed 1; Time 2
239 |
240 |
241 | -
242 |
243 | Speed 2; Time 2
244 |
245 |
246 | -
247 |
248 | Speed 3; Time 2
249 |
250 |
251 | -
252 |
253 | Speed 4; Time 2
254 |
255 |
256 | -
257 |
258 | Speed 1; Time 3
259 |
260 |
261 | -
262 |
263 | Speed 2; Time 3
264 |
265 |
266 | -
267 |
268 | Speed 3; Time 3
269 |
270 |
271 | -
272 |
273 | Speed 4; Time 3
274 |
275 |
276 | -
277 |
278 | Speed 1; Time 4
279 |
280 |
281 | -
282 |
283 | Speed 2; Time 4
284 |
285 |
286 | -
287 |
288 | Speed 3; Time 4
289 |
290 |
291 | -
292 |
293 | Speed 4; Time 4
294 |
295 |
296 |
297 |
298 | -
299 |
300 |
301 | RAW data
302 |
303 |
304 |
305 | -
306 |
307 |
308 | 255
309 |
310 |
311 |
312 | -
313 |
314 |
315 | Qt::Horizontal
316 |
317 |
318 |
319 | 42
320 | 20
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 | S&witches
331 |
332 |
333 |
334 |
335 | 8
336 | 10
337 | 301
338 | 401
339 |
340 |
341 |
342 | true
343 |
344 |
345 | false
346 |
347 |
348 |
349 | 1
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 | 430
359 | 467
360 | 421
361 | 155
362 |
363 |
364 |
365 | Level properties
366 |
367 |
368 | -
369 |
370 |
371 | Level
372 |
373 |
374 |
375 | -
376 |
377 |
378 |
379 |
380 |
381 | 255
382 |
383 |
384 |
385 | -
386 |
387 |
388 | Qt::Horizontal
389 |
390 |
391 |
392 | 115
393 | 20
394 |
395 |
396 |
397 |
398 | -
399 |
400 |
401 | false
402 |
403 |
404 | Save current level
405 |
406 |
407 |
408 | -
409 |
410 |
411 | Size
412 |
413 |
414 |
415 | -
416 |
417 |
-
418 |
419 | (00) 20x18 Tiles (32x18)
420 |
421 |
422 | -
423 |
424 | (01) 30x28 Tiles (32x28)
425 |
426 |
427 | -
428 |
429 | (02) 20x28 Tiles (32x28)
430 |
431 |
432 | -
433 |
434 | (03) 30x18 Tiles (32x28)
435 |
436 |
437 | -
438 |
439 | (04) 20x18 Tiles (1-2)
440 |
441 |
442 |
443 |
444 | -
445 |
446 |
447 | Qt::Horizontal
448 |
449 |
450 |
451 | 29
452 | 20
453 |
454 |
455 |
456 |
457 | -
458 |
459 |
460 | Time (sec)
461 |
462 |
463 |
464 | -
465 |
466 |
467 | 999
468 |
469 |
470 |
471 | -
472 |
473 |
474 | Music
475 |
476 |
477 |
478 | -
479 |
480 |
481 | Qt::LeftToRight
482 |
483 |
-
484 |
485 | (00) No music
486 |
487 |
488 | -
489 |
490 | (01) Time's almost up!
491 |
492 |
493 | -
494 |
495 | (02) Item active
496 |
497 |
498 | -
499 |
500 | (03) 30 seconds left
501 |
502 |
503 | -
504 |
505 | (04) Pauline abducted
506 |
507 |
508 | -
509 |
510 | (05) Arcade Level 1
511 |
512 |
513 | -
514 |
515 | (06) Arcade Level 2
516 |
517 |
518 | -
519 |
520 | (07) Big City
521 |
522 |
523 | -
524 |
525 | (08) Forest
526 |
527 |
528 | -
529 |
530 | (09) Ship
531 |
532 |
533 | -
534 |
535 | (0A) Jungle
536 |
537 |
538 | -
539 |
540 | (0B) Desert
541 |
542 |
543 | -
544 |
545 | (0C) Desert 2
546 |
547 |
548 | -
549 |
550 | (0D) Airplane
551 |
552 |
553 | -
554 |
555 | (0E) Iceberg
556 |
557 |
558 | -
559 |
560 | (0F) Rockey Valley 1
561 |
562 |
563 | -
564 |
565 | (10) Tower 1
566 |
567 |
568 | -
569 |
570 | (11) Tower 2
571 |
572 |
573 | -
574 |
575 | (12) Level 1-3
576 |
577 |
578 | -
579 |
580 | (13) Spooky
581 |
582 |
583 | -
584 |
585 | (14) Level 1-7
586 |
587 |
588 | -
589 |
590 | (15) Monkey Stage
591 |
592 |
593 | -
594 |
595 | (16) Dangerous
596 |
597 |
598 | -
599 |
600 | (17) Rock Valley 2
601 |
602 |
603 | -
604 |
605 | (18) Level 1-2
606 |
607 |
608 | -
609 |
610 | (19) Battle 3
611 |
612 |
613 | -
614 |
615 | (1A) Battle 1 (1-4)
616 |
617 |
618 | -
619 |
620 | (1B) Battle 2 (1-8)
621 |
622 |
623 | -
624 |
625 | (1C) Level 9-8
626 |
627 |
628 | -
629 |
630 | (1D) Level 9-9
631 |
632 |
633 | -
634 |
635 | (1E) ???
636 |
637 |
638 | -
639 |
640 | (1F) ???
641 |
642 |
643 | -
644 |
645 | (20) ???
646 |
647 |
648 | -
649 |
650 | (21) Level 6-2
651 |
652 |
653 | -
654 |
655 | (22) Level 6-4
656 |
657 |
658 | -
659 |
660 | (23) Level 6-8
661 |
662 |
663 |
664 |
665 | -
666 |
667 |
668 | Qt::Horizontal
669 |
670 |
671 |
672 | 29
673 | 20
674 |
675 |
676 |
677 |
678 | -
679 |
680 |
681 | Palette
682 |
683 |
684 |
685 | -
686 |
687 |
688 | 384
689 |
690 |
691 | 511
692 |
693 |
694 |
695 | -
696 |
697 |
698 | Tileset
699 |
700 |
701 |
702 | -
703 |
704 |
-
705 |
706 | (00) Classic Girder 1
707 |
708 |
709 | -
710 |
711 | (01) Airplane 1
712 |
713 |
714 | -
715 |
716 | (02) Iceberg 1
717 |
718 |
719 | -
720 |
721 | (03) Forest 1
722 |
723 |
724 | -
725 |
726 | (04) Rocky Valley 1
727 |
728 |
729 | -
730 |
731 | (05) Ship 1
732 |
733 |
734 | -
735 |
736 | (06) Desert 1
737 |
738 |
739 | -
740 |
741 | (07) Airplane 2
742 |
743 |
744 | -
745 |
746 | (08) Tower 1
747 |
748 |
749 | -
750 |
751 | (09) Clouds 1
752 |
753 |
754 | -
755 |
756 | (0A) Rocky Valley 2
757 |
758 |
759 | -
760 |
761 | (0B) Desert 2
762 |
763 |
764 | -
765 |
766 | (0C) Big City 1
767 |
768 |
769 | -
770 |
771 | (0D) Ship 2
772 |
773 |
774 | -
775 |
776 | (0E) Jungle 1
777 |
778 |
779 | -
780 |
781 | (0F) Tower 2
782 |
783 |
784 | -
785 |
786 | (10) Classic Girder 2
787 |
788 |
789 | -
790 |
791 | (11) Airplane 3
792 |
793 |
794 | -
795 |
796 | (12) Iceberg 2
797 |
798 |
799 | -
800 |
801 | (13) Forest 2
802 |
803 |
804 | -
805 |
806 | (14) Rocky Valley 3
807 |
808 |
809 | -
810 |
811 | (15) Ship 3
812 |
813 |
814 | -
815 |
816 | (16) Desert 3
817 |
818 |
819 | -
820 |
821 | (17) Airplane 4
822 |
823 |
824 | -
825 |
826 | (18) Tower 3
827 |
828 |
829 | -
830 |
831 | (19) Clouds 2
832 |
833 |
834 | -
835 |
836 | (1A) Rocky Valley 4
837 |
838 |
839 | -
840 |
841 | (1B) Desert 4
842 |
843 |
844 | -
845 |
846 | (1C) Big City 2
847 |
848 |
849 | -
850 |
851 | (1D) Ship 4
852 |
853 |
854 | -
855 |
856 | (1E) Jungle 2
857 |
858 |
859 | -
860 |
861 | (1F) Tower 4
862 |
863 |
864 | -
865 |
866 | (20) Classic Girder 3
867 |
868 |
869 | -
870 |
871 | (21) Giant DK Battle
872 |
873 |
874 |
875 |
876 | -
877 |
878 |
879 | 236
880 |
881 |
882 | 24
883 |
884 |
885 | Tiles %v
886 |
887 |
888 |
889 | -
890 |
891 |
892 | 256
893 |
894 |
895 | 24
896 |
897 |
898 | Sprites %v
899 |
900 |
901 |
902 |
903 |
904 |
905 |
937 |
938 |
939 | &Open ROM
940 |
941 |
942 |
943 |
944 |
945 |
946 |
947 | &Save ROM
948 |
949 |
950 |
951 |
952 | &Undo
953 |
954 |
955 |
956 |
957 | &Clear Level
958 |
959 |
960 |
961 |
962 | &Export level
963 |
964 |
965 |
966 |
967 | &Import level
968 |
969 |
970 |
971 |
972 |
973 |
974 | QDKEdit
975 | QWidget
976 |
977 | 1
978 |
979 |
980 | QTileSelector
981 | QWidget
982 |
983 | 1
984 |
985 |
986 |
987 |
988 |
989 |
--------------------------------------------------------------------------------
/QDKEdit.h:
--------------------------------------------------------------------------------
1 | #ifndef QDKEDIT_H
2 | #define QDKEDIT_H
3 |
4 | #include "QTileEdit.h"
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | //find rombanks containing the level data
12 | #define ROMBANK_1 0x05
13 | #define COMPARE_POS_1 0x25FC // < 0x2D
14 |
15 | #define ROMBANK_POS_2 0x25FF // 0x06
16 | #define COMPARE_POS_2 0x2603 // < 0x50
17 |
18 | #define ROMBANK_POS_3 0x2606 // 0x12
19 |
20 | // some constants
21 | #define MAX_LEVEL_ID 256
22 | #define LAST_LEVEL 105
23 | #define MAX_TILESETS 0x22
24 | #define MAX_SPRITES 0x1B
25 | #define POINTER_TABLE 0x14000
26 | #define SUBTILESET_TABLE 0x30EEB
27 | #define SGB_SYSTEM_PAL 0x786F0 // decompressed size 0x1000
28 | // the SGB packet is always 51.(quint16)var+0x80.E4 00.E5.00.E6 00.C1.00.00 00.00.00.00
29 | // asm @ 0x0E70
30 | #define PAL_ARCADE 0x30F9A
31 | // the palette for the 4 arcade levels are @ 0x4F9A (bank 0x0C) + (6*id)
32 | // this value is palette index + 0xC8
33 | // not freely editable!
34 | #define PAL_TABLE 0x6093B
35 | // PAL_TABLE + (id-4)*8
36 | // palettes for all other levels
37 |
38 | // BGP depends on tileset asm @ 0E9D
39 | #define TILE_INDEX_TABLE 0x60E5
40 | #define ADDITIONAL_TILES_TABLE 0x30E55
41 |
42 | #define VRAM_TILES 0x50
43 | #define VRAM_SPRITES 0x100
44 |
45 | #define ELEVATOR_TABLE 0x30F77
46 | class QMouseEvent;
47 |
48 | struct QDKSprite : QSprite
49 | {
50 | quint16 ramPos;
51 | quint32 levelPos;
52 | // quint8 addFlag;
53 | };
54 |
55 | struct QDKSwitchObject
56 | {
57 | quint8 x, y;
58 | quint16 ramPos;
59 | quint32 levelPos;
60 | bool isSprite;
61 | };
62 |
63 | struct QDKSwitch
64 | {
65 | quint8 state;
66 | quint8 x, y;
67 | quint32 levelPos;
68 | quint16 ramPos;
69 | QList connectedTo;
70 | };
71 |
72 | typedef QColor QGBPalette[4];
73 |
74 | struct QDKLevel
75 | {
76 | quint8 id; // max 256 !
77 | quint8 rombank;
78 | quint32 offset;
79 |
80 | quint8 size; // 0x00 -> 0x240 bytes for tilemap else 0x380 bytes
81 | quint8 music;
82 | quint8 tileset;
83 | quint16 time;
84 |
85 | bool switchData;
86 | bool addSpriteData;
87 | bool fullDataUpToDate;
88 |
89 | QByteArray rawTilemap;
90 | QByteArray displayTilemap;
91 | quint16 paletteIndex;
92 |
93 | QList sprites;
94 | QList switches;
95 |
96 | QByteArray rawSwitchData; // 0x11 + 0x90 bytes
97 | QByteArray rawAddSpriteData; // 0x40 bytes
98 |
99 | QByteArray fullData;
100 | };
101 |
102 | struct QTileInfo
103 | {
104 | quint8 w, h;
105 | quint8 type; // that's a bit vague - this is the pointer+N value
106 | quint8 count;
107 | quint8 fullCount;
108 | quint8 setSpecific;
109 | quint16 additionalTilesAt;
110 | quint32 romOffset;
111 | QList needsTiles;
112 | quint8 projectileTileCount;
113 | bool compressed;
114 | };
115 |
116 | class QDKEdit : public QTileEdit
117 | {
118 | Q_OBJECT
119 | public:
120 | explicit QDKEdit(QWidget *parent = 0);
121 | ~QDKEdit();
122 | bool loadAllLevels(QString romFile);
123 | bool saveAllLevels(QString romFile);
124 | bool exportCurrentLevel(QString filename);
125 | bool importLevel(QString filename);
126 | QString getLevelInfo();
127 | void fillSpriteNames();
128 | void fillTileNames();
129 | void setupTileSelector(QTileSelector *tileSelector, float scale, int limitTileCount);
130 |
131 | private:
132 | void paintLevel(QPainter *painter);
133 | void mouseMoveEvent(QMouseEvent *e);
134 | void mousePressEvent(QMouseEvent *e);
135 | QByteArray LZSSDecompress(QDataStream *in, quint16 decompressedSize);
136 | QByteArray LZSSCompress(QByteArray *src);
137 | bool readLevel(QFile *src, quint8 id, bool fromLvlFile = false);
138 | bool readSGBPalettes(QFile *src);
139 | bool recompressLevel(quint8 id);
140 | bool expandRawTilemap(quint8 id);
141 | bool updateRawTilemap(quint8 id);
142 | void copyTileToSet(QFile *src, quint32 offset, QImage *img, quint16 tileID, quint8 tileSetID, bool compressed, quint8 tileCount, quint16 superOffset);
143 | bool getTileInfo(QFile *src);
144 | bool createTileSets(QFile *src, QGBPalette palette);
145 | bool createSprites(QFile *src, QGBPalette palette);
146 | void sortSprite(QImage *sprite, int id);
147 | void copyTile(QImage *img, int x1, int y1, int x2, int y2, bool mirror);
148 | void fillTile(QImage *img, int x, int y, int index);
149 | void swapTiles(QImage *img, int x1, int y1, int x2, int y2);
150 | QMap spritePix;
151 | QMap spriteImg;
152 | void updateTileset();
153 | quint8 getSpriteDefaultFlag(int id);
154 | void rebuildAddSpriteData(int id);
155 | void rebuildSwitchData(int id);
156 | quint16 vramTiles;
157 | quint16 vramSprites;
158 |
159 | QDKLevel levels[MAX_LEVEL_ID];
160 | QImage tilesets[MAX_TILESETS];
161 | quint8 tilesetBGP[MAX_TILESETS];
162 | QTileInfo tiles[256];
163 | QGBPalette sgbPal[512];
164 | int currentLevel;
165 |
166 | quint8 currentSize; // 0x00 -> 0x240 bytes for tilemap else 0x380 bytes
167 | quint8 currentMusic;
168 | quint8 currentTileset;
169 | quint16 currentTime;
170 | quint16 currentPalIndex;
171 |
172 | bool switchMode;
173 | int switchToEdit;
174 | int swObjToMove;
175 |
176 | QList currentSwitches;
177 | QStack > undoSwitches;
178 |
179 | bool romLoaded;
180 | bool transparentSprites;
181 | static bool isSprite[256];
182 |
183 | signals:
184 | void paletteChanged(int palette);
185 | void tilesetChanged(int set);
186 | void timeChanged(int time);
187 | void musicChanged(int music);
188 | void sizeChanged(int size);
189 | void switchAdded(QDKSwitch *sw);
190 | void switchRemoved(int i);
191 | void switchUpdated(int i, QDKSwitch *sw);
192 | void tilesVRAMchanged(int used);
193 | void spriteVRAMchanged(int used);
194 |
195 | private slots:
196 | void checkForLargeTile(int x, int y, int drawnTile);
197 | void updateSprite(int num);
198 | void undo();
199 | void createUndoData();
200 | void clearUndoData();
201 | void deleteLastUndo();
202 | bool calcVRAMusageOld();
203 | bool calcVRAMusage();
204 |
205 | public slots:
206 | void changeLevel(int id);
207 | void saveLevel();
208 | void changeTime(int time);
209 | void changePalette(int palette);
210 | void changeTileset(int tileset);
211 | void changeMusic(int music);
212 | void changeSize(int size);
213 | void changeSpriteTransparency(bool transparent);
214 | void addSprite(int id);
215 |
216 | void clearLevel();
217 |
218 | void toggleSwitchMode(bool enabled);
219 | void toggleSwitchMode(int enabled);
220 |
221 | void selectSwitch(int num);
222 | void deleteSwitchObj(int num);
223 | void deleteCurrentSwitch();
224 | QPixmap *getTilePixmap(int num);
225 | };
226 |
227 | #endif // QDKEDIT_H
228 |
229 |
230 | /* sprite tiles:
231 |
232 | 3a
233 | 44
234 | 47
235 | 48
236 | 4d
237 | 4e
238 | 4f
239 | 50
240 | 54
241 | 57
242 | 58
243 | 5a
244 | 5c
245 | 5e
246 | 64
247 | 6e
248 | 7a
249 | 7c
250 | 7f
251 | 80
252 | 84
253 | 86
254 | 88
255 | 8a
256 | 8e
257 | 90
258 | 92
259 | 94
260 | 96
261 | 98
262 | 9a
263 | 9d
264 | a2
265 | a4
266 | a6
267 | a8
268 | aa
269 | ac
270 | ae
271 | b0
272 | b6
273 | b8
274 | ba
275 | be
276 | c0
277 | c2
278 | c6
279 | c8
280 | ca
281 | cc
282 |
283 | from add sprite data:
284 | 54
285 | 5C
286 | 5E
287 | 6E
288 | 70 <-- new; but elevator "tiles"
289 | 72 <-- new; but elevator "tiles"
290 | 7F
291 | 80
292 | 84
293 | 98
294 | 9A
295 | B8
296 | CC
297 | */
298 |
--------------------------------------------------------------------------------
/QTileEdit.cpp:
--------------------------------------------------------------------------------
1 | #include "QTileEdit.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include
9 |
10 | #include "QTileSelector.h"
11 |
12 | QTileEdit::QTileEdit(QWidget *parent) :
13 | QWidget(parent), dataIsChanged(false), mousePressed(false), emptyTile(0), tileToDraw(emptyTile), keepAspect(true), selector(NULL), tileDataIs16bit(false), spriteMode(false), spriteContext(false), spriteToMove(-1)
14 | {
15 | //setMouseTracking(true);
16 | background = QImage(levelDimension.width()*tileSize.width(), levelDimension.height()*tileSize.height(), QImage::Format_ARGB32);
17 | background.fill(Qt::white);
18 | originalSize = QImage(0, 0, QImage::Format_RGB32);
19 | connect(this, SIGNAL(dataChanged()), this, SLOT(keepUndoData()));
20 | }
21 |
22 | void QTileEdit::getMouse(bool enable)
23 | {
24 | setMouseTracking(enable);
25 | if (selector != NULL)
26 | selector->getMouse(enable);
27 | }
28 |
29 | bool QTileEdit::isChanged()
30 | {
31 | return dataIsChanged;
32 | }
33 |
34 | void QTileEdit::updateLevel()
35 | {
36 | update();
37 | }
38 |
39 | void QTileEdit::resized(QSize newSize)
40 | {
41 | scaledSize = orgSize;
42 | scaledSize.scale(newSize, Qt::KeepAspectRatio);
43 | scaleFactorX = (float)scaledSize.width() / (float)orgSize.width();
44 | scaleFactorY = (float)scaledSize.height() / (float)orgSize.height();
45 | }
46 |
47 | void QTileEdit::setLevelDimension(int width, int height)
48 | {
49 | levelDimension = QSize(width, height);
50 | orgSize = QSize(levelDimension.width()*tileSize.width(), levelDimension.height()*tileSize.height());
51 | resized(this->size());
52 | originalSize = QImage(orgSize, QImage::Format_RGB32);
53 | }
54 |
55 | void QTileEdit::setTileSize(int width, int height)
56 | {
57 | tileSize = QSize(width, height);
58 | orgSize = QSize(levelDimension.width()*tileSize.width(), levelDimension.height()*tileSize.height());
59 | resized(this->size());
60 | originalSize = QImage(orgSize, QImage::Format_RGB32);
61 | }
62 |
63 | void QTileEdit::setTileToDraw(int tileNumber)
64 | {
65 | tileToDraw = tileNumber;
66 | }
67 |
68 | bool QTileEdit::loadTileSet(QString filename, int emptyTileNumber, int count)
69 | {
70 | if (!tileSet.load(filename))
71 | return false;
72 |
73 | emptyTile = emptyTileNumber;
74 | tileCount = count;
75 | tileSetDimension = QSize(tileSet.width() / tileSize.width(), tileSet.height() / tileSize.height());
76 | return true;
77 | }
78 |
79 | void QTileEdit::setLevelData(QByteArray data, int start = 0, int length = 0)
80 | {
81 | lvlData = data;
82 | lvlDataStart = start;
83 | if (length == 0)
84 | lvlDataLength = data.size() - start;
85 | else
86 | lvlDataLength = length;
87 |
88 | update();
89 | }
90 |
91 | int QTileEdit::getTile(int x, int y)
92 | {
93 | if (!lvlData.size())
94 | return -1;
95 |
96 | return getTile(y*levelDimension.width()+x);
97 | }
98 |
99 | int QTileEdit::getTile(int offset)
100 | {
101 | if (!lvlData.size() || (offset < 0))
102 | return -1;
103 |
104 | int tileNumber;
105 | if (tileDataIs16bit)
106 | {
107 | if (lvlDataStart+offset*2 >= lvlData.size())
108 | return -1;
109 | tileNumber = (unsigned char)lvlData[lvlDataStart+offset*2] + (unsigned char)lvlData[lvlDataStart+2*offset+1]*0x100;
110 | }
111 | else
112 | {
113 | if (lvlDataStart+offset >= lvlData.size())
114 | return -1;
115 | tileNumber = (unsigned char)lvlData[lvlDataStart+offset];
116 | }
117 | return tileNumber;
118 |
119 | }
120 |
121 | void QTileEdit::setTile(int x, int y, int tileNumber)
122 | {
123 | if ((x < 0) || (y < 0))
124 | return;
125 |
126 | setTile(y*levelDimension.width()+x, tileNumber);
127 | }
128 |
129 | void QTileEdit::setTile(int offset, int tileNumber)
130 | {
131 | if (tileDataIs16bit)
132 | {
133 | if (lvlDataStart+offset*2+1 >= lvlData.size())
134 | return;
135 | lvlData[lvlDataStart + offset * 2] = tileNumber % 0x100;
136 | lvlData[lvlDataStart + offset * 2 + 1] = tileNumber / 0x100;
137 | }
138 | else
139 | {
140 | if (lvlDataStart+offset >= lvlData.size())
141 | return;
142 | lvlData[lvlDataStart + offset] = (unsigned char)tileNumber;
143 | }
144 | }
145 |
146 |
147 | QString QTileEdit::spriteNumToString(int sprite)
148 | {
149 | return spriteNames.value(sprite, QString("Sprite 0x%1").arg(sprite, 2, 16, QChar('0')));
150 | }
151 |
152 | QString QTileEdit::tileNumToString(int tile)
153 | {
154 | if (tileDataIs16bit)
155 | return tileNames.value(tile, QString("0x%1").arg(tile, 4, 16, QChar('0')))+QString(" (0x%1)").arg(tile, 4, 16, QChar('0'));
156 | else
157 | return tileNames.value(tile, QString("0x%1").arg(tile, 2, 16, QChar('0')))+QString(" (0x%1)").arg(tile, 2, 16, QChar('0'));;
158 | }
159 |
160 | QRect QTileEdit::tileNumberToQRect(int tileNumber)
161 | {
162 | int x, y, x2, y2;
163 |
164 | y = tileNumber / tileSetDimension.width();
165 | x = tileNumber % tileSetDimension.width();
166 | if (y > tileSetDimension.height())
167 | return QRect(0, 0, tileSize.width(), tileSize.height());
168 |
169 | x2 = x*tileSize.width();
170 | y2 = y*tileSize.height();
171 |
172 | return QRect(x2, y2, tileSize.width(), tileSize.height());
173 | }
174 |
175 | void QTileEdit::drawTile(int tileNumber, int x, int y)
176 | {
177 | QPainter painter(this);
178 |
179 | painter.setBackgroundMode(Qt::TransparentMode);
180 | painter.drawPixmap(QRect(x*tileSize.width(), y*tileSize.height(), tileSize.width(), tileSize.height()), tileSet, tileNumberToQRect(tileNumber));
181 |
182 | }
183 |
184 | void QTileEdit::setBackground(QImage backgroundImage)
185 | {
186 | background = backgroundImage;
187 | }
188 |
189 | void QTileEdit::paintEvent(QPaintEvent *e)
190 | {
191 | QPainter *painter = getPainter();
192 |
193 | if (!painter)
194 | return;
195 |
196 | paintLevel(painter);
197 |
198 | finishPainter(painter);
199 | }
200 |
201 | void QTileEdit::paintLevel(QPainter *painter)
202 | {
203 | //draw background
204 | //QPainter painter(this);
205 | painter->drawImage(QRect(0, 0, orgSize.width(), orgSize.height()), background);
206 |
207 | //draw tiles
208 | painter->setBackgroundMode(Qt::TransparentMode);
209 | int tileNumber;
210 | for (int i = 0; i < levelDimension.height(); i++)
211 | for (int j = 0; j < levelDimension.width(); j++)
212 | {
213 | tileNumber = getTile(j, i);
214 | painter->drawPixmap(QRect(j*tileSize.width(), i*tileSize.height(), tileSize.width(), tileSize.height()), tileSet, tileNumberToQRect(tileNumber));
215 | }
216 |
217 | if (spriteMode)
218 | {
219 | int x, y;
220 |
221 | // draw sprites
222 | for (int i = 0; i < sprites.size(); i++)
223 | {
224 | if (!sprites.at(i).pixelPerfect)
225 | {
226 | x = sprites.at(i).x*tileSize.width() + (sprites.at(i).drawOffset.x()*(qreal)tileSize.width());
227 | y = sprites.at(i).y*tileSize.height() + (sprites.at(i).drawOffset.y()*(qreal)tileSize.height());
228 | }
229 | else
230 | {
231 | x = sprites.at(i).x;
232 | y = sprites.at(i).y;
233 | }
234 |
235 | switch (sprites.at(i).rotate)
236 | {
237 | case LEFT:
238 | painter->save();
239 | painter->translate(sprites.at(i).sprite->height(), 0);
240 | painter->rotate(90);
241 | painter->drawPixmap(y, -1*x, *sprites.at(i).sprite);
242 | painter->restore();
243 | break;
244 | case RIGHT:
245 | painter->save();
246 | painter->translate(0, sprites.at(i).sprite->width());
247 | painter->rotate(-90);
248 | painter->drawPixmap(-1*y, x, *sprites.at(i).sprite);
249 | painter->restore();
250 | break;
251 | case TOP:
252 | painter->save();
253 | painter->translate(sprites.at(i).sprite->width(), sprites.at(i).sprite->height());
254 | painter->rotate(180);
255 | painter->drawPixmap(-1*x, -1*y, *sprites.at(i).sprite);
256 | painter->restore();
257 | break;
258 | case FLIPPED:
259 | painter->save();
260 | painter->translate(sprites.at(i).sprite->width(), 0);
261 | painter->scale(-1, 1);
262 | painter->drawPixmap(-1*x, y, *sprites.at(i).sprite);
263 | painter->restore();
264 | break;
265 | case BOTTOM:
266 | default: painter->drawPixmap(x, y, *sprites.at(i).sprite); break;
267 | }
268 | }
269 | }
270 |
271 | //draw selection
272 | painter->setPen(Qt::gray);
273 | painter->drawRect(mouseOverTile.x(), mouseOverTile.y(), mouseOverTile.width(), mouseOverTile.height());
274 |
275 | //draw sprite selection
276 | if (spriteMode)
277 | {
278 | painter->setPen(Qt::red);
279 | painter->drawRect(spriteSelection.x(), spriteSelection.y(), spriteSelection.width(), spriteSelection.height());
280 | }
281 | }
282 |
283 | QPainter *QTileEdit::getPainter()
284 | {
285 | QPainter *painter = new QPainter();
286 |
287 | bool ok;
288 | if (this->size() == orgSize)
289 | ok = painter->begin(this);
290 | else
291 | ok = painter->begin(&originalSize);
292 |
293 | if (!ok)
294 | return NULL;
295 | else
296 | return painter;
297 | }
298 |
299 | void QTileEdit::finishPainter(QPainter *painter)
300 | {
301 | if (!painter)
302 | return;
303 |
304 | painter->end();
305 |
306 | if (this->size() != orgSize)
307 | {
308 | QPainter widgetPainter(this);
309 | QRect target;
310 | if (!keepAspect)
311 | target = this->rect();
312 | else
313 | target = QRect(0, 0, scaledSize.width(), scaledSize.height());
314 |
315 | widgetPainter.drawImage(target, originalSize);
316 | }
317 |
318 | delete painter;
319 | }
320 |
321 | void QTileEdit::resizeEvent(QResizeEvent *e)
322 | {
323 | resized(e->size());
324 | }
325 |
326 | void QTileEdit::mouseMoveEvent(QMouseEvent *e)
327 | {
328 | if ((e->x()+1 > scaledSize.width()) || (e->x() < 0) || (e->y()+1 > scaledSize.height()) || (e->y() < 0))
329 | return;
330 |
331 | if (!spriteMode)
332 | {
333 | //check if selection rect has moved
334 | int xTile= (float)e->x() / (float)tileSize.width() / scaleFactorX;
335 | int yTile = (float)e->y() / (float)tileSize.height() / scaleFactorY;
336 |
337 | setToolTip(QString("%1 (%2x%3)").arg(tileNumToString(getTile(xTile, yTile))).arg(xTile).arg(yTile));
338 |
339 | QRect newSelection(xTile * tileSize.width(), yTile * tileSize.height(), tileSize.width()-1, tileSize.height()-1);
340 |
341 | if (mouseOverTile != newSelection)
342 | {
343 | mouseOverTile = newSelection;
344 | update();
345 | }
346 |
347 | //check whether left or right mouse button has been pressed
348 | if ((e->buttons() != Qt::LeftButton) && (e->buttons() != Qt::RightButton))
349 | return;
350 |
351 | int tmpTileToDraw = tileToDraw;
352 |
353 | //always draw emptyTile for right click
354 | if (e->buttons() == Qt::RightButton)
355 | tmpTileToDraw = emptyTile;
356 |
357 |
358 | if (getTile(xTile, yTile) != tmpTileToDraw)
359 | {
360 | setTile(xTile, yTile, tmpTileToDraw);
361 | dataIsChanged = true;
362 | emit dataChanged();
363 | emit singleTileChanged(xTile, yTile, tmpTileToDraw);
364 | update();
365 | }
366 | }
367 | else
368 | {
369 | //check if mouse is over a sprite
370 | QRect spriteRect;
371 |
372 | if (!mousePressed)
373 | {
374 | spriteToMove = getSpriteAtXY((float)e->x() / scaleFactorX, (float)e->y() / scaleFactorY, &spriteRect);
375 |
376 | if (spriteToMove == -1)
377 | {
378 | mouseOverTile = QRect();
379 | if (e->buttons() != Qt::LeftButton)
380 | {
381 | update();
382 | return;
383 | }
384 | }
385 | }
386 | else
387 | spriteRect = getSpriteRect(spriteToMove);
388 |
389 | if (spriteToMove == -1)
390 | return;
391 |
392 | setToolTip(QString("0x%1").arg(sprites.at(spriteToMove).id, 2, 16, QChar('0')));
393 |
394 | // sprite changed - update all selections
395 | if (mouseOverTile != spriteRect)
396 | {
397 | mouseOverTile = spriteRect;
398 | update();
399 | }
400 |
401 | if (e->buttons() != Qt::LeftButton)
402 | return;
403 |
404 | // mouse button is pressed
405 | // update sprite selection
406 | spriteSelection = spriteRect;
407 |
408 | // sprite gets moved
409 | // calculate new x,y
410 | int newX, newY;
411 |
412 | if (sprites.at(spriteToMove).pixelPerfect)
413 | {
414 | newX = e->x();
415 | newY = e->y();
416 | }
417 | else
418 | {
419 | newX = (float)e->x() / (float)tileSize.width() / scaleFactorX;
420 | newY = (float)e->y() / (float)tileSize.height() / scaleFactorY;
421 | }
422 |
423 | if ((newX != sprites.at(spriteToMove).x) || (newY != sprites.at(spriteToMove).y))
424 | {
425 | sprites[spriteToMove].x = newX;
426 | sprites[spriteToMove].y = newY;
427 |
428 | if (sprites.at(spriteToMove).pixelPerfect)
429 | spriteRect = QRect(newX, newY, sprites.at(spriteToMove).size.width(), sprites.at(spriteToMove).size.height());
430 | else
431 | spriteRect = QRect(newX*tileSize.width(), newY*tileSize.height(), sprites.at(spriteToMove).size.width()*tileSize.width(), sprites.at(spriteToMove).size.height()*tileSize.height());
432 |
433 | mouseOverTile = spriteRect;
434 | spriteSelection = spriteRect;
435 | dataIsChanged = true;
436 |
437 | emit dataChanged();
438 | update();
439 | }
440 | }
441 | }
442 |
443 | void QTileEdit::mousePressEvent(QMouseEvent *e)
444 | {
445 | if (e->type() == QEvent::MouseButtonPress)
446 | {
447 | createUndoData();
448 | mousePressed = true;
449 | }
450 |
451 | if (!spriteMode)
452 | mouseMoveEvent(e);
453 | else
454 | {
455 | if ((e->button() == Qt::LeftButton) || (e->button() == Qt::RightButton))
456 | {
457 | if (spriteSelection != mouseOverTile)
458 | {
459 | spriteSelection = mouseOverTile;
460 | selectedSprite = spriteToMove;
461 | emit spriteSelected(selectedSprite);
462 | update();
463 | }
464 | }
465 | if (e->button() == Qt::RightButton)
466 | {
467 | if (!spriteContext)
468 | {
469 | if (spriteToMove == -1)
470 | return;
471 | sprites.remove(spriteToMove);
472 | mouseOverTile = QRect();
473 | spriteSelection = QRect();
474 | dataIsChanged = true;
475 | emit spriteSelected(-1);
476 | emit spriteRemoved(spriteToMove);
477 | emit dataChanged();
478 | spriteToMove = -1;
479 | selectedSprite = -1;
480 | update();
481 | return;
482 | }
483 | else
484 | {
485 | emit customContextMenuRequested(e->pos());
486 | mouseOverTile = QRect();
487 | mousePressed = false;
488 | spriteToMove = -1;
489 | update();
490 | return;
491 | }
492 | }
493 | }
494 | }
495 |
496 | int QTileEdit::getSpriteAtXY(int x, int y, QRect *spriteRect = NULL)
497 | {
498 | int i;
499 | int sx1, sx2, sy1, sy2;
500 |
501 | for (i = 0; i < sprites.size(); i++)
502 | {
503 | if (sprites.at(i).pixelPerfect)
504 | {
505 | sx1 = sprites.at(i).x;
506 | sy1 = sprites.at(i).y;
507 | if ((sprites.at(i).rotate == LEFT) || (sprites.at(i).rotate == RIGHT))
508 | {
509 | sx2 = sprites.at(i).x + sprites.at(i).size.width();
510 | sy2 = sprites.at(i).x + sprites.at(i).size.width();
511 | }
512 | else
513 | {
514 | sx2 = sprites.at(i).x + sprites.at(i).size.height();
515 | sy2 = sprites.at(i).x + sprites.at(i).size.height();
516 | }
517 | }
518 | else
519 | {
520 | sx1 = sprites.at(i).x * tileSize.width() + (sprites.at(i).drawOffset.x()*(qreal)tileSize.width());
521 | sy1 = sprites.at(i).y * tileSize.height() + (sprites.at(i).drawOffset.y()*(qreal)tileSize.height());
522 |
523 | if ((sprites.at(i).rotate == LEFT) || (sprites.at(i).rotate == RIGHT))
524 | {
525 | sx2 = (sprites.at(i).x + sprites.at(i).size.height()) * tileSize.width() + (sprites.at(i).drawOffset.x()*(qreal)tileSize.width());
526 | sy2 = (sprites.at(i).y + sprites.at(i).size.width()) * tileSize.height() + (sprites.at(i).drawOffset.y()*(qreal)tileSize.height());
527 | }
528 | else
529 | {
530 | sx2 = (sprites.at(i).x + sprites.at(i).size.width()) * tileSize.width() + (sprites.at(i).drawOffset.x()*(qreal)tileSize.width());
531 | sy2 = (sprites.at(i).y + sprites.at(i).size.height()) * tileSize.height() + (sprites.at(i).drawOffset.y()*(qreal)tileSize.height());
532 | }
533 | }
534 |
535 | if ((x >= sx1) && (x < sx2) && (y >= sy1) && (y < sy2))
536 | break;
537 | }
538 |
539 | if (i == sprites.size())
540 | return -1;
541 | else
542 | {
543 | if (spriteRect)
544 | *spriteRect = QRect(sx1, sy1, sx2-sx1-1, sy2-sy1-1);
545 | return i;
546 | }
547 | }
548 |
549 | void QTileEdit::mouseReleaseEvent(QMouseEvent *)
550 | {
551 | mousePressed = false;
552 |
553 | if (!keepUndo)
554 | deleteLastUndo();
555 | }
556 |
557 | int QTileEdit::getSelectedSprite(int *id)
558 | {
559 | if (selectedSprite != -1)
560 | *id = sprites.at(selectedSprite).id;
561 | else
562 | *id = 0x00;
563 |
564 | return selectedSprite;
565 | }
566 |
567 |
568 | QRect QTileEdit::getSpriteRect(int num)
569 | {
570 | int sx1, sx2, sy1, sy2;
571 |
572 | if (num >= sprites.size())
573 | return QRect();
574 |
575 | if (sprites.at(num).pixelPerfect)
576 | {
577 | sx1 = sprites.at(num).x;
578 | sy1 = sprites.at(num).y;
579 | if ((sprites.at(num).rotate == LEFT) || (sprites.at(num).rotate == RIGHT))
580 | {
581 | sx2 = sprites.at(num).x + sprites.at(num).size.height();
582 | sy2 = sprites.at(num).x + sprites.at(num).size.width();
583 | }
584 | else
585 | {
586 | sx2 = sprites.at(num).x + sprites.at(num).size.width();
587 | sy2 = sprites.at(num).x + sprites.at(num).size.height();
588 | }
589 | }
590 | else
591 | {
592 | sx1 = sprites.at(num).x * tileSize.width() + (sprites.at(num).drawOffset.x()*(qreal)tileSize.width());
593 | sy1 = sprites.at(num).y * tileSize.height() + (sprites.at(num).drawOffset.y()*(qreal)tileSize.height());
594 | if ((sprites.at(num).rotate == LEFT) || (sprites.at(num).rotate == RIGHT))
595 | {
596 | sx2 = (sprites.at(num).x + sprites.at(num).size.height()) * tileSize.width() + (sprites.at(num).drawOffset.x()*(qreal)tileSize.width());
597 | sy2 = (sprites.at(num).y + sprites.at(num).size.width()) * tileSize.height() + (sprites.at(num).drawOffset.y()*(qreal)tileSize.height());
598 | }
599 | else
600 | {
601 | sx2 = (sprites.at(num).x + sprites.at(num).size.width()) * tileSize.width() + (sprites.at(num).drawOffset.x()*(qreal)tileSize.width());
602 | sy2 = (sprites.at(num).y + sprites.at(num).size.height()) * tileSize.height() + (sprites.at(num).drawOffset.y()*(qreal)tileSize.height());
603 | }
604 |
605 | }
606 |
607 | return QRect(sx1, sy1, sx2-sx1-1, sy2-sy1-1);
608 | }
609 |
610 | void QTileEdit::setupTileSelector(QTileSelector *tileSelector, float scale, int limitTileCount)
611 | {
612 | selector = tileSelector;
613 | QFile names("tiles.txt");
614 | QStringList tileNames;
615 | if (names.exists())
616 | {
617 | if (names.open(QIODevice::ReadOnly))
618 | {
619 | QTextStream namesStream(&names);
620 | while (!namesStream.atEnd())
621 | {
622 | tileNames.append(namesStream.readLine());
623 | }
624 | }
625 | }
626 | else
627 | for (int i = 0; i < limitTileCount; i++)
628 | tileNames.append(tileNumToString(i));
629 |
630 | int count;
631 |
632 | if (limitTileCount != 0)
633 | count = limitTileCount;
634 | else
635 | count = tileCount;
636 |
637 | selector->setTilePixmap(tileSet, tileSize, scale, count, tileNames);
638 | if (hasMouseTracking())
639 | selector->getMouse(true);
640 | connect(selector, SIGNAL(tileSelected(int)), this, SLOT(setTileToDraw(int)));
641 | }
642 |
643 | void QTileEdit::toggleSpriteMode(bool enabled)
644 | {
645 | if (enabled != spriteMode)
646 | {
647 | spriteMode = enabled;
648 | update();
649 | }
650 | }
651 |
652 | void QTileEdit::deleteSprite(int num)
653 | {
654 | if (num >= sprites.size())
655 | return;
656 |
657 | sprites.remove(num);
658 |
659 | mouseOverTile = QRect();
660 | spriteSelection = QRect();
661 | dataIsChanged = true;
662 | emit spriteSelected(-1);
663 | emit spriteRemoved(num);
664 | emit dataChanged();
665 | spriteToMove = -1;
666 | selectedSprite = -1;
667 | update();
668 | }
669 |
670 | void QTileEdit::toggleSpriteMode(int enabled)
671 | {
672 | toggleSpriteMode((bool)enabled);
673 | }
674 |
675 | void QTileEdit::getSpriteFlag(int num, quint8 *flag)
676 | {
677 | if (num < sprites.size())
678 | *flag = sprites.at(num).flagByte;
679 | }
680 |
681 | void QTileEdit::setSpriteFlag(int num, quint8 flag)
682 | {
683 | if (num < sprites.size())
684 | sprites[num].flagByte = flag;
685 |
686 | emit flagByteChanged(num);
687 | }
688 |
689 | void QTileEdit::keepUndoData()
690 | {
691 | keepUndo = true;
692 | }
693 |
694 | void QTileEdit::createUndoData()
695 | {
696 | QByteArray undoBytes;
697 | QVector undoSprites;
698 |
699 | keepUndo = false;
700 |
701 | undoBytes.clear();
702 | undoBytes.append(lvlData);
703 |
704 | for (int i = 0; i < sprites.size(); i++)
705 | undoSprites.append(sprites.at(i));
706 |
707 | undoStack.push(qMakePair(undoBytes, undoSprites));
708 | }
709 |
710 | void QTileEdit::undo()
711 | {
712 | if (undoStack.isEmpty())
713 | return;
714 |
715 | QVector undoSprites;
716 | QPair > undoData;
717 |
718 | spriteToMove = -1;
719 |
720 | undoData = undoStack.pop();
721 |
722 | lvlData.clear();
723 | lvlData.append(undoData.first);
724 |
725 | for (int i = sprites.size()-1; i >= 0; i--)
726 | emit spriteRemoved(i);
727 |
728 | sprites.clear();
729 |
730 | undoSprites = undoData.second;
731 | for (int i = 0; i < undoSprites.size(); i++)
732 | {
733 | sprites.append(undoSprites.at(i));
734 | emit spriteAdded(spriteNumToString(undoSprites.at(i).id), undoSprites.at(i).id);
735 | }
736 | undoSprites.clear();
737 |
738 | spriteSelection = QRect();
739 |
740 | update();
741 | }
742 |
743 | void QTileEdit::clearUndoData()
744 | {
745 | QPair > undoData;
746 |
747 | while (!undoStack.isEmpty())
748 | {
749 | undoData = undoStack.pop();
750 | undoData.first.clear();
751 | undoData.second.clear();
752 | }
753 | }
754 |
755 | void QTileEdit::deleteLastUndo()
756 | {
757 | if (!undoStack.isEmpty())
758 | {
759 | QPair > undoData = undoStack.pop();
760 | undoData.first.clear();
761 | undoData.second.clear();
762 | }
763 | }
764 | void QTileEdit::clearLevel()
765 | {
766 | createUndoData();
767 |
768 | if (tileDataIs16bit)
769 | {
770 | for (int i = 0; i < lvlData.size(); i+=2)
771 | {
772 | lvlData[i] = (quint8)(emptyTile % 0x100);
773 | lvlData[i+1] = (quint8)(emptyTile / 0x100);
774 | }
775 | }
776 | else
777 | lvlData.fill(emptyTile);
778 |
779 | for (int i = sprites.size()-1; i >= 0; i--)
780 | emit spriteRemoved(i);
781 |
782 | sprites.clear();
783 | spriteSelection = QRect();
784 | spriteToMove = -1;
785 |
786 | dataIsChanged = true;
787 | emit dataChanged();
788 |
789 | update();
790 | }
791 |
--------------------------------------------------------------------------------
/QTileEdit.h:
--------------------------------------------------------------------------------
1 | #ifndef QTILEEDIT_H
2 | #define QTILEEDIT_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class QTileSelector;
9 |
10 | enum { BOTTOM = 0, FLIPPED = 1, TOP = 2, LEFT = 3, RIGHT = 4 };
11 |
12 | struct QSprite
13 | {
14 | bool pixelPerfect;
15 | int x, y;
16 | QSize size;
17 | QPointF drawOffset;
18 | QPixmap *sprite;
19 | int rotate;
20 | int id;
21 | quint8 flagByte;
22 | };
23 |
24 | class QTileEdit : public QWidget
25 | {
26 | Q_OBJECT
27 | public:
28 | explicit QTileEdit(QWidget *parent = 0);
29 |
30 | bool isChanged();
31 | bool loadTileSet(QString filename, int emptyTileNumber, int count);
32 |
33 | void setLevelDimension(int width, int height);
34 | void setTileSize(int width, int height);
35 | void setLevelData(QByteArray data, int start, int length);
36 | void setBackground(QImage backgroundImage);
37 | int getSelectedSprite(int *id);
38 |
39 | QString spriteNumToString(int sprite);
40 | QString tileNumToString(int tile);
41 |
42 | void getMouse(bool enable);
43 |
44 | void setupTileSelector(QTileSelector *tileSelector, float scale = 1.25f, int limitTileCount = 0);
45 |
46 | protected:
47 | void paintEvent(QPaintEvent *e);
48 | virtual void paintLevel(QPainter *painter);
49 | void mouseMoveEvent(QMouseEvent *e);
50 | void mousePressEvent(QMouseEvent *e);
51 | void mouseReleaseEvent(QMouseEvent *);
52 | void resizeEvent(QResizeEvent *e);
53 | QPainter *getPainter();
54 | void finishPainter(QPainter *painter);
55 |
56 | void resized(QSize newSize);
57 | void drawTile(int tileNumber, int x, int y);
58 | QRect tileNumberToQRect(int tileNumber);
59 | int getSpriteAtXY(int x, int y, QRect *spriteRect);
60 | QRect getSpriteRect(int num);
61 | int getTile(int x, int y);
62 | int getTile(int offset);
63 | void setTile(int x, int y, int tileNumber);
64 | void setTile(int offset, int tileNumber);
65 |
66 | QMap spriteNames;
67 | QMap tileNames;
68 |
69 | QStack > > undoStack;
70 | bool keepUndo;
71 | virtual void createUndoData();
72 | virtual void clearUndoData();
73 | virtual void deleteLastUndo();
74 |
75 | QVector sprites;
76 | QByteArray lvlData;
77 | int lvlDataStart;
78 | int lvlDataLength;
79 | int emptyTile;
80 | int tileToDraw;
81 | int tileCount;
82 | int spriteToMove;
83 | int selectedSprite;
84 | int mousePressed;
85 | float scaleFactorX;
86 | float scaleFactorY;
87 | bool dataIsChanged;
88 | bool tileDataIs16bit;
89 | bool spriteContext;
90 | bool keepAspect;
91 | bool spriteMode;
92 | QRect mouseOverTile;
93 | QRect spriteSelection;
94 | QSize orgSize;
95 | QSize scaledSize;
96 | QSize levelDimension;
97 | QSize tileSetDimension;
98 | QSize tileSize;
99 | QPixmap tileSet;
100 | QImage background;
101 | QImage tiledLevel;
102 | QImage originalSize;
103 | QTileSelector *selector;
104 |
105 | signals:
106 | void dataChanged();
107 | void singleTileChanged(int x, int y, int drawnTile);
108 | void spriteSelected(int spriteNo);
109 | void spriteAdded(QString text, int id);
110 | void spriteRemoved(int index);
111 | void flagByteChanged(int num);
112 |
113 | public slots:
114 | void updateLevel();
115 | void keepUndoData();
116 | virtual void clearLevel();
117 | void setTileToDraw(int tileNumber);
118 | void toggleSpriteMode(bool enabled);
119 | void toggleSpriteMode(int enabled);
120 | void getSpriteFlag(int num, quint8 *flag);
121 | void setSpriteFlag(int num, quint8 flag);
122 | void deleteSprite(int num);
123 | virtual void undo();
124 | };
125 |
126 | #endif // QTILEEDIT_H
127 |
--------------------------------------------------------------------------------
/QTileSelector.cpp:
--------------------------------------------------------------------------------
1 | #include "QTileSelector.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | QTileSelector::QTileSelector(QWidget *parent) :
10 | QWidget(parent), pixSrc(NULL)
11 | {
12 | arrangedTiles = QImage(this->rect().width(), this->rect().height(), QImage::Format_ARGB32);
13 | QPainter painter;
14 | painter.begin(&arrangedTiles);
15 | painter.fillRect(this->rect(), Qt::white);
16 | painter.end();
17 | mouseOverTile = QRect(0, 0, 0, 0);
18 | //setMouseTracking(true);
19 | }
20 |
21 | void QTileSelector::changeTilePixmap(QPixmap tilePixmap)
22 | {
23 | tiles = tilePixmap;
24 | updateImage();
25 | }
26 |
27 | void QTileSelector::setTilePixmap(QPixmap tilePixmap, QSize size, float scale, int count, QStringList names)
28 | {
29 | tiles = tilePixmap;
30 | orgTileSize = size;
31 | tileCount = count;
32 | tileNames = names;
33 | scaleFactor = scale;
34 | tileSize = orgTileSize * scale;
35 |
36 | updateImage();
37 | }
38 |
39 | bool QTileSelector::event(QEvent *e)
40 | {
41 | if (e->type() == QEvent::ToolTip)
42 | {
43 | if (groups.isEmpty())
44 | {
45 | QHelpEvent *he = static_cast(e);
46 | int xTile= he->x() / tileSize.width();
47 | int yTile = he->y() / tileSize.height();
48 |
49 | if ((xTile >= arrangedTilesDimension.width()) || (yTile >= arrangedTilesDimension.height()))
50 | {
51 | setToolTip("");
52 | return QWidget::event(e);
53 | }
54 |
55 | int num = yTile*imageDimension.width()+xTile;
56 | if (num < tileCount)
57 | setToolTip(tileNames.value(num, QString("TileNumber: 0x%1").arg(num, 0, 16)));
58 | else
59 | setToolTip("");
60 | }
61 | else
62 | {
63 | QHelpEvent *he = static_cast(e);
64 | if ((he->x() >= allTilesRect.width()) || (he->y() >= allTilesRect.height()))
65 | {
66 | setToolTip("");
67 | return QWidget::event(e);
68 | }
69 |
70 | int tile = -1;
71 | QRect tileRect;
72 | QMap::const_iterator it = tileRects.constBegin();
73 | while (it != tileRects.constEnd())
74 | {
75 | tileRect = it.value();
76 |
77 | if ((he->x() >= tileRect.x()) && (he->y() >= tileRect.y()) &&
78 | (he->x() < tileRect.x() + tileRect.width()) && (he->y() < tileRect.y() + tileRect.height()))
79 | {
80 | tile = it.key();
81 | break;
82 | }
83 |
84 | ++it;
85 | }
86 |
87 | if (tile != -1)
88 | setToolTip(tileNames.value(tile, QString("TileNumber: 0x%1").arg(tile, 0, 16)));
89 | else
90 | setToolTip("");
91 | }
92 | }
93 |
94 | return QWidget::event(e);
95 | }
96 |
97 | void QTileSelector::groupTiles(QTileGroups grouping, QSpacings spacings)
98 | {
99 | groups = grouping;
100 | space = spacings;
101 | updateImage();
102 | }
103 |
104 | void QTileSelector::setTilePixSrc(QDKEdit *src)
105 | {
106 | pixSrc = src;
107 | updateImage();
108 | }
109 |
110 | void QTileSelector::updateImage()
111 | {
112 | int pixmapX, pixmapY;
113 | pixmapX = tiles.width() / orgTileSize.width();
114 | pixmapY = tiles.height() / orgTileSize.height();
115 |
116 | imageDimension.setWidth(this->rect().width() / tileSize.width());
117 | imageDimension.setHeight(this->rect().height() / tileSize.height());
118 |
119 | if (groups.isEmpty())
120 | allTilesRect = QRect(0, 0, imageDimension.width()*tileSize.width(), ((tileCount / imageDimension.width()) + 1)*tileSize.height());
121 | else
122 | allTilesRect = this->rect();
123 |
124 | arrangedTilesDimension.setWidth(allTilesRect.width() / tileSize.width());
125 | arrangedTilesDimension.setHeight(allTilesRect.height() / tileSize.height());
126 |
127 | int x, x2, y, y2;
128 | QPainter painter;
129 |
130 | arrangedTiles = QImage(this->rect().width(), this->rect().height(), QImage::Format_ARGB32);
131 | arrangedTiles.fill(Qt::white);
132 | painter.begin(&arrangedTiles);
133 | painter.fillRect(allTilesRect, Qt::white);
134 |
135 | if (groups.empty())
136 | {
137 | for (int i = 0; i < tileCount; i++)
138 | {
139 | x = (i % imageDimension.width()) * tileSize.width();
140 | y = (i / imageDimension.width()) * tileSize.height();
141 | x2 = (i % pixmapX) * orgTileSize.width();
142 | y2 = (i / pixmapX) * orgTileSize.height();
143 | QRect target(x, y, tileSize.width(), tileSize.height());
144 | QRect source(x2, y2, orgTileSize.width(), orgTileSize.height());
145 | painter.drawPixmap(target, tiles, source);
146 | }
147 | }
148 |
149 | else
150 | {
151 | QVector tilelist;
152 | int vPos, hPos, x, y;
153 | QString title;
154 | QPixmap *pix;
155 |
156 | vPos = space.top;
157 | hPos = space.left;
158 |
159 | tileRects.clear();
160 |
161 | QTileGroups::const_iterator it = groups.constBegin();
162 | while (it != groups.constEnd())
163 | {
164 | title = it.key();
165 | tilelist = it.value();
166 | ++it;
167 |
168 | hPos = space.left;
169 | painter.drawText(QRect(hPos, vPos, allTilesRect.width(), tileSize.height()), Qt::AlignVCenter, title);
170 | vPos += tileSize.height() + space.afterText;
171 |
172 | for (int i = 0; i < tilelist.size(); i++)
173 | {
174 | x = (tilelist[i] % pixmapX) * orgTileSize.width();
175 | y = (tilelist[i] / pixmapX) * orgTileSize.height();
176 | if (pixSrc)
177 | pix = pixSrc->getTilePixmap(tilelist[i]);
178 |
179 | else
180 | {
181 | pix = new QPixmap(tileSize);
182 | QPainter p;
183 | p.begin(pix);
184 | x2 = (tilelist[i] % pixmapX) * orgTileSize.width();
185 | y2 = (tilelist[i] / pixmapX) * orgTileSize.height();
186 | QRect source(x2, y2, orgTileSize.width(), orgTileSize.height());
187 | p.drawPixmap(pix->rect(), tiles, source);
188 | p.end();
189 | }
190 | QSize pixSize = pix->size();
191 | pixSize.scale(tileSize, Qt::KeepAspectRatio);
192 | QRect target(hPos+(tileSize.width() - pixSize.width())/2, vPos+(tileSize.height() - pixSize.height())/2, pixSize.width(), pixSize.height());
193 | tileRects.insert(tilelist[i], QRect(hPos, vPos, tileSize.width(), tileSize.height()));
194 | painter.drawPixmap(target, *pix);
195 | delete pix;
196 |
197 | hPos += tileSize.width() + space.hSpace;
198 | if (hPos + tileSize.width() + space.right > arrangedTiles.width())
199 | {
200 | hPos = space.left;
201 | vPos += tileSize.height() + space.vSpace;
202 | }
203 | }
204 | if (hPos == space.left)
205 | vPos -= tileSize.height() + space.vSpace;
206 | vPos += tileSize.height() + space.beforeText;
207 | }
208 |
209 | }
210 |
211 | painter.end();
212 | update();
213 | }
214 |
215 | void QTileSelector::paintEvent(QPaintEvent *)
216 | {
217 | QPainter painter(this);
218 | painter.drawImage(QPoint(0, 0), arrangedTiles);
219 |
220 | //draw selection
221 | painter.setPen(Qt::black);
222 | painter.drawRect(mouseOverTile.x(), mouseOverTile.y(), mouseOverTile.width(), mouseOverTile.height());
223 |
224 | //draw selected tile
225 | painter.setPen(Qt::red);
226 | painter.drawRect(selectedTile.x(), selectedTile.y(), selectedTile.width(), selectedTile.height());
227 | }
228 |
229 | void QTileSelector::mouseMoveEvent(QMouseEvent *e)
230 | {
231 | if (groups.isEmpty())
232 | {
233 | //check if selection rect has moved
234 | int xTile= e->x() / tileSize.width();
235 | int yTile = e->y() / tileSize.height();
236 |
237 | QRect newSelection;
238 |
239 | if ((xTile >= arrangedTilesDimension.width()) || (yTile >= arrangedTilesDimension.height()))
240 | newSelection = QRect(0, 0, 0, 0);
241 | else
242 | newSelection = QRect(xTile * tileSize.width(), yTile * tileSize.height(), tileSize.width()-1, tileSize.height()-1);
243 |
244 | if (mouseOverTile != newSelection)
245 | {
246 | mouseOverTile = newSelection;
247 | update();
248 | }
249 |
250 |
251 | //check whether left or right mouse button has been pressed
252 | if (e->buttons() != Qt::LeftButton)
253 | return;
254 |
255 | if (newSelection != selectedTile)
256 | {
257 | int newTileNumber = xTile + (yTile * imageDimension.width());
258 | if (newTileNumber >= tileCount)
259 | return;
260 |
261 | selectedTile = newSelection;
262 | selectedTileNumber = newTileNumber;
263 | update();
264 | emit tileSelected(selectedTileNumber);
265 | }
266 | }
267 | else
268 | {
269 | QRect newSelection;
270 | int tile = -1;
271 | QRect tileRect;
272 | //check if mouse is inside rect
273 | if ((e->x() >= allTilesRect.width()) || (e->y() >= allTilesRect.height()))
274 | newSelection = QRect(0, 0, 0, 0);
275 | else
276 | {
277 | QMap::const_iterator it = tileRects.constBegin();
278 | while (it != tileRects.constEnd())
279 | {
280 | tileRect = it.value();
281 |
282 | if ((e->x() >= tileRect.x()) && (e->y() >= tileRect.y()) &&
283 | (e->x() < tileRect.x() + tileRect.width()) && (e->y() < tileRect.y() + tileRect.height()))
284 | {
285 | tile = it.key();
286 | break;
287 | }
288 |
289 | ++it;
290 | }
291 |
292 | if (tile != -1)
293 | newSelection = tileRect;
294 | }
295 |
296 | if (mouseOverTile != newSelection)
297 | {
298 | mouseOverTile = newSelection;
299 | update();
300 | }
301 |
302 | //check whether left or right mouse button has been pressed
303 | if (e->buttons() != Qt::LeftButton)
304 | return;
305 |
306 | if (newSelection != selectedTile)
307 | {
308 | if ((tile >= tileCount) || (tile == -1))
309 | return;
310 |
311 | selectedTile = newSelection;
312 | selectedTileNumber = tile;
313 | update();
314 |
315 | emit tileSelected(selectedTileNumber);
316 | }
317 | }
318 | }
319 |
320 | void QTileSelector::getMouse(bool enable)
321 | {
322 | setMouseTracking(enable);
323 | }
324 |
325 | void QTileSelector::mousePressEvent(QMouseEvent *e)
326 | {
327 | mouseMoveEvent(e);
328 | }
329 |
330 | void QTileSelector::resizeEvent(QResizeEvent *e)
331 | {
332 | if (e->oldSize() != QSize(-1, -1))
333 | updateImage();
334 | }
335 |
--------------------------------------------------------------------------------
/QTileSelector.h:
--------------------------------------------------------------------------------
1 | #ifndef QTILESELECTOR_H
2 | #define QTILESELECTOR_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class QDKEdit;
9 |
10 | typedef QColor QGBPalette[4];
11 |
12 | typedef QMap > QTileGroups;
13 |
14 | struct QSpacings
15 | {
16 | int top;
17 | int bottom;
18 | int left;
19 | int right;
20 | int vSpace;
21 | int hSpace;
22 | int beforeText;
23 | int afterText;
24 | };
25 |
26 | class QTileSelector : public QWidget
27 | {
28 | Q_OBJECT
29 | public:
30 | explicit QTileSelector(QWidget *parent = 0);
31 | void getMouse(bool enable);
32 |
33 |
34 | private:
35 | QSize tileSize;
36 | QSize orgTileSize;
37 | QSize imageDimension;
38 | QSize arrangedTilesDimension;
39 | QPixmap tiles;
40 | QImage arrangedTiles;
41 | QRect selectedTile;
42 | QRect mouseOverTile;
43 | QRect allTilesRect;
44 | QStringList tileNames;
45 | int tileCount;
46 | int selectedTileNumber;
47 | void updateImage();
48 | float scaleFactor;
49 | QTileGroups groups;
50 | QSpacings space;
51 | QMap tileRects;
52 | QDKEdit *pixSrc;
53 |
54 | protected:
55 | void paintEvent(QPaintEvent *);
56 | void mouseMoveEvent(QMouseEvent *e);
57 | void mousePressEvent(QMouseEvent *e);
58 | void resizeEvent(QResizeEvent *e);
59 | bool event(QEvent *e);
60 |
61 |
62 | signals:
63 | void tileSelected(int tileNumber);
64 |
65 | public slots:
66 | void setTilePixmap(QPixmap tilePixmap, QSize size, float scale, int count, QStringList names);
67 | void changeTilePixmap(QPixmap tilePixmap);
68 | void groupTiles(QTileGroups grouping, QSpacings spacings);
69 | void setTilePixSrc(QDKEdit *src);
70 | };
71 |
72 | #endif // QTILESELECTOR_H
73 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | eDKit
2 | =====
3 |
4 | Level editor for Donkey Kong '94 for Classic Gameboy
5 |
6 | =====
7 |
8 | Just run qmake && make. This has only been tested on Linux (Kubuntu 12.04 x64)
9 |
10 | The application expects "base.gb" in its directory.
11 |
--------------------------------------------------------------------------------
/eDKit.pro:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------
2 | #
3 | # Project created by QtCreator 2014-01-31T20:08:43
4 | #
5 | #-------------------------------------------------
6 |
7 | QT += core gui
8 |
9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
10 |
11 | TARGET = eDKit
12 | TEMPLATE = app
13 |
14 |
15 | SOURCES += main.cpp\
16 | MainWindow.cpp\
17 | QTileEdit.cpp\
18 | QTileSelector.cpp\
19 | QDKEdit.cpp
20 |
21 | HEADERS += MainWindow.h\
22 | QTileEdit.h\
23 | QTileSelector.h\
24 | QDKEdit.h
25 |
26 | FORMS += MainWindow.ui
27 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "MainWindow.h"
5 |
6 | int main(int argc, char *argv[])
7 | {
8 | QApplication a(argc, argv);
9 |
10 | if (!QFile::exists(qApp->applicationDirPath() + "/" BASE_ROM))
11 | {
12 | QMessageBox::warning(NULL, "Error", "You need a base.gb in the editor's directory!");
13 | return 1;
14 | }
15 |
16 | MainWindow w;
17 | w.show();
18 |
19 | return a.exec();
20 | }
21 |
--------------------------------------------------------------------------------
/win32/eDKit.rar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bailli/eDKit/63d3d3d48d4de75bc0b4c5e6da9b9f9d74c08251/win32/eDKit.rar
--------------------------------------------------------------------------------