/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include #include #include #include #include #include #include #include "editor.h" #include #include #include CLayerTiles::CLayerTiles(int w, int h) { m_Type = LAYERTYPE_TILES; m_aName[0] = '\0'; m_Width = w; m_Height = h; m_Image = -1; m_Game = 0; m_Color.r = 255; m_Color.g = 255; m_Color.b = 255; m_Color.a = 255; m_ColorEnv = -1; m_ColorEnvOffset = 0; m_Tele = 0; m_Speedup = 0; m_Front = 0; m_Switch = 0; m_Tune = 0; m_AutoMapperConfig = -1; m_Seed = 0; m_AutoAutoMap = false; m_pTiles = new CTile[m_Width*m_Height]; mem_zero(m_pTiles, m_Width*m_Height*sizeof(CTile)); } CLayerTiles::~CLayerTiles() { delete [] m_pTiles; } CTile CLayerTiles::GetTile(int x, int y) { return m_pTiles[y*m_Width+x]; } void CLayerTiles::SetTile(int x, int y, CTile tile) { m_pTiles[y*m_Width+x] = tile; } void CLayerTiles::PrepareForSave() { for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width; x++) m_pTiles[y*m_Width+x].m_Flags &= TILEFLAG_VFLIP|TILEFLAG_HFLIP|TILEFLAG_ROTATE; if(m_Image != -1 && m_Color.a == 255) { for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width; x++) m_pTiles[y*m_Width+x].m_Flags |= m_pEditor->m_Map.m_lImages[m_Image]->m_aTileFlags[m_pTiles[y*m_Width+x].m_Index]; } } void CLayerTiles::MakePalette() { for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width; x++) m_pTiles[y*m_Width+x].m_Index = y*16+x; } void CLayerTiles::Render(bool Tileset) { if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size()) m_Texture = m_pEditor->m_Map.m_lImages[m_Image]->m_Texture; Graphics()->TextureSet(m_Texture); ColorRGBA Color = ColorRGBA(m_Color.r/255.0f, m_Color.g/255.0f, m_Color.b/255.0f, m_Color.a/255.0f); Graphics()->BlendNone(); m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE, m_pEditor->EnvelopeEval, m_pEditor, m_ColorEnv, m_ColorEnvOffset); Graphics()->BlendNormal(); m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_TRANSPARENT, m_pEditor->EnvelopeEval, m_pEditor, m_ColorEnv, m_ColorEnvOffset); // Render DDRace Layers if(!Tileset) { if(m_Tele) m_pEditor->RenderTools()->RenderTeleOverlay(((CLayerTele*)this)->m_pTeleTile, m_Width, m_Height, 32.0f); if(m_Speedup) m_pEditor->RenderTools()->RenderSpeedupOverlay(((CLayerSpeedup*)this)->m_pSpeedupTile, m_Width, m_Height, 32.0f); if(m_Switch) m_pEditor->RenderTools()->RenderSwitchOverlay(((CLayerSwitch*)this)->m_pSwitchTile, m_Width, m_Height, 32.0f); if(m_Tune) m_pEditor->RenderTools()->RenderTuneOverlay(((CLayerTune*)this)->m_pTuneTile, m_Width, m_Height, 32.0f); } } int CLayerTiles::ConvertX(float x) const { return (int)(x/32.0f); } int CLayerTiles::ConvertY(float y) const { return (int)(y/32.0f); } void CLayerTiles::Convert(CUIRect Rect, RECTi *pOut) { pOut->x = ConvertX(Rect.x); pOut->y = ConvertY(Rect.y); pOut->w = ConvertX(Rect.x+Rect.w+31) - pOut->x; pOut->h = ConvertY(Rect.y+Rect.h+31) - pOut->y; } void CLayerTiles::Snap(CUIRect *pRect) { RECTi Out; Convert(*pRect, &Out); pRect->x = Out.x*32.0f; pRect->y = Out.y*32.0f; pRect->w = Out.w*32.0f; pRect->h = Out.h*32.0f; } void CLayerTiles::Clamp(RECTi *pRect) { if(pRect->x < 0) { pRect->w += pRect->x; pRect->x = 0; } if(pRect->y < 0) { pRect->h += pRect->y; pRect->y = 0; } if(pRect->x+pRect->w > m_Width) pRect->w = m_Width - pRect->x; if(pRect->y+pRect->h > m_Height) pRect->h = m_Height - pRect->y; if(pRect->h < 0) pRect->h = 0; if(pRect->w < 0) pRect->w = 0; } bool CLayerTiles::IsEmpty(CLayerTiles *pLayer) { for(int y = 0; y < pLayer->m_Height; y++) for(int x = 0; x < pLayer->m_Width; x++) { int Index = pLayer->GetTile(x, y).m_Index; if(Index) { if(pLayer->m_Game) { if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidGameTile(Index)) return false; } else if(pLayer->m_Front) { if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidFrontTile(Index)) return false; } else return false; } } return true; } void CLayerTiles::BrushSelecting(CUIRect Rect) { Graphics()->TextureClear(); m_pEditor->Graphics()->QuadsBegin(); m_pEditor->Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.4f); Snap(&Rect); IGraphics::CQuadItem QuadItem(Rect.x, Rect.y, Rect.w, Rect.h); m_pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1); m_pEditor->Graphics()->QuadsEnd(); char aBuf[16]; str_format(aBuf, sizeof(aBuf), "%d,%d", ConvertX(Rect.w), ConvertY(Rect.h)); TextRender()->Text(0, Rect.x+3.0f, Rect.y+3.0f, m_pEditor->m_ShowPicker?15.0f:15.0f*m_pEditor->m_WorldZoom, aBuf, -1); } int CLayerTiles::BrushGrab(CLayerGroup *pBrush, CUIRect Rect) { RECTi r; Convert(Rect, &r); Clamp(&r); if(!r.w || !r.h) return 0; // create new layers if(this->m_Tele) { CLayerTele *pGrabbed = new CLayerTele(r.w, r.h); pGrabbed->m_pEditor = m_pEditor; pGrabbed->m_Texture = m_Texture; pGrabbed->m_Image = m_Image; pGrabbed->m_Game = m_Game; if (m_pEditor->m_BrushColorEnabled) { pGrabbed->m_Color = m_Color; pGrabbed->m_Color.a = 255; } pBrush->AddLayer(pGrabbed); // copy the tiles for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) pGrabbed->m_pTiles[y*pGrabbed->m_Width+x] = GetTile(r.x+x, r.y+y); // copy the tele data if(!m_pEditor->Input()->KeyIsPressed(KEY_SPACE)) for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) { pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x] = ((CLayerTele*)this)->m_pTeleTile[(r.y+y)*m_Width+(r.x+x)]; if(pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEIN || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEOUT || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEINEVIL // || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECKINEVIL || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECK || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECKOUT // || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELECHECKIN || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEINWEAPON || pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Type == TILE_TELEINHOOK) { m_pEditor->m_TeleNumber = pGrabbed->m_pTeleTile[y*pGrabbed->m_Width+x].m_Number; } } pGrabbed->m_TeleNum = m_pEditor->m_TeleNumber; str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName, sizeof(pGrabbed->m_aFileName)); } else if(this->m_Speedup) { CLayerSpeedup *pGrabbed = new CLayerSpeedup(r.w, r.h); pGrabbed->m_pEditor = m_pEditor; pGrabbed->m_Texture = m_Texture; pGrabbed->m_Image = m_Image; pGrabbed->m_Game = m_Game; if (m_pEditor->m_BrushColorEnabled) { pGrabbed->m_Color = m_Color; pGrabbed->m_Color.a = 255; } pBrush->AddLayer(pGrabbed); // copy the tiles for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) pGrabbed->m_pTiles[y*pGrabbed->m_Width+x] = GetTile(r.x+x, r.y+y); // copy the speedup data if(!m_pEditor->Input()->KeyIsPressed(KEY_SPACE)) for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) { pGrabbed->m_pSpeedupTile[y*pGrabbed->m_Width+x] = ((CLayerSpeedup*)this)->m_pSpeedupTile[(r.y+y)*m_Width+(r.x+x)]; if(pGrabbed->m_pSpeedupTile[y*pGrabbed->m_Width+x].m_Type == TILE_BOOST) { m_pEditor->m_SpeedupAngle = pGrabbed->m_pSpeedupTile[y*pGrabbed->m_Width+x].m_Angle; m_pEditor->m_SpeedupForce = pGrabbed->m_pSpeedupTile[y*pGrabbed->m_Width+x].m_Force; m_pEditor->m_SpeedupMaxSpeed = pGrabbed->m_pSpeedupTile[y*pGrabbed->m_Width+x].m_MaxSpeed; } } pGrabbed->m_SpeedupForce = m_pEditor->m_SpeedupForce; pGrabbed->m_SpeedupMaxSpeed = m_pEditor->m_SpeedupMaxSpeed; pGrabbed->m_SpeedupAngle = m_pEditor->m_SpeedupAngle; str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName, sizeof(pGrabbed->m_aFileName)); } else if(this->m_Switch) { CLayerSwitch *pGrabbed = new CLayerSwitch(r.w, r.h); pGrabbed->m_pEditor = m_pEditor; pGrabbed->m_Texture = m_Texture; pGrabbed->m_Image = m_Image; pGrabbed->m_Game = m_Game; if (m_pEditor->m_BrushColorEnabled) { pGrabbed->m_Color = m_Color; pGrabbed->m_Color.a = 255; } pBrush->AddLayer(pGrabbed); // copy the tiles for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) pGrabbed->m_pTiles[y*pGrabbed->m_Width+x] = GetTile(r.x+x, r.y+y); // copy the switch data if(!m_pEditor->Input()->KeyIsPressed(KEY_SPACE)) for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) { pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x] = ((CLayerSwitch*)this)->m_pSwitchTile[(r.y+y)*m_Width+(r.x+x)]; if(pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_DOOR + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_HIT_START || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_HIT_END || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHOPEN || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHCLOSE || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHTIMEDOPEN || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_SWITCHTIMEDCLOSE || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_LASER_LONG + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_LASER_MEDIUM + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == ENTITY_LASER_SHORT + ENTITY_OFFSET || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_JUMP || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_PENALTY || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_BONUS || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_ALLOW_TELE_GUN || pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Type == TILE_ALLOW_BLUE_TELE_GUN) { m_pEditor->m_SwitchNum = pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Number; m_pEditor->m_SwitchDelay = pGrabbed->m_pSwitchTile[y*pGrabbed->m_Width+x].m_Delay; } } pGrabbed->m_SwitchNumber = m_pEditor->m_SwitchNum; pGrabbed->m_SwitchDelay = m_pEditor->m_SwitchDelay; str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName, sizeof(pGrabbed->m_aFileName)); } else if(this->m_Tune) { CLayerTune *pGrabbed = new CLayerTune(r.w, r.h); pGrabbed->m_pEditor = m_pEditor; pGrabbed->m_Texture = m_Texture; pGrabbed->m_Image = m_Image; pGrabbed->m_Game = m_Game; if (m_pEditor->m_BrushColorEnabled) { pGrabbed->m_Color = m_Color; pGrabbed->m_Color.a = 255; } pBrush->AddLayer(pGrabbed); // copy the tiles for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) pGrabbed->m_pTiles[y*pGrabbed->m_Width+x] = GetTile(r.x+x, r.y+y); // copy the tiles if(!m_pEditor->Input()->KeyIsPressed(KEY_SPACE)) for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) { pGrabbed->m_pTuneTile[y*pGrabbed->m_Width+x] = ((CLayerTune*)this)->m_pTuneTile[(r.y+y)*m_Width+(r.x+x)]; if(pGrabbed->m_pTuneTile[y*pGrabbed->m_Width+x].m_Type == TILE_TUNE1) { m_pEditor->m_TuningNum = pGrabbed->m_pTuneTile[y*pGrabbed->m_Width+x].m_Number; } } pGrabbed->m_TuningNumber = m_pEditor->m_TuningNum; str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName, sizeof(pGrabbed->m_aFileName)); } else if(this->m_Front) { CLayerFront *pGrabbed = new CLayerFront(r.w, r.h); pGrabbed->m_pEditor = m_pEditor; pGrabbed->m_Texture = m_Texture; pGrabbed->m_Image = m_Image; pGrabbed->m_Game = m_Game; if (m_pEditor->m_BrushColorEnabled) { pGrabbed->m_Color = m_Color; pGrabbed->m_Color.a = 255; } pBrush->AddLayer(pGrabbed); // copy the tiles for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) pGrabbed->m_pTiles[y*pGrabbed->m_Width+x] = GetTile(r.x+x, r.y+y); str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName, sizeof(pGrabbed->m_aFileName)); } else { CLayerTiles *pGrabbed = new CLayerTiles(r.w, r.h); pGrabbed->m_pEditor = m_pEditor; pGrabbed->m_Texture = m_Texture; pGrabbed->m_Image = m_Image; pGrabbed->m_Game = m_Game; if (m_pEditor->m_BrushColorEnabled) { pGrabbed->m_Color = m_Color; pGrabbed->m_Color.a = 255; } pBrush->AddLayer(pGrabbed); // copy the tiles for(int y = 0; y < r.h; y++) for(int x = 0; x < r.w; x++) pGrabbed->m_pTiles[y*pGrabbed->m_Width+x] = GetTile(r.x+x, r.y+y); str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName, sizeof(pGrabbed->m_aFileName)); } return 1; } void CLayerTiles::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) { if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) return; Snap(&Rect); int sx = ConvertX(Rect.x); int sy = ConvertY(Rect.y); int w = ConvertX(Rect.w); int h = ConvertY(Rect.h); CLayerTiles *pLt = static_cast(pBrush); bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); for(int y = 0; y < h; y++) { for(int x = 0; x < w; x++) { int fx = x+sx; int fy = y+sy; if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; bool HasTile = GetTile(fx, fy).m_Index; if(!Empty && pLt->GetTile(x, y).m_Index == TILE_THROUGH_CUT) { if(m_Game && m_pEditor->m_Map.m_pFrontLayer) { HasTile = HasTile || m_pEditor->m_Map.m_pFrontLayer->GetTile(fx, fy).m_Index; } else if(m_Front) { HasTile = HasTile || m_pEditor->m_Map.m_pGameLayer->GetTile(fx, fy).m_Index; } } if(!Destructive && HasTile) continue; if(Empty) m_pTiles[fy*m_Width+fx].m_Index = 0; else SetTile(fx, fy, pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]); } } FlagModified(sx, sy, w, h); } void CLayerTiles::BrushDraw(CLayer *pBrush, float wx, float wy) { if(m_Readonly) return; // CLayerTiles *l = (CLayerTiles *)pBrush; int sx = ConvertX(wx); int sy = ConvertY(wy); bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(l); for(int y = 0; y < l->m_Height; y++) for(int x = 0; x < l->m_Width; x++) { int fx = x+sx; int fy = y+sy; if(fx<0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; bool HasTile = GetTile(fx, fy).m_Index; if(l->GetTile(x, y).m_Index == TILE_THROUGH_CUT) { if(m_Game && m_pEditor->m_Map.m_pFrontLayer) { HasTile = HasTile || m_pEditor->m_Map.m_pFrontLayer->GetTile(fx, fy).m_Index; } else if(m_Front) { HasTile = HasTile || m_pEditor->m_Map.m_pGameLayer->GetTile(fx, fy).m_Index; } } if(!Destructive && HasTile) continue; SetTile(fx, fy, l->GetTile(x, y)); } FlagModified(sx, sy, l->m_Width, l->m_Height); } void CLayerTiles::BrushFlipX() { for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width/2; x++) { CTile Tmp = m_pTiles[y*m_Width+x]; m_pTiles[y*m_Width+x] = m_pTiles[y*m_Width+m_Width-1-x]; m_pTiles[y*m_Width+m_Width-1-x] = Tmp; } if(m_Tele || m_Speedup || m_Tune) return; bool Rotate = !(m_Game || m_Front || m_Switch) || m_pEditor->m_AllowPlaceUnusedTiles; for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width; x++) if(!Rotate && !IsRotatableTile(m_pTiles[y*m_Width+x].m_Index)) m_pTiles[y*m_Width+x].m_Flags = 0; else m_pTiles[y*m_Width+x].m_Flags ^= m_pTiles[y*m_Width+x].m_Flags&TILEFLAG_ROTATE ? TILEFLAG_HFLIP : TILEFLAG_VFLIP; } void CLayerTiles::BrushFlipY() { for(int y = 0; y < m_Height/2; y++) for(int x = 0; x < m_Width; x++) { CTile Tmp = m_pTiles[y*m_Width+x]; m_pTiles[y*m_Width+x] = m_pTiles[(m_Height-1-y)*m_Width+x]; m_pTiles[(m_Height-1-y)*m_Width+x] = Tmp; } if(m_Tele || m_Speedup || m_Tune) return; bool Rotate = !(m_Game || m_Front || m_Switch) || m_pEditor->m_AllowPlaceUnusedTiles; for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width; x++) if(!Rotate && !IsRotatableTile(m_pTiles[y*m_Width+x].m_Index)) m_pTiles[y*m_Width+x].m_Flags = 0; else m_pTiles[y*m_Width+x].m_Flags ^= m_pTiles[y*m_Width+x].m_Flags&TILEFLAG_ROTATE ? TILEFLAG_VFLIP : TILEFLAG_HFLIP; } void CLayerTiles::BrushRotate(float Amount) { int Rotation = (round_to_int(360.0f*Amount/(pi*2))/90)%4; // 0=0°, 1=90°, 2=180°, 3=270° if(Rotation < 0) Rotation +=4; if(Rotation == 1 || Rotation == 3) { // 90° rotation CTile *pTempData = new CTile[m_Width*m_Height]; mem_copy(pTempData, m_pTiles, m_Width*m_Height*sizeof(CTile)); CTile *pDst = m_pTiles; bool Rotate = !(m_Game || m_Front) || m_pEditor->m_AllowPlaceUnusedTiles; for(int x = 0; x < m_Width; ++x) for(int y = m_Height-1; y >= 0; --y, ++pDst) { *pDst = pTempData[y*m_Width+x]; if(!Rotate && !IsRotatableTile(pDst->m_Index)) pDst->m_Flags = 0; else { if(pDst->m_Flags&TILEFLAG_ROTATE) pDst->m_Flags ^= (TILEFLAG_HFLIP|TILEFLAG_VFLIP); pDst->m_Flags ^= TILEFLAG_ROTATE; } } int Temp = m_Width; m_Width = m_Height; m_Height = Temp; delete[] pTempData; } if(Rotation == 2 || Rotation == 3) { BrushFlipX(); BrushFlipY(); } } void CLayerTiles::Resize(int NewW, int NewH) { CTile *pNewData = new CTile[NewW*NewH]; mem_zero(pNewData, NewW*NewH*sizeof(CTile)); // copy old data for(int y = 0; y < minimum(NewH, m_Height); y++) mem_copy(&pNewData[y*NewW], &m_pTiles[y*m_Width], minimum(m_Width, NewW)*sizeof(CTile)); // replace old delete [] m_pTiles; m_pTiles = pNewData; m_Width = NewW; m_Height = NewH; // resize tele layer if available if(m_Game && m_pEditor->m_Map.m_pTeleLayer && (m_pEditor->m_Map.m_pTeleLayer->m_Width != NewW || m_pEditor->m_Map.m_pTeleLayer->m_Height != NewH)) m_pEditor->m_Map.m_pTeleLayer->Resize(NewW, NewH); // resize speedup layer if available if(m_Game && m_pEditor->m_Map.m_pSpeedupLayer && (m_pEditor->m_Map.m_pSpeedupLayer->m_Width != NewW || m_pEditor->m_Map.m_pSpeedupLayer->m_Height != NewH)) m_pEditor->m_Map.m_pSpeedupLayer->Resize(NewW, NewH); // resize front layer if(m_Game && m_pEditor->m_Map.m_pFrontLayer && (m_pEditor->m_Map.m_pFrontLayer->m_Width != NewW || m_pEditor->m_Map.m_pFrontLayer->m_Height != NewH)) m_pEditor->m_Map.m_pFrontLayer->Resize(NewW, NewH); // resize switch layer if available if(m_Game && m_pEditor->m_Map.m_pSwitchLayer && (m_pEditor->m_Map.m_pSwitchLayer->m_Width != NewW || m_pEditor->m_Map.m_pSwitchLayer->m_Height != NewH)) m_pEditor->m_Map.m_pSwitchLayer->Resize(NewW, NewH); // resize tune layer if available if(m_Game && m_pEditor->m_Map.m_pTuneLayer && (m_pEditor->m_Map.m_pTuneLayer->m_Width != NewW || m_pEditor->m_Map.m_pTuneLayer->m_Height != NewH)) m_pEditor->m_Map.m_pTuneLayer->Resize(NewW, NewH); } void CLayerTiles::Shift(int Direction) { int o = m_pEditor->m_ShiftBy; switch(Direction) { case 1: { // left for(int y = 0; y < m_Height; ++y) { mem_move(&m_pTiles[y*m_Width], &m_pTiles[y*m_Width+o], (m_Width-o)*sizeof(CTile)); mem_zero(&m_pTiles[y*m_Width + (m_Width-o)], o*sizeof(CTile)); } } break; case 2: { // right for(int y = 0; y < m_Height; ++y) { mem_move(&m_pTiles[y*m_Width+o], &m_pTiles[y*m_Width], (m_Width-o)*sizeof(CTile)); mem_zero(&m_pTiles[y*m_Width], o*sizeof(CTile)); } } break; case 4: { // up for(int y = 0; y < m_Height-o; ++y) { mem_copy(&m_pTiles[y*m_Width], &m_pTiles[(y+o)*m_Width], m_Width*sizeof(CTile)); mem_zero(&m_pTiles[(y+o)*m_Width], m_Width*sizeof(CTile)); } } break; case 8: { // down for(int y = m_Height-1; y >= o; --y) { mem_copy(&m_pTiles[y*m_Width], &m_pTiles[(y-o)*m_Width], m_Width*sizeof(CTile)); mem_zero(&m_pTiles[(y-o)*m_Width], m_Width*sizeof(CTile)); } } } } void CLayerTiles::ShowInfo() { float ScreenX0, ScreenY0, ScreenX1, ScreenY1; Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); Graphics()->TextureSet(m_pEditor->Client()->GetDebugFont()); Graphics()->QuadsBegin(); int StartY = maximum(0, (int)(ScreenY0/32.0f)-1); int StartX = maximum(0, (int)(ScreenX0/32.0f)-1); int EndY = minimum((int)(ScreenY1/32.0f)+1, m_Height); int EndX = minimum((int)(ScreenX1/32.0f)+1, m_Width); for(int y = StartY; y < EndY; y++) for(int x = StartX; x < EndX; x++) { int c = x + y*m_Width; if(m_pTiles[c].m_Index) { char aBuf[64]; str_format(aBuf, sizeof(aBuf), "%i", m_pTiles[c].m_Index); m_pEditor->Graphics()->QuadsText(x*32, y*32, 16.0f, aBuf); char aFlags[4] = { m_pTiles[c].m_Flags&TILEFLAG_VFLIP ? 'V' : ' ', m_pTiles[c].m_Flags&TILEFLAG_HFLIP ? 'H' : ' ', m_pTiles[c].m_Flags&TILEFLAG_ROTATE? 'R' : ' ', 0}; m_pEditor->Graphics()->QuadsText(x*32, y*32+16, 16.0f, aFlags); } x += m_pTiles[c].m_Skip; } Graphics()->QuadsEnd(); Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } int CLayerTiles::RenderProperties(CUIRect *pToolBox) { CUIRect Button; bool IsGameLayer = ( m_pEditor->m_Map.m_pGameLayer == this || m_pEditor->m_Map.m_pTeleLayer == this || m_pEditor->m_Map.m_pSpeedupLayer == this || m_pEditor->m_Map.m_pFrontLayer == this || m_pEditor->m_Map.m_pSwitchLayer == this || m_pEditor->m_Map.m_pTuneLayer == this ); CLayerGroup *pGroup = m_pEditor->m_Map.m_lGroups[m_pEditor->m_SelectedGroup]; // Game tiles can only be constructed if the layer is relative to the game layer if(!IsGameLayer && !(pGroup->m_OffsetX % 32) && !(pGroup->m_OffsetY % 32) && pGroup->m_ParallaxX == 100 && pGroup->m_ParallaxY == 100) { pToolBox->HSplitBottom(12.0f, pToolBox, &Button); static int s_ColclButton = 0; if(m_pEditor->DoButton_Editor(&s_ColclButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer")) m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->UI()->MouseX(), m_pEditor->UI()->MouseY()); int Result = m_pEditor->PopupSelectGameTileOpResult(); switch(Result) { case 4: Result = TILE_THROUGH_CUT; break; case 5: Result = TILE_FREEZE; break; case 6: Result = TILE_UNFREEZE; break; case 7: Result = TILE_DFREEZE; break; case 8: Result = TILE_DUNFREEZE; break; case 9: Result = TILE_TELECHECKIN; break; case 10: Result = TILE_TELECHECKINEVIL; default: break; } if(Result > -1) { int OffsetX = -pGroup->m_OffsetX / 32; int OffsetY = -pGroup->m_OffsetY / 32; if(Result != TILE_TELECHECKIN && Result != TILE_TELECHECKINEVIL) { CLayerTiles *pGLayer = m_pEditor->m_Map.m_pGameLayer; if(pGLayer->m_Width < m_Width + OffsetX || pGLayer->m_Height < m_Height + OffsetY) { int NewW = pGLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pGLayer->m_Width; int NewH = pGLayer->m_Height < m_Height + OffsetY ? m_Height + OffsetY : pGLayer->m_Height; pGLayer->Resize(NewW, NewH); } for(int y = OffsetY < 0 ? -OffsetY : 0; y < m_Height; y++) for(int x = OffsetX < 0 ? -OffsetX : 0; x < m_Width; x++) if(GetTile(x, y).m_Index) { CTile ResultTile = {(unsigned char)Result}; pGLayer->SetTile(x + OffsetX, y + OffsetY, ResultTile); } } else { if(!m_pEditor->m_Map.m_pTeleLayer) { CLayer *pLayer = new CLayerTele(m_Width, m_Height); m_pEditor->m_Map.MakeTeleLayer(pLayer); m_pEditor->m_Map.m_pGameGroup->AddLayer(pLayer); } CLayerTele *pTLayer = m_pEditor->m_Map.m_pTeleLayer; if(pTLayer->m_Width < m_Width + OffsetX || pTLayer->m_Height < m_Height + OffsetY) { int NewW = pTLayer->m_Width < m_Width + OffsetX ? m_Width + OffsetX : pTLayer->m_Width; int NewH = pTLayer->m_Height < m_Height + OffsetY ? m_Height + OffsetY : pTLayer->m_Height; pTLayer->Resize(NewW, NewH); } for(int y = OffsetY < 0 ? -OffsetY : 0; y < m_Height; y++) for(int x = OffsetX < 0 ? -OffsetX : 0; x < m_Width; x++) if(GetTile(x, y).m_Index) { pTLayer->m_pTiles[(y+OffsetY)*pTLayer->m_Width+x+OffsetX].m_Index = TILE_AIR+Result; pTLayer->m_pTeleTile[(y+OffsetY)*pTLayer->m_Width+x+OffsetX].m_Number = 1; pTLayer->m_pTeleTile[(y+OffsetY)*pTLayer->m_Width+x+OffsetX].m_Type = TILE_AIR+Result; } } return 1; } } if(m_pEditor->m_Map.m_pGameLayer != this) { if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size() && m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.IsLoaded() && m_AutoMapperConfig != -1) { static int s_AutoMapperButton = 0; static int s_AutoMapperButtonAuto = 0; pToolBox->HSplitBottom(2.0f, pToolBox, 0); pToolBox->HSplitBottom(12.0f, pToolBox, &Button); if(m_Seed != 0) { CUIRect ButtonAuto; Button.VSplitRight(16.0f, &Button, &ButtonAuto); Button.VSplitRight(2.0f, &Button, 0); if(m_pEditor->DoButton_Editor(&s_AutoMapperButtonAuto, "A", m_AutoAutoMap, &ButtonAuto, 0, "Automatically run automap after modifications.")) { m_AutoAutoMap = !m_AutoAutoMap; FlagModified(0, 0, m_Width, m_Height); } } if(m_pEditor->DoButton_Editor(&s_AutoMapperButton, "Automap", 0, &Button, 0, "Run the automapper")) { m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.Proceed(this, m_AutoMapperConfig, m_Seed); return 1; } } } enum { PROP_WIDTH=0, PROP_HEIGHT, PROP_SHIFT, PROP_SHIFT_BY, PROP_IMAGE, PROP_COLOR, PROP_COLOR_ENV, PROP_COLOR_ENV_OFFSET, PROP_AUTOMAPPER, PROP_SEED, NUM_PROPS, }; int Color = 0; Color |= m_Color.r<<24; Color |= m_Color.g<<16; Color |= m_Color.b<<8; Color |= m_Color.a; CProperty aProps[] = { {"Width", m_Width, PROPTYPE_INT_SCROLL, 1, 100000}, {"Height", m_Height, PROPTYPE_INT_SCROLL, 1, 100000}, {"Shift", 0, PROPTYPE_SHIFT, 0, 0}, {"Shift by", m_pEditor->m_ShiftBy, PROPTYPE_INT_SCROLL, 1, 100000}, {"Image", m_Image, PROPTYPE_IMAGE, 0, m_pEditor->m_Map.m_lImages.size()}, {"Color", Color, PROPTYPE_COLOR, 0, 0}, {"Color Env", m_ColorEnv+1, PROPTYPE_ENVELOPE, 0, 0}, {"Color TO", m_ColorEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000}, {"Auto Rule", m_AutoMapperConfig, PROPTYPE_AUTOMAPPER, m_Image, 0}, {"Seed", m_Seed, PROPTYPE_INT_SCROLL, 0, 1000000000}, {0}, }; if(IsGameLayer) // remove the image and color properties if this is a game layer { aProps[PROP_IMAGE].m_pName = 0; aProps[PROP_COLOR].m_pName = 0; aProps[PROP_AUTOMAPPER].m_pName = 0; } if(m_Image == -1) { aProps[PROP_AUTOMAPPER].m_pName = 0; aProps[PROP_SEED].m_pName = 0; } static int s_aIds[NUM_PROPS] = {0}; int NewVal = 0; int Prop = m_pEditor->DoProperties(pToolBox, aProps, s_aIds, &NewVal); if(Prop == PROP_WIDTH && NewVal > 1) { if(NewVal > 1000 && !m_pEditor->m_LargeLayerWasWarned) { m_pEditor->m_PopupEventType = m_pEditor->POPEVENT_LARGELAYER; m_pEditor->m_PopupEventActivated = true; m_pEditor->m_LargeLayerWasWarned = true; } Resize(NewVal, m_Height); } else if(Prop == PROP_HEIGHT && NewVal > 1) { if(NewVal > 1000 && !m_pEditor->m_LargeLayerWasWarned) { m_pEditor->m_PopupEventType = m_pEditor->POPEVENT_LARGELAYER; m_pEditor->m_PopupEventActivated = true; m_pEditor->m_LargeLayerWasWarned = true; } Resize(m_Width, NewVal); } else if(Prop == PROP_SHIFT) Shift(NewVal); else if(Prop == PROP_SHIFT_BY) m_pEditor->m_ShiftBy = NewVal; else if(Prop == PROP_IMAGE) { m_Image = NewVal; if (NewVal == -1) { m_Texture = IGraphics::CTextureHandle(); } else { m_AutoMapperConfig = -1; } } else if(Prop == PROP_COLOR) { m_Color.r = (NewVal>>24)&0xff; m_Color.g = (NewVal>>16)&0xff; m_Color.b = (NewVal>>8)&0xff; m_Color.a = NewVal&0xff; } if(Prop == PROP_COLOR_ENV) { int Index = clamp(NewVal-1, -1, m_pEditor->m_Map.m_lEnvelopes.size()-1); int Step = (Index-m_ColorEnv)%2; if(Step != 0) { for(; Index >= -1 && Index < m_pEditor->m_Map.m_lEnvelopes.size(); Index += Step) if(Index == -1 || m_pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 4) { m_ColorEnv = Index; break; } } } if(Prop == PROP_COLOR_ENV_OFFSET) m_ColorEnvOffset = NewVal; else if(Prop == PROP_SEED) m_Seed = NewVal; else if(Prop == PROP_AUTOMAPPER) { if (m_Image >= 0 && m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.ConfigNamesNum() > 0 && NewVal >= 0) m_AutoMapperConfig = NewVal%m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.ConfigNamesNum(); else m_AutoMapperConfig = -1; } if(Prop != -1) { FlagModified(0, 0, m_Width, m_Height); } return 0; } int CLayerTiles::RenderCommonProperties(SCommonPropState &State, CEditor *pEditor, CUIRect *pToolbox, array &pLayers) { if(State.Modified) { CUIRect Commit; pToolbox->HSplitBottom(20.0f, pToolbox, &Commit); static int s_CommitButton = 0; if(pEditor->DoButton_Editor(&s_CommitButton, "Commit", 0, &Commit, 0, "Applies the changes")) { dbg_msg("editor", "applying changes"); for_each(pLayers.all(), [&State](CLayerTiles *pLayer){ pLayer->Resize(State.Width, State.Height); pLayer->m_Color.r = (State.Color>>24)&0xff; pLayer->m_Color.g = (State.Color>>16)&0xff; pLayer->m_Color.b = (State.Color>>8)&0xff; pLayer->m_Color.a = State.Color&0xff; pLayer->FlagModified(0, 0, pLayer->m_Width, pLayer->m_Height); }); State.Modified = false; } } else { for_each(pLayers.all(), [&State](CLayerTiles *pLayer){ if(pLayer->m_Width > State.Width) State.Width = pLayer->m_Width; if(pLayer->m_Height > State.Height) State.Height = pLayer->m_Height; }); } { CUIRect Warning; pToolbox->HSplitTop(13.0f, &Warning, pToolbox); Warning.HMargin(0.5f, &Warning); pEditor->TextRender()->TextColor(ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f)); pEditor->UI()->DoLabel(&Warning, "Editing multiple layers", 9.0f, -1, Warning.w); pEditor->TextRender()->TextColor(ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f)); pToolbox->HSplitTop(2.0f, 0, pToolbox); } enum { PROP_WIDTH=0, PROP_HEIGHT, PROP_SHIFT, PROP_SHIFT_BY, PROP_COLOR, NUM_PROPS, }; CProperty aProps[] = { {"Width", State.Width, PROPTYPE_INT_SCROLL, 1, 100000}, {"Height", State.Height, PROPTYPE_INT_SCROLL, 1, 100000}, {"Shift", 0, PROPTYPE_SHIFT, 0, 0}, {"Shift by", pEditor->m_ShiftBy, PROPTYPE_INT_SCROLL, 1, 100000}, {"Color", State.Color, PROPTYPE_COLOR, 0, 0}, {0}, }; static int s_aIds[NUM_PROPS] = {0}; int NewVal = 0; int Prop = pEditor->DoProperties(pToolbox, aProps, s_aIds, &NewVal); if(Prop == PROP_WIDTH && NewVal > 1) { if(NewVal > 1000 && !pEditor->m_LargeLayerWasWarned) { pEditor->m_PopupEventType = pEditor->POPEVENT_LARGELAYER; pEditor->m_PopupEventActivated = true; pEditor->m_LargeLayerWasWarned = true; } State.Width = NewVal; } else if(Prop == PROP_HEIGHT && NewVal > 1) { if(NewVal > 1000 && !pEditor->m_LargeLayerWasWarned) { pEditor->m_PopupEventType = pEditor->POPEVENT_LARGELAYER; pEditor->m_PopupEventActivated = true; pEditor->m_LargeLayerWasWarned = true; } State.Height = NewVal; } else if(Prop == PROP_SHIFT) { for_each(pLayers.all(), [NewVal](CLayerTiles *pLayer){ pLayer->Shift(NewVal); }); } else if(Prop == PROP_SHIFT_BY) pEditor->m_ShiftBy = NewVal; else if(Prop == PROP_COLOR) { State.Color = NewVal; } if(Prop != -1 && Prop != PROP_SHIFT) { State.Modified = true; } return 0; } void CLayerTiles::FlagModified(int x, int y, int w, int h) { m_pEditor->m_Map.m_Modified = true; if (m_Seed != 0 && m_AutoMapperConfig != -1 && m_AutoAutoMap) { m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.ProceedLocalized(this, m_AutoMapperConfig, m_Seed, x, y, w, h); } } void CLayerTiles::ModifyImageIndex(INDEX_MODIFY_FUNC Func) { Func(&m_Image); } void CLayerTiles::ModifyEnvelopeIndex(INDEX_MODIFY_FUNC Func) { Func(&m_ColorEnv); } CLayerTele::CLayerTele(int w, int h) : CLayerTiles(w, h) { //m_Type = LAYERTYPE_TELE; str_copy(m_aName, "Tele", sizeof(m_aName)); m_Tele = 1; m_pTeleTile = new CTeleTile[w*h]; mem_zero(m_pTeleTile, w*h*sizeof(CTeleTile)); } CLayerTele::~CLayerTele() { delete[] m_pTeleTile; } void CLayerTele::Resize(int NewW, int NewH) { // resize tele data CTeleTile *pNewTeleData = new CTeleTile[NewW*NewH]; mem_zero(pNewTeleData, NewW*NewH*sizeof(CTeleTile)); // copy old data for(int y = 0; y < minimum(NewH, m_Height); y++) mem_copy(&pNewTeleData[y*NewW], &m_pTeleTile[y*m_Width], minimum(m_Width, NewW)*sizeof(CTeleTile)); // replace old delete [] m_pTeleTile; m_pTeleTile = pNewTeleData; // resize tile data CLayerTiles::Resize(NewW, NewH); // resize gamelayer too if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); } void CLayerTele::Shift(int Direction) { CLayerTiles::Shift(Direction); int o = m_pEditor->m_ShiftBy; switch(Direction) { case 1: { // left for(int y = 0; y < m_Height; ++y) { mem_move(&m_pTeleTile[y*m_Width], &m_pTeleTile[y*m_Width+o], (m_Width-o)*sizeof(CTeleTile)); mem_zero(&m_pTeleTile[y*m_Width + (m_Width-o)], o*sizeof(CTeleTile)); } } break; case 2: { // right for(int y = 0; y < m_Height; ++y) { mem_move(&m_pTeleTile[y*m_Width+o], &m_pTeleTile[y*m_Width], (m_Width-o)*sizeof(CTeleTile)); mem_zero(&m_pTeleTile[y*m_Width], o*sizeof(CTeleTile)); } } break; case 4: { // up for(int y = 0; y < m_Height-o; ++y) { mem_copy(&m_pTeleTile[y*m_Width], &m_pTeleTile[(y+o)*m_Width], m_Width*sizeof(CTeleTile)); mem_zero(&m_pTeleTile[(y+o)*m_Width], m_Width*sizeof(CTeleTile)); } } break; case 8: { // down for(int y = m_Height-1; y >= o; --y) { mem_copy(&m_pTeleTile[y*m_Width], &m_pTeleTile[(y-o)*m_Width], m_Width*sizeof(CTeleTile)); mem_zero(&m_pTeleTile[(y-o)*m_Width], m_Width*sizeof(CTeleTile)); } } } } bool CLayerTele::IsEmpty(CLayerTiles *pLayer) { for(int y = 0; y < pLayer->m_Height; y++) for(int x = 0; x < pLayer->m_Width; x++) if(IsValidTeleTile(pLayer->GetTile(x, y).m_Index)) return false; return true; } void CLayerTele::BrushDraw(CLayer *pBrush, float wx, float wy) { if(m_Readonly) return; CLayerTele *l = (CLayerTele *)pBrush; int sx = ConvertX(wx); int sy = ConvertY(wy); if(str_comp(l->m_aFileName, m_pEditor->m_aFileName)) m_pEditor->m_TeleNumber = l->m_TeleNum; bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(l); for(int y = 0; y < l->m_Height; y++) for(int x = 0; x < l->m_Width; x++) { int fx = x+sx; int fy = y+sy; if(fx<0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; if(!Destructive && GetTile(fx, fy).m_Index) continue; if(IsValidTeleTile(l->m_pTiles[y*l->m_Width+x].m_Index)) { if(m_pEditor->m_TeleNumber != l->m_TeleNum) { m_pTeleTile[fy*m_Width+fx].m_Number = m_pEditor->m_TeleNumber; } else if(l->m_pTeleTile[y*l->m_Width+x].m_Number) m_pTeleTile[fy*m_Width+fx].m_Number = l->m_pTeleTile[y*l->m_Width+x].m_Number; else { if(!m_pEditor->m_TeleNumber) { m_pTeleTile[fy*m_Width+fx].m_Number = 0; m_pTeleTile[fy*m_Width+fx].m_Type = 0; m_pTiles[fy*m_Width+fx].m_Index = 0; continue; } else m_pTeleTile[fy*m_Width+fx].m_Number = m_pEditor->m_TeleNumber; } m_pTeleTile[fy*m_Width+fx].m_Type = l->m_pTiles[y*l->m_Width+x].m_Index; m_pTiles[fy*m_Width+fx].m_Index = l->m_pTiles[y*l->m_Width+x].m_Index; } else { m_pTeleTile[fy*m_Width+fx].m_Number = 0; m_pTeleTile[fy*m_Width+fx].m_Type = 0; m_pTiles[fy*m_Width+fx].m_Index = 0; } } FlagModified(sx, sy, l->m_Width, l->m_Height); } void CLayerTele::BrushFlipX() { CLayerTiles::BrushFlipX(); for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width/2; x++) { CTeleTile Tmp = m_pTeleTile[y*m_Width+x]; m_pTeleTile[y*m_Width+x] = m_pTeleTile[y*m_Width+m_Width-1-x]; m_pTeleTile[y*m_Width+m_Width-1-x] = Tmp; } } void CLayerTele::BrushFlipY() { CLayerTiles::BrushFlipY(); for(int y = 0; y < m_Height/2; y++) for(int x = 0; x < m_Width; x++) { CTeleTile Tmp = m_pTeleTile[y*m_Width+x]; m_pTeleTile[y*m_Width+x] = m_pTeleTile[(m_Height-1-y)*m_Width+x]; m_pTeleTile[(m_Height-1-y)*m_Width+x] = Tmp; } } void CLayerTele::BrushRotate(float Amount) { int Rotation = (round_to_int(360.0f*Amount/(pi*2))/90)%4; // 0=0°, 1=90°, 2=180°, 3=270° if(Rotation < 0) Rotation +=4; if(Rotation == 1 || Rotation == 3) { // 90° rotation CTeleTile *pTempData1 = new CTeleTile[m_Width*m_Height]; CTile *pTempData2 = new CTile[m_Width*m_Height]; mem_copy(pTempData1, m_pTeleTile, m_Width*m_Height*sizeof(CTeleTile)); mem_copy(pTempData2, m_pTiles, m_Width*m_Height*sizeof(CTile)); CTeleTile *pDst1 = m_pTeleTile; CTile *pDst2 = m_pTiles; for(int x = 0; x < m_Width; ++x) for(int y = m_Height-1; y >= 0; --y, ++pDst1, ++pDst2) { *pDst1 = pTempData1[y*m_Width+x]; *pDst2 = pTempData2[y*m_Width+x]; } int Temp = m_Width; m_Width = m_Height; m_Height = Temp; delete[] pTempData1; delete[] pTempData2; } if(Rotation == 2 || Rotation == 3) { BrushFlipX(); BrushFlipY(); } } void CLayerTele::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) { if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) return; Snap(&Rect); // corrects Rect; no need of <= Snap(&Rect); int sx = ConvertX(Rect.x); int sy = ConvertY(Rect.y); int w = ConvertX(Rect.w); int h = ConvertY(Rect.h); CLayerTele *pLt = static_cast(pBrush); bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); for(int y = 0; y < h; y++) { for(int x = 0; x < w; x++) { int fx = x+sx; int fy = y+sy; if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; if(!Destructive && GetTile(fx, fy).m_Index) continue; if(Empty || !IsValidTeleTile((pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]).m_Index)) { m_pTiles[fy*m_Width+fx].m_Index = 0; m_pTeleTile[fy*m_Width+fx].m_Type = 0; m_pTeleTile[fy*m_Width+fx].m_Number = 0; } else { m_pTiles[fy*m_Width+fx] = pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]; if(pLt->m_Tele && m_pTiles[fy*m_Width+fx].m_Index > 0) { if((!pLt->m_pTeleTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Number && m_pEditor->m_TeleNumber) || m_pEditor->m_TeleNumber != pLt->m_TeleNum) m_pTeleTile[fy*m_Width+fx].m_Number = m_pEditor->m_TeleNumber; else m_pTeleTile[fy*m_Width+fx].m_Number = pLt->m_pTeleTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Number; m_pTeleTile[fy*m_Width+fx].m_Type = m_pTiles[fy*m_Width+fx].m_Index; } } } } FlagModified(sx, sy, w, h); } bool CLayerTele::ContainsElementWithId(int Id) { for(int y = 0; y < m_Height; ++y) { for(int x = 0; x < m_Width; ++x) { if(m_pTeleTile[y*m_Width+x].m_Number == Id) { return true; } } } return false; } CLayerSpeedup::CLayerSpeedup(int w, int h) : CLayerTiles(w, h) { //m_Type = LAYERTYPE_SPEEDUP; str_copy(m_aName, "Speedup", sizeof(m_aName)); m_Speedup = 1; m_pSpeedupTile = new CSpeedupTile[w*h]; mem_zero(m_pSpeedupTile, w*h*sizeof(CSpeedupTile)); } CLayerSpeedup::~CLayerSpeedup() { delete[] m_pSpeedupTile; } void CLayerSpeedup::Resize(int NewW, int NewH) { // resize speedup data CSpeedupTile *pNewSpeedupData = new CSpeedupTile[NewW*NewH]; mem_zero(pNewSpeedupData, NewW*NewH*sizeof(CSpeedupTile)); // copy old data for(int y = 0; y < minimum(NewH, m_Height); y++) mem_copy(&pNewSpeedupData[y*NewW], &m_pSpeedupTile[y*m_Width], minimum(m_Width, NewW)*sizeof(CSpeedupTile)); // replace old delete [] m_pSpeedupTile; m_pSpeedupTile = pNewSpeedupData; // resize tile data CLayerTiles::Resize(NewW, NewH); // resize gamelayer too if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); } void CLayerSpeedup::Shift(int Direction) { CLayerTiles::Shift(Direction); int o = m_pEditor->m_ShiftBy; switch(Direction) { case 1: { // left for(int y = 0; y < m_Height; ++y) { mem_move(&m_pSpeedupTile[y*m_Width], &m_pSpeedupTile[y*m_Width+o], (m_Width-o)*sizeof(CSpeedupTile)); mem_zero(&m_pSpeedupTile[y*m_Width + (m_Width-o)], o*sizeof(CSpeedupTile)); } } break; case 2: { // right for(int y = 0; y < m_Height; ++y) { mem_move(&m_pSpeedupTile[y*m_Width+o], &m_pSpeedupTile[y*m_Width], (m_Width-o)*sizeof(CSpeedupTile)); mem_zero(&m_pSpeedupTile[y*m_Width], o*sizeof(CSpeedupTile)); } } break; case 4: { // up for(int y = 0; y < m_Height-o; ++y) { mem_copy(&m_pSpeedupTile[y*m_Width], &m_pSpeedupTile[(y+o)*m_Width], m_Width*sizeof(CSpeedupTile)); mem_zero(&m_pSpeedupTile[(y+o)*m_Width], m_Width*sizeof(CSpeedupTile)); } } break; case 8: { // down for(int y = m_Height-1; y >= o; --y) { mem_copy(&m_pSpeedupTile[y*m_Width], &m_pSpeedupTile[(y-o)*m_Width], m_Width*sizeof(CSpeedupTile)); mem_zero(&m_pSpeedupTile[(y-o)*m_Width], m_Width*sizeof(CSpeedupTile)); } } } } bool CLayerSpeedup::IsEmpty(CLayerTiles *pLayer) { for(int y = 0; y < pLayer->m_Height; y++) for(int x = 0; x < pLayer->m_Width; x++) if(IsValidSpeedupTile(pLayer->GetTile(x, y).m_Index)) return false; return true; } void CLayerSpeedup::BrushDraw(CLayer *pBrush, float wx, float wy) { if(m_Readonly) return; CLayerSpeedup *l = (CLayerSpeedup *)pBrush; int sx = ConvertX(wx); int sy = ConvertY(wy); if(str_comp(l->m_aFileName, m_pEditor->m_aFileName)) { m_pEditor->m_SpeedupAngle = l->m_SpeedupAngle; m_pEditor->m_SpeedupForce = l->m_SpeedupForce; m_pEditor->m_SpeedupMaxSpeed = l->m_SpeedupMaxSpeed; } bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(l); for(int y = 0; y < l->m_Height; y++) for(int x = 0; x < l->m_Width; x++) { int fx = x+sx; int fy = y+sy; if(fx<0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; if(!Destructive && GetTile(fx, fy).m_Index) continue; if(IsValidSpeedupTile(l->m_pTiles[y*l->m_Width+x].m_Index)) { if(m_pEditor->m_SpeedupAngle != l->m_SpeedupAngle || m_pEditor->m_SpeedupForce != l->m_SpeedupForce || m_pEditor->m_SpeedupMaxSpeed != l->m_SpeedupMaxSpeed) { m_pSpeedupTile[fy*m_Width+fx].m_Force = m_pEditor->m_SpeedupForce; m_pSpeedupTile[fy*m_Width+fx].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed; m_pSpeedupTile[fy*m_Width+fx].m_Angle = m_pEditor->m_SpeedupAngle; m_pSpeedupTile[fy*m_Width+fx].m_Type = l->m_pTiles[y*l->m_Width+x].m_Index; m_pTiles[fy*m_Width+fx].m_Index = l->m_pTiles[y*l->m_Width+x].m_Index; } else if(l->m_pSpeedupTile[y*l->m_Width+x].m_Force) { m_pSpeedupTile[fy*m_Width+fx].m_Force = l->m_pSpeedupTile[y*l->m_Width+x].m_Force; m_pSpeedupTile[fy*m_Width+fx].m_Angle = l->m_pSpeedupTile[y*l->m_Width+x].m_Angle; m_pSpeedupTile[fy*m_Width+fx].m_MaxSpeed = l->m_pSpeedupTile[y*l->m_Width+x].m_MaxSpeed; m_pSpeedupTile[fy*m_Width+fx].m_Type = l->m_pTiles[y*l->m_Width+x].m_Index; m_pTiles[fy*m_Width+fx].m_Index = l->m_pTiles[y*l->m_Width+x].m_Index; } else if(m_pEditor->m_SpeedupForce) { m_pSpeedupTile[fy*m_Width+fx].m_Force = m_pEditor->m_SpeedupForce; m_pSpeedupTile[fy*m_Width+fx].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed; m_pSpeedupTile[fy*m_Width+fx].m_Angle = m_pEditor->m_SpeedupAngle; m_pSpeedupTile[fy*m_Width+fx].m_Type = l->m_pTiles[y*l->m_Width+x].m_Index; m_pTiles[fy*m_Width+fx].m_Index = l->m_pTiles[y*l->m_Width+x].m_Index; } else { m_pSpeedupTile[fy*m_Width+fx].m_Force = 0; m_pSpeedupTile[fy*m_Width+fx].m_MaxSpeed = 0; m_pSpeedupTile[fy*m_Width+fx].m_Angle = 0; m_pTiles[fy*m_Width+fx].m_Index = 0; } } else { m_pSpeedupTile[fy*m_Width+fx].m_Force = 0; m_pSpeedupTile[fy*m_Width+fx].m_MaxSpeed = 0; m_pSpeedupTile[fy*m_Width+fx].m_Angle = 0; m_pTiles[fy*m_Width+fx].m_Index = 0; } } FlagModified(sx, sy, l->m_Width, l->m_Height); } void CLayerSpeedup::BrushFlipX() { CLayerTiles::BrushFlipX(); for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width/2; x++) { CSpeedupTile Tmp = m_pSpeedupTile[y*m_Width+x]; m_pSpeedupTile[y*m_Width+x] = m_pSpeedupTile[y*m_Width+m_Width-1-x]; m_pSpeedupTile[y*m_Width+m_Width-1-x] = Tmp; } } void CLayerSpeedup::BrushFlipY() { CLayerTiles::BrushFlipY(); for(int y = 0; y < m_Height/2; y++) for(int x = 0; x < m_Width; x++) { CSpeedupTile Tmp = m_pSpeedupTile[y*m_Width+x]; m_pSpeedupTile[y*m_Width+x] = m_pSpeedupTile[(m_Height-1-y)*m_Width+x]; m_pSpeedupTile[(m_Height-1-y)*m_Width+x] = Tmp; } } void CLayerSpeedup::BrushRotate(float Amount) { int Rotation = (round_to_int(360.0f*Amount/(pi*2))/90)%4; // 0=0°, 1=90°, 2=180°, 3=270° if(Rotation < 0) Rotation +=4; if(Rotation == 1 || Rotation == 3) { // 90° rotation CSpeedupTile *pTempData1 = new CSpeedupTile[m_Width*m_Height]; CTile *pTempData2 = new CTile[m_Width*m_Height]; mem_copy(pTempData1, m_pSpeedupTile, m_Width*m_Height*sizeof(CSpeedupTile)); mem_copy(pTempData2, m_pTiles, m_Width*m_Height*sizeof(CTile)); CSpeedupTile *pDst1 = m_pSpeedupTile; CTile *pDst2 = m_pTiles; for(int x = 0; x < m_Width; ++x) for(int y = m_Height-1; y >= 0; --y, ++pDst1, ++pDst2) { *pDst1 = pTempData1[y*m_Width+x]; *pDst2 = pTempData2[y*m_Width+x]; } int Temp = m_Width; m_Width = m_Height; m_Height = Temp; delete[] pTempData1; delete[] pTempData2; } if(Rotation == 2 || Rotation == 3) { BrushFlipX(); BrushFlipY(); } } void CLayerSpeedup::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) { if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) return; Snap(&Rect); // corrects Rect; no need of <= Snap(&Rect); int sx = ConvertX(Rect.x); int sy = ConvertY(Rect.y); int w = ConvertX(Rect.w); int h = ConvertY(Rect.h); CLayerSpeedup *pLt = static_cast(pBrush); bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); for(int y = 0; y < h; y++) { for(int x = 0; x < w; x++) { int fx = x+sx; int fy = y+sy; if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; if(!Destructive && GetTile(fx, fy).m_Index) continue; if(Empty || !IsValidSpeedupTile((pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]).m_Index)) // no speed up tile chosen: reset { m_pTiles[fy*m_Width+fx].m_Index = 0; m_pSpeedupTile[fy*m_Width+fx].m_Force = 0; m_pSpeedupTile[fy*m_Width+fx].m_Angle = 0; } else { m_pTiles[fy*m_Width+fx] = pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]; if(pLt->m_Speedup && m_pTiles[fy*m_Width+fx].m_Index > 0) { if((!pLt->m_pSpeedupTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Force && m_pEditor->m_SpeedupForce) || m_pEditor->m_SpeedupForce != pLt->m_SpeedupForce) m_pSpeedupTile[fy*m_Width+fx].m_Force = m_pEditor->m_SpeedupForce; else m_pSpeedupTile[fy*m_Width+fx].m_Force = pLt->m_pSpeedupTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Force; if((!pLt->m_pSpeedupTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Angle && m_pEditor->m_SpeedupAngle) || m_pEditor->m_SpeedupAngle != pLt->m_SpeedupAngle) m_pSpeedupTile[fy*m_Width+fx].m_Angle = m_pEditor->m_SpeedupAngle; else m_pSpeedupTile[fy*m_Width+fx].m_Angle = pLt->m_pSpeedupTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Angle; if((!pLt->m_pSpeedupTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_MaxSpeed && m_pEditor->m_SpeedupMaxSpeed) || m_pEditor->m_SpeedupMaxSpeed != pLt->m_SpeedupMaxSpeed) m_pSpeedupTile[fy*m_Width+fx].m_MaxSpeed = m_pEditor->m_SpeedupMaxSpeed; else m_pSpeedupTile[fy*m_Width+fx].m_MaxSpeed = pLt->m_pSpeedupTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_MaxSpeed; m_pSpeedupTile[fy*m_Width+fx].m_Type = m_pTiles[fy*m_Width+fx].m_Index; } } } } FlagModified(sx, sy, w, h); } CLayerFront::CLayerFront(int w, int h) : CLayerTiles(w, h) { //m_Type = LAYERTYPE_FRONT; str_copy(m_aName, "Front", sizeof(m_aName)); m_Front = 1; } void CLayerFront::SetTile(int x, int y, CTile tile) { if(tile.m_Index == TILE_THROUGH_CUT) { CTile nohook = {TILE_NOHOOK}; m_pEditor->m_Map.m_pGameLayer->CLayerTiles::SetTile(x, y, nohook); } else if(tile.m_Index == TILE_AIR && CLayerTiles::GetTile(x, y).m_Index == TILE_THROUGH_CUT) { CTile air = {TILE_AIR}; m_pEditor->m_Map.m_pGameLayer->CLayerTiles::SetTile(x, y, air); } if(m_pEditor->m_AllowPlaceUnusedTiles || IsValidFrontTile(tile.m_Index)) { CLayerTiles::SetTile(x, y, tile); } else { CTile air = {TILE_AIR}; CLayerTiles::SetTile(x, y, air); if(!m_pEditor->m_PreventUnusedTilesWasWarned) { m_pEditor->m_PopupEventType = m_pEditor->POPEVENT_PREVENTUNUSEDTILES; m_pEditor->m_PopupEventActivated = true; m_pEditor->m_PreventUnusedTilesWasWarned = true; } } } void CLayerFront::Resize(int NewW, int NewH) { // resize tile data CLayerTiles::Resize(NewW, NewH); // resize gamelayer too if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); } CLayerSwitch::CLayerSwitch(int w, int h) : CLayerTiles(w, h) { //m_Type = LAYERTYPE_SWITCH; str_copy(m_aName, "Switch", sizeof(m_aName)); m_Switch = 1; m_pSwitchTile = new CSwitchTile[w*h]; mem_zero(m_pSwitchTile, w*h*sizeof(CSwitchTile)); } CLayerSwitch::~CLayerSwitch() { delete[] m_pSwitchTile; } void CLayerSwitch::Resize(int NewW, int NewH) { // resize switch data CSwitchTile *pNewSwitchData = new CSwitchTile[NewW*NewH]; mem_zero(pNewSwitchData, NewW*NewH*sizeof(CSwitchTile)); // copy old data for(int y = 0; y < minimum(NewH, m_Height); y++) mem_copy(&pNewSwitchData[y*NewW], &m_pSwitchTile[y*m_Width], minimum(m_Width, NewW)*sizeof(CSwitchTile)); // replace old delete [] m_pSwitchTile; m_pSwitchTile = pNewSwitchData; // resize tile data CLayerTiles::Resize(NewW, NewH); // resize gamelayer too if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); } void CLayerSwitch::Shift(int Direction) { CLayerTiles::Shift(Direction); int o = m_pEditor->m_ShiftBy; switch(Direction) { case 1: { // left for(int y = 0; y < m_Height; ++y) { mem_move(&m_pSwitchTile[y*m_Width], &m_pSwitchTile[y*m_Width+o], (m_Width-o)*sizeof(CSwitchTile)); mem_zero(&m_pSwitchTile[y*m_Width + (m_Width-o)], o*sizeof(CSwitchTile)); } } break; case 2: { // right for(int y = 0; y < m_Height; ++y) { mem_move(&m_pSwitchTile[y*m_Width+o], &m_pSwitchTile[y*m_Width], (m_Width-o)*sizeof(CSwitchTile)); mem_zero(&m_pSwitchTile[y*m_Width], o*sizeof(CSwitchTile)); } } break; case 4: { // up for(int y = 0; y < m_Height-o; ++y) { mem_copy(&m_pSwitchTile[y*m_Width], &m_pSwitchTile[(y+o)*m_Width], m_Width*sizeof(CSwitchTile)); mem_zero(&m_pSwitchTile[(y+o)*m_Width], m_Width*sizeof(CSwitchTile)); } } break; case 8: { // down for(int y = m_Height-1; y >= o; --y) { mem_copy(&m_pSwitchTile[y*m_Width], &m_pSwitchTile[(y-o)*m_Width], m_Width*sizeof(CSwitchTile)); mem_zero(&m_pSwitchTile[(y-o)*m_Width], m_Width*sizeof(CSwitchTile)); } } } } bool CLayerSwitch::IsEmpty(CLayerTiles *pLayer) { for(int y = 0; y < pLayer->m_Height; y++) for(int x = 0; x < pLayer->m_Width; x++) if(IsValidSwitchTile(pLayer->GetTile(x, y).m_Index)) return false; return true; } void CLayerSwitch::BrushDraw(CLayer *pBrush, float wx, float wy) { if(m_Readonly) return; CLayerSwitch *l = (CLayerSwitch *)pBrush; int sx = ConvertX(wx); int sy = ConvertY(wy); if(str_comp(l->m_aFileName, m_pEditor->m_aFileName)) { m_pEditor->m_SwitchNum = l->m_SwitchNumber; m_pEditor->m_SwitchDelay = l->m_SwitchDelay; } bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(l); for(int y = 0; y < l->m_Height; y++) for(int x = 0; x < l->m_Width; x++) { int fx = x+sx; int fy = y+sy; if(fx<0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; if(!Destructive && GetTile(fx, fy).m_Index) continue; if(IsValidSwitchTile(l->m_pTiles[y*l->m_Width+x].m_Index)) { if(m_pEditor->m_SwitchNum != l->m_SwitchNumber || m_pEditor->m_SwitchDelay != l->m_SwitchDelay) { m_pSwitchTile[fy*m_Width+fx].m_Number = m_pEditor->m_SwitchNum; m_pSwitchTile[fy*m_Width+fx].m_Delay = m_pEditor->m_SwitchDelay; } else if(l->m_pSwitchTile[y*l->m_Width+x].m_Number) { m_pSwitchTile[fy*m_Width+fx].m_Number = l->m_pSwitchTile[y*l->m_Width+x].m_Number; m_pSwitchTile[fy*m_Width+fx].m_Delay = l->m_pSwitchTile[y*l->m_Width+x].m_Delay; } else { m_pSwitchTile[fy*m_Width+fx].m_Number = m_pEditor->m_SwitchNum; m_pSwitchTile[fy*m_Width+fx].m_Delay = m_pEditor->m_SwitchDelay; } m_pSwitchTile[fy*m_Width+fx].m_Type = l->m_pTiles[y*l->m_Width+x].m_Index; m_pSwitchTile[fy*m_Width+fx].m_Flags = l->m_pTiles[y*l->m_Width+x].m_Flags; m_pTiles[fy*m_Width+fx].m_Index = l->m_pTiles[y*l->m_Width+x].m_Index; m_pTiles[fy*m_Width+fx].m_Flags = l->m_pTiles[y*l->m_Width+x].m_Flags; } else { m_pSwitchTile[fy*m_Width+fx].m_Number = 0; m_pSwitchTile[fy*m_Width+fx].m_Type = 0; m_pSwitchTile[fy*m_Width+fx].m_Flags = 0; m_pSwitchTile[fy*m_Width+fx].m_Delay = 0; m_pTiles[fy*m_Width+fx].m_Index = 0; } if(l->m_pTiles[y*l->m_Width+x].m_Index == TILE_FREEZE) { m_pSwitchTile[fy*m_Width+fx].m_Flags = 0; } else if(l->m_pTiles[y*l->m_Width+x].m_Index == TILE_DFREEZE || l->m_pTiles[y*l->m_Width+x].m_Index == TILE_DUNFREEZE) { m_pSwitchTile[fy*m_Width+fx].m_Flags = 0; m_pSwitchTile[fy*m_Width+fx].m_Delay = 0; } } FlagModified(sx, sy, l->m_Width, l->m_Height); } void CLayerSwitch::BrushFlipX() { CLayerTiles::BrushFlipX(); for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width/2; x++) { CSwitchTile Tmp = m_pSwitchTile[y*m_Width+x]; m_pSwitchTile[y*m_Width+x] = m_pSwitchTile[y*m_Width+m_Width-1-x]; m_pSwitchTile[y*m_Width+m_Width-1-x] = Tmp; } } void CLayerSwitch::BrushFlipY() { CLayerTiles::BrushFlipY(); for(int y = 0; y < m_Height/2; y++) for(int x = 0; x < m_Width; x++) { CSwitchTile Tmp = m_pSwitchTile[y*m_Width+x]; m_pSwitchTile[y*m_Width+x] = m_pSwitchTile[(m_Height-1-y)*m_Width+x]; m_pSwitchTile[(m_Height-1-y)*m_Width+x] = Tmp; } } void CLayerSwitch::BrushRotate(float Amount) { int Rotation = (round_to_int(360.0f*Amount/(pi*2))/90)%4; // 0=0°, 1=90°, 2=180°, 3=270° if(Rotation < 0) Rotation +=4; if(Rotation == 1 || Rotation == 3) { // 90° rotation CSwitchTile *pTempData1 = new CSwitchTile[m_Width*m_Height]; CTile *pTempData2 = new CTile[m_Width*m_Height]; mem_copy(pTempData1, m_pSwitchTile, m_Width*m_Height*sizeof(CSwitchTile)); mem_copy(pTempData2, m_pTiles, m_Width*m_Height*sizeof(CTile)); CSwitchTile *pDst1 = m_pSwitchTile; CTile *pDst2 = m_pTiles; for(int x = 0; x < m_Width; ++x) for(int y = m_Height-1; y >= 0; --y, ++pDst1, ++pDst2) { *pDst1 = pTempData1[y*m_Width+x]; *pDst2 = pTempData2[y*m_Width+x]; if(IsRotatableTile(pDst2->m_Index)) { if(pDst2->m_Flags&TILEFLAG_ROTATE) pDst2->m_Flags ^= (TILEFLAG_HFLIP|TILEFLAG_VFLIP); pDst2->m_Flags ^= TILEFLAG_ROTATE; } } int Temp = m_Width; m_Width = m_Height; m_Height = Temp; delete[] pTempData1; delete[] pTempData2; } if(Rotation == 2 || Rotation == 3) { BrushFlipX(); BrushFlipY(); } } void CLayerSwitch::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) { if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) return; Snap(&Rect); // corrects Rect; no need of <= Snap(&Rect); int sx = ConvertX(Rect.x); int sy = ConvertY(Rect.y); int w = ConvertX(Rect.w); int h = ConvertY(Rect.h); CLayerSwitch *pLt = static_cast(pBrush); bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); for(int y = 0; y < h; y++) { for(int x = 0; x < w; x++) { int fx = x+sx; int fy = y+sy; if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; if(!Destructive && GetTile(fx, fy).m_Index) continue; if(Empty || !IsValidSwitchTile((pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]).m_Index)) { m_pTiles[fy*m_Width+fx].m_Index = 0; m_pSwitchTile[fy*m_Width+fx].m_Type = 0; m_pSwitchTile[fy*m_Width+fx].m_Number = 0; m_pSwitchTile[fy*m_Width+fx].m_Delay = 0; } else { m_pTiles[fy*m_Width+fx] = pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]; m_pSwitchTile[fy*m_Width+fx].m_Type = m_pTiles[fy*m_Width+fx].m_Index; if(pLt->m_Switch && m_pEditor->m_SwitchNum && m_pTiles[fy*m_Width+fx].m_Index > 0) { if((!pLt->m_pSwitchTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Number) || m_pEditor->m_SwitchNum != pLt->m_SwitchNumber) m_pSwitchTile[fy*m_Width+fx].m_Number = m_pEditor->m_SwitchNum; else m_pSwitchTile[fy*m_Width+fx].m_Number = pLt->m_pSwitchTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Number; if((!pLt->m_pSwitchTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Delay) || m_pEditor->m_SwitchDelay != pLt->m_SwitchDelay) m_pSwitchTile[fy*m_Width+fx].m_Delay = m_pEditor->m_SwitchDelay; else m_pSwitchTile[fy*m_Width+fx].m_Delay = pLt->m_pSwitchTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Delay; m_pSwitchTile[fy*m_Width+fx].m_Flags = pLt->m_pSwitchTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Flags; } } } } FlagModified(sx, sy, w, h); } bool CLayerSwitch::ContainsElementWithId(int Id) { for(int y = 0; y < m_Height; ++y) { for(int x = 0; x < m_Width; ++x) { if(m_pSwitchTile[y*m_Width+x].m_Number == Id) { return true; } } } return false; } //------------------------------------------------------ CLayerTune::CLayerTune(int w, int h) : CLayerTiles(w, h) { //m_Type = LAYERTYPE_TUNE; str_copy(m_aName, "Tune", sizeof(m_aName)); m_Tune = 1; m_pTuneTile = new CTuneTile[w*h]; mem_zero(m_pTuneTile, w*h*sizeof(CTuneTile)); } CLayerTune::~CLayerTune() { delete[] m_pTuneTile; } void CLayerTune::Resize(int NewW, int NewH) { // resize Tune data CTuneTile *pNewTuneData = new CTuneTile[NewW*NewH]; mem_zero(pNewTuneData, NewW*NewH*sizeof(CTuneTile)); // copy old data for(int y = 0; y < minimum(NewH, m_Height); y++) mem_copy(&pNewTuneData[y*NewW], &m_pTuneTile[y*m_Width], minimum(m_Width, NewW)*sizeof(CTuneTile)); // replace old delete [] m_pTuneTile; m_pTuneTile = pNewTuneData; // resize tile data CLayerTiles::Resize(NewW, NewH); // resize gamelayer too if(m_pEditor->m_Map.m_pGameLayer->m_Width != NewW || m_pEditor->m_Map.m_pGameLayer->m_Height != NewH) m_pEditor->m_Map.m_pGameLayer->Resize(NewW, NewH); } void CLayerTune::Shift(int Direction) { CLayerTiles::Shift(Direction); int o = m_pEditor->m_ShiftBy; switch(Direction) { case 1: { // left for(int y = 0; y < m_Height; ++y) { mem_move(&m_pTuneTile[y*m_Width], &m_pTuneTile[y*m_Width+o], (m_Width-o)*sizeof(CTuneTile)); mem_zero(&m_pTuneTile[y*m_Width + (m_Width-o)], o*sizeof(CTuneTile)); } } break; case 2: { // right for(int y = 0; y < m_Height; ++y) { mem_move(&m_pTuneTile[y*m_Width+o], &m_pTuneTile[y*m_Width], (m_Width-o)*sizeof(CTuneTile)); mem_zero(&m_pTuneTile[y*m_Width], o*sizeof(CTuneTile)); } } break; case 4: { // up for(int y = 0; y < m_Height-o; ++y) { mem_copy(&m_pTuneTile[y*m_Width], &m_pTuneTile[(y+o)*m_Width], m_Width*sizeof(CTuneTile)); mem_zero(&m_pTuneTile[(y+o)*m_Width], m_Width*sizeof(CTuneTile)); } } break; case 8: { // down for(int y = m_Height-1; y >= o; --y) { mem_copy(&m_pTuneTile[y*m_Width], &m_pTuneTile[(y-o)*m_Width], m_Width*sizeof(CTuneTile)); mem_zero(&m_pTuneTile[(y-o)*m_Width], m_Width*sizeof(CTuneTile)); } } } } bool CLayerTune::IsEmpty(CLayerTiles *pLayer) { for(int y = 0; y < pLayer->m_Height; y++) for(int x = 0; x < pLayer->m_Width; x++) if(IsValidTuneTile(pLayer->GetTile(x, y).m_Index)) return false; return true; } void CLayerTune::BrushDraw(CLayer *pBrush, float wx, float wy) { if(m_Readonly) return; CLayerTune *l = (CLayerTune *)pBrush; int sx = ConvertX(wx); int sy = ConvertY(wy); if(str_comp(l->m_aFileName, m_pEditor->m_aFileName)) { m_pEditor->m_TuningNum = l->m_TuningNumber; } bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(l); for(int y = 0; y < l->m_Height; y++) for(int x = 0; x < l->m_Width; x++) { int fx = x+sx; int fy = y+sy; if(fx<0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; if(!Destructive && GetTile(fx, fy).m_Index) continue; if(IsValidTuneTile(l->m_pTiles[y*l->m_Width+x].m_Index)) { if(m_pEditor->m_TuningNum != l->m_TuningNumber) { m_pTuneTile[fy*m_Width+fx].m_Number = m_pEditor->m_TuningNum; } else if(l->m_pTuneTile[y*l->m_Width+x].m_Number) m_pTuneTile[fy*m_Width+fx].m_Number = l->m_pTuneTile[y*l->m_Width+x].m_Number; else { if(!m_pEditor->m_TuningNum) { m_pTuneTile[fy*m_Width+fx].m_Number = 0; m_pTuneTile[fy*m_Width+fx].m_Type = 0; m_pTiles[fy*m_Width+fx].m_Index = 0; continue; } else m_pTuneTile[fy*m_Width+fx].m_Number = m_pEditor->m_TuningNum; } m_pTuneTile[fy*m_Width+fx].m_Type = l->m_pTiles[y*l->m_Width+x].m_Index; m_pTiles[fy*m_Width+fx].m_Index = l->m_pTiles[y*l->m_Width+x].m_Index; } else { m_pTuneTile[fy*m_Width+fx].m_Number = 0; m_pTuneTile[fy*m_Width+fx].m_Type = 0; m_pTiles[fy*m_Width+fx].m_Index = 0; } } FlagModified(sx, sy, l->m_Width, l->m_Height); } void CLayerTune::BrushFlipX() { CLayerTiles::BrushFlipX(); for(int y = 0; y < m_Height; y++) for(int x = 0; x < m_Width/2; x++) { CTuneTile Tmp = m_pTuneTile[y*m_Width+x]; m_pTuneTile[y*m_Width+x] = m_pTuneTile[y*m_Width+m_Width-1-x]; m_pTuneTile[y*m_Width+m_Width-1-x] = Tmp; } } void CLayerTune::BrushFlipY() { CLayerTiles::BrushFlipY(); for(int y = 0; y < m_Height/2; y++) for(int x = 0; x < m_Width; x++) { CTuneTile Tmp = m_pTuneTile[y*m_Width+x]; m_pTuneTile[y*m_Width+x] = m_pTuneTile[(m_Height-1-y)*m_Width+x]; m_pTuneTile[(m_Height-1-y)*m_Width+x] = Tmp; } } void CLayerTune::BrushRotate(float Amount) { int Rotation = (round_to_int(360.0f*Amount/(pi*2))/90)%4; // 0=0°, 1=90°, 2=180°, 3=270° if(Rotation < 0) Rotation +=4; if(Rotation == 1 || Rotation == 3) { // 90° rotation CTuneTile *pTempData1 = new CTuneTile[m_Width*m_Height]; CTile *pTempData2 = new CTile[m_Width*m_Height]; mem_copy(pTempData1, m_pTuneTile, m_Width*m_Height*sizeof(CTuneTile)); mem_copy(pTempData2, m_pTiles, m_Width*m_Height*sizeof(CTile)); CTuneTile *pDst1 = m_pTuneTile; CTile *pDst2 = m_pTiles; for(int x = 0; x < m_Width; ++x) for(int y = m_Height-1; y >= 0; --y, ++pDst1, ++pDst2) { *pDst1 = pTempData1[y*m_Width+x]; *pDst2 = pTempData2[y*m_Width+x]; } int Temp = m_Width; m_Width = m_Height; m_Height = Temp; delete[] pTempData1; delete[] pTempData2; } if(Rotation == 2 || Rotation == 3) { BrushFlipX(); BrushFlipY(); } } void CLayerTune::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect) { if(m_Readonly || (!Empty && pBrush->m_Type != LAYERTYPE_TILES)) return; Snap(&Rect); // corrects Rect; no need of <= int sx = ConvertX(Rect.x); int sy = ConvertY(Rect.y); int w = ConvertX(Rect.w); int h = ConvertY(Rect.h); CLayerTune *pLt = static_cast(pBrush); bool Destructive = m_pEditor->m_BrushDrawDestructive || Empty || IsEmpty(pLt); for(int y = 0; y < h; y++) { for(int x = 0; x < w; x++) { int fx = x+sx; int fy = y+sy; if(fx < 0 || fx >= m_Width || fy < 0 || fy >= m_Height) continue; if(!Destructive && GetTile(fx, fy).m_Index) continue; if(Empty || !IsValidTuneTile((pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]).m_Index)) // \o/ this fixes editor bug; TODO: use IsUsedInThisLayer here { m_pTiles[fy*m_Width+fx].m_Index = 0; m_pTuneTile[fy*m_Width+fx].m_Type = 0; m_pTuneTile[fy*m_Width+fx].m_Number = 0; } else { m_pTiles[fy*m_Width+fx] = pLt->m_pTiles[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)]; if(pLt->m_Tune && m_pTiles[fy*m_Width+fx].m_Index > 0) { if((!pLt->m_pTuneTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Number && m_pEditor->m_TuningNum) || m_pEditor->m_TuningNum != pLt->m_TuningNumber) m_pTuneTile[fy*m_Width+fx].m_Number = m_pEditor->m_TuningNum; else m_pTuneTile[fy*m_Width+fx].m_Number = pLt->m_pTuneTile[(y*pLt->m_Width + x%pLt->m_Width) % (pLt->m_Width*pLt->m_Height)].m_Number; m_pTuneTile[fy*m_Width+fx].m_Type = m_pTiles[fy*m_Width+fx].m_Index; } } } } FlagModified(sx, sy, w, h); }