OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4EditCursor.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Handles viewport editing in console mode */
19 
20 #include "C4Include.h"
21 #include "editor/C4EditCursor.h"
22 
23 #include "editor/C4Console.h"
24 #include "object/C4Def.h"
25 #include "object/C4Object.h"
26 #include "game/C4Application.h"
27 #include "lib/C4Random.h"
28 #include "gui/C4MouseControl.h"
29 #include "landscape/C4Landscape.h"
30 #include "landscape/C4Texture.h"
32 #include "game/C4Game.h"
33 #include "object/C4GameObjects.h"
34 #include "control/C4GameControl.h"
35 #include "script/C4AulExec.h"
36 #ifdef WITH_QT_EDITOR
38 #endif
39 #include "lib/StdMesh.h"
40 
41 #ifdef _WIN32
42 #include "res/resource.h"
43 #endif
44 
45 
47 {
48  StdStrBuf Output;
49  // Compose info text by selected object(s)
50  int32_t obj_count = size();
51  switch (obj_count)
52  {
53  // No selection
54  case 0:
55  Output = LoadResStr("IDS_CNS_NOOBJECT");
56  break;
57  // One selected object
58  case 1:
59  {
60  C4Object *obj = GetObject();
61  if (obj)
62  Output.Take(obj->GetDataString());
63  else
64  Output.Take(front().GetDataString());
65  break;
66  }
67  // Multiple selected objects
68  default:
69  Output.Format(LoadResStr("IDS_CNS_MULTIPLEOBJECTS"), obj_count);
70  break;
71  }
72  return Output;
73 }
74 
76 {
77  // Get indexed C4Object * in list
78  C4Object *obj;
79  for (const C4Value &v : (*this))
80  if ((obj = v.getObj()))
81  if (!index--)
82  return obj;
83  return nullptr;
84 }
85 
87 {
88  C4Object *obj, *last = nullptr;
89  for (const C4Value &v : (*this))
90  if ((obj = v.getObj()))
91  last = obj;
92  return last;
93 }
94 
96 {
97  // remove nullptred entries that may happen because objects got deleted
98  this->remove(C4VNull);
99 }
100 
102 {
103  bool found = false;
104  for (C4Value &v : (*this))
105  if (obj == v.getObj())
106  {
107  found = true;
108  v.Set0();
109  }
110  if (found) ConsolidateEmpty();
111  return found;
112 }
113 
115 {
116  for (const C4Value &v : (*this)) if (obj == v.getPropList()) return true;
117  return false;
118 }
119 
121 {
122  // count only C4Object *
123  int32_t count = 0;
124  for (const C4Value &v : *this) if (v.getObj()) ++count;
125  return count;
126 }
127 
128 
130 #ifdef WITH_QT_EDITOR
131  : shapes(new C4ConsoleQtShapes())
132 #endif
133 {
134  Default();
135 }
136 
138 {
139  Clear();
140 }
141 
143 {
144  // drawing
145  switch (Mode)
146  {
147  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
148  case C4CNS_ModeEdit:
149  // Hold selection
150  if (Hold)
151  EMMoveObject(fShiftWasDown ? EMMO_MoveForced : EMMO_Move, Fix0, Fix0, nullptr, &selection, nullptr, false);
152  break;
153  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
154  case C4CNS_ModeDraw:
155  switch (Console.ToolsDlg.Tool)
156  {
157  case C4TLS_Fill:
158  if (Hold) if (!Game.HaltCount) if (Console.Editing) ApplyToolFill();
159  break;
160  }
161  break;
162  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
163  }
165  {
168  }
169 }
170 
172 {
173 
174 #ifdef USE_WIN32_WINDOWS
175  if (!(hMenu = LoadMenu(Application.GetInstance(),MAKEINTRESOURCE(IDR_CONTEXTMENUS))))
176  return false;
177 #endif
179 
180  return true;
181 }
182 
184 {
185  if (Target==pObj) Target=nullptr;
186  if (selection.ClearPointers(pObj))
188 }
189 
190 bool C4EditCursor::Move(float iX, float iY, float iZoom, DWORD dwKeyState)
191 {
192  // alt check
193  bool fAltIsDown = (dwKeyState & MK_ALT) != 0;
194  if (fAltIsDown != fAltWasDown)
195  {
196  if ((fAltWasDown = fAltIsDown))
197  AltDown();
198  else
199  AltUp();
200  }
201 
202  // shift check
203  bool fShiftIsDown = (dwKeyState & MK_SHIFT) != 0;
204  if(fShiftIsDown != fShiftWasDown)
205  fShiftWasDown = fShiftIsDown;
206 
207  // Offset movement
208  float xoff = iX-X; float yoff = iY-Y;
209  X = iX; Y = iY; Zoom = iZoom;
210 
211  // Drag rotation/scale of object
212  if (DragTransform)
213  {
214  C4Object *obj = selection.GetObject();
215  if (obj)
216  {
217  int32_t new_rot = (DragRot0 + int32_t(float(X - X2)*Zoom)) % 360;
218  if (new_rot < 0) new_rot += 360;
219  if (fShiftIsDown) new_rot = (new_rot + 23) / 45 * 45;
220  int32_t new_con = DragCon0 + int32_t(float(Y2 - Y)*Zoom*(FullCon / 200));
221  int32_t con_step = FullCon / 5;
222  if (fShiftIsDown) new_con = (new_con + con_step/2) / con_step * con_step;
223  if (!obj->Def->Oversize) new_con = std::min<int32_t>(new_con, FullCon);
224  new_con = std::max<int32_t>(new_con, fShiftIsDown ? 1 : con_step);
225  bool any_change = false;
226  if (obj->Def->Rotateable)
227  if (new_rot != DragRotLast)
228  any_change = true;
229  if (obj->Def->GrowthType)
230  if (new_con != DragConLast)
231  any_change = true;
232  if (any_change)
233  {
234  EMMoveObject(EMMO_Transform, itofix(new_rot, 1), itofix(new_con, FullCon/100), obj, nullptr);
235  DragRotLast = new_rot;
236  DragConLast = new_con;
237  }
238  }
239 
240  }
241 
242  switch (Mode)
243  {
244  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
245  case C4CNS_ModeEdit:
246 #ifdef WITH_QT_EDITOR
247  shapes->MouseMove(X, Y, Hold, 3.0f/Zoom, !!(dwKeyState & MK_SHIFT), !!(dwKeyState & MK_CONTROL));
248 #endif
249  // Hold
250  if (!DragFrame && Hold && !DragShape && !DragTransform)
251  {
252  MoveSelection(ftofix(xoff),ftofix(yoff), false);
253  UpdateDropTarget(dwKeyState);
254  }
255  // Update target
256  // Shift always indicates a target outside the current selection
257  else
258  {
259  Target = (dwKeyState & MK_SHIFT) ? selection.GetLastObject() : nullptr;
260  do
261  {
262  Target = Game.FindObject(nullptr,X,Y,0,0,OCF_NotContained, Target);
263  }
264  while ((dwKeyState & MK_SHIFT) && Target && selection.IsContained(Target));
265  }
266  break;
267  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
269  // Drop target for contained object creation
270  UpdateDropTarget(dwKeyState);
271  break;
272  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
273  case C4CNS_ModeDraw:
274  switch (Console.ToolsDlg.Tool)
275  {
276  case C4TLS_Brush:
277  if (Hold) ApplyToolBrush();
278  break;
279  case C4TLS_Line: case C4TLS_Rect:
280  break;
281  }
282  break;
283  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
284  }
285 
286  // Update
287  UpdateStatusBar();
288  return true;
289 }
290 
291 bool C4EditCursor::Move(DWORD new_key_state)
292 {
293  // Move at last position with new key state
294  return Move(X, Y, Zoom, new_key_state);
295 }
296 
298 {
299  int32_t X=this->X, Y=this->Y;
300  StdStrBuf str;
301  switch (Mode)
302  {
303  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
304  case C4CNS_ModePlay:
306  break;
307  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
308  case C4CNS_ModeEdit:
309  str.Format("%i/%i (%s)",X,Y,Target ? (Target->GetName()) : LoadResStr("IDS_CNS_NOTHING") );
310  break;
311  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
313  str.Format(LoadResStr("IDS_CNS_CREATESTATUS"), creator_def ? (creator_def->GetName()) : LoadResStr("IDS_CNS_NOTHING"));
314  break;
315  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
316  case C4CNS_ModeDraw:
317  str.Format("%i/%i (fg: %s, bg: %s)",X,Y,
318  MatValid(::Landscape.GetMat(X,Y)) ? ::MaterialMap.Map[::Landscape.GetMat(X,Y)].Name : LoadResStr("IDS_CNS_NOTHING"),
319  MatValid(::Landscape.GetBackMat(X,Y)) ? ::MaterialMap.Map[::Landscape.GetBackMat(X,Y)].Name : LoadResStr("IDS_CNS_NOTHING") );
320  break;
321  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
322  }
324 }
325 
326 void C4EditCursor::OnSelectionChanged(bool by_objectlist)
327 {
329  if (!by_objectlist) ::Console.ObjectListDlg.Update(selection);
330 }
331 
333 {
334  if (!add_proplist) return;
335  C4Object *add_obj = add_proplist->GetObject();
336  if (add_obj && !add_obj->Status) return;
337  if (selection.IsContained(add_proplist)) return;
338  // add object to selection and do script callback
339  selection.push_back(C4VPropList(add_proplist));
340 }
341 
343 {
344  if (!remove_proplist) return false;
345  C4Object *remove_obj = remove_proplist->GetObject();
346  if (remove_obj && !remove_obj->Status) return false;
347  // remove object from selection and do script callback
348  if (!selection.IsContained(remove_proplist)) return false;
349  selection.remove(C4VPropList(remove_proplist));
350  return true;
351 }
352 
354 {
355  // remove everything from selection
356  selection.clear();
357 }
358 
360 {
361 
362  // Hold
363  Hold=true;
364 
365  switch (Mode)
366  {
367  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
368  case C4CNS_ModeEdit:
369  // Click on shape?
370 #ifdef WITH_QT_EDITOR
371  if (shapes->MouseDown(X, Y, 3.0f / Zoom, !!(dwKeyState & MK_SHIFT), !!(dwKeyState & MK_CONTROL)))
372  {
373  DragShape = shapes->IsDragging();
374  break;
375  }
376 #endif
377  if (dwKeyState & MK_CONTROL)
378  {
379  // Toggle target
380  if (Target)
383  }
384  else
385  {
386  // Click rotate/scale marker?
388  {
389  DragTransform = true;
390  X2 = X; Y2 = Y;
391  C4Object *dragged_obj = selection.GetObject();
392  DragRot0 = DragRotLast = dragged_obj->GetR();
393  DragCon0 = DragConLast = dragged_obj->GetCon();
394  break;
395  }
396  // Click on unselected: select single
397  if (Target)
398  {
399  bool found = false;
400  for (C4Value &obj : selection)
401  {
402  if(obj.getObj() && obj.getObj()->At(X, Y))
403  {
404  found = true;
405  break;
406  }
407  }
408  if(!found) // means loop didn't break
409  {
412  }
413  }
414  // Click on nothing: drag frame
415  if (!Target)
416  { ClearSelection(); DragFrame=true; X2=X; Y2=Y; }
417  }
419  break;
420  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
422  ApplyCreateObject(!!(dwKeyState & MK_CONTROL));
423  break;
424  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
425  case C4CNS_ModeDraw:
426  switch (Console.ToolsDlg.Tool)
427  {
428  case C4TLS_Brush: ApplyToolBrush(); break;
429  case C4TLS_Line: DragLine=true; X2=X; Y2=Y; break;
430  case C4TLS_Rect: DragFrame=true; X2=X; Y2=Y; break;
431  case C4TLS_Fill:
432  if (Game.HaltCount)
433  { Hold=false; Console.Message(LoadResStr("IDS_CNS_FILLNOHALT")); return false; }
434  break;
435  case C4TLS_Picker: ApplyToolPicker(); break;
436  }
437  break;
438  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
439  }
440 
441  DropTarget=nullptr;
442 
443  return true;
444 }
445 
447 {
448 
449  switch (Mode)
450  {
451  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
452  case C4CNS_ModeEdit:
453  if ( (dwKeyState & MK_CONTROL) == 0)
454  {
455  // Check whether cursor is on anything in the selection
456  bool fCursorIsOnSelection = false;
457  for (C4Value &obj : selection)
458  {
459  if (obj.getObj() && obj.getObj()->At(X,Y))
460  {
461  fCursorIsOnSelection = true;
462  break;
463  }
464  }
465  if (!fCursorIsOnSelection)
466  {
467  // Click on unselected
468  if (Target && !selection.IsContained(Target))
469  {
471  }
472  // Click on nothing
473  if (!Target) ClearSelection();
474  }
475  }
477  break;
478  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
479  }
480 
481  return true;
482 }
483 
485 {
486  // Finish edit/tool
487  switch (Mode)
488  {
489  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
490  case C4CNS_ModeEdit:
491  // Finish object drag
492  if (!DragFrame && Hold && !DragShape && !DragTransform)
493  {
494  MoveSelection(Fix0, Fix0, true);
495  }
496  if (DragFrame) FrameSelection();
497  if (DropTarget) PutContents();
498  break;
499  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
500  case C4CNS_ModeDraw:
501  switch (Console.ToolsDlg.Tool)
502  {
503  case C4TLS_Line:
504  if (DragLine) ApplyToolLine();
505  break;
506  case C4TLS_Rect:
507  if (DragFrame) ApplyToolRect();
508  break;
509  }
510  break;
511  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
512  }
513 
514  // Release
515 #ifdef WITH_QT_EDITOR
516  shapes->MouseUp(X, Y, !!(dwKeyState & MK_SHIFT), !!(dwKeyState & MK_CONTROL));
517 #endif
518  Hold=false;
519  DragFrame=false;
520  DragLine=false;
521  DragShape = false;
522  DragTransform = false;
523  DropTarget=nullptr;
524  // Update
525  UpdateStatusBar();
526  return true;
527 }
528 
529 bool C4EditCursor::KeyDown(C4KeyCode KeyCode, DWORD dwKeyState)
530 {
531  // alt check
532  bool fAltIsDown = (dwKeyState & MK_ALT) != 0;
533  fAltIsDown = fAltIsDown || KeyCode == K_ALT_L || KeyCode == K_ALT_R;
534  if (fAltIsDown != fAltWasDown)
535  {
536  if ((fAltWasDown = fAltIsDown))
537  AltDown();
538  else
539  AltUp();
540  }
541 
542  // shift check
543  bool fShiftIsDown = (dwKeyState & MK_SHIFT) != 0;
544  fShiftIsDown = fShiftIsDown || KeyCode == K_SHIFT_L || KeyCode == K_SHIFT_R;
545  if(fShiftIsDown != fShiftWasDown)
546  fShiftWasDown = fShiftIsDown;
547 
548  return true;
549 }
550 
551 bool C4EditCursor::KeyUp(C4KeyCode KeyCode, DWORD dwKeyState)
552 {
553  // alt check
554  bool fAltIsDown = (dwKeyState & MK_ALT) != 0;
555  fAltIsDown = fAltIsDown && !( KeyCode == K_ALT_L || KeyCode == K_ALT_R);
556  if (fAltIsDown != fAltWasDown)
557  {
558  if ((fAltWasDown = fAltIsDown))
559  AltDown();
560  else
561  AltUp();
562  }
563 
564  // shift check
565  bool fShiftIsDown = (dwKeyState & MK_SHIFT) != 0;
566  fShiftIsDown = fShiftIsDown && !(KeyCode == K_SHIFT_L || KeyCode == K_SHIFT_R);
567  if(fShiftIsDown != fShiftWasDown)
568  fShiftWasDown = fShiftIsDown;
569 
570  return true;
571 }
572 
573 #ifdef USE_WIN32_WINDOWS
574 bool SetMenuItemEnable(HMENU hMenu, WORD id, bool fEnable)
575 {
576  return !!EnableMenuItem(hMenu,id,MF_BYCOMMAND | MF_ENABLED | ( fEnable ? 0 : MF_GRAYED));
577 }
578 
579 bool SetMenuItemText(HMENU hMenu, WORD id, const char *szText)
580 {
581  MENUITEMINFOW minfo;
582  ZeroMem(&minfo,sizeof(minfo));
583  minfo.cbSize = sizeof(minfo);
584  minfo.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA;
585  minfo.fType = MFT_STRING;
586  minfo.wID = id;
587  StdBuf td = GetWideCharBuf(szText);
588  minfo.dwTypeData = getMBufPtr<wchar_t>(td);
589  minfo.cch = wcslen(minfo.dwTypeData);
590  return !!SetMenuItemInfoW(hMenu,id,false,&minfo);
591 }
592 #endif
593 
595 {
596  Target=nullptr;
597 #ifndef WITH_QT_EDITOR
598  DoContextMenu(dwKeyState);
599 #endif
600  // Update
601  UpdateStatusBar();
602 
603  return true;
604 }
605 
607 {
608  if (!EditingOK()) return false;
610  if (::Control.isCtrlHost())
611  {
613  }
614  return true;
615 }
616 
618 {
619  switch (Mode)
620  {
621  case C4CNS_ModeEdit: case C4CNS_ModePlay:
624  break;
625  case C4CNS_ModeDraw:
627  break;
628  }
629  return true;
630 }
631 
633 {
635  return true;
636 }
637 
638 void C4EditCursor::PerformDuplication(int32_t *object_numbers, int32_t object_count, bool local_call)
639 {
640  if (!object_count) return;
641  // Remember last OEI so duplicated objects can be determined
642  int32_t prev_oei = C4PropListNumbered::GetEnumerationIndex();
643  // Get serialized objects
644  C4RefCntPointer<C4ValueArray> object_numbers_c4v = new C4ValueArray();
645  object_numbers_c4v->SetSize(object_count);
646  int32_t total_object_count = 0;
647  for (int32_t i = 0; i < object_count; ++i)
648  {
649  C4Object *obj = ::Objects.SafeObjectPointer(object_numbers[i]);
650  if (!obj) continue;
651  total_object_count = obj->AddObjectAndContentsToArray(object_numbers_c4v, total_object_count);
652  }
653  object_numbers_c4v->SetSize(total_object_count);
654  int32_t objects_file_handle = ::ScriptEngine.CreateUserFile();
655  C4AulParSet pars(C4VInt(objects_file_handle), C4VArray(object_numbers_c4v.Get()));
657  bool result = !!result_c4v;
658  if (result_c4v.GetType() == C4V_Nil)
659  {
660  // Function returned nil: This usually means there was a script error during object writing.
661  // It could also mean the scripter overloaded global func SaveScenarioObjects and returned nil.
662  // In either case, no the objects file will contain garbage so no objects were duplicated.
663  LogF("ERROR: No valid result from global func " PSF_SaveScenarioObjects ". Regular object duplication failed.");
664  }
665  else
666  {
667  // Function completed successfully (returning true or false)
668  C4AulUserFile *file = ::ScriptEngine.GetUserFile(objects_file_handle);
669  if (!result || !file || !file->GetFileLength())
670  {
671  // Nothing written? Then we don't have objects.
672  // That's OK; not an error.
673  }
674  else
675  {
676  // Create copy of objects by executing duplication script
677  StdStrBuf data = file->GrabFileContents();
678  AulExec.DirectExec(&::ScriptEngine, data.getData(), "object duplication", false, nullptr, true);
679  }
680  }
681  ::ScriptEngine.CloseUserFile(objects_file_handle);
682  // Did duplication work?
683  bool any_duplicates = false;
684  for (C4Object *obj : ::Objects)
685  {
686  if (obj->Number > prev_oei)
687  {
688  any_duplicates = true;
689  break;
690  }
691  }
692  // If duplication created no objects, the user probably tried to copy a non-saved object
693  // Just copy the old way then
694  if (!any_duplicates)
695  {
696  PerformDuplicationLegacy(object_numbers, object_count, local_call);
697  return;
698  }
699  // Update local client status: Put new objects into selection
700  if (local_call)
701  {
702  selection.clear();
703  int64_t X_all = 0, Y_all = 0, n_selected = 0;
704  float old_x = X, old_y = Y;
705  for (C4Object *obj : ::Objects)
706  {
707  if (obj->Number > prev_oei)
708  {
709  selection.push_back(C4VObj(obj));
710  X_all += obj->GetX();
711  Y_all += obj->GetY();
712  ++n_selected;
713  }
714  }
715  // Reset EditCursor pos to center of duplicated objects, so they will be dragged along with the cursor
716  if (n_selected)
717  {
718  X = X_all / n_selected;
719  Y = Y_all / n_selected;
720  }
721  SetHold(true);
723  // Ensure duplicated objects moved to the cursor without extra mouse movement
724  // Move with shift key pressed to allow initial shift of HorizontalFixed items
725  DWORD last_key_state = MK_SHIFT;
726  if (fAltWasDown) last_key_state |= MK_ALT;
727  bool shift_was_down = fShiftWasDown;
728  Move(old_x, old_y, Zoom, last_key_state);
729  fShiftWasDown = shift_was_down;
730  }
731 }
732 
733 void C4EditCursor::PerformDuplicationLegacy(int32_t *pObjects, int32_t iObjectNum, bool fLocalCall)
734 {
735  // Old-style object copying: Just create new objects at old object position with same prototype
736  C4Object *pOldObj, *pObj;
737  for (int i = 0; i<iObjectNum; ++i)
738  if ((pOldObj = ::Objects.SafeObjectPointer(pObjects[i])))
739  {
740  pObj = Game.CreateObject(pOldObj->GetPrototype(), pOldObj, pOldObj->Owner, pOldObj->GetX(), pOldObj->GetY());
741  if (pObj && pObj->Status)
742  {
743  // local call? adjust selection then
744  // do callbacks for all clients for sync reasons
745  if (fLocalCall) selection.push_back(C4VObj(pObj));
746  C4AulParSet pars(C4VObj(pObj));
747  if (pOldObj->Status) pOldObj->Call(PSF_EditCursorDeselection, &pars);
748  if (pObj->Status) pObj->Call(PSF_EditCursorSelection);
749  }
750  }
751  // update status
752  if (fLocalCall)
753  {
754  for (int i = 0; i<iObjectNum; ++i)
755  if ((pOldObj = ::Objects.SafeObjectPointer(pObjects[i])))
756  selection.remove(C4VObj(pOldObj));
757  SetHold(true);
759  }
760 }
761 
762 void C4EditCursor::DrawObject(C4TargetFacet &cgo, C4Object *cobj, uint32_t select_mark_color, bool highlight, bool draw_transform_marker)
763 {
764  // target pos (parallax)
765  float line_width = std::max<float>(1.0f, 1.0f / cgo.Zoom);
766  float offX, offY, newzoom;
767  cobj->GetDrawPosition(cgo, offX, offY, newzoom);
768  ZoomDataStackItem zdsi(cgo.X, cgo.Y, newzoom);
769  if (select_mark_color)
770  {
771  FLOAT_RECT frame =
772  {
773  offX + cobj->Shape.x,
774  offX + cobj->Shape.x + cobj->Shape.Wdt,
775  offY + cobj->Shape.y,
776  offY + cobj->Shape.y + cobj->Shape.Hgt
777  };
778  DrawSelectMark(cgo, frame, line_width, select_mark_color);
779  }
780  if (highlight)
781  {
782  uint32_t dwOldMod = cobj->ColorMod;
783  uint32_t dwOldBlitMode = cobj->BlitMode;
784  cobj->ColorMod = 0xffffffff;
786 
787  if (cobj->pMeshInstance)
789 
790  cobj->Draw(cgo, -1);
791  cobj->DrawTopFace(cgo, -1);
792 
793  if (cobj->pMeshInstance)
795 
796  cobj->ColorMod = dwOldMod;
797  cobj->BlitMode = dwOldBlitMode;
798  }
799  // Transformer knob
800  if (draw_transform_marker)
801  {
802  float transform_marker_x = 0.0f, transform_marker_y = 0.0f;
803  if (HasTransformMarker(&transform_marker_x, &transform_marker_y, cgo.Zoom))
804  {
805  transform_marker_x += offX; transform_marker_y += offY;
806  float sz = float(::GraphicsResource.fctTransformKnob.Hgt) / cgo.Zoom;
807  C4Facet transform_target_sfc(cgo.Surface, transform_marker_x-sz/2, transform_marker_y-sz/2, sz, sz);
808  ::GraphicsResource.fctTransformKnob.Draw(transform_target_sfc);
809  // Transform knob while dragging
810  if (DragTransform)
811  {
813  transform_target_sfc.X += X - X2;
814  transform_target_sfc.Y += Y - Y2;
815  ::GraphicsResource.fctTransformKnob.Draw(transform_target_sfc);
816  pDraw->ResetBlitMode();
817  }
818  }
819  }
820 }
821 
822 bool C4EditCursor::HasTransformMarker(float *x, float *y, float zoom) const
823 {
824  // Single selection only (assume obj is in selection)
825  if (selection.size() != 1) return false;
826  C4Object *obj = selection.GetObject();
827  if (!obj) return false;
828  // Show knob only for objects that can be scaled or rotated
829  if (!obj->Def->GrowthType && !obj->Def->Rotateable) return false;
830  // Show knob only if the shape has a certain minimum size in either extent (so small objects can still be moved)
831  float vis_wdt = float(obj->Shape.Wdt) * zoom;
832  float vis_hgt = float(obj->Shape.Wdt) * zoom;
833  if (vis_wdt < ::GraphicsResource.fctTransformKnob.Hgt && vis_hgt < ::GraphicsResource.fctTransformKnob.Hgt) return false;
834  // It's visible: Put it to the bottom of the shape without the shape expansion through rotation
835  *x = 0;
836  *y = float(obj->Def->Shape.y + obj->Def->Shape.Hgt) * obj->GetCon() / FullCon - float(::GraphicsResource.fctTransformKnob.Hgt) / (zoom*2);
837  return true;
838 }
839 
841 {
842  ZoomDataStackItem zdsi(cgo.X, cgo.Y, cgo.Zoom);
843  float line_width = std::max<float>(1.0f, 1.0f / cgo.Zoom);
844 #ifdef WITH_QT_EDITOR
845  // Draw shapes of selection
846  shapes->Draw(cgo);
847 #endif
848  // Draw selection marks
849  for (C4Value &obj : selection)
850  {
851  C4Object *cobj = obj.getObj();
852  if (!cobj) continue;
853  DrawObject(cgo, cobj, 0xffffffff, fShiftWasDown, true); // highlight selection if shift is pressed
854  }
855  // Draw drag frame
856  if (DragFrame)
858  std::min(X, X2) + cgo.X - cgo.TargetX, std::min(Y, Y2) + cgo.Y - cgo.TargetY,
859  std::max(X, X2) + cgo.X - cgo.TargetX, std::max(Y, Y2) + cgo.Y - cgo.TargetY, 0xffffffff, line_width);
860  // Draw drag line
861  if (DragLine)
862  pDraw->DrawLineDw(cgo.Surface,
863  X + cgo.X - cgo.TargetX, Y + cgo.Y - cgo.TargetY,
864  X2 + cgo.X - cgo.TargetX, Y2 + cgo.Y - cgo.TargetY, 0xffffffff, line_width);
865  // Draw drop target
866  if (DropTarget)
868  DropTarget->GetX() + cgo.X - cgo.TargetX - ::GraphicsResource.fctMouseCursor.Wdt / 2 / cgo.Zoom,
870  float(::GraphicsResource.fctMouseCursor.Wdt) / cgo.Zoom,
872  // Draw paint circle
874  {
875  // shadow for recognition on white background/material
876  pDraw->DrawCircleDw(cgo.Surface, X + cgo.X - cgo.TargetX + 1.0f/cgo.Zoom, Y + cgo.Y - cgo.TargetY + 1.0f / cgo.Zoom, ::Console.ToolsDlg.Grade, 0xff000000, line_width);
877  // actual circle
878  pDraw->DrawCircleDw(cgo.Surface, X + cgo.X - cgo.TargetX, Y + cgo.Y - cgo.TargetY, ::Console.ToolsDlg.Grade, 0xffffffff, line_width);
879  }
880  // Draw creator preview
882  {
883  C4TargetFacet cgo_creator;
884  cgo_creator.Set(cgo.Surface, X + cgo.X - cgo.TargetX, Y + cgo.Y - cgo.TargetY,
885  creator_def->Shape.Wdt, creator_def->Shape.Hgt, 0, 0, cgo.Zoom, 0, 0);
886  if (!creator_overlay)
887  {
888  creator_overlay.reset(new C4GraphicsOverlay());
890  }
891  creator_overlay->Draw(cgo_creator, nullptr, NO_OWNER);
892  }
893  // Draw object highlight
894  C4Object *highlight = highlighted_object.getObj();
895  if (highlight) DrawObject(cgo, highlight, 0xffff8000, true, false); // highlight selection if shift is pressed
896 }
897 
898 
899 void C4EditCursor::DrawSelectMark(C4Facet &cgo, FLOAT_RECT frame, float width, uint32_t color)
900 {
901  if ((cgo.Wdt<1) || (cgo.Hgt<1)) return;
902 
903  if (!cgo.Surface) return;
904 
905  const float EDGE_WIDTH = 2.f;
906 
907  unsigned char c[4] = {
908  static_cast<unsigned char>((color >> 16) & 0xff),
909  static_cast<unsigned char>((color >> 8) & 0xff),
910  static_cast<unsigned char>((color >> 0) & 0xff),
911  static_cast<unsigned char>((color >> 24) & 0xff)
912  };
913 
914  const C4BltVertex vertices[] = {
915  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left + EDGE_WIDTH, frame.top, 0.f },
916  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.top, 0.f },
917  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.top, 0.f },
918  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.top+EDGE_WIDTH, 0.f },
919 
920  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left+EDGE_WIDTH, frame.bottom-1, 0.f },
921  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.bottom-1, 0.f },
922  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.bottom-1, 0.f },
923  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.left, frame.bottom-1-EDGE_WIDTH, 0.f },
924 
925  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1-EDGE_WIDTH, frame.top, 0.f },
926  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.top, 0.f },
927  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.top, 0.f },
928  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.top+EDGE_WIDTH, 0.f },
929 
930  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1-EDGE_WIDTH, frame.bottom-1, 0.f },
931  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.bottom-1, 0.f },
932  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.bottom-1, 0.f },
933  { 0.f, 0.f, { c[0], c[1], c[2], c[3] }, frame.right-1, frame.bottom-1-EDGE_WIDTH, 0.f },
934  };
935 
936  const unsigned int n_vertices = sizeof(vertices) / sizeof(vertices[0]);
937 
938  pDraw->PerformMultiLines(cgo.Surface, vertices, n_vertices, width, nullptr);
939 }
940 
941 
942 void C4EditCursor::MoveSelection(C4Real XOff, C4Real YOff, bool drag_finished)
943 {
944  EMMoveObject(fShiftWasDown ? EMMO_MoveForced : EMMO_Move, XOff, YOff, nullptr, &selection, nullptr, drag_finished);
945 }
946 
948 {
949  ClearSelection();
950  for (C4Object *cobj : Objects)
951  {
952  if (cobj->Status && cobj->OCF & OCF_NotContained)
953  {
954  if (Inside(cobj->GetX(),std::min(X,X2),std::max(X,X2)) && Inside(cobj->GetY(),std::min(Y,Y2),std::max(Y,Y2)))
955  AddToSelection(cobj);
956  }
957  }
959 }
960 
961 bool C4EditCursor::In(const char *szText)
962 {
964  EMMoveObject(EMMO_Script, Fix0, Fix0, nullptr, &selection, szText);
967  return true;
968 }
969 
971 {
972  fAltWasDown=false;
973  fShiftWasDown=false;
975  X=Y=X2=Y2=0;
976  Target=DropTarget=nullptr;
977 #ifdef USE_WIN32_WINDOWS
978  hMenu=nullptr;
979 #endif
981  selection.clear();
982  creator_def = nullptr;
983  creator_overlay = nullptr;
984  has_mouse_hover = false;
985  selection_invalid = false;
987 }
988 
990 {
991 #ifdef USE_WIN32_WINDOWS
992  if (hMenu) DestroyMenu(hMenu); hMenu=nullptr;
993 #endif
994 #ifdef WITH_DEBUG_MODE
996 #endif
997  selection.clear();
999  creator_overlay.reset(nullptr);
1000 #ifdef WITH_QT_EDITOR
1001  shapes->ClearShapes(); // Should really be empty already
1002 #endif
1003 }
1004 
1005 bool C4EditCursor::SetMode(int32_t iMode)
1006 {
1007  // Store focus
1008 #ifdef USE_WIN32_WINDOWS
1009  HWND hFocus=GetFocus();
1010 #endif
1011  // Update console buttons (always)
1012  Console.UpdateModeCtrls(iMode);
1013  // No change
1014  if (iMode==Mode) return true;
1015  // Set mode
1016  Mode = iMode;
1017  // Update prop tools by mode
1018  switch (Mode)
1019  {
1022  break;
1023  case C4CNS_ModeDraw:
1025  break;
1026  }
1027  if (Mode == C4CNS_ModePlay)
1028  {
1030  }
1031  else
1032  {
1033  OpenPropTools();
1035  }
1036  // Restore focus
1037 #ifdef USE_WIN32_WINDOWS
1038  SetFocus(hFocus);
1039 #endif
1040  // Done
1041  return true;
1042 }
1043 
1045 {
1046 
1047  if (!EditingOK()) return false;
1048 
1049  // Step through modes
1050  int32_t iNewMode;
1051  switch (Mode)
1052  {
1053  case C4CNS_ModePlay: iNewMode=C4CNS_ModeEdit; break;
1054 #ifdef WITH_QT_EDITOR
1055  case C4CNS_ModeEdit: iNewMode=C4CNS_ModeCreateObject; break;
1056 #else
1057  case C4CNS_ModeEdit: iNewMode = C4CNS_ModeDraw; break;
1058 #endif
1059  case C4CNS_ModeCreateObject: iNewMode = C4CNS_ModeDraw; break;
1060  case C4CNS_ModeDraw: iNewMode=C4CNS_ModePlay; break;
1061  default: iNewMode=C4CNS_ModePlay; break;
1062  }
1063 
1064  // Set new mode
1065  SetMode(iNewMode);
1066 
1067  return true;
1068 }
1069 
1071 {
1072  if (!EditingOK()) return;
1073  if (!creator_def) return;
1074  if (container && !DropTarget) return;
1075  // execute/send control
1077 }
1078 
1080 {
1081  if (!EditingOK(true)) return;
1082  C4ToolsDlg *pTools=&Console.ToolsDlg;
1083  // execute/send control
1084  EMControl(CID_EMDrawTool, new C4ControlEMDrawTool(EMDT_Brush, ::Landscape.GetMode(), X,Y,0,0, pTools->Grade, pTools->Material, pTools->Texture, pTools->BackMaterial, pTools->BackTexture));
1085 }
1086 
1088 {
1089  if (!EditingOK(true)) return;
1090  C4ToolsDlg *pTools=&Console.ToolsDlg;
1091  // execute/send control
1092  EMControl(CID_EMDrawTool, new C4ControlEMDrawTool(EMDT_Line, ::Landscape.GetMode(), X,Y,X2,Y2, pTools->Grade, pTools->Material,pTools->Texture, pTools->BackMaterial, pTools->BackTexture));
1093 }
1094 
1096 {
1097  if (!EditingOK(true)) return;
1098  C4ToolsDlg *pTools=&Console.ToolsDlg;
1099  // execute/send control
1100  EMControl(CID_EMDrawTool, new C4ControlEMDrawTool(EMDT_Rect, ::Landscape.GetMode(), X,Y,X2,Y2, pTools->Grade, pTools->Material, pTools->Texture, pTools->BackMaterial, pTools->BackTexture));
1101 }
1102 
1104 {
1105  if (!EditingOK(true)) return;
1106  C4ToolsDlg *pTools=&Console.ToolsDlg;
1107  // execute/send control
1108  EMControl(CID_EMDrawTool, new C4ControlEMDrawTool(EMDT_Fill, ::Landscape.GetMode(), X,Y,0,Y2, pTools->Grade, pTools->Material, nullptr, nullptr, nullptr));
1109 }
1110 
1111 void C4EditCursor::AppendMenuItem(int num, const StdStrBuf & label)
1112 {
1113 #ifdef USE_WIN32_WINDOWS
1114  itemsObjselect[num].ItemId = IDM_VPORTDYN_FIRST + num;
1115  if (num)
1116  AppendMenu(GetSubMenu(hMenu,0), MF_STRING, IDM_VPORTDYN_FIRST + num, label.GetWideChar());
1117  else
1118  AppendMenu(GetSubMenu(hMenu,0), MF_SEPARATOR, IDM_VPORTDYN_FIRST, nullptr);
1119 #endif
1120 }
1121 
1123 {
1124  bool fObjectSelected = !!selection.GetObject();
1125 #ifdef USE_WIN32_WINDOWS
1126  POINT point; GetCursorPos(&point);
1127  HMENU hContext = GetSubMenu(hMenu,0);
1128  SetMenuItemEnable(hContext, IDM_VIEWPORT_DELETE, fObjectSelected && Console.Editing);
1129  SetMenuItemEnable(hContext, IDM_VIEWPORT_DUPLICATE, fObjectSelected && Console.Editing);
1130  SetMenuItemEnable(hContext, IDM_VIEWPORT_CONTENTS, fObjectSelected && selection.GetObject()->Contents.ObjectCount() && Console.Editing);
1131  SetMenuItemText(hContext,IDM_VIEWPORT_DELETE,LoadResStr("IDS_MNU_DELETE"));
1132  SetMenuItemText(hContext,IDM_VIEWPORT_DUPLICATE,LoadResStr("IDS_MNU_DUPLICATE"));
1133  SetMenuItemText(hContext,IDM_VIEWPORT_CONTENTS,LoadResStr("IDS_MNU_CONTENTS"));
1134 #endif
1135 
1136  // Add selection and custom command entries for any objects at the cursor
1137  ObjselectDelItems(); // clear previous entries
1138  C4FindObjectAtPoint pFO(X,Y);
1139  C4ValueArray * atcursor; atcursor = pFO.FindMany(::Objects, ::Objects.Sectors); // needs freeing (single object ptr)
1140  int itemcount = atcursor->GetSize();
1141  if(itemcount > 0)
1142  {
1143  // Count required entries for all objects and their custom commands
1144  int entrycount = itemcount;
1145  for (int i_item = 0; i_item < itemcount; ++i_item)
1146  {
1147  C4Object *pObj = (*atcursor)[i_item].getObj(); assert(pObj);
1148  C4ValueArray *custom_commands = pObj->GetPropertyArray(P_EditCursorCommands);
1149  if (custom_commands) entrycount += custom_commands->GetSize();
1150  }
1151 #ifdef USE_WIN32_WINDOWS
1152  // If too many entries would be shown, add a "..." in the end
1153  const int maxentries = 25; // Maximum displayed objects. if you raise it, also change note with IDM_VPORTDYN_FIRST in resource.h
1154  bool has_too_many_entries = (entrycount > maxentries);
1155  if (has_too_many_entries) entrycount = maxentries + 1;
1156 #else
1157  const int maxentries = std::numeric_limits<int>::max();
1158 #endif
1159  itemsObjselect.resize(entrycount + 1); // +1 for a separator
1160  // Add a separator bar
1161  itemsObjselect[0].Object = nullptr;
1162  itemsObjselect[0].Command.Clear();
1163  itemsObjselect[0].EditCursor = this;
1164  AppendMenuItem(0, StdStrBuf());
1165  // Add all objects
1166  int i_entry = 0;
1167  for (int i_item = 0; i_item < itemcount; ++i_item)
1168  {
1169  ++i_entry; if (i_entry >= maxentries) break;
1170  // Add selection entry
1171  C4Object *obj = (*atcursor)[i_item].getObj();
1172  assert(obj);
1173  itemsObjselect[i_entry].Object = obj;
1174  itemsObjselect[i_entry].Command.Clear();
1175  itemsObjselect[i_entry].EditCursor = this;
1176  AppendMenuItem(i_entry, FormatString("%s #%i (%i/%i)", obj->GetName(), obj->Number, obj->GetX(), obj->GetY()));
1177 
1178  // Add custom command entries
1179  C4ValueArray *custom_commands = obj->GetPropertyArray(P_EditCursorCommands);
1180  if (custom_commands) for (int i_cmd = 0; i_cmd < custom_commands->GetSize(); ++i_cmd)
1181  {
1182  ++i_entry; if (i_entry >= maxentries) break;
1183  const C4Value &cmd = custom_commands->GetItem(i_cmd);
1184  StdStrBuf custom_command_szstr; C4AulFunc *custom_command; C4String *custom_command_string;
1185  // Custom command either by string or by function pointer
1186  if ((custom_command = cmd.getFunction()))
1187  custom_command_szstr.Format("%s()", custom_command->GetName());
1188  else if ((custom_command_string = cmd.getStr()))
1189  custom_command_szstr.Copy(custom_command_string->GetData()); // copy just in case script get reloaded inbetween
1190  if (custom_command_szstr.getLength())
1191  {
1192  itemsObjselect[i_entry].Object = obj;
1193  itemsObjselect[i_entry].Command.Take(custom_command_szstr);
1194  itemsObjselect[i_entry].EditCursor = this;
1195  AppendMenuItem(i_entry, FormatString("%s->%s", obj->GetName(), custom_command_szstr.getData()));
1196  }
1197  }
1198  }
1199 #ifdef USE_WIN32_WINDOWS
1200  if (has_too_many_entries)
1201  {
1202  AppendMenu(hContext, MF_GRAYED, IDM_VPORTDYN_FIRST + maxentries + 1, L"...");
1203  itemsObjselect[maxentries + 1].ItemId = IDM_VPORTDYN_FIRST + maxentries + 1;
1204  itemsObjselect[maxentries + 1].Object = nullptr;
1205  itemsObjselect[maxentries + 1].Command.Clear();
1206  }
1207 #endif
1208  }
1209  delete atcursor;
1210 
1211 #ifdef USE_WIN32_WINDOWS
1212  int32_t iItem = TrackPopupMenu(
1213  hContext,
1214  TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_NONOTIFY,
1215  point.x,point.y, 0,
1216  Console.hWindow,
1217  nullptr);
1218  switch (iItem)
1219  {
1220  case IDM_VIEWPORT_DELETE: Delete(); break;
1221  case IDM_VIEWPORT_DUPLICATE: Duplicate(); break;
1222  case IDM_VIEWPORT_CONTENTS: GrabContents(); break;
1223  case 0: break;
1224  default:
1225  for(std::vector<ObjselItemDt>::iterator it = itemsObjselect.begin() + 1; it != itemsObjselect.end(); ++it)
1226  if(it->ItemId == iItem)
1227  {
1228  if (it->Command.getLength())
1229  DoContextObjCommand(it->Object, it->Command.getData());
1230  else
1231  DoContextObjsel(it->Object, (dwKeyState & MK_SHIFT) == 0);
1232  break;
1233  }
1234  break;
1235  }
1237 #endif
1238  return true;
1239 }
1240 
1242 {
1243  // Set selection
1244  C4Object *pFrom;
1245  if (!( pFrom = selection.GetObject() )) return;
1246  ClearSelection();
1247  for (C4Object *cont : pFrom->Contents) AddToSelection(cont);
1249  Hold=true;
1250 
1251  // Exit all objects
1252  EMMoveObject(EMMO_Exit, Fix0, Fix0, nullptr, &selection);
1253 }
1254 
1256 {
1257  // A drop target is set if holding down control either while moving an object or in object creation mode
1258  DropTarget=nullptr;
1259 
1260  if (dwKeyState & MK_CONTROL)
1262  for (C4Object *cobj : Objects)
1263  {
1264  if (cobj->Status)
1265  if (!cobj->Contained)
1266  if (Inside<int32_t>(X-(cobj->GetX()+cobj->Shape.x),0,cobj->Shape.Wdt-1))
1267  if (Inside<int32_t>(Y-(cobj->GetY()+cobj->Shape.y),0,cobj->Shape.Hgt-1))
1268  if (!selection.IsContained(cobj))
1269  { DropTarget=cobj; break; }
1270  }
1271 
1272 }
1273 
1275 {
1276  if (!DropTarget) return;
1278 }
1279 
1281 {
1282  return Target;
1283 }
1284 
1285 bool C4EditCursor::EditingOK(bool for_landscape_drawing)
1286 {
1287  if (!Console.Editing)
1288  {
1289  Hold=false;
1290  Console.Message(LoadResStr("IDS_CNS_NONETEDIT"));
1291  return false;
1292  }
1293  return true;
1294 }
1295 
1297 {
1298  return Mode;
1299 }
1300 
1302 {
1303  C4ToolsDlg *pTools=&Console.ToolsDlg;
1304  Hold=false;
1305  Console.Message(FormatString(LoadResStr("IDS_CNS_NOMATDEF"),pTools->Material,pTools->Texture).getData());
1306 }
1307 
1309 {
1310  int32_t iMaterial;
1311  BYTE byIndex;
1312  switch (::Landscape.GetMode())
1313  {
1314  case LandscapeMode::Static:
1315  {
1316  bool material_set = false;
1317  int32_t x = X/::Landscape.GetMapZoom();
1318  int32_t y = Y/::Landscape.GetMapZoom();
1319  // Material-texture from map
1320  if ((byIndex = ::Landscape.GetMapIndex(x, y)))
1321  {
1322  const C4TexMapEntry *pTex = ::TextureMap.GetEntry(byIndex);
1323  if (pTex && pTex->GetMaterialName() && *pTex->GetMaterialName())
1324  {
1325  const BYTE byIndexBkg = Landscape.GetBackMapIndex(x, y);
1328 
1329  // Set background index if GUI backend supports it
1331  {
1332  const C4TexMapEntry *pBgTex = ::TextureMap.GetEntry(byIndexBkg);
1333  if (pBgTex && !pBgTex->isNull())
1334  {
1337  }
1338  else
1339  {
1341  }
1342  }
1343  else
1344  {
1345  Console.ToolsDlg.SetIFT(byIndexBkg != 0);
1346  }
1347 
1348  material_set = true;
1349  }
1350  }
1351  // default to sky, because invalid materials are always rendered as sky
1352  if (!material_set) Console.ToolsDlg.SelectMaterial(C4TLS_MatSky);
1353  break;
1354  }
1355  case LandscapeMode::Exact:
1356  // Material only from landscape
1357  if (MatValid(iMaterial=GBackMat(X,Y)))
1358  {
1361  }
1362  else
1364  break;
1365  }
1366  Hold=false;
1367 }
1368 
1369 void C4EditCursor::EMMoveObject(C4ControlEMObjectAction eAction, C4Real tx, C4Real ty, C4Object *pTargetObj, const C4EditCursorSelection *pObjs, const char *szScript, bool drag_finished)
1370 {
1371  // construct object list
1372  int32_t iObjCnt = 0; int32_t *pObjIDs = nullptr;
1373  if (pObjs && (iObjCnt = pObjs->ObjectCount()))
1374  {
1375  pObjIDs = new int32_t [iObjCnt];
1376  // fill
1377  int32_t i = 0;
1378  for (const C4Value &vobj : *pObjs)
1379  {
1380  C4Object *obj = vobj.getObj();
1381  if (!obj) continue;
1382  if (obj && obj->Status)
1383  pObjIDs[i++] = obj->Number;
1384  else
1385  pObjIDs[i++] = 0;
1386  }
1387  }
1388 
1389  // execute control
1390  EMControl(CID_EMMoveObj, new C4ControlEMMoveObject(eAction, tx, ty, pTargetObj, iObjCnt, pObjIDs, szScript, drag_finished));
1391 
1392 }
1393 
1395 {
1396  ::Control.DoInput(eCtrlType, pCtrl, CDT_Decide);
1397 }
1398 
1399 
1401  if(!itemsObjselect.size()) return;
1402  std::vector<ObjselItemDt>::iterator it = itemsObjselect.begin();
1403  while(it != itemsObjselect.end()) {
1404  #if defined(USE_WIN32_WINDOWS)
1405  if(!it->ItemId) { ++it; continue; }
1406  HMENU hContext = GetSubMenu(hMenu,0);
1407  DeleteMenu(hContext, it->ItemId, MF_BYCOMMAND);
1408  #endif
1409  ++it;
1410  }
1411  itemsObjselect.resize(0);
1412 }
1413 
1415 {
1416  // alt only has an effect in draw mode (picker)
1417  if (Mode == C4CNS_ModeDraw)
1418  {
1420  }
1421  // key not processed - allow further usages of Alt
1422  return false;
1423 }
1424 
1426 {
1427  if (Mode == C4CNS_ModeDraw)
1428  {
1430  }
1431  // key not processed - allow further usages of Alt
1432  return false;
1433 }
1434 
1436 {
1437  if (clear) ClearSelection(obj);
1438  AddToSelection(obj);
1440 }
1441 
1442 void C4EditCursor::DoContextObjCommand(C4Object * obj, const char *cmd)
1443 {
1444  // Command going through queue for sync
1445  if (!obj || !cmd) return;
1446  In(FormatString("Object(%d)->%s", obj->Number, cmd).getData());
1447 }
1448 
1449 bool C4EditCursor::GetCurrentSelectionPosition(int32_t *x, int32_t *y)
1450 {
1451  C4Object *obj = selection.GetObject();
1452  if (!obj || !obj->Status) return false;
1453  *x = obj->GetX();
1454  *y = obj->GetY();
1455  return true;
1456 }
1457 
1459 {
1460  highlighted_object = C4VObj(new_highlight);
1461 }
1462 
1464 {
1465  float trf_marker_x, trf_marker_y;
1466  if (HasTransformMarker(&trf_marker_x, &trf_marker_y, Zoom))
1467  {
1468  C4Object *obj = selection.GetObject();
1469  float dx = (float(X - obj->GetX()) - trf_marker_x) * Zoom;
1470  float dy = (float(Y - obj->GetY()) - trf_marker_y) * Zoom;
1472  return true;
1473  }
1474  return false;
1475 }
const char * getData() const
Definition: StdBuf.h:450
C4EditCursor EditCursor
Definition: C4Console.h:90
int32_t GetY() const
Definition: C4Object.h:287
int32_t ObjectCount() const
StdStrBuf GetData() const
Definition: C4StringTable.h:50
int32_t DragCon0
Definition: C4EditCursor.h:58
C4ID id
Definition: C4Def.h:103
void DoContextObjCommand(C4Object *, const char *cmd)
Definition: StdBuf.h:37
float Y
Definition: C4Facet.h:120
bool SetMode(int32_t iMode)
void ApplyToolPicker()
StdStrBuf GrabFileContents()
Definition: C4Aul.h:101
virtual const char * GetName() const
Definition: C4PropList.cpp:267
int32_t GetCon() const
Definition: C4Object.h:272
bool In(const char *szText)
float Zoom
Definition: C4Facet.h:167
void UpdateDropTarget(DWORD dwKeyState)
bool IsContained(C4PropList *obj) const
C4Console Console
Definition: C4Globals.cpp:45
bool DragTransform
Definition: C4EditCursor.h:57
void UpdateStatusBar()
C4PropListStatic * GetPropList()
Definition: C4Aul.h:153
bool OpenPropTools()
C4String * getStr() const
Definition: C4Value.h:117
bool isCtrlHost() const
Definition: C4GameControl.h:99
C4Game Game
Definition: C4Globals.cpp:52
#define IDM_VIEWPORT_CONTENTS
Definition: resource.h:90
void PerformDuplication(int32_t *object_numbers, int32_t object_count, bool local_call)
int32_t iTick35
Definition: C4Game.h:131
float bottom
Definition: C4Rect.h:27
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
const uint32_t OCF_NotContained
Definition: C4Constants.h:97
void SetAlternateTool()
Definition: C4ToolsDlg.cpp:283
void ResetBlitMode()
Definition: C4Draw.h:192
void PerformDuplicationLegacy(int32_t *pObjects, int32_t iObjectNum, bool fLocalCall)
C4Object * Target
Definition: C4EditCursor.h:59
void Draw(C4TargetFacet &cgo, int32_t iByPlayer=-1, DrawMode eDrawMode=ODM_Normal, float offX=0, float offY=0)
Definition: C4Object.cpp:1792
float right
Definition: C4Rect.h:27
void SetFaceOrderingForClrModulation(uint32_t clrmod)
Definition: StdMesh.cpp:1148
std::unique_ptr< C4GraphicsOverlay > creator_overlay
Definition: C4EditCursor.h:62
float left
Definition: C4Rect.h:27
const char * GetName() const
Definition: C4AulFunc.h:57
void ApplyToolLine()
C4ToolsDlg ToolsDlg
Definition: C4Console.h:88
C4Material * Map
Definition: C4Material.h:171
C4Value C4VInt(int32_t i)
Definition: C4Value.h:242
#define C4GFXBLIT_ADDITIVE
Definition: C4Surface.h:28
void FrameSelection()
void EMMoveObject(enum C4ControlEMObjectAction eAction, C4Real tx, C4Real ty, C4Object *pTargetObj, const C4EditCursorSelection *pObjs=nullptr, const char *szScript=nullptr, bool drag_finished=false)
void RegisterRecentInput(const char *input, RecentScriptInputLists section)
Definition: C4Console.cpp:644
int32_t DragRot0
Definition: C4EditCursor.h:58
C4PacketType
Definition: C4PacketBase.h:76
bool selection_invalid
Definition: C4EditCursor.h:54
uint8_t BYTE
void DrawSelectMark(C4Facet &cgo, FLOAT_RECT r, float width, uint32_t color=0xffffffff)
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:181
void ApplyToolRect()
C4TextureMap TextureMap
Definition: C4Texture.cpp:585
C4AulExec AulExec
Definition: C4AulExec.cpp:33
void DrawTopFace(C4TargetFacet &cgo, int32_t iByPlayer=-1, DrawMode eDrawMode=ODM_Normal, float offX=0, float offY=0)
Definition: C4Object.cpp:2052
void ObjselectDelItems()
bool ClearPointers(C4Object *obj)
void DoContextObjsel(C4Object *, bool clear)
C4MouseControl MouseControl
Definition: C4Globals.cpp:47
const C4TexMapEntry * GetEntry(int32_t iIndex) const
Definition: C4Texture.h:85
Definition: C4Texture.h:48
C4AulUserFile * GetUserFile(int32_t handle)
const int C4CNS_ModeEdit
Definition: C4Console.h:31
C4GraphicsResource GraphicsResource
bool KeyDown(C4KeyCode KeyCode, DWORD dwKeyState)
void DrawLineDw(C4Surface *sfcTarget, float x1, float y1, float x2, float y2, DWORD dwClr, float width=1.0f)
Definition: C4Draw.cpp:617
C4Value C4VObj(C4Object *pObj)
Definition: C4Value.cpp:90
void PropertyDlgUpdate(class C4EditCursorSelection &rSelection, bool force_function_update)
Definition: C4Console.cpp:692
C4Object * GetLastObject() const
void SetBlitMode(DWORD dwBlitMode)
Definition: C4Draw.h:191
bool PropertyDlgOpen()
Definition: C4Console.cpp:690
bool IsSelectionInvalidated() const
Definition: C4EditCursor.h:155
virtual C4Object * GetObject()
Definition: C4PropList.cpp:644
void CloseUserFile(int32_t handle)
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
Definition: C4Real.h:58
const C4Value & GetItem(int32_t iElem) const
Definition: C4ValueArray.h:38
const int32_t FullCon
Definition: C4Constants.h:182
void ClearSelection(C4PropList *next_selection=nullptr)
int32_t Rotateable
Definition: C4Def.h:121
const int32_t C4TLS_Rect
Definition: C4ToolsDlg.h:30
int32_t Grade
Definition: C4ToolsDlg.h:54
int32_t Wdt
Definition: C4Rect.h:32
C4Object * GetObject(int32_t index=0) const
bool IsGradedTool() const
Definition: C4ToolsDlg.h:81
void DrawFrameDw(C4Surface *sfcDest, int x1, int y1, int x2, int y2, DWORD dwClr, float width=1.0f)
Definition: C4Draw.cpp:644
void ApplyToolBrush()
char Texture[C4M_MaxName+1]
Definition: C4ToolsDlg.h:57
C4ValueArray * FindMany(const C4ObjectList &Objs)
void EMControl(enum C4PacketType eCtrlType, class C4ControlPacket *pCtrl)
#define IDM_VIEWPORT_DELETE
Definition: resource.h:91
C4NotifyingObjectList Contents
Definition: C4Object.h:152
static int32_t GetEnumerationIndex()
Definition: C4PropList.h:227
int32_t GrowthType
Definition: C4Def.h:113
void DrawCircleDw(C4Surface *sfcTarget, float cx, float cy, float r, DWORD dwClr, float width=1.0f)
Definition: C4Draw.cpp:627
C4Shape Shape
Definition: C4Def.h:106
C4Object * FindObject(C4Def *pDef, int32_t iX=0, int32_t iY=0, int32_t iWdt=0, int32_t iHgt=0, DWORD ocf=OCF_All, C4Object *pFindNext=nullptr)
Definition: C4Game.cpp:1162
#define IDR_CONTEXTMENUS
Definition: resource.h:97
C4Def * Def
Definition: C4Object.h:143
bool DoContextMenu(DWORD dwKeyState)
const char * GetMaterialName() const
Definition: C4Texture.h:60
char Name[C4M_MaxName+1]
Definition: C4Material.h:91
const int32_t C4TLS_Picker
Definition: C4ToolsDlg.h:32
int32_t y
Definition: C4Rect.h:32
bool fAltWasDown
Definition: C4EditCursor.h:51
void OnSelectionChanged(bool by_objectlist=false)
StdStrBuf GetDataString()
Definition: C4Object.cpp:1632
int32_t GetR() const
Definition: C4Object.h:288
T * Get() const
bool SetMenuItemText(HMENU hMenu, WORD id, const char *szText)
C4Value C4VPropList(C4PropList *p)
Definition: C4Value.h:245
bool LeftButtonUp(DWORD dwKeyState)
void DrawX(C4Surface *sfcTarget, float iX, float iY, float iWdt, float iHgt, int32_t iPhaseX=0, int32_t iPhaseY=0) const
Definition: C4Facet.cpp:358
C4GameControl Control
void AppendMenuItem(int num, const StdStrBuf &label)
const char * GetTextureName() const
Definition: C4Texture.h:61
C4Landscape Landscape
bool SetIFT(bool fIFT)
Definition: C4ToolsDlg.cpp:130
virtual const char * GetName() const
Definition: C4PropList.cpp:626
C4PropList * GetPrototype() const
Definition: C4PropList.h:83
BYTE GetBackMapIndex(int32_t iX, int32_t iY) const
const int C4CNS_ModePlay
Definition: C4Console.h:30
void ResetAlternateTool()
Definition: C4ToolsDlg.cpp:289
char Material[C4M_MaxName+1]
Definition: C4ToolsDlg.h:56
void DoInput(C4PacketType eCtrlType, C4ControlPacket *pPkt, C4ControlDeliveryType eDelivery)
void Set(const C4Facet &cpy)
Definition: C4Facet.h:184
void Take(char *pnData)
Definition: StdBuf.h:465
int32_t Owner
Definition: C4Object.h:110
C4ControlEMObjectAction
Definition: C4Control.h:447
void Draw(C4Facet &cgo, bool fAspect=true, int32_t iPhaseX=0, int32_t iPhaseY=0, bool fTransparent=true)
Definition: C4Facet.cpp:154
int32_t GetX() const
Definition: C4Object.h:286
#define PSF_SaveScenarioObjects
Definition: C4GameScript.h:101
const int NO_OWNER
Definition: C4Constants.h:138
const int32_t C4MC_Cursor_DropInto
C4Fixed itofix(int32_t x)
Definition: C4Real.h:261
class C4Def * creator_def
Definition: C4EditCursor.h:61
void SetSize(int32_t inSize)
C4Draw * pDraw
Definition: C4Draw.cpp:45
const int C4CNS_ModeDraw
Definition: C4Console.h:33
virtual void PerformMultiLines(C4Surface *sfcTarget, const C4BltVertex *vertices, unsigned int n_vertices, float width, C4ShaderCall *shader_call)=0
C4ObjectListDlg ObjectListDlg
Definition: C4Console.h:89
void ClearPointers(C4Object *pObj)
int32_t Status
Definition: C4PropList.h:170
int32_t GetMat(int32_t x, int32_t y) const
bool isNull() const
Definition: C4Texture.h:59
bool LeftButtonDown(DWORD dwKeyState)
C4Value C4VArray(C4ValueArray *pArray)
Definition: C4Value.h:249
C4Fixed ftofix(float x)
Definition: C4Real.h:258
int32_t GetSize() const
Definition: C4ValueArray.h:36
int32_t Oversize
Definition: C4Def.h:132
LandscapeMode GetMode() const
C4ValueArray * GetPropertyArray(C4PropertyName n) const
Definition: C4PropList.cpp:766
void CopyUntil(const char *szString, char cUntil)
Definition: StdBuf.h:621
bool KeyUp(C4KeyCode KeyCode, DWORD dwKeyState)
bool IsHoveringTransformMarker() const
C4LSectors Sectors
Definition: C4GameObjects.h:42
std::vector< ObjselItemDt > itemsObjselect
Definition: C4EditCursor.h:71
int32_t x
Definition: C4Rect.h:32
int32_t GetBackMat(int32_t x, int32_t y) const
bool SelectBackTexture(const char *szTexture, bool by_console_gui=false)
Definition: C4ToolsDlg.cpp:269
BYTE GetMapIndex(int32_t iX, int32_t iY) const
void ApplyCreateObject(bool contained)
bool ModeBack
Definition: C4ToolsDlg.h:58
#define PSF_EditCursorDeselection
Definition: C4GameScript.h:97
int ObjectCount(C4ID id=C4ID::None) const
bool Move(float iX, float iY, float zoom, DWORD dwKeyState)
BYTE GetBackPix(int32_t x, int32_t y) const
const C4Value C4VNull
Definition: C4Value.cpp:32
int32_t DragConLast
Definition: C4EditCursor.h:58
#define C4GFXBLIT_CLRSFC_MOD2
Definition: C4Surface.h:31
float TargetX
Definition: C4Facet.h:167
bool RemoveFromSelection(C4PropList *remove_obj)
bool SelectBackMaterial(const char *szMaterial, bool by_console_gui=false)
Definition: C4ToolsDlg.cpp:276
size_t GetFileLength()
Definition: C4Aul.h:102
void SetFaceOrdering(FaceOrdering ordering)
Definition: StdMesh.cpp:1131
bool GetCurrentSelectionPosition(int32_t *x, int32_t *y)
const int32_t C4TLS_Brush
Definition: C4ToolsDlg.h:28
void Draw(C4TargetFacet &cgo)
void MoveSelection(C4Real iXOff, C4Real iYOff, bool drag_finished)
StdBuf GetWideCharBuf(const char *utf8)
uint32_t ColorMod
Definition: C4Object.h:161
C4Object * DropTarget
Definition: C4EditCursor.h:59
float Hgt
Definition: C4Facet.h:120
bool UpdateModeCtrls(int iMode)
Definition: C4Console.cpp:704
const int32_t C4TLS_Fill
Definition: C4ToolsDlg.h:31
void SetHighlightedObject(C4Object *new_highlight)
bool fShiftWasDown
Definition: C4EditCursor.h:52
const int C4CNS_ModeCreateObject
Definition: C4Console.h:32
void DrawObject(C4TargetFacet &cgo, C4Object *cobj, uint32_t select_mark_color, bool highlight, bool draw_transform_marker)
int32_t GetMapZoom() const
#define IDM_VIEWPORT_DUPLICATE
Definition: resource.h:92
C4Object * SafeObjectPointer(int32_t iNumber)
C4MaterialMap MaterialMap
Definition: C4Material.cpp:970
char BackMaterial[C4M_MaxName+1]
Definition: C4ToolsDlg.h:59
bool RightButtonDown(DWORD dwKeyState)
int32_t AddObjectAndContentsToArray(C4ValueArray *target_array, int32_t index=0)
Definition: C4Object.cpp:4305
uint32_t BlitMode
Definition: C4Object.h:162
void ToolsDlgClose()
Definition: C4Console.cpp:671
bool Open()
Definition: C4ToolsDlg.cpp:27
size_t getLength() const
Definition: StdBuf.h:453
static C4ControlEMMoveObject * CreateObject(const C4ID &id, C4Real x, C4Real y, C4Object *container)
Definition: C4Control.cpp:1226
int32_t Mode
Definition: C4EditCursor.h:55
#define C4TLS_MatSky
Definition: C4ToolsDlg.h:39
int32_t HaltCount
Definition: C4Game.h:114
void AddToSelection(C4PropList *add_obj)
int32_t Hgt
Definition: C4Rect.h:32
C4EditCursorSelection selection
Definition: C4EditCursor.h:76
std::enable_if< std::is_pod< T >::value >::type ZeroMem(T *lpMem, size_t dwSize)
Definition: Standard.h:63
void DisplayInfoText(InfoTextType type, StdStrBuf &text)
Definition: C4Console.cpp:684
bool SelectMaterial(const char *szMaterial, bool by_console_gui=false)
Definition: C4ToolsDlg.cpp:262
void SetHold(bool fToState)
Definition: C4EditCursor.h:106
C4Shape Shape
Definition: C4Object.h:148
bool SelectTexture(const char *szTexture, bool by_console_gui=false)
Definition: C4ToolsDlg.cpp:255
C4Surface * Surface
Definition: C4Facet.h:119
float TargetY
Definition: C4Facet.h:167
int32_t DragRotLast
Definition: C4EditCursor.h:58
char BackTexture[C4M_MaxName+1]
Definition: C4ToolsDlg.h:60
C4Value Call(C4PropertyName k, C4AulParSet *pPars=0, bool fPassErrors=false)
Definition: C4PropList.h:112
bool RightButtonUp(DWORD dwKeyState)
const int32_t C4TLS_Line
Definition: C4ToolsDlg.h:29
uint32_t DWORD
int32_t CreateUserFile()
const char * GetCaption()
bool MatValid(int32_t mat)
Definition: C4Material.h:212
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253
void Copy()
Definition: StdBuf.h:475
float Wdt
Definition: C4Facet.h:120
bool EditingOK(bool for_landscape_drawing=false)
C4Object * GetTarget()
void ApplyToolFill()
#define IDM_VPORTDYN_FIRST
Definition: resource.h:94
C4DefGraphics Graphics
Definition: C4Def.h:194
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:45
C4AulFunc * getFunction() const
Definition: C4Value.h:119
StdMeshInstance * pMeshInstance
Definition: C4Object.h:155
C4Value highlighted_object
Definition: C4EditCursor.h:60
float X
Definition: C4Facet.h:120
void Update(class C4EditCursorSelection &rSelection)
unsigned long C4KeyCode
StdStrBuf GetDataString() const
C4Object * getObj() const
Definition: C4Value.cpp:70
C4Application Application
Definition: C4Globals.cpp:44
int32_t GBackMat(int32_t x, int32_t y)
Definition: C4Landscape.h:222
uint16_t WORD
C4Value DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors=false, C4AulScriptContext *context=nullptr, bool parse_function=false)
Definition: C4AulExec.cpp:1018
float top
Definition: C4Rect.h:27
#define PSF_EditCursorSelection
Definition: C4GameScript.h:96
C4Object * CreateObject(C4PropList *type, C4Object *pCreator, int32_t owner=NO_OWNER, int32_t x=50, int32_t y=50, int32_t r=0, bool grow_from_center=false, C4Real xdir=Fix0, C4Real ydir=Fix0, C4Real rdir=Fix0, int32_t iController=NO_OWNER)
Definition: C4Game.cpp:1076
int32_t GetMode()
bool has_mouse_hover
Definition: C4EditCursor.h:53
C4GameObjects Objects
Definition: C4Globals.cpp:48
int32_t Tool
Definition: C4ToolsDlg.h:53
const C4Real Fix0
Definition: C4Real.h:312
bool Message(const char *szMessage, bool fQuery=false)
Definition: C4Console.cpp:297
bool HasTransformMarker(float *x, float *y, float zoom) const
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277
void PropertyDlgClose()
Definition: C4Console.cpp:691
bool GetDrawPosition(const C4TargetFacet &cgo, float &resultx, float &resulty, float &resultzoom) const
Definition: C4Object.cpp:4337