/* * Copyright (c) 2002-2021 Julien Nadeau Carriere * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #if 0 static const AG_Version brushSetVersion = { "agar-map brush set", 6, 0 }; #endif typedef struct merge_tool { MAP_Tool tool; /* MAP_Tool -> MERGE_Tool */ TAILQ_HEAD_(ag_object) brushes; /* Brush maps */ AG_Tlist *tlBrushes; /* Brush selection */ } MERGE_Tool; enum { NODE, FILL, EDNW, EDNO, EDNE, EDWE, EDEA, EDSW, EDSO, EDSE }; #if 0 static const int MergeTable[9][9] = { /* nw n ne w fill e sw s se */ { EDNW, EDNO, EDNO, EDWE, NODE, FILL, FILL, FILL, FILL }, /* nw */ { EDNO, EDNO, EDNO, FILL, NODE, FILL, FILL, FILL, FILL }, /* n */ { EDNO, EDNO, NODE, 0 , NODE, EDEA, FILL, FILL, EDEA }, /* ne */ { EDWE, FILL, FILL, EDWE, NODE, FILL, EDWE, FILL, FILL }, /* w */ { FILL, FILL, FILL, FILL, NODE, FILL, FILL, FILL, FILL }, /* fill */ { FILL, FILL, EDEA, FILL, NODE, EDEA, FILL, FILL, EDEA }, /* e */ { EDWE, 0, EDEA, EDWE, NODE, 0, EDSW, EDSO, EDSO }, /* sw */ { FILL, FILL, FILL, FILL, NODE, FILL, EDSO, EDSO, EDSO }, /* s */ { EDWE, 0, EDEA, EDNO, NODE, EDNO, EDSO, EDSO, EDSE } /* se */ }; #endif static void FreeBrushes(MAP_Tool *_Nonnull t) { MERGE_Tool *mt = t; AG_Object *ob, *obNext; for (ob = TAILQ_FIRST(&mt->brushes); ob != TAILQ_END(&mt->brushes); ob = obNext) { obNext = TAILQ_NEXT(ob, cobjs); AG_ObjectDestroy(ob); } TAILQ_INIT(&mt->brushes); } static void Destroy(MAP_Tool *_Nonnull t) { FreeBrushes(t); } static void CreateBrush(AG_Event *_Nonnull event) { char name[AG_OBJECT_NAME_MAX]; char mapName[AG_OBJECT_NAME_MAX]; MERGE_Tool *mt = AG_PTR(1); AG_Textbox *tbName = AG_TEXTBOX_PTR(2); MAP *m; AG_TextboxCopyString(tbName, name, sizeof(name) - sizeof("brush()")); if (name[0] == '\0') { AG_TextMsg(AG_MSG_ERROR, _("No brush name was given.")); return; } Snprintf(mapName, sizeof(mapName), "brush(%s)", name); if (AG_TlistFindText(mt->tlBrushes, mapName) != NULL) { AG_TextMsg(AG_MSG_ERROR, _("A `%s' brush exists."), mapName); return; } m = MAP_New(NULL, mapName); if (AG_ObjectLoad(m) == -1) { if (MAP_AllocNodes(map, 4,4) == -1) { AG_TextMsg(AG_MSG_ERROR, "MAP_AllocNodes: %s", AG_GetError()); goto fail; } } TAILQ_INSERT_HEAD(&mt->brushes, OBJECT(m), cobjs); AG_TlistDeselectAll(mt->tlBrushes); AG_TlistSelect(mt->tlBrushes, AG_TlistAddPtrHead(mt->tlBrushes, NULL, mapName, m)); AG_TextboxPrintf(tbName, NULL); return; fail: AG_ObjectDestroy(m); } static void EditBrush(AG_Event *_Nonnull event) { AG_TlistItem *it; MERGE_Tool *mt = AG_PTR(1); AG_Window *pwin = AG_WINDOW_PTR(2); TAILQ_FOREACH(it, &mt->tlBrushes->items, items) { MAP *brush = it->p1; AG_Window *win; AG_Toolbar *tbar; if (!it->selected) continue; if ((win = AG_WindowNewNamed(0, "mapedit-tool-merge-%s", OBJECT(brush)->name)) == NULL) continue; tbar = AG_ToolbarNew(win, AG_TOOLBAR_HORIZ, 1, 0); MAP_ViewNew(win, brush, MAP_VIEW_EDIT | MAP_VIEW_GRID | MAPVIEW_PROPS, tbar, NULL); AG_WindowAttach(pwin, win); AG_WindowShow(win); } } static void RemoveBrush(AG_Event *_Nonnull event) { MERGE_Tool *mt = AG_PTR(1); AG_TlistItem *it, *nit; for (it = TAILQ_FIRST(&mt->tlBrushes->items); it != TAILQ_END(&mt->tlBrushes->items); it = nit) { nit = TAILQ_NEXT(it, items); if (it->selected) { char wname[AG_OBJECT_NAME_MAX]; AG_Object *brush = it->p1; AG_Window *win; Snprintf(wname, sizeof(wname), "win-mapedit-tool-merge-%s", OBJECT(brush)->name); if ((win = AG_FindWindow(wname)) != NULL) { AG_WindowHide(win); AG_ObjectDetach(win); } TAILQ_REMOVE(&brushes, brush, cobjs); AG_TlistDel(mt->tlBrushes, it); AG_ObjectDestroy(brush); } } } static void Interpolate(MAP *_Nonnull mapSrc, MAP_Node *_Nonnull nodeSrc, MAP_Item *_Nonnull miSrc, MAP *_Nonnull mapDst, MAP_Node *_Nonnull nodeDest, MAP_Item *_Nonnull miDst) { /* TODO */ } static int Effect(MAP_Tool *_Nonnull t, MAP_Node *_Nonnull n) { MERGE_Tool *mt = t; MAP_View *mv = t->mv; MAP *map = mv->map; AG_TlistItem *it; /* Avoid circular references. XXX ugly */ if (strncmp(OBJECT(m)->name, "brush(", 6) == 0) return (1); TAILQ_FOREACH(it, &mt->tlBrushes->items, items) { MAP *mapSrc; int sx, sy, dx, dy; if (!it->selected) continue; mapSrc = it->p1; for (sy = 0, dy = mv->cy; sy < mapSrc->h && dy < map->h; sy++, dy++) { for (sx = 0, dx = mv->cx; sx < mapSrc->w && dx < map->w; sx++, dx++) { MAP_Node *nodeSrc = &mapSrc->map[sy][sx]; MAP_Node *nodeDst = &map->map[dy][dx]; MAP_Item *miSrc, *miDst; TAILQ_FOREACH(miSrc, &nodeSrc->items, items) { TAILQ_FOREACH(miDst, &nodeDst->items, items) { Interpolate( mapSrc, nodeSrc, miSrc, m, nodeDst, miDst); } } } } } return (1); } #if 0 static int Load(MAP_Tool *_Nonnull t, AG_DataSource *_Nonnull buf) { MERGE_Tool *mt = t; Uint32 i, nBrushes; if (AG_ReadVersion(buf, &brushSetVersion, NULL) != 0) return (-1); FreeBrushes(t); AG_TlistClear(mt->tlBrushes); nBrushes = AG_ReadUint32(buf); for (i = 0; i < nBrushes; i++) { char mapName[AG_OBJECT_NAME_MAX]; MAP *map; AG_CopyString(mapName, buf, sizeof(mapName)); m = Malloc(sizeof(MAP)); MAP_Init(map, mapName); map_load(map, buf); TAILQ_INSERT_HEAD(&mt->brushes, OBJECT(nbrush), cobjs); AG_TlistAddPtrHead(mt->tlBrushes, NULL, mapName, nbrush); } return (0); } static int Save(MAP_Tool *_Nonnull t, AG_DataSource *_Nonnull buf) { MERGE_Tool *mt = t; AG_Object *ob; Uint32 nBrushes = 0; off_t count_offs; AG_WriteVersion(buf, &brushSetVersion); count_offs = AG_Tell(buf); /* Skip count */ AG_WriteUint32(buf, 0); TAILQ_FOREACH(ob, &mt->brushes, cobjs) { struct brush *brush = (struct brush *)ob; AG_WriteString(buf, ob->name); map_save(brush, buf); nBrushes++; } AG_WriteUint32At(buf, nBrushes, count_offs); /* Write count */ return (0); } #endif static int Cursor(MAP_Tool *_Nonnull t, AG_Rect *_Nonnull rd) { #if 0 MERGE_Tool *mt = t; MAP_View *mv = t->mv; MAP *map = mv->map, *mapSrc; AG_TlistItem *it; int sx,sy, dx,dy; int rv = -1; const int tileSz = MAPTILESZ(mv); /* XXX ugly work around circular ref */ if (strncmp(OBJECT(map)->name, "brush(", 6) == 0) return (-1); TAILQ_FOREACH(it, &mt->tlBrushes->items, items) { if (!it->selected) { continue; } mapSrc = it->p1; for (sy = 0, dy = rd->y; sy < mapSrc->h; sy++, dy += tileSz) { for (sx = 0, dx = rd->x; sx < mapSrc->w; sx++, dx += tileSz) { MAP_Node *nodeSrc = &mapSrc->map[sy][sx]; MAP_Item *mi; TAILQ_FOREACH(mi, &nodeSrc->items, items) { MAP_ItemDraw(map, mi, WIDGET(mv)->rView.x1 + dx, WIDGET(mv)->rView.y1 + dy, mv->cam); rv = 0; } if (mv->flags & MAPVIEW_PROPS) MAP_ViewDraw_props(mv, nodeSrc, dx,dy, -1, -1); } } } return (rv); #else return (-1); #endif } static void Init(MAP_Tool *_Nonnull t) { MERGE_Tool *mt = t; AG_Window *win; AG_Box *hb; AG_Button *bu; AG_Textbox *tb; if ((win = MAP_ToolWindow(t, "mapedit-tool-merge")) == NULL) return; hb = AG_BoxNewHoriz(win, AG_BOX_HFILL); { tb = AG_TextboxNewS(hb, AG_TEXTBOX_FOCUS, _("Name: ")); AG_SetEvent(tb, "textbox-return", CreateBrush, "%p,%p", t, tb); bu = AG_ButtonNew(hb, 0, _("Create")); AG_ButtonSetPadding(bu, 6); /* Align */ AG_ButtonSetFocusable(bu, 0); AG_SetEvent(bu, "button-pushed", CreateBrush, "%p,%p", t, tb); } hb = AG_BoxNewHoriz(win, AG_BOX_HOMOGENOUS | AG_BOX_HFILL); { #if 0 bu = AG_ButtonNew(hb, 0, _("Load set")); AG_SetEvent(bu, "button-pushed", LoadBrushes, NULL); bu = AG_ButtonNew(hb, 0, _("Save set")); AG_SetEvent(bu, "button-pushed", SaveBrushes, NULL); #endif bu = AG_ButtonNew(hb, 0, _("Edit")); AG_SetEvent(bu, "button-pushed", EditBrush, "%p,%p", t, win); bu = AG_ButtonNew(hb, 0, _("Remove")); AG_SetEvent(bu, "button-pushed", RemoveBrush, "%p", t); } TAILQ_INIT(&mt->brushes); mt->tlBrushes = AG_TlistNew(win, AG_TLIST_MULTI | AG_TLIST_EXPAND); } const MAP_Tool mapMergeOps = { "Merge", N_("Merge Pattern"), &mapIconMerge, sizeof(MERGE_Tool), 0, Init, Destroy, NULL, /* pane */ NULL, /* edit */ Cursor, Effect, NULL, /* mousemotion */ NULL, /* mousebuttondown */ NULL, /* mousebuttonup */ NULL, /* keydown */ NULL, /* keyup */ NULL /* pane */ };