Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_PlacingEditorComponent.c
Go to the documentation of this file.
1[ComponentEditorProps(category: "GameScripted/Editor", description: "Content browser component. Works only with SCR_EditorBaseEntity!", icon: "WBData/ComponentEditorProps/componentEditor.png")]
3{
4 [Attribute(category: "Placing", uiwidget: UIWidgets.Flags, desc: "System type of the entity.", enums: ParamEnumArray.FromEnum(EEditorPlacingFlags))]
6
7 [Attribute(category: "Placing", uiwidget: UIWidgets.ResourcePickerThumbnail, desc: "Editable entity used for testing. Should be a sphere 1 m in diameter.")]
9
10 [Attribute(category: "Placing")]
11 protected ref array<ref SCR_PlaceableEntitiesRegistry> m_Registries;
12
13 [Attribute(category: "Effects")]
14 protected ref array<ref SCR_BaseEditorEffect> m_EffectsPlaceStart;
15
16 [Attribute(category: "Effects")]
17 protected ref array<ref SCR_BaseEditorEffect> m_EffectsPlaceConfirm;
18
19 [Attribute(category: "Effects")]
20 protected ref array<ref SCR_BaseEditorEffect> m_EffectsPlaceCancel;
21
22 protected int m_iPrefabCount;
23 protected ref array<int> m_aIndexes = {};
24
25 array<ref SCR_BaseEditorEffect> GetEffectsPlaceStart()
26 {
28 }
29 array<ref SCR_BaseEditorEffect> GetEffectsPlaceConfirm()
30 {
32 }
33 array<ref SCR_BaseEditorEffect> GetEffectsPlaceCancel()
34 {
36 }
37
38 /*
39 Get registered prefab with given index.
40 \param index Prefab index from the registry
41 \return Prefab path (empty when the index is invalid)
42 */
44 {
45 for (int i = m_aIndexes.Count() - 1; i >= 0; i--)
46 {
47 int registryIndex = m_aIndexes[i];
48 if (index >= registryIndex)
49 return m_Registries[i].GetPrefabs()[index - registryIndex];
50 }
51 return ResourceName.Empty;
52 }
53 /*
54 Get index of registered prefab
55 \param prefab Prefab path
56 \return Prefab index (-1 when the prefab is not found)
57 */
59 {
60 foreach (int i, SCR_PlaceableEntitiesRegistry registry: m_Registries)
61 {
62 int index = registry.GetPrefabs().Find(prefab);
63 if (index != -1)
64 return index + m_aIndexes[i];
65 }
66 return -1;
67 }
68 /*
69 Get number of prefabs in the registry.
70 \return Number of prefabs
71 */
73 {
74 return m_iPrefabCount;
75 }
76 /*
77 Get registered prefabs.
78 \param[out] outPrefabs Array to be filled with prefabs
79 \return Number of prefabs
80 */
81 int GetPrefabs(out notnull array<ResourceName> outPrefabs, bool onlyExposed = false)
82 {
84 {
85 outPrefabs.InsertAll(registry.GetPrefabs(onlyExposed));
86 }
87 return outPrefabs.Count();
88 }
89 /*
90 Check if given placing flag is allowed to be changed.
91 \return True when the flag can be changed
92 */
94 {
95 return SCR_Enum.HasFlag(m_AllowedPlacingFlags, flag);
96 }
97 /*
98 Get prefab used for testing.
99 \return Prefab
100 */
102 {
103 return m_TestPrefab;
104 }
105
107 {
109 {
111 m_iPrefabCount += registry.GetPrefabs().Count();
112 }
113 }
114};
115
119{
120 [Attribute(defvalue: "10", category: "Placing", desc: "Spacing between entities placed for multiple recipients")]
121 protected float m_fSpacing;
122
123 private ResourceName m_SelectedPrefab;
124 private SCR_StatesEditorComponent m_StatesManager;
128 protected int m_iActionInfo;
129
130 private SCR_SiteSlotEntity m_Slot;
131 private int m_iEntityIndex;
132 private ref map<int, IEntity> m_WaitingPreviews = new map<int, IEntity>;
133 private vector m_vFixedPosition;
134 private float m_fPreviewAnimationProgress = -1;
135 private EEditorPlacingFlags m_PlacingFlags;
136 private EEditorPlacingFlags m_CompatiblePlacingFlags;
137 private ref set<SCR_EditableEntityComponent> m_Recipients;
139
140 protected bool m_bBlockPlacing;
141
142 private ref ScriptInvoker Event_OnSelectedPrefabChange = new ScriptInvoker;
143 private ref ScriptInvoker Event_OnPlacingPlayerChange = new ScriptInvoker;
144 private ref ScriptInvoker Event_OnPlacingFlagsChange = new ScriptInvoker;
145
146 private ref ScriptInvoker Event_OnRequestEntity = new ScriptInvoker;
147 private ref ScriptInvoker Event_OnPlaceEntity = new ScriptInvoker;
148
149 private ref ScriptInvoker Event_OnPlaceEntityServer = new ScriptInvoker;
150
153
155
156 //Basically every time someone has GM rights a new entity with its own SCR_PlacingEditorComponent is used.
157 //Each GM client has ownership of that entity and will use it to send RPC's to server
158 //However when server handles the spawning of the entity instead of using the instance owned by the server
159 //(in which variables in other components like the budget one have been initialized)
160 //they use the entity owned by the client that sent the RPC
161 //This is one of the many, many, many issues that GM has with replication but there is no time for a rewrite,
162 //so instead we have this single static variable. Welp.
165
167 protected const float MAX_HEIGHT_ABOVE_TERRAIN = 2;
168
169 bool IsThereEnoughBudgetToSpawnVehicleOccupants(array<ResourceName> resources)
170 {
171 foreach (ResourceName prefab : resources)
172 {
173 Resource prefabResource = Resource.Load(prefab);
174
175 if(!prefabResource)
176 return false;
177
179
180 if(!IsThereEnoughBudgetToSpawn(editableEntitySource))
181 return false;
182 }
183
184 return true;
185 }
186
187 void SetPlacingBlocked(bool blocked)
188 {
189 m_bBlockPlacing = blocked;
190 }
191
193 {
194 return m_bBlockPlacing;
195 }
196
197 /*
198 Get registered prefab with given index.
199 \param index Prefab index from the registry
200 \return Prefab path (empty when the index is invalid)
201 */
203 {
204 if (prefab.IsEmpty())
205 return -1;
206
208 if (!prefabData)
209 return -1;
210
211 return prefabData.GetPrefabID(prefab);
212 }
213
214 //------------------------------------------------------------------------------------------------
221 bool CreateEntity(bool unselectPrefab = true, bool canBePlayer = false, SCR_EditableEntityComponent holder = null, SCR_BaseEditorAction sourceAction = null)
222 {
224 if (!prefabData || !m_SelectedPrefab)
225 return false;
226
227 //--- Create packet
230 {
232 }
233 else
234 {
237 if (layersManager)
238 parent = layersManager.GetCurrentLayer();
239
241 }
242
243 if (!params)
244 return false;
245
246 //--- Save placing flags
247 params.m_PlacingFlags = m_PlacingFlags;
248
249 if (sourceAction)
250 {
251 params.m_iActionInfo = params.PackActionInfo(sourceAction, m_Owner);
252 if (params.m_iActionInfo < 0)
253 return false;
254
255 SetPlacingFlag(EEditorPlacingFlags.PLACING_ACTION, true);
256 }
257 else if (m_PlacingFlags & EEditorPlacingFlags.PLACING_ACTION)
258 {
259 params.m_iActionInfo = m_iActionInfo;
260 }
261
262 int prefabID = prefabData.GetPrefabID(m_SelectedPrefab);
263 if (prefabID == -1)
264 {
265 Print(string.Format("Cannot place prefab @\"%1\", it's not registered in placeable entities!", m_SelectedPrefab.GetPath()), LogLevel.WARNING);
266 SetSelectedPrefab(ResourceName.Empty, true);
267 return false;
268 }
269
270 //--- Check if placing is allowed. If not, prevent the operation and send a notification to player.
271 ENotification notification = -1;
272 if (!CanCreateEntity(notification, params: params, prefabID: prefabID))
273 {
274 if (notification != -1)
275 SendNotification(notification);
276
277 return false;
278 }
279
280 if (!m_InstantPlacingParam && !m_PreviewManager.IsChange())
281 {
282 SendNotification(ENotification.EDITOR_TRANSFORMING_INCORRECT_POSITION);
283 //SetSelectedPrefab(ResourceName.Empty, true);
284 return false;
285 }
286
287 if (!canBePlayer)
288 canBePlayer = HasPlacingFlag(EEditorPlacingFlags.CHARACTER_PLAYER);
289
290 if (canBePlayer)
291 {
292 Resource prefabResource = Resource.Load(m_SelectedPrefab);
293 IEntityComponentSource component = SCR_BaseContainerTools.FindComponentSource(prefabResource, SCR_EditableEntityComponent);
294 if (component && SCR_EditableEntityComponentClass.GetEntityType(component) != EEditableEntityType.CHARACTER)
295 {
296 SendNotification(ENotification.EDITOR_PLACING_CANNOT_AS_PLAYER);
297 SetSelectedPrefab(ResourceName.Empty, true);
298 return false;
299 }
300 }
301
302 //~ If placing with crew or passengers. Send notification if not enough budget
303 if (SCR_Enum.HasFlag(m_PlacingFlags, EEditorPlacingFlags.VEHICLE_CREWED) ||SCR_Enum.HasFlag(m_PlacingFlags, EEditorPlacingFlags.VEHICLE_PASSENGER))
304 {
305 array<ref SCR_EntityBudgetValue> budgetCosts = {};
306 Resource resource = Resource.Load(m_SelectedPrefab);
307 if (!resource.IsValid())
308 {
309 Print("Cannot load prefab " + m_SelectedPrefab + " | " + FilePath.StripPath(__FILE__) + ":" + __LINE__, LogLevel.WARNING);
310 return false;
311 }
312
313 IEntityComponentSource source = SCR_EditableEntityComponentClass.GetEditableEntitySource(resource.GetResource().ToEntitySource());
314 m_BudgetManager.GetVehicleOccupiedBudgetCosts(source, m_PlacingFlags, budgetCosts, false);
315 EEditableEntityBudget blockingBudget;
316
317 if (!m_BudgetManager.CanPlace(budgetCosts, blockingBudget))
318 SCR_NotificationsComponent.SendLocal(ENotification.EDITOR_PLACING_BUDGET_MAX_FOR_VEHICLE_OCCUPANTS);
319 }
320
321 //--- Copy the preview, it will remain visible until callback from server is received
322 int simulatedDelay = DiagMenu.GetValue(SCR_DebugMenuID.DEBUGUI_EDITOR_NETWORK_DELAY) * 100;
323 m_iEntityIndex++;
324 if (IsProxy() || simulatedDelay > 0)
325 m_WaitingPreviews.Insert(m_iEntityIndex, m_PreviewManager.CreateWaitingPreview());
326
327 array<RplId> recipientIds;
328 if (m_Recipients)
329 {
330 recipientIds = {};
331 foreach (SCR_EditableEntityComponent entity: m_Recipients)
332 {
333 RplId id = Replication.FindItemId(entity);
334 if (id.IsValid())
335 recipientIds.Insert(id);
336 }
337 }
338
339 RplId holderId = RplId.Invalid();
340 if (holder)
341 {
342 holderId = Replication.FindItemId(holder);
343 }
344
345 //--- Send request to server
346 m_StatesManager.SetIsWaiting(true);
347 Event_OnRequestEntity.Invoke(prefabID, params.m_vTransform, null);
348
349 //--- If the entity can be spawned as player, get player ID
350 int playerID = GetManager().GetPlayerID();
351
352 if (simulatedDelay > 0 && !Replication.IsRunning())
353 GetGame().GetCallqueue().CallLater(CreateEntityServer, simulatedDelay, false, params, prefabID, playerID, m_iEntityIndex, !unselectPrefab, recipientIds, canBePlayer, holderId);
354 else
355 Rpc(CreateEntityServer, params, prefabID, playerID, m_iEntityIndex, !unselectPrefab, recipientIds, canBePlayer, holderId);
356
357 //--- ToDo: Fail the request if server didn't respond in given time
358 //GetGame().GetCallqueue().CallLater(CreateEntityOwner, 10000, false, prefabID, -1);
359
360 m_Slot = null;
362
363 //--- Stop placing (only after packet was created)
364 if (unselectPrefab || m_InstantPlacingParam)
365 SetSelectedPrefab(ResourceName.Empty, true);
366
367 return true;
368 }
369
370 //------------------------------------------------------------------------------------------------
380 bool CreateEntity(ResourceName prefab, SCR_EditorPreviewParams param, bool unselectPrefab = true, bool canBePlayer = false, set<SCR_EditableEntityComponent> recipients = null, SCR_EditableEntityComponent holder = null, SCR_BaseEditorAction sourceAction = null)
381 {
382 if (m_bBlockPlacing)
383 return false;
384
385 m_SelectedPrefab = prefab;
386 m_Recipients = recipients;
387 SetInstantPlacing(param);
388 return CreateEntity(unselectPrefab, canBePlayer, holder, sourceAction);
389 }
390
391 [RplRpc(RplChannel.Reliable, RplRcver.Server)]
392 protected void CreateEntityServer(SCR_EditorPreviewParams params, RplId prefabID, int playerID, int entityIndex, bool isQueue, array<RplId> recipientIds, bool canBePlayer, RplId holderId)
393 {
394 array<RplId> entityIds = {};
395 if (m_bBlockPlacing)
396 {
397 Rpc(CreateEntityOwner, prefabID, entityIds, entityIndex, false, false, RplId.Invalid(), 0);
398 return;
399 }
400
401 PlayerManager playerMgr = GetGame().GetPlayerManager();
402 if (!m_Manager || m_Manager.GetPlayerID() != playerID)
403 {
404 int ownerId = -1;
405 if (m_Manager)
406 ownerId = m_Manager.GetPlayerID();
407
408 PrintFormat("WARNING: Editor %1: Editor that belongs to player %2 was trying to spawn prefab while providing different player id (%3)!", GetEditorModeLogInfo(), SCR_PlayerIdentityUtils.GetPlayerLogInfo(playerID), playerMgr.GetPlayerName(playerID) + " (playerID = " + playerID + ")", level: LogLevel.WARNING);
409 return; // that should not happen as it would mean that either someone made a mistake in the code or someone was tinkering with this RPC call
410 }
411
413 if (RplSession.Mode() == RplMode.Client || !prefabData)
414 return;
415
416 if (!params.Deserialize() || !CanCreateEntity(params: params))
417 {
418 Rpc(CreateEntityOwner, prefabID, entityIds, entityIndex, false, false, RplId.Invalid(), 0);
419 return;
420 }
421
422 //--- Get prefab
423 ResourceName prefab = prefabData.GetPrefab(prefabID);
424 if (prefab.IsEmpty())
425 {
426 PrintFormat("INFO: Editor %1: Cannot create entity, prefab not defined!", GetEditorModeLogInfo(), level: LogLevel.ERROR);
427 Rpc(CreateEntityOwner, prefabID, entityIds, entityIndex, false, false, RplId.Invalid(), 0);
428 return;
429 }
430
431 //~ Get variant if any
433
434 Resource prefabResource = Resource.Load(prefab);
435 if (!prefabResource.IsValid())
436 {
437 PrintFormat("INFO: Editor %1: Cannot create entity, error when loading prefab '%2'! Editor owned by player with ID = %3", GetEditorModeLogInfo(), prefab, playerID, level: LogLevel.ERROR);
438 Rpc(CreateEntityOwner, prefabID, entityIds, entityIndex, false, false, RplId.Invalid(), 0);
439 return;
440 }
441
443 if (!editableEntitySource)
444 {
445 PrintFormat("INFO: Editor %1: Cannot create entity, prefab '%2' does not contain SCR_EditableEntityComponent! Editor owned by player with ID = %3", GetEditorModeLogInfo(), prefab, playerID, level: LogLevel.ERROR);
446 Rpc(CreateEntityOwner, prefabID, entityIds, entityIndex, false, false, RplId.Invalid(), 0);
447 return;
448 }
449
450 EEditableEntityBudget blockingBudget;
451 if (!CanPlaceEntityServer(editableEntitySource, blockingBudget, false, false, prefabID, playerID, params))
452 {
453 if (blockingBudget > -1)
454 PrintFormat("INFO: Editor %1: Entity budget exceeded for player! Editor owned by player with ID = %2", GetEditorModeLogInfo(), playerID, level: LogLevel.ERROR);
455
456 Rpc(CreateEntityOwner, prefabID, entityIds, entityIndex, false, false, RplId.Invalid(), 0);
457 return;
458 }
459
460 //always allow player spawning
461 if(!canBePlayer)
462 {
463 //server one should check if possible or not. Check variable description if you are confused
464 bool canSpawn = IsThereEnoughBudgetToSpawn(editableEntitySource);
465
466 if(!canSpawn)
467 {
468 Rpc(CreateEntityOwner, prefabID, entityIds, entityIndex, false, false, RplId.Invalid(), 0);
469 return;
470 }
471 }
472
474
476 bool hasRecipients = false;
477 RplId currentLayerID;
478 array<SCR_EditableEntityComponent> entities = {};
479 if (recipientIds && !recipientIds.IsEmpty())
480 {
481 //--- Spawn entity for selected entities (e.g., waypoints for groups)
482 array<SCR_EditableEntityComponent> recipients = {};
483 foreach (RplId id: recipientIds)
484 {
486 if (recipient)
487 recipients.Insert(recipient);
488 }
489 array<vector> offsets = GetOffsets(recipients.Count());
490 array<ref array<int>> distances = {};
491
492 for (int i = 0; i < recipients.Count(); i++)
493 {
494 array<int> distToPosition = {};
496 recipients[i].GetPos(position);
497 for (int j = 0; j < offsets.Count(); j++)
498 {
499 //precision to meters and 46km as max distance should be enough
500 distToPosition.Insert(Math.Clamp(vector.DistanceSq(position, offsets[j]), 0, int.MAX));
501 }
502 distances.Insert(distToPosition);
503 }
504
505 // we try to minimize distances from groups to waypoints
506 // in general it produces paths with less scrossings between them
507 array<int> permutation = {};
508 AssignmentSolver.Solve(distances, permutation);
509
510 foreach (int i, SCR_EditableEntityComponent recipient: recipients)
511 {
512 params.m_Offset = offsets[permutation[i]];
513 entity = SpawnEntityResource(params, prefabResource, playerID, isQueue, recipient, canBePlayer);
514
515 entity.SetAuthor(playerID);
516
517 entities.Insert(entity);
518 entityIds.Insert(Replication.FindItemId(entity));
519 }
520 hasRecipients = true;
521 currentLayerID = Replication.FindItemId(recipients[0].GetParentEntity());
522 }
523 else
524 {
525 //--- Spawn stand-alone entity
526 entity = SpawnEntityResource(params, prefabResource, playerID, isQueue, null, canBePlayer);
527
528 entity.SetAuthor(playerID);
529
530 entities.Insert(entity);
531 entityIds.Insert(Replication.FindItemId(entity));
532 currentLayerID = Replication.FindItemId(params.m_CurrentLayer);
533 }
534
535 PrintFormat("INFO: Editor %1: Player %2 spawned %3 prefab at %4", GetEditorModeLogInfo(), SCR_PlayerIdentityUtils.GetPlayerLogInfo(playerID), FilePath.StripPath(prefab), entity.GetOwner().GetOrigin(), level: LogLevel.NORMAL);
536
537 //++ Attach if there is an attacher
538 if (holderId)
539 {
541 if (holder)
542 {
543 foreach(SCR_EditableEntityComponent ent : entities)
544 {
545 ent.SetParentEntity(holder);
546 }
547 }
548 }
549
550 entity.OnCreatedServer(this);
551 OnEntityCreatedServer(entities);
552
553 Rpc(CreateEntityOwner, prefabID, entityIds, entityIndex, isQueue, hasRecipients, currentLayerID, 0);
554 Event_OnPlaceEntityServer.Invoke(prefabID, entity, playerID);
555 }
556
557 //------------------------------------------------------------------------------------------------
560 protected string GetEditorModeLogInfo()
561 {
563 if (!modeEnt)
564 return string.Empty;
565
566 return typename.EnumToString(EEditorMode, modeEnt.GetModeType());
567 }
568
570 {
571 if(!entitySource)
572 return false;
573
574 if (!m_BudgetManager)
575 {
577 if (!m_BudgetManager)
578 return true;
579 }
580
581 array<ref SCR_EntityBudgetValue> budgetCosts = {};
583 SCR_EditableEntityCoreBudgetSetting budgetSettings;
584
585 m_BudgetManager.GetEntitySourcePreviewBudgetCosts(entitySource, budgetCosts);
586
587 bool budgetFound = false;
588 foreach(SCR_EntityBudgetValue budget : budgetCosts)
589 {
590 const EEditableEntityBudget budgetType = budget.GetBudgetType();
591 int accumulatedBudgetCost = 0;
592 int maxBudgetForType = 0;
593
594 bool found = tempBudgetAggregation.Find(budgetType, accumulatedBudgetCost);
595 m_BudgetManager.GetMaxBudgetValue(budgetType, maxBudgetForType);
596
597 bool hasBudget = m_editableEntityCore.GetBudget(budgetType, budgetSettings);
598 int currentBudgetValue = 0;
599
600 if(hasBudget)
601 currentBudgetValue = budgetSettings.GetCurrentBudget();
602
603 //We never take reserved budget into account, is this intentional?
604 //const int reservedBudget = budgetSettings.GetReservedBudget();
605
606 //if same budget, we aggregate them
607 const int newAggregatedBudget = budget.GetBudgetValue() + accumulatedBudgetCost;
608 const int estimatedBudget = newAggregatedBudget + currentBudgetValue;
609
610 //check if budget goes over max
611 if(estimatedBudget > maxBudgetForType)
612 {
613 EEditableEntityBudget blockingBudget;
614 bool canPlace = m_BudgetManager.CanPlaceEntitySource(entitySource, blockingBudget, false, false);
615
616 return false;
617 }
618 //if all budgets are valid, we will update accumulated budgets and return true
619 tempBudgetAggregation[budgetType] = newAggregatedBudget;
620 }
621
622 m_accumulatedBudgetChanges = tempBudgetAggregation;
623
625 {
626 GetGame().GetCallqueue().CallLater(ClearAccumulatedBudgetChanges);
628 }
629
630 return true;
631 }
632
633 protected static void ClearAccumulatedBudgetChanges()
634 {
637 }
638
639 [RplRpc(RplChannel.Reliable, RplRcver.Owner)]
640 protected void CreateEntityOwner(int prefabID, array<RplId> entityIds, int entityIndex, int isQueue, bool hasRecipients, RplId currentLayerID, int attempt)
641 {
642 //~ Remove placing flag player
643 if (HasPlacingFlag(EEditorPlacingFlags.CHARACTER_PLAYER))
644 SetPlacingFlag(EEditorPlacingFlags.CHARACTER_PLAYER, false);
645
646 //~ Todo: Make sure crew and Passenger que placing and placing again budget is updated correctly.
647 //~ Now if placing flag is active on start placing the budget is not correct. Same goes for queing
648 if (HasPlacingFlag(EEditorPlacingFlags.VEHICLE_CREWED))
649 SetPlacingFlag(EEditorPlacingFlags.VEHICLE_CREWED, false);
650 if (HasPlacingFlag(EEditorPlacingFlags.VEHICLE_PASSENGER))
651 SetPlacingFlag(EEditorPlacingFlags.VEHICLE_PASSENGER, false);
652
653 //--- Delete the ghost preview which was waiting for server callback
654 IEntity waitingPreview;
655 if (m_WaitingPreviews.Find(entityIndex, waitingPreview))
656 {
657 m_WaitingPreviews.Remove(entityIndex);
658 delete waitingPreview;
659 }
660
661 if (m_StatesManager && !m_StatesManager.SetIsWaiting(false))
662 {
664 return;
665 }
666
667 set<SCR_EditableEntityComponent> entities = new set<SCR_EditableEntityComponent>();
669 for (int i, count = entityIds.Count(); i < count; i++)
670 {
671 entity = SCR_EditableEntityComponent.Cast(Replication.FindItem(entityIds[i]));
672 if (entity)
673 entities.Insert(entity);
674 }
675
676 //--- Wait for entities to be streamed in
677 if (entities.IsEmpty())
678 {
679 if (attempt < 30)
680 {
681 GetGame().GetCallqueue().CallLater(CreateEntityOwner, 1, false, prefabID, entityIds, entityIndex, isQueue, hasRecipients, currentLayerID, attempt + 1);
682 }
683 else
684 {
686 Print(string.Format("Error when creating entity from prefab '%1' (id = %2)!", prefabData.GetPrefab(prefabID), prefabID), LogLevel.ERROR);
687 }
688
689 return;
690 }
691
692 //--- Select placed entities
694 if (selectedFilter)
695 selectedFilter.Replace(entities);
696
697 //--- Set current layer to be the parent of newly created entity, to make sure its icon is visible
698 SCR_EditableEntityComponent currentLayer = SCR_EditableEntityComponent.Cast(Replication.FindItem(currentLayerID));
699 if (!currentLayerID.IsValid() || currentLayer)
700 {
702 if (layerManager)
703 layerManager.SetCurrentLayer(currentLayer);
704 }
705
706 //--- Delayed check if entity is within budget (character budget update is delayed)
707 if (isQueue)
708 {
709 GetGame().GetCallqueue().CallLater(CheckBudgetOwner, 100, false);
710 }
711 else
712 {
713 m_BudgetManager.ResetPreviewCost();
714 }
715
716 //--- Call event
717 Event_OnPlaceEntity.Invoke(prefabID, entity);
718
719 //--- Activate effects
720 vector pos;
721 entities[0].GetPos(pos);
722
724 if (prefabData) SCR_BaseEditorEffect.Activate(prefabData.GetEffectsPlaceConfirm(), this, pos, entities);
725 }
726
727 //------------------------------------------------------------------------------------------------
736 bool CanCreateEntity(out ENotification outNotification = -1, inout SCR_EPreviewState previewStateToShow = SCR_EPreviewState.PLACEABLE, SCR_EditorPreviewParams params = null, int prefabID = -1)
737 {
738 if (m_bBlockPlacing)
739 return false;
740
741 if (!params)
742 return true;
743
744 vector mat[4];
745 params.GetWorldTransform(mat);
747 {
748 previewStateToShow = SCR_EPreviewState.BLOCKED;
749 outNotification = ENotification.EDITOR_TRANSFORMING_INCORRECT_POSITION;
750 return false; // dont allow for spawning of objects outside of the terrain
751 }
752
753 if (m_PreviewManager && !m_PreviewManager.GetCanAdjustVerticalPosition())
754 {
755 float x = mat[3][0];
756 float y = mat[3][1];
757 float z = mat[3][2];
758 float surfaceY = GetGame().GetWorld().GetSurfaceY(x, z);
759
760 switch (m_PreviewManager.GetVerticalMode())
761 {
762 case EEditorTransformVertical.TERRAIN:
763 if (!float.AlmostEqual(surfaceY, y, MAX_HEIGHT_ABOVE_TERRAIN))
764 {
765 previewStateToShow = SCR_EPreviewState.BLOCKED;
766 outNotification = ENotification.EDITOR_TRANSFORMING_INCORRECT_POSITION;
767 return false; // dont allow for placing mid air
768 }
769
770 break;
771
772 case EEditorTransformVertical.GEOMETRY:
773 TraceParam param = new TraceParam();
774 param.Start = mat[3] + vector.Up * 0.5; // to avoid missing the entity on which it is placed when trace would start inside the collider
775 param.End = param.Start - vector.Up * MAX_HEIGHT_ABOVE_TERRAIN;
776 param.Flags = TraceFlags.ENTS | TraceFlags.WORLD | TraceFlags.OCEAN;
777 param.Exclude = m_PreviewManager.GetPreviewEntity();
778 float distPercentage = GetGame().GetWorld().TraceMove(param);
779 if (distPercentage == 1 && param.TraceEnt == null)
780 {
781 previewStateToShow = SCR_EPreviewState.BLOCKED;
782 outNotification = ENotification.EDITOR_TRANSFORMING_INCORRECT_POSITION;
783 return false; // dont allow for placing mid air
784 }
785
786 break;
787 }
788 }
789
790 // only client has proper data setup for validating if this prefab is available, thus server uses default attribute value for prefabID
791 if (prefabID < 0)
792 return true;
793
794 if (params.m_PlacingFlags & EEditorPlacingFlags.PLACING_ACTION)
795 return IsActionValid(prefabID, params);
796
798 if (!editorBrowser)
799 return false;
800
801 return editorBrowser.IsPrefabIDAvailable(prefabID);
802 }
803
804 //------------------------------------------------------------------------------------------------
809 bool IsActionValid(int prefabID, notnull SCR_EditorPreviewParams params)
810 {
812 if (!editorBrowser)
813 return false;
814
815 SCR_BaseCommandAction action = SCR_BaseCommandAction.Cast(params.GetSourceAction(m_Owner));
816 if (!action)
817 return false;
818
819 return action.GetCommandPrefab() == editorBrowser.GetResourceNamePrefabID(prefabID);
820 }
821
833 protected void OnEntityCreatedServer(array<SCR_EditableEntityComponent> entities);
834
835 protected void CheckBudgetOwner()
836 {
837 EEditableEntityBudget blockingBudget;
838 if (!CanSelectEntityPrefab(m_SelectedPrefab, blockingBudget, true))
839 {
840 m_BudgetManager.ResetPreviewCost();
841 SetSelectedPrefab(ResourceName.Empty, true);
842 }
843 }
844
845 protected void OnBudgetMaxReached(EEditableEntityBudget entityBudget, bool maxReached)
846 {
847 if (maxReached)
848 SetSelectedPrefab(ResourceName.Empty, true);
849 }
850
858 {
859 return SpawnEntityResource(SCR_EditorPreviewParams.CreateParams(transform), Resource.Load(prefab));
860 }
861
870 static SCR_EditableEntityComponent SpawnEntityResource(SCR_EditorPreviewParams params, Resource prefabResource, int playerID = 0, bool isQueue = false, SCR_EditableEntityComponent recipient = null, bool canBePlayer = false)
871 {
872 if (Replication.IsClient() || !prefabResource|| !prefabResource.IsValid())
873 return null;
874
875 //--- When spawning a player, get their respawn component first
876 EEditorPlacingFlags compatiblePlacingFlags = GetCompatiblePlacingFlags(prefabResource);
877 SCR_RespawnComponent respawnComponent;
878 if (canBePlayer)
879 {
880 respawnComponent = SCR_RespawnComponent.Cast(GetGame().GetPlayerManager().GetPlayerRespawnComponent(playerID));
881 if (!respawnComponent)
882 return null;
883 }
884
885 IEntity owner;
886 if (params.m_TargetInteraction == EEditableEntityInteraction.SLOT)
887 {
888 //--- Create in slot
889 GenericEntity targetOwner = params.m_Target.GetOwner();
890 SCR_SiteSlotEntity slot = SCR_SiteSlotEntity.Cast(targetOwner);
891 if (slot)
892 {
893 //--- Use special slot function
894 owner = slot.SpawnEntityInSlot(prefabResource, Math3D.MatrixToAngles(params.m_vTransform)[0], -1/*verticalMode*/);
895 }
896 else
897 {
898 //--- Position on the target
899 EntitySpawnParams spawnParams = new EntitySpawnParams();
900 spawnParams.TransformMode = ETransformMode.WORLD;
901 targetOwner.GetWorldTransform(spawnParams.Transform);
902 owner = GetGame().SpawnEntityPrefab(prefabResource, GetGame().GetWorld(), spawnParams);
903 }
904 }
905 else
906 {
907 //--- Place freely
908 //SCR_AIGroup.IgnoreSnapToTerrain(true);
909 EntitySpawnParams spawnParams = new EntitySpawnParams();
910 spawnParams.TransformMode = ETransformMode.WORLD;
911 params.GetWorldTransform(spawnParams.Transform);
912 owner = GetGame().SpawnEntityPrefab(prefabResource, GetGame().GetWorld(), spawnParams);
913
914 if (respawnComponent && SCR_Enum.HasFlag(compatiblePlacingFlags, EEditorPlacingFlags.CHARACTER_PLAYER))
915 {
917 spawnData.SetSkipPreload(true);
918 if (respawnComponent.RequestSpawn(spawnData))
919 {
920 const Faction playerFaction = SCR_FactionManager.SGetPlayerFaction(playerID);
921 const Faction entityFaction = SCR_FactionManager.SGetFaction(owner);
922 if (!playerFaction || playerFaction != entityFaction)
923 {
924 // Assign player faction not set yet or has changed in GM
925 UpdatePlayerFactionFromEditableEntity(entityFaction,playerID)
926 }
927 else
928 {
929 // Rejoin group on secondary character entity spawn after deletion
930 SCR_PlayerControllerGroupComponent playerContGroupComp = SCR_PlayerControllerGroupComponent.GetPlayerControllerComponent(playerID);
931 playerContGroupComp.CreateAndJoinGroup(playerFaction);
932 }
933 }
934 else
935 {
936 Print(string.Format("@\"%1\" control cannot be given to playerID=%2, error in RequestSpawn!", prefabResource.GetResource().GetResourceName().GetPath(), playerID), LogLevel.ERROR);
937 }
938 }
939 }
940
941 //--- Initialize
943 if (!entity)
944 return null;
945
946 SCR_EditableCommentComponent comment = SCR_EditableCommentComponent.Cast(params.m_Target);
947 SCR_CompositionSlotManagerComponent slotManager = SCR_CompositionSlotManagerComponent.GetInstance();
948 if (comment && slotManager)
949 slotManager.SetOccupant(comment.GetOwnerScripted(), owner);
950
951 //--- Assign faction when placed for a recipient
952 if (recipient)
953 SCR_FactionAffiliationComponent.SetFaction(entity.GetOwner(), recipient.GetFaction());
954
955 //--- Call custom event
956 SCR_EditableEntityComponent childEntity = entity.EOnEditorPlace(params.m_Parent, recipient, params.m_PlacingFlags, isQueue, playerID);
957
958 //--- Set parent to current layer
959 if (params.m_Parent)
960 {
961 //~ Force entity as passenger
962 if (params.m_TargetInteraction == EEditableEntityInteraction.PASSENGER)
963 {
964 //~ Forces entities to be spawned into passenger positions
965 SCR_EditableVehicleComponent vehicle = SCR_EditableVehicleComponent.Cast(params.m_Parent);
966 if (vehicle)
967 entity.ForceVehicleCompartments(SCR_BaseCompartmentManagerComponent.PASSENGER_COMPARTMENT_TYPES);
968 }
969 //~ Not forced passenger set parent
970 else
971 {
972 childEntity.SetParentEntity(params.m_Parent);
973 }
974 }
975
976 //--- New parent was created in EOnEditorPlace, reflect that
977 if (childEntity != entity)
978 params.m_Parent = childEntity;
979
980 //--- Decide which layer should be set as current
981 params.m_CurrentLayer = childEntity.GetParentEntity();
982
983 //--- Original entity deleted in SetParentEntity, use parent as the entity (e.g., when placing a group inside a group)
984 if (!owner)
985 entity = params.m_Parent;
986
987 //--- Orient according to vertical settings
988 if (!entity.HasEntityFlag(EEditableEntityFlag.STATIC_POSITION))
989 {
991
992 if (groupComp)
993 {
994 //GetGame().GetCallqueue().CallLater(SCR_RefPreviewEntity.SpawnAndApplyReference, 500, false, entity, params);
995
996 SCR_AIGroup group = SCR_AIGroup.Cast(groupComp.GetOwnerScripted());
997 if (group)
998 {
999 m_DelayedSpawnEntity = entity;
1002 }
1003 }
1004 else
1005 {
1006 SCR_RefPreviewEntity.SpawnAndApplyReference(entity, params);
1007 }
1008 }
1009
1010 //--- Log message. Important for identifying problematic prefabs!
1011 vector logTransform[4];
1012 if (owner)
1013 owner.GetWorldTransform(logTransform);
1014 else
1015 params.GetWorldTransform(logTransform);
1016
1017 if (recipient)
1018 Print(string.Format("@\"%1\" placed for %3 at %2", prefabResource.GetResource().GetResourceName().GetPath(), logTransform, recipient.GetDisplayName()), LogLevel.VERBOSE);
1019 else if (prefabResource)
1020 Print(string.Format("@\"%1\" placed at %2", prefabResource.GetResource().GetResourceName().GetPath(), logTransform), LogLevel.VERBOSE);
1021 else
1022 Print(string.Format("@\"%1\" placed at %2", "Entity", logTransform), LogLevel.VERBOSE);
1023
1024 return entity;
1025 }
1026 //------------------------------------------------------------------------------------------------
1028 protected static void UpdatePlayerFactionFromEditableEntity(Faction editableEntityFaction, int playerID)
1029 {
1030 if (!editableEntityFaction)
1031 return;
1032
1033 PlayerController playerController = GetGame().GetPlayerManager().GetPlayerController(playerID);
1034 if (!playerController)
1035 return;
1036
1038 if (!playerFactionAff)
1039 return;
1040
1041 playerFactionAff.SetFaction_S(editableEntityFaction);
1042 }
1043
1044
1045 //------------------------------------------------------------------------------------------------
1047 protected static void OnAIGroupAllEntitiesSpawned(SCR_AIGroup group)
1048 {
1049 SCR_RefPreviewEntity.SpawnAndApplyReference(m_DelayedSpawnEntity, m_DelayedSpawnPreviewParams);
1050
1051 // Cleanup
1052 m_DelayedSpawnEntity = null;
1055 }
1056
1058 {
1060
1062 {
1063 case EEditableEntityType.CHARACTER:
1064 return EEditorPlacingFlags.CHARACTER_PLAYER;
1065
1066 //~ Check if can spawn occupants within vehicle
1067 case EEditableEntityType.VEHICLE:
1068 {
1070 if (!uiInfo)
1071 return 0;
1072
1073 EEditorPlacingFlags vehiclePlacingFlags = 0;
1074
1075 //~ Check if can spawn with crew
1076 if (uiInfo.CanFillWithCrew())
1077 vehiclePlacingFlags |= EEditorPlacingFlags.VEHICLE_CREWED;
1078
1079 //~ Check if can spawn with passengers
1080 if (uiInfo.CanFillWithPassengers())
1081 vehiclePlacingFlags |= EEditorPlacingFlags.VEHICLE_PASSENGER;
1082
1083 return vehiclePlacingFlags;
1084 }
1085
1086 case EEditableEntityType.TASK:
1087 return EEditorPlacingFlags.TASK_INACTIVE;
1088 }
1089 return 0;
1090 }
1091
1092 //------------------------------------------------------------------------------------------------
1101 protected bool CanPlaceEntityServer(IEntityComponentSource editableEntitySource, out EEditableEntityBudget blockingBudget, bool updatePreview, bool showNotification, int prefabID = -1, int playerID = -1, SCR_EditorPreviewParams params = null)
1102 {
1103 if (!m_BudgetManager)
1104 {
1106 if (!m_BudgetManager)
1107 return true;
1108 }
1109
1110 bool validAction = true;
1111 if (prefabID >= 0 && params && params.m_PlacingFlags & EEditorPlacingFlags.PLACING_ACTION)
1112 validAction = IsActionValid(prefabID, params);
1113
1114 return validAction && m_BudgetManager.CanPlaceEntitySource(editableEntitySource, blockingBudget, HasPlacingFlag(EEditorPlacingFlags.CHARACTER_PLAYER), updatePreview, showNotification);
1115 }
1116
1117 protected bool CanSelectEntityPrefab(ResourceName prefab, out EEditableEntityBudget blockingBudget, bool updatePreview = true, bool showBudgetMaxNotification = true)
1118 {
1119 if (!m_BudgetManager || prefab.IsEmpty())
1120 return false;
1121
1122 // Load prefab to check entity budgets or entity type
1123 Resource entityPrefab = Resource.Load(prefab);
1124 // Get entity source
1125 IEntitySource entitySource = SCR_BaseContainerTools.FindEntitySource(entityPrefab);
1127
1128 return CanPlaceEntityServer(editableEntitySource, blockingBudget, updatePreview, showBudgetMaxNotification);
1129 }
1130
1131 protected array<vector> GetOffsets(int count)
1132 {
1133 array<vector> result = {};
1134 if (count == 0)
1135 return result;
1136
1137 if (count == 1)
1138 {
1139 result.Insert(vector.Zero);
1140 return result;
1141 }
1142
1143 int rowCount = Math.Round(Math.Sqrt(count));
1144 int columnCount = Math.Ceil(Math.Sqrt(count));
1145 vector offset = -Vector((rowCount - 1) * m_fSpacing / 2, 0, (columnCount - 1) * m_fSpacing / 2);
1146
1147 int row, column;
1148 for (int i = 0; i < count; i++)
1149 {
1150 row = Math.Floor(i / columnCount);
1151 column = i % columnCount;
1152 result.Insert(offset + Vector(row * m_fSpacing, 0, column * m_fSpacing));
1153 }
1154 return result;
1155 }
1157 {
1158 int index = m_Recipients.Find(entity);
1159 if (index >= 0)
1160 {
1161 m_Recipients.Remove(index);
1162
1163 //--- No remaining recipients, stop placing
1164 if (m_Recipients.IsEmpty())
1166 }
1167 }
1168
1175 {
1176 m_InstantPlacingParam = param;
1177 }
1178
1184 bool SetSelectedPrefab(ResourceName prefab = "", bool onConfirm = false, bool showBudgetMaxNotification = true, set<SCR_EditableEntityComponent> recipients = null, SCR_BaseEditorAction sourceAction = null)
1185 {
1186 if (prefab == m_SelectedPrefab) return true;
1187
1188 //--- Check if entity is within budget
1189 EEditableEntityBudget blockingBudget;
1190 bool isPrefabEmpty = prefab.IsEmpty();
1191 if (!isPrefabEmpty && !CanSelectEntityPrefab(prefab, blockingBudget, showBudgetMaxNotification))
1192 {
1193 return false;
1194 }
1195
1196 //--- Place instantly
1197 if (m_InstantPlacingParam && !isPrefabEmpty)
1198 {
1199 //--- This will prevent m_StatesManager.SetIsWaiting(false) from failing in CreateEntityOwner()
1200 m_StatesManager.SetSafeDialog(true);
1201 m_SelectedPrefab = prefab;
1202 m_Recipients = recipients;
1203 CreateEntity();
1204 return true;
1205 }
1206
1207 //--- Check if the prefab is registered
1209 if (!isPrefabEmpty && prefabData.GetPrefabID(prefab) == -1)
1210 {
1211 Print(string.Format("Cannot initiate placing of prefab @\"%1\", it's not registered in placeable entities!", prefab), LogLevel.WARNING);
1212 return false;
1213 }
1214
1215 if (isPrefabEmpty || HasPlacingFlag(EEditorPlacingFlags.CHARACTER_PLAYER))
1216 {
1217 m_BudgetManager.ResetPreviewCost();
1218 }
1219
1220 if (m_StatesManager && isPrefabEmpty)
1221 m_StatesManager.UnsetState(EEditorState.PLACING);
1222
1223 //--- Track if recipients are deleted (stop placing when no recipient remains)
1224 if (!isPrefabEmpty && recipients)
1225 {
1226 m_editableEntityCore.Event_OnEntityUnregistered.Insert(OnEntityUnregistered);
1227 }
1228 else if (isPrefabEmpty && m_Recipients)
1229 {
1230 m_editableEntityCore.Event_OnEntityUnregistered.Remove(OnEntityUnregistered);
1231 }
1232
1233 //--- Set the prefab
1234 ResourceName prefabPrev = m_SelectedPrefab;
1235 m_SelectedPrefab = prefab;
1236 m_Recipients = recipients;
1237
1238 //--- Create preview entity
1239 int instanceCount = 1;
1240 if (recipients) instanceCount = recipients.Count();
1241 IEntity previewEntity = m_PreviewManager.CreatePreview(prefab, GetOffsets(instanceCount));//, fixedTransform);
1242
1243 if (previewEntity)
1244 {
1245 if (m_StatesManager && !m_StatesManager.SetState(EEditorState.PLACING))
1246 {
1247 m_PreviewManager.DeletePreview();
1248 return false; //--- Exit when cannot set placing state
1249 }
1250
1251
1252 //--- Teleport camera to fixed position
1253 if (m_PreviewManager.IsFixedPosition())
1254 {
1256 if (camera)
1257 {
1259 if (teleportComponent) teleportComponent.TeleportCamera(previewEntity.GetOrigin(), true, true);
1260 }
1261 }
1262 m_CompatiblePlacingFlags = GetCompatiblePlacingFlags(Resource.Load(m_SelectedPrefab));
1263 }
1264 else
1265 {
1266 SetPlacingFlag(EEditorPlacingFlags.CHARACTER_PLAYER, false);
1267 }
1268
1269 m_iActionInfo = -1;
1270 m_PlacingFlags &= ~EEditorPlacingFlags.PLACING_ACTION;
1271 if (sourceAction)
1272 {
1274 if (m_iActionInfo >= 0)
1275 SetPlacingFlag(EEditorPlacingFlags.PLACING_ACTION, true);
1276 }
1277
1278 //--- Call event handlers
1279 Event_OnSelectedPrefabChange.Invoke(prefab, prefabPrev);
1280
1281 if (prefab && !prefabPrev)
1282 SCR_BaseEditorEffect.Activate(prefabData.GetEffectsPlaceStart(), this);
1283 else if (!prefab && prefabPrev && !onConfirm)
1284 SCR_BaseEditorEffect.Activate(prefabData.GetEffectsPlaceCancel(), this);
1285
1286 return true;
1287 }
1288
1293 {
1294 return m_SelectedPrefab;
1295 }
1297 {
1298 return !m_SelectedPrefab.IsEmpty();
1299 }
1300
1305 {
1306 m_Slot = slot;
1307 }
1308
1313 {
1314 return m_Slot;
1315 }
1316
1322 {
1323 //--- When enabling, check if it's allowed (disabloing is always possible)
1324 if (toAdd && !IsPlacingFlagAllowed(flag))
1325 {
1326 Print(string.Format("Cannot enable placing flag %1, placing manager does not allow it!", typename.EnumToString(EEditorPlacingFlags, flag)), LogLevel.WARNING);
1327 return;
1328 }
1329
1330 //--- Set the state
1331 EEditorPlacingFlags prevPlacingFlag = m_PlacingFlags;
1332 if (toAdd)
1333 m_PlacingFlags |= flag;
1334 else
1335 m_PlacingFlags &= ~flag;
1336
1337 //--- If there was a change, call event
1338 if (m_PlacingFlags != prevPlacingFlag)
1339 Event_OnPlacingFlagsChange.Invoke(flag, toAdd);
1340
1341 //~ Exception - update budget preview when placing as player or placing occupied vehicle
1342 UpdatePlacingFlagBudget(m_SelectedPrefab, flag, m_PlacingFlags, prevPlacingFlag);
1343 }
1344
1352 protected void UpdatePlacingFlagBudget(ResourceName selectedPrefab, EEditorPlacingFlags flagChanged, EEditorPlacingFlags currentPlacingFlag, EEditorPlacingFlags prevPlacingFlag)
1353 {
1354 if (m_SelectedPrefab.IsEmpty())
1355 return;
1356
1357 if (flagChanged == EEditorPlacingFlags.CHARACTER_PLAYER)
1358 {
1359 bool isPlacingPlayer = SCR_Enum.HasFlag(currentPlacingFlag, EEditorPlacingFlags.CHARACTER_PLAYER);
1360 EEditableEntityBudget blockingBudget;
1361 Resource resource = Resource.Load(selectedPrefab);
1362 if (!resource.IsValid())
1363 {
1364 Print("Cannot load " + selectedPrefab + " | " + FilePath.StripPath(__FILE__) + ":" + __LINE__, LogLevel.WARNING);
1365 return;
1366 }
1367
1368 m_BudgetManager.CanPlaceEntitySource(SCR_EditableEntityComponentClass.GetEditableEntitySource(resource.GetResource().ToEntitySource()), blockingBudget, isPlacingPlayer);
1369 }
1370
1371 if ((currentPlacingFlag & EEditorPlacingFlags.VEHICLE_CREWED || currentPlacingFlag & EEditorPlacingFlags.VEHICLE_PASSENGER) || (prevPlacingFlag & EEditorPlacingFlags.VEHICLE_CREWED || prevPlacingFlag & EEditorPlacingFlags.VEHICLE_PASSENGER))
1372 {
1373 array<ref SCR_EntityBudgetValue> budgetCosts = {};
1374 Resource resource = Resource.Load(selectedPrefab);
1375 if ( !resource.IsValid())
1376 {
1377 Print("Cannot load " + selectedPrefab + " | " + FilePath.StripPath(__FILE__) + ":" + __LINE__, LogLevel.WARNING);
1378 return;
1379 }
1380
1381 IEntityComponentSource source = SCR_EditableEntityComponentClass.GetEditableEntitySource(resource.GetResource().ToEntitySource());
1382 m_BudgetManager.GetVehicleOccupiedBudgetCosts(source, currentPlacingFlag, budgetCosts);
1383 m_BudgetManager.UpdatePreviewCost(budgetCosts);
1384 }
1385 }
1386
1392 {
1393 SetPlacingFlag(flag, !HasPlacingFlag(flag));
1394 }
1395
1401 {
1402 return SCR_Enum.HasFlag(m_PlacingFlags, flag);
1403 }
1404
1410 {
1411 return SCR_Enum.HasFlag(m_CompatiblePlacingFlags, flag);
1412 }
1413
1419 {
1421 return prefabData && prefabData.HasPlacingFlag(flag);
1422 }
1423
1428 {
1429 return Event_OnSelectedPrefabChange;
1430 }
1431
1437 {
1438 return Event_OnPlacingFlagsChange;
1439 }
1440
1446 {
1447 return Event_OnRequestEntity;
1448 }
1449
1455 {
1456 return Event_OnPlaceEntity;
1457 }
1458
1460 {
1461 return Event_OnPlaceEntityServer;
1462 }
1463
1464 override void EOnEditorDebug(array<string> debugTexts)
1465 {
1466 if (!IsActive()) return;
1467 if (m_SelectedPrefab)
1468 {
1469 debugTexts.Insert(string.Format("Placing prefab: %1", FilePath.StripPath(m_SelectedPrefab.GetPath())));
1470 }
1471 else
1472 {
1473 debugTexts.Insert("Placing prefab: N/A");
1474 }
1475 debugTexts.Insert(string.Format("Placing flags: %1", SCR_Enum.FlagsToString(EEditorPlacingFlags, m_PlacingFlags)));
1476 }
1477
1478 override protected void EOnEditorInitServer()
1479 {
1480 super.EOnEditorInitServer();
1481
1484
1485 if(!IsMaster())
1486 return;
1487
1488 /*
1489 if(m_editableEntityCore)
1490 {
1491 m_editableEntityCore.Event_OnEntityBudgetUpdated.Insert(OnBudgetsUpdated);
1492 }
1493
1494 serverPlacingEditorComponent = this;
1495 */
1496 }
1497
1498 override protected void EOnEditorDeleteServer()
1499 {
1500 super.EOnEditorDeleteServer();
1501
1502 /*
1503 if(!IsMaster())
1504 return;
1505
1506 if(m_editableEntityCore)
1507 {
1508 m_editableEntityCore.Event_OnEntityBudgetUpdated.Remove(OnBudgetsUpdated);
1509 }
1510 */
1511 }
1512
1527
1528 override void EOnEditorDeactivate()
1529 {
1530 m_SelectedPrefab = ResourceName.Empty;
1531
1532 if (m_BudgetManager)
1533 {
1534 m_BudgetManager.Event_OnBudgetMaxReached.Remove(OnBudgetMaxReached);
1535 }
1536 }
1537
1538 //------------------------------------------------------------------------------------------------
1542 void SetCycleWaypoints(notnull set<SCR_EditableGroupComponent> selectedGroups, bool value)
1543 {
1544
1545 if (Replication.IsRunning())
1546 {
1547 array<RplId> recipientIds = {};
1548 foreach (SCR_EditableGroupComponent entity: selectedGroups)
1549 {
1550 RplId id = Replication.FindItemId(entity);
1551 if (id.IsValid())
1552 recipientIds.Insert(id);
1553 }
1554 Rpc(SetCycleWaypointsServer, recipientIds, value);
1555 }
1556 else
1557 {
1558 foreach (SCR_EditableGroupComponent group : selectedGroups)
1559 {
1560 group.EnableCycledWaypoints(value);
1561 }
1562 }
1563 }
1564
1565 //------------------------------------------------------------------------------------------------
1566 [RplRpc(RplChannel.Reliable, RplRcver.Server)]
1567 protected void SetCycleWaypointsServer(array<RplId> selectedGroups, bool value)
1568 {
1569 foreach (RplId group : selectedGroups)
1570 {
1571 SCR_EditableGroupComponent.Cast(Replication.FindItem(group)).EnableCycledWaypoints(value);
1572 }
1573 }
1574};
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition DebugMenuID.c:4
EEditableEntityBudget
EEditorPlacingFlags
ENotification
ArmaReforgerScripted GetGame()
Definition game.c:1398
RplMode
Mode of replication.
Definition RplMode.c:9
enum EAIGroupCombatMode ComponentEditorProps(category:"GameScripted/AI", description:"Component for utility AI system for groups")
vector position
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
void SCR_EditableGroupComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
void SCR_FactionManager(IEntitySource src, IEntity parent)
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
void SCR_RespawnComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
Diagnostic and developer menu system.
Definition DiagMenu.c:18
proto external bool IsActive()
void Rpc(func method, void p0=NULL, void p1=NULL, void p2=NULL, void p3=NULL, void p4=NULL, void p5=NULL, void p6=NULL, void p7=NULL)
proto external vector GetOrigin()
proto external void GetWorldTransform(out vector mat[])
See IEntity::GetTransform.
Definition Math.c:13
Main replication API.
Definition Replication.c:14
Object holding reference to resource. In destructor release the resource.
Definition Resource.c:25
Replication item identifier.
Definition RplId.c:14
ScriptInvokerBase< ScriptInvokerAIGroup > GetOnAllDelayedEntitySpawned()
bool Replace(SCR_EditableEntityComponent entityInsert, bool onlyDirect=false, bool keepExisting=false)
static SCR_BaseEditableEntityFilter GetInstance(EEditableEntityState state, bool showError=false)
void SCR_BaseEditorComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
SCR_EditorManagerEntity m_Manager
static Managed GetInstance(typename type, bool showError=false, bool modeFirst=false)
EntityComponentPrefabData GetEditorComponentData()
void SendNotification(ENotification notificationID, int selfID=0, int targetID=0, vector position=vector.Zero)
SCR_BaseEditorComponent FindEditorComponent(typename type, bool showError=false, bool modeFirst=false)
SCR_EditorManagerEntity GetManager()
static SCR_ManualCamera GetCameraInstance()
static ResourceName GetRandomVariant(ResourceName prefab)
static IEntityComponentSource GetEditableEntitySource(Resource entityResource)
bool HasEntityFlag(EEditableEntityFlag flag)
SCR_EditableEntityComponent EOnEditorPlace(out SCR_EditableEntityComponent parent, SCR_EditableEntityComponent recipient, EEditorPlacingFlags flags, bool isQueue, int playerID=0)
void ForceVehicleCompartments(notnull array< ECompartmentType > forceVehicleCompartments)
static SCR_EditableEntityComponent GetEditableEntity(IEntity owner)
SCR_EditableEntityComponent SetParentEntity(SCR_EditableEntityComponent parentEntity, bool changedByUser=false)
void OnCreatedServer(notnull SCR_PlacingEditorComponent placedEditorComponent)
SCR_EditableEntityComponent GetParentEntity()
Network packet of variables for entity placing and transformation.
static SCR_EditorPreviewParams CreateParamsFromPreview(SCR_PreviewEntityEditorComponent previewManager, SCR_EditableEntityComponent parent=null, bool parentChanged=false)
static SCR_EditorPreviewParams CreateParams(vector transform[4], RplId parentID=Replication.INVALID_ID, EEditorTransformVertical verticalMode=EEditorTransformVertical.SEA, bool isUnderwater=false, SCR_EditableEntityComponent target=null, EEditableEntityInteraction targetInteraction=EEditableEntityInteraction.NONE)
static int PackActionInfo(notnull SCR_BaseEditorAction action, notnull IEntity actionCompOwner)
static void SetFaction(IEntity owner, Faction faction)
static bool IsPositionWithinTerrainBounds(vector pos)
Definition Functions.c:1981
SCR_EditableEntityComponent GetCurrentLayer()
void SetCurrentLayer(SCR_EditableEntityComponent entity)
SCR_BaseManualCameraComponent FindCameraComponent(typename type)
ref array< ref SCR_BaseEditorEffect > m_EffectsPlaceConfirm
ref array< ref SCR_BaseEditorEffect > m_EffectsPlaceCancel
array< ref SCR_BaseEditorEffect > GetEffectsPlaceConfirm()
ref array< ref SCR_BaseEditorEffect > m_EffectsPlaceStart
int GetPrefabs(out notnull array< ResourceName > outPrefabs, bool onlyExposed=false)
array< ref SCR_BaseEditorEffect > GetEffectsPlaceCancel()
ref array< ref SCR_PlaceableEntitiesRegistry > m_Registries
void SCR_PlacingEditorComponentClass(IEntityComponentSource componentSource, IEntitySource parentSource, IEntitySource prefabSource)
array< ref SCR_BaseEditorEffect > GetEffectsPlaceStart()
bool HasPlacingFlag(EEditorPlacingFlags flag)
bool IsPlacingFlagAllowed(EEditorPlacingFlags flag)
static ref SCR_EditorPreviewParams m_DelayedSpawnPreviewParams
static SCR_EditableEntityComponent SpawnEntityResource(SCR_EditorPreviewParams params, Resource prefabResource, int playerID=0, bool isQueue=false, SCR_EditableEntityComponent recipient=null, bool canBePlayer=false)
array< vector > GetOffsets(int count)
static SCR_EditableEntityComponent SpawnEntityResource(ResourceName prefab, vector transform[4])
override void EOnEditorDebug(array< string > debugTexts)
bool HasPlacingFlag(EEditorPlacingFlags flag)
static EEditorPlacingFlags GetCompatiblePlacingFlags(Resource prefabResource)
SCR_PreviewEntityEditorComponent m_PreviewManager
bool CreateEntity(ResourceName prefab, SCR_EditorPreviewParams param, bool unselectPrefab=true, bool canBePlayer=false, set< SCR_EditableEntityComponent > recipients=null, SCR_EditableEntityComponent holder=null, SCR_BaseEditorAction sourceAction=null)
bool IsActionValid(int prefabID, notnull SCR_EditorPreviewParams params)
bool IsThereEnoughBudgetToSpawnVehicleOccupants(array< ResourceName > resources)
void SetPlacingFlag(EEditorPlacingFlags flag, bool toAdd)
bool SetSelectedPrefab(ResourceName prefab="", bool onConfirm=false, bool showBudgetMaxNotification=true, set< SCR_EditableEntityComponent > recipients=null, SCR_BaseEditorAction sourceAction=null)
void SetCycleWaypoints(notnull set< SCR_EditableGroupComponent > selectedGroups, bool value)
bool IsThereEnoughBudgetToSpawn(IEntityComponentSource entitySource)
bool CreateEntity(bool unselectPrefab=true, bool canBePlayer=false, SCR_EditableEntityComponent holder=null, SCR_BaseEditorAction sourceAction=null)
static SCR_EditableEntityComponent m_DelayedSpawnEntity
bool IsPlacingFlagCompatible(EEditorPlacingFlags flag)
ref SCR_EditorPreviewParams m_InstantPlacingParam
void CreateEntityServer(SCR_EditorPreviewParams params, RplId prefabID, int playerID, int entityIndex, bool isQueue, array< RplId > recipientIds, bool canBePlayer, RplId holderId)
SCR_EditableEntityCore m_editableEntityCore
static void UpdatePlayerFactionFromEditableEntity(Faction editableEntityFaction, int playerID)
helper function to avoid nesting
void SetInstantPlacing(SCR_EditorPreviewParams param)
void SetSlot(SCR_SiteSlotEntity slot)
void OnBudgetMaxReached(EEditableEntityBudget entityBudget, bool maxReached)
bool CanSelectEntityPrefab(ResourceName prefab, out EEditableEntityBudget blockingBudget, bool updatePreview=true, bool showBudgetMaxNotification=true)
void OnEntityUnregistered(SCR_EditableEntityComponent entity)
bool CanCreateEntity(out ENotification outNotification=-1, inout SCR_EPreviewState previewStateToShow=SCR_EPreviewState.PLACEABLE, SCR_EditorPreviewParams params=null, int prefabID=-1)
const float MAX_HEIGHT_ABOVE_TERRAIN
the height above the terrain [m], that is a margin of error when player is trying to spawn something ...
void SetCycleWaypointsServer(array< RplId > selectedGroups, bool value)
void TogglePlacingFlag(EEditorPlacingFlags flag)
SCR_BudgetEditorComponent m_BudgetManager
bool CanPlaceEntityServer(IEntityComponentSource editableEntitySource, out EEditableEntityBudget blockingBudget, bool updatePreview, bool showNotification, int prefabID=-1, int playerID=-1, SCR_EditorPreviewParams params=null)
void CreateEntityOwner(int prefabID, array< RplId > entityIds, int entityIndex, int isQueue, bool hasRecipients, RplId currentLayerID, int attempt)
void OnEntityCreatedServer(array< SCR_EditableEntityComponent > entities)
static void OnAIGroupAllEntitiesSpawned(SCR_AIGroup group)
Callback reacting to all delayed spawned entities being created.
void OnBeforeEntityCreatedServer(ResourceName prefab)
static SCR_PlacingEditorComponent serverPlacingEditorComponent
ref map< EEditableEntityBudget, int > m_accumulatedBudgetChanges
void UpdatePlacingFlagBudget(ResourceName selectedPrefab, EEditorPlacingFlags flagChanged, EEditorPlacingFlags currentPlacingFlag, EEditorPlacingFlags prevPlacingFlag)
static SCR_PossessSpawnData FromEntity(notnull IEntity entity)
override void SetSkipPreload(bool skip)
Teleport the camera to the cursor's world position.
proto external GenericEntity GetOwner()
Get owner entity.
Definition Types.c:486
void EntitySpawnParams()
Definition gameLib.c:130
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
proto void PrintFormat(string fmt, void param1=NULL, void param2=NULL, void param3=NULL, void param4=NULL, void param5=NULL, void param6=NULL, void param7=NULL, void param8=NULL, void param9=NULL, LogLevel level=LogLevel.NORMAL)
EEditableEntityType
Defines type of SCR_EditableEntityComponent. Assigned automatically based on IEntity inheritance.
EEditableEntityFlag
Unique flags of the entity.
EEditableEntityState
SCR_FieldOfViewSettings Attribute
EEditorMode
Editor mode that defines overall functionality.
Definition EEditorMode.c:6
EEditorTransformVertical
Vertical transformation mode.
EEditorState
Unique editor state.
Definition EEditorState.c:6
EEditableEntityInteraction
Type of suggested interaction when hovering edited entity on top of another entity.
void RplRpc(RplChannel channel, RplRcver rcver, RplCondition condition=RplCondition.None, string customConditionName="")
Definition EnNetwork.c:95
RplRcver
Definition RplRcver.c:59
RplChannel
Communication channel. Reliable is guaranteed to be delivered. Unreliable not.
Definition RplChannel.c:14
proto native vector Vector(float x, float y, float z)
TraceFlags
Definition TraceFlags.c:13
ScriptInvokerBase< func > ScriptInvoker
Definition tools.c:134