00001
00002
00003
00004
00005
00006
00007
00008
00009
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00030
00031 #include "stratagus.h"
00032
00033 #include <sstream>
00034
00035 #include "sound.h"
00036 #include "iolib.h"
00037 #include "network.h"
00038 #include "interface.h"
00039 #include "cursor.h"
00040 #include "map.h"
00041 #include "guichan.h"
00042 #include "patch_type.h"
00043 #include "patch.h"
00044 #include "patch_manager.h"
00045 #include "ui.h"
00046 #include "font.h"
00047
00048
00049 extern void DrawGuichanWidgets();
00050
00051 bool PatchEditorRunning;
00052
00053 extern gcn::Gui *Gui;
00054 static gcn::Container *PatchEditorContainer;
00055
00056 static CPatch *Patch;
00057
00058 static const int PatchMenuWidth = 150;
00059
00060 static float ScrollX;
00061 static float ScrollY;
00062
00063 static CGraphic *TransparentG;
00064 static CGraphic *TransparentSmallG;
00065 static CGraphic *ImpassableG;
00066 static CGraphic *ImpassableSmallG;
00067 static CGraphic *WaterG;
00068 static CGraphic *WaterSmallG;
00069 #define NumSpeeds 7
00070 static CGraphic *SpeedG[NumSpeeds + 1];
00071 static CGraphic *SpeedSmallG[NumSpeeds + 1];
00072
00073 enum PatchButton {
00074 ButtonNone,
00075 ButtonImpassable,
00076 ButtonWater,
00077 ButtonSpeed0,
00078 ButtonSpeed1,
00079 ButtonSpeed2,
00080 ButtonSpeed3,
00081 ButtonSpeed4,
00082 ButtonSpeed5,
00083 ButtonSpeed6,
00084 ButtonSpeed7,
00085 ButtonTransparent,
00086 };
00087
00088 struct PatchIcon
00089 {
00090 int X;
00091 int Y;
00092 CGraphic *G;
00093 PatchButton Button;
00094 };
00095
00096 static std::vector<PatchIcon> PatchIcons;
00097
00098 static std::map<int, unsigned short> FlagMap;
00099 static PatchButton CurrentButton;
00100 static PatchButton MouseOverButton;
00101 static int MouseOverTileX;
00102 static int MouseOverTileY;
00103
00104 static bool DraggingFlag;
00105 static bool SetFlagWhileDragging;
00106
00107
00108 static void DoScroll()
00109 {
00110 int state = MouseScrollState | KeyScrollState;
00111 static Uint32 lastTicks = GetTicks() - 1;
00112
00113 Uint32 ticks = GetTicks();
00114 float speed = (ticks - lastTicks) / 1000.f * 400;
00115 lastTicks = ticks;
00116
00117 if (state == ScrollNone) {
00118 return;
00119 }
00120
00121 if ((state & (ScrollLeft | ScrollRight)) &&
00122 (state & (ScrollLeft | ScrollRight)) != (ScrollLeft | ScrollRight)) {
00123 if (state & ScrollRight) {
00124 ScrollX += speed;
00125 } else {
00126 ScrollX -= speed;
00127 }
00128 }
00129 if ((state & (ScrollUp | ScrollDown)) &&
00130 (state & (ScrollUp | ScrollDown)) != (ScrollUp | ScrollDown)) {
00131 if (state & ScrollDown) {
00132 ScrollY += speed;
00133 } else {
00134 ScrollY -= speed;
00135 }
00136 }
00137
00138 if (ScrollX < 0.f ||
00139 Patch->getType()->getGraphic()->Width < (Video.Width - PatchMenuWidth)) {
00140 ScrollX = 0.f;
00141 } else if (ScrollX > Patch->getType()->getGraphic()->Width - (Video.Width - PatchMenuWidth)) {
00142 ScrollX = (float)(Patch->getType()->getGraphic()->Width - (Video.Width - PatchMenuWidth));
00143 }
00144 if (ScrollY < 0.f ||
00145 Patch->getType()->getGraphic()->Height < Video.Height) {
00146 ScrollY = 0.f;
00147 } else if (ScrollY > Patch->getType()->getGraphic()->Height - Video.Height) {
00148 ScrollY = (float)(Patch->getType()->getGraphic()->Height - Video.Height);
00149 }
00150 }
00151
00152 static void PatchEditorCallbackButtonDown(unsigned button)
00153 {
00154 if ((1 << button) != LeftButton) {
00155 return;
00156 }
00157
00158
00159 if (MouseOverButton != ButtonNone) {
00160 CurrentButton = MouseOverButton;
00161 }
00162
00163
00164 if (MouseOverTileX != -1) {
00165 unsigned short newFlag;
00166 unsigned short flag = Patch->getType()->getFlag(MouseOverTileX, MouseOverTileY);
00167 if (ButtonSpeed0 <= CurrentButton && CurrentButton <= ButtonSpeed7) {
00168
00169 newFlag = (flag & ~MapFieldSpeedMask) | FlagMap[CurrentButton];
00170 SetFlagWhileDragging = true;
00171 } else {
00172
00173 newFlag = flag ^ FlagMap[CurrentButton];
00174 SetFlagWhileDragging = (flag & FlagMap[CurrentButton]) == 0;
00175 }
00176 Patch->getType()->setFlag(MouseOverTileX, MouseOverTileY, newFlag);
00177 DraggingFlag = true;
00178 }
00179 }
00180
00181 static void PatchEditorCallbackButtonUp(unsigned button)
00182 {
00183 DraggingFlag = false;
00184 }
00185
00186 static void PatchEditorCallbackKeyDown(unsigned key, unsigned keychar)
00187 {
00188 if (HandleKeyModifiersDown(key, keychar)) {
00189 return;
00190 }
00191
00192 switch (key) {
00193 case 'f':
00194 if (!(KeyModifiers & (ModifierAlt | ModifierControl))) {
00195 break;
00196 }
00197 ToggleFullScreen();
00198 break;
00199
00200 case 'x':
00201 if (!(KeyModifiers & (ModifierAlt | ModifierControl))) {
00202 break;
00203 }
00204 Exit(0);
00205
00206 case SDLK_UP:
00207 case SDLK_KP8:
00208 KeyScrollState |= ScrollUp;
00209 break;
00210 case SDLK_DOWN:
00211 case SDLK_KP2:
00212 KeyScrollState |= ScrollDown;
00213 break;
00214 case SDLK_LEFT:
00215 case SDLK_KP4:
00216 KeyScrollState |= ScrollLeft;
00217 break;
00218 case SDLK_RIGHT:
00219 case SDLK_KP6:
00220 KeyScrollState |= ScrollRight;
00221 break;
00222
00223 default:
00224 HandleCommandKey(key);
00225 return;
00226 }
00227 }
00228
00229 static void PatchEditorCallbackKeyUp(unsigned key, unsigned keychar)
00230 {
00231 if (HandleKeyModifiersUp(key, keychar)) {
00232 return;
00233 }
00234
00235 switch (key) {
00236 case SDLK_UP:
00237 case SDLK_KP8:
00238 KeyScrollState &= ~ScrollUp;
00239 break;
00240 case SDLK_DOWN:
00241 case SDLK_KP2:
00242 KeyScrollState &= ~ScrollDown;
00243 break;
00244 case SDLK_LEFT:
00245 case SDLK_KP4:
00246 KeyScrollState &= ~ScrollLeft;
00247 break;
00248 case SDLK_RIGHT:
00249 case SDLK_KP6:
00250 KeyScrollState &= ~ScrollRight;
00251 break;
00252 default:
00253 break;
00254 }
00255 }
00256
00257 static void PatchEditorCallbackKeyRepeated(unsigned dummy1, unsigned dummy2)
00258 {
00259 }
00260
00261 static void PatchEditorCallbackMouse(int x, int y)
00262 {
00263 HandleCursorMove(&x, &y);
00264
00265 int oldTileX = MouseOverTileX;
00266 int oldTileY = MouseOverTileY;
00267
00268 GameCursor = UI.Point.Cursor;
00269 MouseOverButton = ButtonNone;
00270 MouseOverTileX = -1;
00271
00272
00273 std::vector<PatchIcon>::iterator i;
00274 for (i = PatchIcons.begin(); i != PatchIcons.end(); ++i) {
00275 if (i->X <= x && x <= i->X + i->G->Width &&
00276 i->Y <= y && y <= i->Y + i->G->Height)
00277 {
00278 MouseOverButton = i->Button;
00279 }
00280 }
00281
00282
00283 if (0 <= x && x < Video.Width - PatchMenuWidth &&
00284 0 <= y && y < Video.Height)
00285 {
00286 int tileX = (x - (int)ScrollX) / TileSizeX;
00287 int tileY = (y - (int)ScrollY) / TileSizeY;
00288 if (0 <= tileX && tileX < Patch->getType()->getTileWidth() &&
00289 0 <= tileY && tileY < Patch->getType()->getTileHeight())
00290 {
00291 MouseOverTileX = tileX;
00292 MouseOverTileY = tileY;
00293
00294 if (DraggingFlag &&
00295 (oldTileX == -1 || oldTileX != MouseOverTileX || oldTileY != MouseOverTileY))
00296 {
00297 unsigned short newFlag;
00298 unsigned short flag = Patch->getType()->getFlag(MouseOverTileX, MouseOverTileY);
00299 if (ButtonSpeed0 <= CurrentButton && CurrentButton <= ButtonSpeed7) {
00300 newFlag = (flag & ~MapFieldSpeedMask) | FlagMap[CurrentButton];
00301 } else {
00302 if (SetFlagWhileDragging) {
00303 newFlag = flag | FlagMap[CurrentButton];
00304 } else {
00305 newFlag = flag & (~FlagMap[CurrentButton]);
00306 }
00307 }
00308 Patch->getType()->setFlag(MouseOverTileX, MouseOverTileY, newFlag);
00309 }
00310 }
00311 }
00312
00313
00314 if (HandleMouseScrollArea(x, y)) {
00315 return;
00316 }
00317 }
00318
00319 static void PatchEditorCallbackExit(void)
00320 {
00321 }
00322
00323 static void DrawPatch()
00324 {
00325 const CGraphic *g = Patch->getType()->getGraphic();
00326 g->DrawClip(-(int)ScrollX, -(int)ScrollY);
00327 }
00328
00329 static void DrawPatchTileIcons()
00330 {
00331 CGraphic *g;
00332 int x, y;
00333
00334 for (int j = 0; j < Patch->getType()->getTileHeight(); ++j) {
00335 y = j * TileSizeY - (int)ScrollY;
00336
00337 for (int i = 0; i < Patch->getType()->getTileWidth(); ++i) {
00338 x = i * TileSizeX - (int)ScrollX;
00339
00340 unsigned short flags = Patch->getType()->getFlag(i, j);
00341 if (flags & MapFieldTransparent) {
00342 g = TransparentSmallG;
00343 g->DrawClip(x, y);
00344 continue;
00345 }
00346 if (flags & MapFieldUnpassable) {
00347 g = ImpassableSmallG;
00348 g->DrawClip(x, y);
00349 }
00350 if (flags & MapFieldWaterAllowed) {
00351 g = WaterSmallG;
00352 g->DrawClip(x + 16, y);
00353 }
00354 if ((flags & MapFieldSpeedMask) != 3) {
00355 g = SpeedSmallG[(flags & MapFieldSpeedMask)];
00356 g->DrawClip(x + 16, y + 16);
00357 }
00358 }
00359 }
00360 }
00361
00362 static void DrawGrids()
00363 {
00364 int i;
00365 int width = Patch->getType()->getTileWidth();
00366 int height = Patch->getType()->getTileHeight();
00367
00368 for (i = 1; i < width; ++i) {
00369 Video.DrawVLineClip(ColorBlack, i * TileSizeX - (int)ScrollX, 0, height * TileSizeY);
00370 }
00371 for (i = 1; i < height; ++i) {
00372 Video.DrawHLineClip(ColorBlack, 0, i * TileSizeY - (int)ScrollY, width * TileSizeX);
00373 }
00374 }
00375
00376 static void DrawIcons()
00377 {
00378 Uint32 color;
00379 std::vector<PatchIcon>::iterator i;
00380
00381 for (i = PatchIcons.begin(); i != PatchIcons.end(); ++i) {
00382 i->G->DrawClip(i->X, i->Y);
00383
00384 if (i->Button == MouseOverButton) {
00385 color = ColorBlue;
00386 } else if (i->Button == CurrentButton) {
00387 color = ColorGreen;
00388 } else {
00389 color = ColorBlack;
00390 }
00391
00392 Video.DrawRectangleClip(color, i->X - 1, i->Y - 1, 50, 50);
00393 Video.DrawRectangleClip(color, i->X - 2, i->Y - 2, 52, 52);
00394 }
00395 }
00396
00397 static void DrawCoordinates()
00398 {
00399 if (MouseOverTileX == -1) {
00400 return;
00401 }
00402
00403 std::ostringstream o;
00404 o << MouseOverTileX << "," << MouseOverTileY;
00405 VideoDrawTextCentered(Video.Width - PatchMenuWidth / 2, Video.Height - 15, GameFont, o.str());
00406 }
00407
00408 static void PatchEditorUpdateDisplay()
00409 {
00410
00411 DrawPatch();
00412 DrawPatchTileIcons();
00413 DrawGrids();
00414
00415
00416 Video.FillRectangle(ColorGray, Video.Width - PatchMenuWidth, 0, PatchMenuWidth, Video.Height);
00417 DrawIcons();
00418 DrawCoordinates();
00419
00420 DrawGuichanWidgets();
00421
00422 DrawCursor();
00423
00424 Invalidate();
00425 RealizeVideoMemory();
00426 }
00427
00428 static gcn::Button *PatchNewButton(const std::string &caption)
00429 {
00430 gcn::Color darkColor(38, 38, 78, 128);
00431 gcn::Button *button;
00432
00433 button = new gcn::Button(caption);
00434 button->setSize(106, 28);
00435 button->setBackgroundColor(darkColor);
00436 button->setBaseColor(darkColor);
00437 return button;
00438 }
00439
00440 class PatchSaveButtonListener : public gcn::ActionListener
00441 {
00442 public:
00443 virtual void action(const std::string &eventId) {
00444 CFile file;
00445 std::string name = UserDirectory + "patches/" + Patch->getType()->getName() + ".lua";
00446 if (file.open(name.c_str(), CL_OPEN_WRITE) == -1) {
00447
00448 return;
00449 }
00450 file.printf("%s", Map.PatchManager.savePatchType(Patch->getType()).c_str());
00451 file.close();
00452
00453 }
00454 };
00455
00456 class PatchExitButtonListener : public gcn::ActionListener
00457 {
00458 public:
00459 virtual void action(const std::string &eventId) {
00460
00461 PatchEditorRunning = false;
00462 }
00463 };
00464
00465 static void InitCallbacks(EventCallback *callbacks)
00466 {
00467 callbacks->ButtonPressed = PatchEditorCallbackButtonDown;
00468 callbacks->ButtonReleased = PatchEditorCallbackButtonUp;
00469 callbacks->MouseMoved = PatchEditorCallbackMouse;
00470 callbacks->MouseExit = PatchEditorCallbackExit;
00471 callbacks->KeyPressed = PatchEditorCallbackKeyDown;
00472 callbacks->KeyReleased = PatchEditorCallbackKeyUp;
00473 callbacks->KeyRepeated = PatchEditorCallbackKeyRepeated;
00474 callbacks->NetworkEvent = NetworkEvent;
00475 }
00476
00477 static void PatchEditorMainLoop()
00478 {
00479 const EventCallback *old_callbacks = GetCallbacks();
00480 EventCallback callbacks;
00481 gcn::Button *saveButton;
00482 gcn::Button *exitButton;
00483 PatchSaveButtonListener *saveButtonListener;
00484 PatchExitButtonListener *exitButtonListener;
00485
00486 InitCallbacks(&callbacks);
00487 SetCallbacks(&callbacks);
00488
00489 ScrollX = 0.f;
00490 ScrollY = 0.f;
00491
00492 CurrentButton = ButtonImpassable;
00493 MouseOverButton = ButtonNone;
00494
00495 gcn::Widget *oldTop = Gui->getTop();
00496
00497 PatchEditorContainer = new gcn::Container();
00498 PatchEditorContainer->setDimension(gcn::Rectangle(0, 0, Video.Width, Video.Height));
00499 PatchEditorContainer->setOpaque(false);
00500 Gui->setTop(PatchEditorContainer);
00501
00502 saveButton = PatchNewButton(_("Save"));
00503 saveButtonListener = new PatchSaveButtonListener();
00504 saveButton->addActionListener(saveButtonListener);
00505 PatchEditorContainer->add(saveButton,
00506 Video.Width - PatchMenuWidth / 2 - saveButton->getWidth() / 2, 20);
00507
00508 exitButton = PatchNewButton(_("Exit"));
00509 exitButtonListener = new PatchExitButtonListener();
00510 exitButton->addActionListener(exitButtonListener);
00511 PatchEditorContainer->add(exitButton,
00512 Video.Width - PatchMenuWidth / 2 - saveButton->getWidth() / 2, Video.Height - exitButton->getHeight() - 20);
00513
00514 Map.PatchManager.load();
00515
00516 PatchEditorRunning = true;
00517 while (PatchEditorRunning) {
00518 CheckMusicFinished();
00519 DoScroll();
00520
00521 PatchEditorUpdateDisplay();
00522
00523 WaitEventsOneFrame();
00524 }
00525
00526 SetCallbacks(old_callbacks);
00527
00528 Gui->setTop(oldTop);
00529 delete PatchEditorContainer;
00530 delete saveButton;
00531 delete saveButtonListener;
00532 delete exitButton;
00533 delete exitButtonListener;
00534 }
00535
00536 static void PatchAddIcon(int x, int y, CGraphic *g, PatchButton b)
00537 {
00538 PatchIcon icon;
00539 icon.X = x;
00540 icon.Y = y;
00541 icon.G = g;
00542 icon.Button = b;
00543 PatchIcons.push_back(icon);
00544 }
00545
00546 static void PatchLoadIcons()
00547 {
00548 std::string patchEditorPath = "ui/patcheditor/";
00549 int spacing = 15;
00550 int leftX = Video.Width - PatchMenuWidth + spacing;
00551 int rightX = Video.Width - 48 - spacing;
00552 int topY = 100;
00553
00554 ImpassableG = CGraphic::New(patchEditorPath + "impassable.png");
00555 ImpassableG->Load();
00556 ImpassableSmallG = CGraphic::New(patchEditorPath + "impassable-small.png");
00557 ImpassableSmallG->Load();
00558 PatchAddIcon(leftX, topY, ImpassableG, ButtonImpassable);
00559 FlagMap[ButtonImpassable] = MapFieldUnpassable;
00560
00561 WaterG = CGraphic::New(patchEditorPath + "water.png");
00562 WaterG->Load();
00563 WaterSmallG = CGraphic::New(patchEditorPath + "water-small.png");
00564 WaterSmallG->Load();
00565 PatchAddIcon(rightX, topY, WaterG, ButtonWater);
00566 FlagMap[ButtonWater] = MapFieldWaterAllowed;
00567
00568 for (int i = 0; i <= NumSpeeds; ++i) {
00569 std::ostringstream o;
00570 o << patchEditorPath << "speed" << i << ".png";
00571 SpeedG[i] = CGraphic::New(o.str());
00572 SpeedG[i]->Load();
00573 o.str(std::string());
00574 o.clear();
00575 o << patchEditorPath << "speed" << i << "-small.png";
00576 SpeedSmallG[i] = CGraphic::New(o.str());
00577 SpeedSmallG[i]->Load();
00578 PatchAddIcon((!(i & 1) ? leftX : rightX), topY + (48 + spacing) * ((i + 2) / 2), SpeedG[i], (PatchButton)(ButtonSpeed0 + i));
00579 FlagMap[ButtonSpeed0 + i] = i;
00580 }
00581
00582 TransparentG = CGraphic::New(patchEditorPath + "transparent.png");
00583 TransparentG->Load();
00584 TransparentSmallG = CGraphic::New(patchEditorPath + "transparent-small.png");
00585 TransparentSmallG->Load();
00586 PatchAddIcon(leftX, topY + (48 + spacing) * ((NumSpeeds + 3) / 2), TransparentG, ButtonTransparent);
00587 FlagMap[ButtonTransparent] = MapFieldTransparent;
00588 }
00589
00590 static void PatchFreeIcons()
00591 {
00592 CGraphic::Free(ImpassableG);
00593 ImpassableG = NULL;
00594 CGraphic::Free(ImpassableSmallG);
00595 ImpassableSmallG = NULL;
00596
00597 CGraphic::Free(WaterG);
00598 WaterG = NULL;
00599 CGraphic::Free(WaterSmallG);
00600 WaterSmallG = NULL;
00601
00602 PatchIcons.clear();
00603 FlagMap.clear();
00604 }
00605
00606 void StartPatchEditor(const std::string &patchName)
00607 {
00608 std::string name;
00609 if (patchName.substr(0, 5) != "user-") {
00610 name = "user-" + patchName;
00611 if (Map.PatchManager.getPatchType(name) == NULL) {
00612 CPatchType *patchType = Map.PatchManager.getPatchType(patchName);
00613 Map.PatchManager.newPatchType(name,
00614 patchType->getFile(),
00615 patchType->getTileWidth(), patchType->getTileHeight(),
00616 patchType->getFlags());
00617 }
00618 } else {
00619 name = patchName;
00620 }
00621
00622 Patch = Map.PatchManager.add(name, 0, 0);
00623 if (!Patch) {
00624 fprintf(stderr, "Invalid patch name: %s\n", patchName.c_str());
00625 return;
00626 }
00627
00628 PatchLoadIcons();
00629
00630 PatchEditorMainLoop();
00631
00632 PatchFreeIcons();
00633
00634 Map.PatchManager.clear();
00635 }
00636