void UIObjectAddChild(UIObject *o, UIObject *c) { CArrayPushBack(&o->Children, &c); c->Parent = o; assert(o->Type != UITYPE_TAB && "need special add child for TAB type"); if (o->Type == UITYPE_CONTEXT_MENU) { // Resize context menu based on children o->Size = Vec2iMax(o->Size, Vec2iAdd(c->Pos, c->Size)); } }
EditorResult EditorBrushStopPainting(EditorBrush *b, Mission *m) { EditorResult result = EDITOR_RESULT_NONE; if (b->IsPainting) { switch (b->Type) { case BRUSHTYPE_LINE: EditorBrushPaintLine(b, m); result = EDITOR_RESULT_CHANGED; break; case BRUSHTYPE_BOX: EditorBrushPaintBox(b, m, b->PaintType, MAP_UNSET); result = EDITOR_RESULT_CHANGED; break; case BRUSHTYPE_BOX_FILLED: EditorBrushPaintBox(b, m, b->PaintType, b->PaintType); result = EDITOR_RESULT_CHANGED; break; case BRUSHTYPE_ROOM: EditorBrushPaintBox(b, m, MAP_WALL, MAP_ROOM); result = EDITOR_RESULT_CHANGED; break; case BRUSHTYPE_ROOM_PAINTER: // Reload map to update tiles result = EDITOR_RESULT_RELOAD; break; case BRUSHTYPE_SELECT: if (b->IsMoving) { // Move the tiles from the source to the target // Need to copy all the tiles to a temp buffer first in case // we are moving to an overlapped position CArray movedTiles; Vec2i v; int i; int delta; CArrayInit(&movedTiles, sizeof(unsigned short)); // Copy tiles to temp from selection, setting them to MAP_FLOOR // in the process for (v.y = 0; v.y < b->SelectionSize.y; v.y++) { for (v.x = 0; v.x < b->SelectionSize.x; v.x++) { Vec2i vOffset = Vec2iAdd(v, b->SelectionStart); int idx = vOffset.y * m->Size.x + vOffset.x; unsigned short *tile = CArrayGet( &m->u.Static.Tiles, idx); CArrayPushBack(&movedTiles, tile); *tile = MAP_FLOOR; } } // Move the selection to the new position b->SelectionStart.x += b->Pos.x - b->DragPos.x; b->SelectionStart.y += b->Pos.y - b->DragPos.y; // Copy tiles to the new area, for parts of the new area that // are valid i = 0; for (v.y = 0; v.y < b->SelectionSize.y; v.y++) { for (v.x = 0; v.x < b->SelectionSize.x; v.x++) { Vec2i vOffset = Vec2iAdd(v, b->SelectionStart); if (vOffset.x >= 0 && vOffset.x < m->Size.x && vOffset.y >= 0 && vOffset.y < m->Size.y) { int idx = vOffset.y * m->Size.x + vOffset.x; unsigned short *tileFrom = CArrayGet(&movedTiles, i); unsigned short *tileTo = CArrayGet( &m->u.Static.Tiles, idx); *tileTo = *tileFrom; result = EDITOR_RESULT_CHANGED_AND_RELOAD; } i++; } } // Update the selection to fit within map boundaries delta = -b->SelectionStart.x; if (delta > 0) { b->SelectionStart.x += delta; b->SelectionSize.x -= delta; } delta = -b->SelectionStart.y; if (delta > 0) { b->SelectionStart.y += delta; b->SelectionSize.y -= delta; } delta = b->SelectionStart.x + b->SelectionSize.x - m->Size.x; if (delta > 0) { b->SelectionSize.x -= delta; } delta = b->SelectionStart.y + b->SelectionSize.y - m->Size.y; if (delta > 0) { b->SelectionSize.y -= delta; } // Check if the selection is still valid; if not, invalidate it if (b->SelectionSize.x < 0 || b->SelectionSize.y < 0) { b->SelectionSize = Vec2iZero(); } b->IsMoving = 0; } else { // Record the selection size b->SelectionStart = Vec2iMin(b->LastPos, b->Pos); b->SelectionSize.x = abs(b->LastPos.x - b->Pos.x) + 1; b->SelectionSize.y = abs(b->LastPos.y - b->Pos.y) + 1; // Disallow 1x1 selection sizes if (b->SelectionSize.x <= 1 && b->SelectionSize.y <= 1) { b->SelectionSize = Vec2iZero(); } } break; case BRUSHTYPE_SET_EXIT: { Vec2i exitStart = Vec2iMin(b->LastPos, b->Pos); Vec2i exitEnd = Vec2iMax(b->LastPos, b->Pos); // Clamp within map boundaries exitStart = Vec2iClamp( exitStart, Vec2iZero(), Vec2iMinus(m->Size, Vec2iUnit())); exitEnd = Vec2iClamp( exitEnd, Vec2iZero(), Vec2iMinus(m->Size, Vec2iUnit())); // Check that size is big enough Vec2i size = Vec2iAdd(Vec2iMinus(exitEnd, exitStart), Vec2iUnit()); if (size.x >= 3 && size.y >= 3) { // Check that exit area has changed if (!Vec2iEqual(exitStart, m->u.Static.Exit.Start) || !Vec2iEqual(exitEnd, m->u.Static.Exit.End)) { m->u.Static.Exit.Start = exitStart; m->u.Static.Exit.End = exitEnd; result = EDITOR_RESULT_CHANGED_AND_RELOAD; } } } break; default: // do nothing break; } } b->IsPainting = 0; CArrayClear(&b->HighlightedTiles); return result; }
// Check that this area does not overlap two or more "walls" int MapIsLessThanTwoWallOverlaps(Map *map, Vec2i pos, Vec2i size) { Vec2i v; int numOverlaps = 0; Vec2i overlapMin = Vec2iZero(); Vec2i overlapMax = Vec2iZero(); if (pos.x < 0 || pos.y < 0 || pos.x + size.x >= map->Size.x || pos.y + size.y >= map->Size.y) { return 0; } for (v.y = pos.y; v.y < pos.y + size.y; v.y++) { for (v.x = pos.x; v.x < pos.x + size.x; v.x++) { // only check perimeter if (v.x != pos.x && v.x != pos.x + size.x - 1 && v.y != pos.y && v.y != pos.y + size.y - 1) { continue; } switch (IMapGet(map, v)) { case MAP_WALL: // Check if this wall is part of a room if (!MapTileIsPartOfRoom(map, v)) { if (numOverlaps == 0) { overlapMin = overlapMax = v; } else { overlapMin = Vec2iMin(overlapMin, v); overlapMax = Vec2iMax(overlapMax, v); } numOverlaps++; } break; default: break; } } } if (numOverlaps < 2) { return 1; } // Now check that all tiles between the first and last tiles are // pillar tiles for (v.y = overlapMin.y; v.y <= overlapMax.y; v.y++) { for (v.x = overlapMin.x; v.x <= overlapMax.x; v.x++) { switch (IMapGet(map, v) & MAP_MASKACCESS) { case MAP_WALL: // Check if this wall is not part of a room if (MapTileIsPartOfRoom(map, v)) { return 0; } break; default: // invalid tile type return 0; } } } return 1; }
// Find the size of the passage created by the overlap of two rooms // To find whether an overlap is valid, // collect the perimeter walls that overlap // Two possible cases where there is a valid overlap: // - if there are exactly two overlapping perimeter walls // i.e. // X // room 2 XXXXXX // X X <-- all tiles between either belong to room 1 or room 2 // XXXXXX // X room 1 // // - if the collection of overlapping tiles are contiguous // i.e. // X room 1 X // XXXXXXXXXXXXXXX // X room 2 X // // In both cases, the overlap is valid if all tiles in between are room or // perimeter tiles. The size of the passage is given by the largest difference // in the x or y coordinates between the first and last intersection tiles, // minus 1 int MapGetRoomOverlapSize( Map *map, Vec2i pos, Vec2i size, unsigned short *overlapAccess) { Vec2i v; int numOverlaps = 0; Vec2i overlapMin = Vec2iZero(); Vec2i overlapMax = Vec2iZero(); if (pos.x < 0 || pos.y < 0 || pos.x + size.x >= map->Size.x || pos.y + size.y >= map->Size.y) { return 0; } // Find perimeter tiles that overlap for (v.y = pos.y; v.y < pos.y + size.y; v.y++) { for (v.x = pos.x; v.x < pos.x + size.x; v.x++) { // only check perimeter if (v.x != pos.x && v.x != pos.x + size.x - 1 && v.y != pos.y && v.y != pos.y + size.y - 1) { continue; } switch (IMapGet(map, v)) { case MAP_WALL: // Check if this wall is part of a room if (MapTileIsPartOfRoom(map, v)) { // Get the access level of the room Vec2i v2; for (v2.y = v.y - 1; v2.y <= v.y + 1; v2.y++) { for (v2.x = v.x - 1; v2.x <= v.x + 1; v2.x++) { if ((IMapGet(map, v2) & MAP_MASKACCESS) == MAP_ROOM) { *overlapAccess |= IMapGet(map, v2) & MAP_ACCESSBITS; } } } if (numOverlaps == 0) { overlapMin = overlapMax = v; } else { overlapMin = Vec2iMin(overlapMin, v); overlapMax = Vec2iMax(overlapMax, v); } numOverlaps++; } break; default: break; } } } if (numOverlaps < 2) { return 0; } // Now check that all tiles between the first and last tiles are room or // perimeter tiles for (v.y = overlapMin.y; v.y <= overlapMax.y; v.y++) { for (v.x = overlapMin.x; v.x <= overlapMax.x; v.x++) { switch (IMapGet(map, v) & MAP_MASKACCESS) { case MAP_ROOM: break; case MAP_WALL: // Check if this wall is part of a room if (!MapTileIsPartOfRoom(map, v)) { return 0; } break; default: // invalid tile type return 0; } } } return MAX(overlapMax.x - overlapMin.x, overlapMax.y - overlapMin.y) - 1; }