diff --git a/planet/Graphics.ocg/TransformKnob.png b/planet/Graphics.ocg/TransformKnob.png new file mode 100644 index 000000000..19623623f Binary files /dev/null and b/planet/Graphics.ocg/TransformKnob.png differ diff --git a/src/c4group/C4Components.h b/src/c4group/C4Components.h index 836ea9adb..1f32ffb03 100644 --- a/src/c4group/C4Components.h +++ b/src/c4group/C4Components.h @@ -193,7 +193,7 @@ #define C4FLS_Material "TexMap.txt|*.ocm|*.jpeg|*.jpg|*.bmp|*.png" #define C4FLS_Graphics "Loader*.bmp|Loader*.png|Loader*.jpeg|Loader*.jpg|*.glsl|Font*.png"\ "|GUIProgress.png|Endeavour.ttf|GUICaption.png|GUIButton.png|GUIButtonDown.png|GUIButtonHighlight.png|GUIButtonHighlightRound.png|GUIIcons.png|GUIIcons2.png|GUIScroll.png|GUIContext.png|GUISubmenu.png|GUICheckBox.png|GUIBigArrows.png"\ - "|Control.png|ClonkSkins.png|Fire.png|Background.png|Flag.png|Crew.png|Wealth.png|Player.png|Rank.png|Captain.png|Cursor.png|SelectMark.png|MenuSymbol.png|Menu.png|Logo.png|Construction.png|Energy.png|Options.png|UpperBoard.png|Arrow.png|Exit.png|Hand.png|Gamepad.png|Build.png|Achv*.png"\ + "|Control.png|ClonkSkins.png|Fire.png|Background.png|Flag.png|Crew.png|Wealth.png|Player.png|Rank.png|Captain.png|Cursor.png|SelectMark.png|MenuSymbol.png|Menu.png|Logo.png|Construction.png|Energy.png|Options.png|UpperBoard.png|Arrow.png|Exit.png|Hand.png|Gamepad.png|Build.png|TransformKnob.png|Achv*.png"\ "|StartupMainMenuBG.*|StartupScenSelBG.*|StartupPlrSelBG.*|StartupPlrPropBG.*|StartupNetworkBG.*|StartupAboutBG.*|StartupBigButton.png|StartupBigButtonDown.png|StartupBookScroll.png|StartupContext.png|StartupScenSelIcons.png|StartupScenSelTitleOv.png|StartupDlgPaper.png|StartupOptionIcons.png|StartupTabClip.png|StartupNetGetRef.png|StartupLogo.png" #define C4FLS_Objects "Names*.txt|Desc*.txt|*.ocd" #define C4FLS_System "*.hlp|*.cnt|Language*.txt|*.fon|*.fnt|*.ttf|*.ttc|*.fot|*.otf|Fonts.txt|StringTbl*.txt|PlayerControls.txt|*.c|Names.txt" diff --git a/src/control/C4Control.cpp b/src/control/C4Control.cpp index 046071711..80135b3d0 100644 --- a/src/control/C4Control.cpp +++ b/src/control/C4Control.cpp @@ -1369,6 +1369,17 @@ void C4ControlEMMoveObject::Execute() const if (container && obj && container->Status && obj->Status) obj->Enter(container); } break; + case EMMO_Transform: + { + C4Object *pTarget = ::Objects.SafeObjectPointer(iTargetObj); + if (pTarget) + { + int32_t new_rot = fixtoi(this->tx, 1); + int32_t new_con = fixtoi(this->ty, FullCon/100); + if (pTarget->Def->Rotateable) pTarget->SetRotation(new_rot); + if (pTarget->Def->GrowthType) pTarget->DoCon(new_con - pTarget->GetCon(), false); + } + } } // update property dlg & status bar if (fLocalCall && eAction != eAction) diff --git a/src/control/C4Control.h b/src/control/C4Control.h index e39de20a3..9b4de4057 100644 --- a/src/control/C4Control.h +++ b/src/control/C4Control.h @@ -453,7 +453,8 @@ enum C4ControlEMObjectAction EMMO_Exit, // exit objects EMMO_Select, // select object EMMO_Deselect, // deselect object - EMMO_Create // create a new object (used by C4Game::DropDef) + EMMO_Create, // create a new object (used by C4Game::DropDef) + EMMO_Transform // adjust rotation / con of selected object }; class C4ControlEMMoveObject : public C4ControlPacket // sync diff --git a/src/editor/C4ConsoleQtViewport.cpp b/src/editor/C4ConsoleQtViewport.cpp index c663f4ea3..90d1553dd 100644 --- a/src/editor/C4ConsoleQtViewport.cpp +++ b/src/editor/C4ConsoleQtViewport.cpp @@ -85,7 +85,14 @@ void C4ConsoleQtViewportView::mouseMoveEvent(QMouseEvent *eventMove) else { cvp->pWindow->EditCursorMove(eventMove->x() * pr, eventMove->y() * pr, GetShiftWParam()); - this->setCursor(::Console.EditCursor.GetShapes()->HasDragCursor() ? ::Console.EditCursor.GetShapes()->GetDragCursor() : Qt::CrossCursor); + Qt::CursorShape cursor; + if (::Console.EditCursor.HasTransformCursor()) + cursor = Qt::SizeAllCursor; + else if (::Console.EditCursor.GetShapes()->HasDragCursor()) + cursor = ::Console.EditCursor.GetShapes()->GetDragCursor(); + else + cursor = Qt::CrossCursor; + this->setCursor(cursor); } } diff --git a/src/editor/C4EditCursor.cpp b/src/editor/C4EditCursor.cpp index cc39c320f..44bbb8724 100644 --- a/src/editor/C4EditCursor.cpp +++ b/src/editor/C4EditCursor.cpp @@ -201,7 +201,7 @@ void C4EditCursor::ClearPointers(C4Object *pObj) OnSelectionChanged(); } -bool C4EditCursor::Move(float iX, float iY, DWORD dwKeyState) +bool C4EditCursor::Move(float iX, float iY, float iZoom, DWORD dwKeyState) { // alt check bool fAltIsDown = (dwKeyState & MK_ALT) != 0; @@ -220,17 +220,48 @@ bool C4EditCursor::Move(float iX, float iY, DWORD dwKeyState) // Offset movement float xoff = iX-X; float yoff = iY-Y; - X=iX; Y=iY; + X = iX; Y = iY; Zoom = iZoom; + + // Drag rotation/scale of object + if (DragTransform) + { + C4Object *obj = selection.GetObject(); + if (obj) + { + int32_t new_rot = (DragRot0 + int32_t(float(X - X2)*Zoom)) % 360; + if (new_rot < 0) new_rot += 360; + if (fShiftIsDown) new_rot = (new_rot + 23) / 45 * 45; + int32_t new_con = DragCon0 + int32_t(float(Y2 - Y)*Zoom*(FullCon / 200)); + int32_t con_step = FullCon / 5; + if (fShiftIsDown) new_con = (new_con + con_step/2) / con_step * con_step; + if (!obj->Def->Oversize) new_con = std::min(new_con, FullCon); + new_con = std::max(new_con, fShiftIsDown ? 1 : con_step); + bool any_change = false; + if (obj->Def->Rotateable) + if (new_rot != DragRotLast) + any_change = true; + if (obj->Def->GrowthType) + if (new_con != DragConLast) + any_change = true; + if (any_change) + { + EMMoveObject(EMMO_Transform, itofix(new_rot, 1), itofix(new_con, FullCon/100), obj, nullptr); + DragRotLast = new_rot; + DragConLast = new_con; + } + } + + } switch (Mode) { // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case C4CNS_ModeEdit: #ifdef WITH_QT_EDITOR - shapes->MouseMove(X, Y, Hold, 3.0f /* TODO: Depend on zoom */); + shapes->MouseMove(X, Y, Hold, 3.0f/Zoom); #endif // Hold - if (!DragFrame && Hold && !DragShape) + if (!DragFrame && Hold && !DragShape && !DragTransform) { MoveSelection(ftofix(xoff),ftofix(yoff)); UpdateDropTarget(dwKeyState); @@ -369,12 +400,22 @@ bool C4EditCursor::LeftButtonDown(DWORD dwKeyState) { // Click on shape? #ifdef WITH_QT_EDITOR - if (shapes->MouseDown(X, Y, 3.0f /* TODO: Depend on zoom */)) + if (shapes->MouseDown(X, Y, 3.0f/Zoom)) { DragShape = true; break; } #endif + // Click rotate/scale marker? + if (IsHoveringTransformMarker()) + { + DragTransform = true; + X2 = X; Y2 = Y; + C4Object *dragged_obj = selection.GetObject(); + DragRot0 = DragRotLast = dragged_obj->GetR(); + DragCon0 = DragConLast = dragged_obj->GetCon(); + break; + } // Click on unselected: select single if (Target) { @@ -496,6 +537,7 @@ bool C4EditCursor::LeftButtonUp(DWORD dwKeyState) DragFrame=false; DragLine=false; DragShape = false; + DragTransform = false; DropTarget=NULL; // Update UpdateStatusBar(); @@ -610,7 +652,7 @@ bool C4EditCursor::Duplicate() return true; } -void C4EditCursor::DrawObject(C4TargetFacet &cgo, C4Object *cobj, uint32_t select_mark_color, bool highlight) +void C4EditCursor::DrawObject(C4TargetFacet &cgo, C4Object *cobj, uint32_t select_mark_color, bool highlight, bool draw_transform_marker) { // target pos (parallax) float line_width = std::max(1.0f, 1.0f / cgo.Zoom); @@ -647,6 +689,45 @@ void C4EditCursor::DrawObject(C4TargetFacet &cgo, C4Object *cobj, uint32_t selec cobj->ColorMod = dwOldMod; cobj->BlitMode = dwOldBlitMode; } + // Transformer knob + if (draw_transform_marker) + { + float transform_marker_x = 0.0f, transform_marker_y = 0.0f; + if (HasTransformMarker(&transform_marker_x, &transform_marker_y, cgo.Zoom)) + { + transform_marker_x += offX; transform_marker_y += offY; + float sz = float(::GraphicsResource.fctTransformKnob.Hgt) / cgo.Zoom; + C4Facet transform_target_sfc(cgo.Surface, transform_marker_x-sz/2, transform_marker_y-sz/2, sz, sz); + ::GraphicsResource.fctTransformKnob.Draw(transform_target_sfc); + // Transform knob while dragging + if (DragTransform) + { + pDraw->SetBlitMode(C4GFXBLIT_ADDITIVE); + transform_target_sfc.X += X - X2; + transform_target_sfc.Y += Y - Y2; + ::GraphicsResource.fctTransformKnob.Draw(transform_target_sfc); + pDraw->ResetBlitMode(); + } + } + } +} + +bool C4EditCursor::HasTransformMarker(float *x, float *y, float zoom) const +{ + // Single selection only (assume obj is in selection) + if (selection.size() != 1) return false; + C4Object *obj = selection.GetObject(); + if (!obj) return false; + // Show knob only for objects that can be scaled or rotated + if (!obj->Def->GrowthType && !obj->Def->Rotateable) return false; + // Show knob only if the shape has a certain minimum size in either extent (so small objects can still be moved) + float vis_wdt = float(obj->Shape.Wdt) * zoom; + float vis_hgt = float(obj->Shape.Wdt) * zoom; + if (vis_wdt < ::GraphicsResource.fctTransformKnob.Hgt && vis_hgt < ::GraphicsResource.fctTransformKnob.Hgt) return false; + // It's visible: Put it to the bottom of the shape without the shape expansion through rotation + *x = 0; + *y = float(obj->Def->Shape.y + obj->Def->Shape.Hgt) * obj->GetCon() / FullCon - float(::GraphicsResource.fctTransformKnob.Hgt) / (zoom*2); + return true; } void C4EditCursor::Draw(C4TargetFacet &cgo) @@ -662,7 +743,7 @@ void C4EditCursor::Draw(C4TargetFacet &cgo) { C4Object *cobj = obj.getObj(); if (!cobj) continue; - DrawObject(cgo, cobj, 0xffffffff, fShiftWasDown); // highlight selection if shift is pressed + DrawObject(cgo, cobj, 0xffffffff, fShiftWasDown, true); // highlight selection if shift is pressed } // Draw drag frame if (DragFrame) @@ -702,7 +783,7 @@ void C4EditCursor::Draw(C4TargetFacet &cgo) } // Draw object highlight C4Object *highlight = highlighted_object.getObj(); - if (highlight) DrawObject(cgo, highlight, 0xffff8000, true); // highlight selection if shift is pressed + if (highlight) DrawObject(cgo, highlight, 0xffff8000, true, false); // highlight selection if shift is pressed } @@ -782,12 +863,13 @@ void C4EditCursor::Default() #ifdef USE_WIN32_WINDOWS hMenu=NULL; #endif - Hold=DragFrame=DragLine=DragShape=false; + Hold=DragFrame=DragLine=DragShape=DragTransform=false; selection.clear(); creator_def = NULL; creator_overlay = NULL; has_mouse_hover = false; selection_invalid = false; + DragRot0 = DragRotLast = 0; DragCon0 = DragConLast = FullCon; } void C4EditCursor::Clear() @@ -1320,4 +1402,18 @@ bool C4EditCursor::GetCurrentSelectionPosition(int32_t *x, int32_t *y) void C4EditCursor::SetHighlightedObject(C4Object *new_highlight) { highlighted_object = C4VObj(new_highlight); +} + +bool C4EditCursor::IsHoveringTransformMarker() const +{ + float trf_marker_x, trf_marker_y; + if (HasTransformMarker(&trf_marker_x, &trf_marker_y, Zoom)) + { + C4Object *obj = selection.GetObject(); + float dx = (float(X - obj->GetX()) - trf_marker_x) * Zoom; + float dy = (float(Y - obj->GetY()) - trf_marker_y) * Zoom; + if (dx*dx + dy*dy <= ::GraphicsResource.fctTransformKnob.Hgt * ::GraphicsResource.fctTransformKnob.Hgt / 4) + return true; + } + return false; } \ No newline at end of file diff --git a/src/editor/C4EditCursor.h b/src/editor/C4EditCursor.h index aba76a514..785b8e1ff 100644 --- a/src/editor/C4EditCursor.h +++ b/src/editor/C4EditCursor.h @@ -56,8 +56,9 @@ protected: bool has_mouse_hover; bool selection_invalid; // if true, the property list should be updated on next execution int32_t Mode; - float X,Y,X2,Y2; - bool Hold,DragFrame,DragLine,DragShape; + float X,Y,X2,Y2,Zoom; + bool Hold,DragFrame,DragLine,DragShape,DragTransform; + int32_t DragRot0, DragCon0, DragRotLast, DragConLast; C4Object *Target,*DropTarget; C4Value highlighted_object; class C4Def *creator_def; @@ -108,7 +109,7 @@ public: bool RightButtonDown(DWORD dwKeyState); bool KeyDown(C4KeyCode KeyCode, DWORD dwKeyState); bool KeyUp(C4KeyCode KeyCode, DWORD dwKeyState); - bool Move(float iX, float iY, DWORD dwKeyState); + bool Move(float iX, float iY, float zoom, DWORD dwKeyState); bool Init(); bool EditingOK(bool for_landscape_drawing=false); C4EditCursorSelection &GetSelection() { return selection; } @@ -118,6 +119,8 @@ public: bool AltUp(); void SetMouseHover(bool h) { has_mouse_hover = h; } class C4ConsoleQtShapes *GetShapes() const { return shapes.get(); } + bool HasTransformCursor() const { return DragTransform || IsHoveringTransformMarker(); } + bool IsHoveringTransformMarker() const; protected: void UpdateStatusBar(); void ApplyCreateObject(bool contained); @@ -131,8 +134,9 @@ protected: void ApplyToolRect(); void ApplyToolLine(); void ApplyToolBrush(); - void DrawObject(C4TargetFacet &cgo, C4Object *cobj, uint32_t select_mark_color, bool highlight); + void DrawObject(C4TargetFacet &cgo, C4Object *cobj, uint32_t select_mark_color, bool highlight, bool draw_transform_marker); void DrawSelectMark(C4Facet &cgo, FLOAT_RECT r, float width, uint32_t color = 0xffffffff); + bool HasTransformMarker(float *x, float *y, float zoom) const; void FrameSelection(); void MoveSelection(C4Real iXOff, C4Real iYOff); void EMMoveObject(enum C4ControlEMObjectAction eAction, C4Real tx, C4Real ty, C4Object *pTargetObj, const C4EditCursorSelection *pObjs = NULL, const char *szScript = NULL); diff --git a/src/editor/C4ViewportWindow.cpp b/src/editor/C4ViewportWindow.cpp index fd85ebe57..e1b00fd52 100644 --- a/src/editor/C4ViewportWindow.cpp +++ b/src/editor/C4ViewportWindow.cpp @@ -219,5 +219,5 @@ void C4ViewportWindow::Close() } void C4ViewportWindow::EditCursorMove(int X, int Y, uint32_t state) { - Console.EditCursor.Move(cvp->WindowToGameX(X), cvp->WindowToGameY(Y), state); + Console.EditCursor.Move(cvp->WindowToGameX(X), cvp->WindowToGameY(Y), cvp->GetZoom(), state); } diff --git a/src/graphics/C4DrawGLMac.mm b/src/graphics/C4DrawGLMac.mm index db5d8dd27..08c954607 100644 --- a/src/graphics/C4DrawGLMac.mm +++ b/src/graphics/C4DrawGLMac.mm @@ -229,7 +229,7 @@ int32_t mouseButtonFromEvent(NSEvent* event, DWORD* modifierFlags) switch (button) { case C4MC_Button_LeftDown: - Console.EditCursor.Move(viewport->GetViewX()+x/viewport->GetZoom(), viewport->GetViewY()+y/viewport->GetZoom(), flags); + Console.EditCursor.Move(viewport->GetViewX()+x/viewport->GetZoom(), viewport->GetViewY()+y/viewport->GetZoom(), viewport->GetZoom(), flags); Console.EditCursor.LeftButtonDown(flags); break; case C4MC_Button_LeftUp: diff --git a/src/graphics/C4GraphicsResource.cpp b/src/graphics/C4GraphicsResource.cpp index 9a8ec6d2e..e315936bd 100644 --- a/src/graphics/C4GraphicsResource.cpp +++ b/src/graphics/C4GraphicsResource.cpp @@ -85,6 +85,8 @@ void C4GraphicsResource::Default() fctOKCancel.Default(); fctMouse.Default(); + fctTransformKnob.Default(); + iNumRanks=1; idRegisteredMainGroupSetFiles=-1; } @@ -123,6 +125,7 @@ void C4GraphicsResource::Clear() fctHand.Clear(); fctGamepad.Clear(); fctBuild.Clear(); + fctTransformKnob.Clear(); // GUI data sfcCaption.Clear(); sfcButton.Clear(); sfcButtonD.Clear(); sfcScroll.Clear(); sfcContext.Clear(); idSfcCaption = idSfcButton = idSfcButtonD = idSfcScroll = idSfcContext = 0; @@ -136,6 +139,7 @@ void C4GraphicsResource::Clear() fctProgressBar.Clear(); fctContext.Default(); + // unhook deflist from font FontRegular.SetCustomImages(NULL); @@ -265,6 +269,7 @@ bool C4GraphicsResource::Init() if (!LoadFile(fctHand, "Hand", Files, C4FCT_Height, C4FCT_Full, false, 0)) return false; if (!LoadFile(fctGamepad, "Gamepad", Files, 80, C4FCT_Full, false, 0)) return false; if (!LoadFile(fctBuild, "Build", Files, C4FCT_Full, C4FCT_Full, false, 0)) return false; + if (!LoadFile(fctTransformKnob,"TransformKnob",Files,C4FCT_Full, C4FCT_Full, false, 0)) return false; // achievements if (!Achievements.Init(Files)) return false; diff --git a/src/graphics/C4GraphicsResource.h b/src/graphics/C4GraphicsResource.h index fb42a3a59..244a998e9 100644 --- a/src/graphics/C4GraphicsResource.h +++ b/src/graphics/C4GraphicsResource.h @@ -75,6 +75,7 @@ public: C4Facet fctCommand; C4Facet fctKey; C4Facet fctOKCancel; + C4FacetID fctTransformKnob; C4FacetID fctCrewClr; // ColorByOwner-surface of fctCrew C4FacetID fctFlagClr; // ColorByOwner-surface of fctFlag C4FacetID fctPlayerClr; // ColorByOwner-surface of fctPlayer