Пример #1
0
        public static PlayerAction_Build ClonePlayerAction_Build(PlayerAction_Build original)
        {
            var nearcdClone = new NearColliderLogic()
            {
                planet             = original.nearcdLogic.planet,
                colChunks          = original.nearcdLogic.colChunks,
                activeColHashes    = new int[600],
                activeColHashCount = 0,
                colliderObjs       = original.nearcdLogic.colliderObjs
            };

            var clone = new PlayerAction_Build()
            {
                factory       = original.factory,
                player        = original.player,
                nearcdLogic   = nearcdClone,
                tmpPackage    = original.tmpPackage,
                planetAux     = original.planetAux,
                cursorTarget  = original.cursorTarget,
                buildPreviews = original.buildPreviews,
                planetPhysics = original.planetPhysics,
                previewPose   = new Pose(Vector3.zero, Quaternion.identity),
                posePairs     = new List <PlayerAction_Build.PosePair>(64),
                startSlots    = new List <PlayerAction_Build.SlotPoint>(64),
                endSlots      = new List <PlayerAction_Build.SlotPoint>(64)
            };

            return(clone);
        }
Пример #2
0
        public static void ActivateColliders(Vector3 center)
        {
            NearColliderLogic nearCdLogic = GameMain.data.mainPlayer.controller.actionBuild.nearcdLogic;

            nearCdLogic.activeColHashCount = 0;
            nearCdLogic.MarkActivePos(center);

            const float sqrAreaRadius = 4f * 4f;

            for (int i = 0; i < nearCdLogic.activeColHashCount; i++)
            {
                int            hash         = nearCdLogic.activeColHashes[i];
                ColliderData[] colliderPool = nearCdLogic.colChunks[hash].colliderPool;

                for (int j = 1; j < nearCdLogic.colChunks[hash].cursor; j++)
                {
                    var collider = colliderPool[j];
                    if (collider.idType != 0 &&
                        (collider.pos - center).sqrMagnitude <= sqrAreaRadius + collider.ext.sqrMagnitude &&
                        (collider.usage != EColliderUsage.Physics || collider.objType != EObjectType.Entity))
                    {
                        int colliderObjId = hash << 20 | j;
                        if (nearCdLogic.colliderObjs.ContainsKey(colliderObjId))
                        {
                            nearCdLogic.colliderObjs[colliderObjId].live = true;
                        }
                        else
                        {
                            nearCdLogic.colliderObjs[colliderObjId] = new ColliderObject(colliderObjId, collider);
                        }
                    }
                }
            }
        }
Пример #3
0
        public static void ActivateColliders(ref NearColliderLogic nearCdLogic, List <Vector3> snaps)
        {
            for (int s = 0; s < snaps.Count; s++)
            {
                nearCdLogic.activeColHashCount = 0;
                var center = snaps[s];

                Vector3 vector  = Vector3.Cross(center, center - GameMain.mainPlayer.position).normalized *(5f);
                Vector3 vector2 = Vector3.Cross(vector, center).normalized *(5f);

                nearCdLogic.MarkActivePos(center);
                nearCdLogic.MarkActivePos(center + vector);
                nearCdLogic.MarkActivePos(center - vector);
                nearCdLogic.MarkActivePos(center + vector2);
                nearCdLogic.MarkActivePos(center - vector2);
                nearCdLogic.MarkActivePos(center + vector + vector2);
                nearCdLogic.MarkActivePos(center - vector + vector2);
                nearCdLogic.MarkActivePos(center + vector - vector2);
                nearCdLogic.MarkActivePos(center - vector - vector2);

                if (nearCdLogic.activeColHashCount > 0)
                {
                    for (int i = 0; i < nearCdLogic.activeColHashCount; i++)
                    {
                        int            num2         = nearCdLogic.activeColHashes[i];
                        ColliderData[] colliderPool = nearCdLogic.colChunks[num2].colliderPool;
                        for (int j = 1; j < nearCdLogic.colChunks[num2].cursor; j++)
                        {
                            if (colliderPool[j].idType != 0)
                            {
                                if ((colliderPool[j].pos - center).sqrMagnitude <= 25f * 4f + colliderPool[j].ext.sqrMagnitude)
                                {
                                    if (colliderPool[j].usage != EColliderUsage.Physics || colliderPool[j].objType != EObjectType.Entity)
                                    {
                                        int num3 = num2 << 20 | j;
                                        if (nearCdLogic.colliderObjs.ContainsKey(num3))
                                        {
                                            nearCdLogic.colliderObjs[num3].live = true;
                                        }
                                        else
                                        {
                                            nearCdLogic.colliderObjs[num3] = new ColliderObject(num3, colliderPool[j]);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Пример #4
0
        public void ProcessPacket(CreatePrebuildsRequest packet, NebulaConnection conn)
        {
            PlanetData planet = GameMain.galaxy.PlanetById(packet.PlanetId);

            if (planet.factory == null)
            {
                Log.Warn($"planet.factory was null create new one");
                planet.factory = GameMain.data.GetOrCreateFactory(planet);
            }

            PlayerAction_Build pab = GameMain.mainPlayer.controller?.actionBuild;

            if (pab != null)
            {
                //Make backup of values that are overwritten
                List <BuildPreview> tmpList   = pab.buildPreviews;
                bool tmpConfirm               = pab.waitConfirm;
                UnityEngine.Vector3    tmpPos = pab.previewPose.position;
                UnityEngine.Quaternion tmpRot = pab.previewPose.rotation;

                PlanetFactory     tmpFactory       = null;
                NearColliderLogic tmpNearcdLogic   = null;
                PlanetPhysics     tmpPlanetPhysics = null;
                float             tmpBuildArea     = GameMain.mainPlayer.mecha.buildArea;
                PlanetData        tmpData          = null;
                bool loadExternalPlanetData        = GameMain.localPlanet != planet;

                //Load temporary planet data, since host is not there
                if (loadExternalPlanetData)
                {
                    tmpFactory       = (PlanetFactory)AccessTools.Field(typeof(PlayerAction_Build), "factory").GetValue(GameMain.mainPlayer.controller.actionBuild);
                    tmpNearcdLogic   = (NearColliderLogic)AccessTools.Field(typeof(PlayerAction_Build), "nearcdLogic").GetValue(GameMain.mainPlayer.controller.actionBuild);
                    tmpPlanetPhysics = (PlanetPhysics)AccessTools.Field(typeof(PlayerAction_Build), "planetPhysics").GetValue(pab);
                    tmpData          = GameMain.mainPlayer.planetData;
                }

                //Create Prebuilds from incomming packet and prepare new position
                pab.buildPreviews = packet.GetBuildPreviews();
                pab.waitConfirm   = true;
                using (FactoryManager.EventFromServer.On())
                {
                    FactoryManager.EventFactory = planet.factory;
                    pab.previewPose.position    = new UnityEngine.Vector3(packet.PosePosition.x, packet.PosePosition.y, packet.PosePosition.z);
                    pab.previewPose.rotation    = new UnityEngine.Quaternion(packet.PoseRotation.x, packet.PoseRotation.y, packet.PoseRotation.z, packet.PoseRotation.w);

                    //Check if some mandatory variables are missing
                    if (planet.physics == null || planet.physics.colChunks == null)
                    {
                        planet.physics = new PlanetPhysics(planet);
                        planet.physics.Init();
                    }
                    if (BeltManager.GetCargoTraffic(planet.factory.cargoTraffic) == null)
                    {
                        planet.factory.cargoTraffic.CreateRenderingBatches();
                    }
                    if (planet.aux == null)
                    {
                        planet.aux = new PlanetAuxData(planet);
                    }

                    //Set temporary Local Planet / Factory data that are needed for original methods CheckBuildConditions() and CreatePrebuilds()
                    AccessTools.Field(typeof(PlayerAction_Build), "factory").SetValue(GameMain.mainPlayer.controller.actionBuild, planet.factory);
                    AccessTools.Field(typeof(PlayerAction_Build), "planetPhysics").SetValue(GameMain.mainPlayer.controller.actionBuild, planet.physics);
                    AccessTools.Field(typeof(PlayerAction_Build), "nearcdLogic").SetValue(GameMain.mainPlayer.controller.actionBuild, planet.physics.nearColliderLogic);
                    AccessTools.Property(typeof(global::Player), "planetData").SetValue(GameMain.mainPlayer, planet, null);

                    //Check if prebuilds can be build (collision check, height check, etc)
                    GameMain.mainPlayer.mecha.buildArea = float.MaxValue;
                    bool canBuild;
                    using (FactoryManager.IgnoreBasicBuildConditionChecks.On())
                    {
                        canBuild  = pab.CheckBuildConditions();
                        canBuild &= CheckBuildingConnections(pab.buildPreviews, planet.factory.entityPool, planet.factory.prebuildPool);
                    }

                    if (canBuild)
                    {
                        FactoryManager.PacketAuthor = packet.AuthorId;
                        pab.CreatePrebuilds();
                        FactoryManager.PacketAuthor = -1;
                    }

                    //Revert changes back to the original planet
                    if (loadExternalPlanetData)
                    {
                        planet.physics.Free();
                        planet.physics = null;
                        AccessTools.Property(typeof(global::Player), "planetData").SetValue(GameMain.mainPlayer, tmpData, null);
                        AccessTools.Field(typeof(PlayerAction_Build), "planetPhysics").SetValue(GameMain.mainPlayer.controller.actionBuild, tmpPlanetPhysics);
                        AccessTools.Field(typeof(PlayerAction_Build), "factory").SetValue(GameMain.mainPlayer.controller.actionBuild, tmpFactory);
                        AccessTools.Field(typeof(PlayerAction_Build), "nearcdLogic").SetValue(GameMain.mainPlayer.controller.actionBuild, tmpNearcdLogic);
                    }

                    GameMain.mainPlayer.mecha.buildArea = tmpBuildArea;
                    FactoryManager.EventFactory         = null;
                }

                pab.buildPreviews        = tmpList;
                pab.waitConfirm          = tmpConfirm;
                pab.previewPose.position = tmpPos;
                pab.previewPose.rotation = tmpRot;
            }
        }
        public static void AfterPrebuild_Postfix(PlayerAction_Build __instance, PlanetFactory ___factory, PlanetAuxData ___planetAux, NearColliderLogic ___nearcdLogic)
        {
            // Do we have cached inserters?
            var ci = cachedInserters;

            if (CopyInserters.copyEnabled && ci.Count > 0 && !__instance.multiLevelCovering)
            {
                foreach (var cachedInserter in ci)
                {
                    var protoId    = cachedInserter.protoId;
                    var modelIndex = (short)LDB.items.Select(cachedInserter.protoId).ModelIndex;

                    foreach (BuildPreview buildPreview in __instance.buildPreviews)
                    {
                        var positionData = GetPositions(__instance, ___factory, ___planetAux, ___nearcdLogic, buildPreview, cachedInserter);

                        if (positionData.otherId != 0)
                        {
                            // Create inserter Prebuild data
                            var pbdata = new PrebuildData
                            {
                                protoId    = (short)protoId,
                                modelIndex = modelIndex,

                                insertOffset = positionData.insertOffset,
                                pickOffset   = positionData.pickOffset,
                                filterId     = cachedInserter.filterId,

                                refCount = cachedInserter.refCount,

                                pos  = positionData.absoluteInserterPos,
                                pos2 = positionData.absoluteInserterPos2,

                                rot  = positionData.absoluteInserterRot,
                                rot2 = positionData.absoluteInserterRot2
                            };


                            // Check the player has the item in inventory, no cheating here
                            var pc        = CopyInserters.pc;
                            var itemcount = pc.player.package.GetItemCount(protoId);
                            // If player has none; skip this request, as we dont create prebuild ghosts, must avoid confusion
                            if (itemcount > 0)
                            {
                                var qty = 1;
                                pc.player.package.TakeTailItems(ref protoId, ref qty);
                                int pbCursor = ___factory.AddPrebuildDataWithComponents(pbdata); // Add the inserter request to Prebuild pool

                                if (cachedInserter.incoming)
                                {
                                    ___factory.WriteObjectConn(-pbCursor, 0, true, buildPreview.objId, positionData.endSlot);      // assembler connection
                                    ___factory.WriteObjectConn(-pbCursor, 1, false, positionData.otherId, positionData.startSlot); // other connection
                                }
                                else
                                {
                                    ___factory.WriteObjectConn(-pbCursor, 0, false, buildPreview.objId, positionData.startSlot); // assembler connection
                                    ___factory.WriteObjectConn(-pbCursor, 1, true, positionData.otherId, positionData.endSlot);  // other connection
                                }
                            }
                        }
                    }
                }
            }
        }
        private static InserterPosition GetPositions(PlayerAction_Build __instance, PlanetFactory ___factory, PlanetAuxData ___planetAux, NearColliderLogic ___nearcdLogic, BuildPreview buildPreview, CachedInserter cachedInserter)
        {
            Vector3    absoluteBuildingPos;
            Quaternion absoluteBuildingRot;

            // When using AdvancedBuildDestruct mod, all buildPreviews are positioned 'absolutely' on the planet surface.
            // In 'normal' mode the buildPreviews are relative to __instance.previewPose.
            // This means that in 'normal' mode the (only) buildPreview is always positioned at {0,0,0}

            if (buildPreview.lpos == Vector3.zero)
            {
                absoluteBuildingPos = __instance.previewPose.position;
                absoluteBuildingRot = __instance.previewPose.rotation;
            }
            else
            {
                absoluteBuildingPos = buildPreview.lpos;
                absoluteBuildingRot = buildPreview.lrot;
            }

            InserterPosition position = null;

            if (currentPositionCache.Count > 0)
            {
                position = currentPositionCache.Dequeue();
            }

            bool isCacheValid = position != null &&
                                position.cachedInserter == cachedInserter &&
                                position.absoluteBuildingPos == absoluteBuildingPos &&
                                position.absoluteBuildingRot == absoluteBuildingRot;

            if (isCacheValid)
            {
                nextPositionCache.Enqueue(position);
                return(position);
            }


            var posDelta  = cachedInserter.posDelta;
            var pos2Delta = cachedInserter.pos2Delta;

            Vector3 absoluteInserterPos  = absoluteBuildingPos + absoluteBuildingRot * cachedInserter.posDelta;
            Vector3 absoluteInserterPos2 = absoluteBuildingPos + absoluteBuildingRot * cachedInserter.pos2Delta;

            Quaternion absoluteInserterRot  = absoluteBuildingRot * cachedInserter.rot;
            Quaternion absoluteInserterRot2 = absoluteBuildingRot * cachedInserter.rot2;

            int startSlot = cachedInserter.startSlot;
            int endSlot   = cachedInserter.endSlot;

            short pickOffset   = cachedInserter.pickOffset;
            short insertOffset = cachedInserter.insertOffset;

            // Find the desired belt/building position
            // As delta doesn't work over distance, re-trace the Grid Snapped steps from the original
            // to find the target belt/building for this inserters other connection
            var testPos = absoluteBuildingPos;

            // Note: rotates each move relative to the rotation of the new building
            for (int u = 0; u < cachedInserter.snapCount; u++)
            {
                testPos = ___planetAux.Snap(testPos + absoluteBuildingRot * cachedInserter.snapMoves[u], true, false);
            }

            // Find the other entity at the target location
            int otherId = 0;

            // find building nearby
            int found = ___nearcdLogic.GetBuildingsInAreaNonAlloc(testPos, 0.2f, _nearObjectIds);

            // find nearest building
            float maxDistance = 0.2f;

            for (int x = 0; x < found; x++)
            {
                var       id = _nearObjectIds[x];
                float     distance;
                ItemProto proto;
                if (id == 0 || id == buildPreview.objId)
                {
                    continue;
                }
                else if (id > 0)
                {
                    EntityData entityData = ___factory.entityPool[id];
                    proto    = LDB.items.Select((int)entityData.protoId);
                    distance = Vector3.Distance(entityData.pos, testPos);
                }
                else
                {
                    PrebuildData prebuildData = ___factory.prebuildPool[-id];
                    proto = LDB.items.Select((int)prebuildData.protoId);
                    if (proto.prefabDesc.isBelt)
                    {
                        // ignore unbuilt belts
                        continue;
                    }
                    distance = Vector3.Distance(prebuildData.pos, testPos);
                }

                // ignore entitites that ore not (built) belts or don't have inserterPoses
                if ((proto.prefabDesc.isBelt == cachedInserter.otherIsBelt || proto.prefabDesc.insertPoses.Length > 0) && distance < maxDistance)
                {
                    otherId     = id;
                    maxDistance = distance;
                }
            }

            if (otherId != 0)
            {
                var buildingId = buildPreview.objId;

                if (buildingId == 0)
                {
                    // the original calculatePose code doesn't correctly handle calculation where one of the entity is a BuildPreview
                    // as it has objId 0 and is not registered in either the entityPool or prebuildPool
                    // To address that we override the 4 critical methods GetObjectPose/GetObjectProtoId/ObjectIsBelt/GetLocalInserts
                    // only for this specific execution of CalculatePose, by returning the expected value for the current buildPreview
                    overridePoseMethods = true;
                    overriddenProto     = buildPreview.item;
                    overriddenPose      = new Pose(absoluteBuildingPos, absoluteBuildingRot);
                }

                if (cachedInserter.incoming)
                {
                    CalculatePose(__instance, otherId, buildingId);
                }
                else
                {
                    CalculatePose(__instance, buildingId, otherId);
                }


                overridePoseMethods = false;

                bool hasNearbyPose = false;
                if (__instance.posePairs.Count > 0)
                {
                    float minDistance = 1000f;
                    PlayerAction_Build.PosePair bestFit = new PlayerAction_Build.PosePair();

                    for (int j = 0; j < __instance.posePairs.Count; ++j)
                    {
                        var posePair = __instance.posePairs[j];
                        if (
                            (cachedInserter.incoming && cachedInserter.endSlot != posePair.endSlot) ||
                            (!cachedInserter.incoming && cachedInserter.startSlot != posePair.startSlot)
                            )
                        {
                            continue;
                        }
                        float startDistance = Vector3.Distance(posePair.startPose.position, absoluteInserterPos);
                        float endDistance   = Vector3.Distance(posePair.endPose.position, absoluteInserterPos2);
                        float poseDistance  = startDistance + endDistance;

                        if (poseDistance < minDistance)
                        {
                            minDistance   = poseDistance;
                            bestFit       = posePair;
                            hasNearbyPose = true;
                        }
                    }
                    if (hasNearbyPose)
                    {
                        // if we were able to calculate a close enough sensible pose
                        // use that instead of the (visually) imprecise default

                        absoluteInserterPos  = bestFit.startPose.position;
                        absoluteInserterPos2 = bestFit.endPose.position;

                        absoluteInserterRot  = bestFit.startPose.rotation;
                        absoluteInserterRot2 = bestFit.endPose.rotation * Quaternion.Euler(0.0f, 180f, 0.0f);

                        pickOffset   = (short)bestFit.startOffset;
                        insertOffset = (short)bestFit.endOffset;

                        startSlot = bestFit.startSlot;
                        endSlot   = bestFit.endSlot;


                        posDelta  = Quaternion.Inverse(absoluteBuildingRot) * (absoluteInserterPos - absoluteBuildingPos);
                        pos2Delta = Quaternion.Inverse(absoluteBuildingRot) * (absoluteInserterPos2 - absoluteBuildingPos);
                    }
                }
            }

            position = new InserterPosition()
            {
                cachedInserter      = cachedInserter,
                absoluteBuildingPos = absoluteBuildingPos,
                absoluteBuildingRot = absoluteBuildingRot,

                otherId = otherId,

                posDelta             = posDelta,
                pos2Delta            = pos2Delta,
                absoluteInserterPos  = absoluteInserterPos,
                absoluteInserterPos2 = absoluteInserterPos2,

                absoluteInserterRot  = absoluteInserterRot,
                absoluteInserterRot2 = absoluteInserterRot2,

                pickOffset   = pickOffset,
                insertOffset = insertOffset,

                startSlot = startSlot,
                endSlot   = endSlot,
            };


            nextPositionCache.Enqueue(position);
            return(position);
        }
        public static void DetermineBuildPreviews_Postfix(PlayerAction_Build __instance, PlanetFactory ___factory, PlanetAuxData ___planetAux, NearColliderLogic ___nearcdLogic, Player ___player)
        {
            // Do we have cached inserters?
            var ci = cachedInserters;

            if (CopyInserters.copyEnabled && ci.Count > 0 && !__instance.multiLevelCovering)
            {
                var bpCount = __instance.buildPreviews.Count;

                for (int i = 0; i < bpCount; i++)
                {
                    BuildPreview buildPreview = __instance.buildPreviews[i];

                    if (!buildPreview.item.prefabDesc.isInserter)
                    {
                        foreach (var cachedInserter in ci)
                        {
                            var positionData = GetPositions(__instance, ___factory, ___planetAux, ___nearcdLogic, buildPreview, cachedInserter);

                            var bp = BuildPreview.CreateSingle(LDB.items.Select(cachedInserter.protoId), LDB.items.Select(cachedInserter.protoId).prefabDesc, true);
                            bp.ResetInfos();


                            bp.lrot  = buildPreview.lrot * cachedInserter.rot;
                            bp.lrot2 = buildPreview.lrot * cachedInserter.rot2;


                            if (buildPreview.lpos == Vector3.zero)
                            {
                                bp.lpos  = buildPreview.lpos + buildPreview.lrot * positionData.posDelta;
                                bp.lpos2 = buildPreview.lpos + buildPreview.lrot * positionData.pos2Delta;
                            }
                            else
                            {
                                bp.lpos  = positionData.absoluteInserterPos;
                                bp.lpos2 = positionData.absoluteInserterPos2;
                            }



                            if (positionData.condition == null)
                            {
                                positionData.condition = EBuildCondition.Ok;

                                Vector3 lpos    = positionData.absoluteInserterPos;
                                Vector3 lpos2   = positionData.absoluteInserterPos2;
                                Vector3 forward = lpos2 - lpos;

                                Pose pose;
                                pose.position = Vector3.Lerp(lpos, lpos2, 0.5f);
                                pose.rotation = Quaternion.LookRotation(forward, lpos.normalized);


                                var colliderData = bp.desc.buildColliders[0];
                                colliderData.ext = new Vector3(colliderData.ext.x, colliderData.ext.y, Vector3.Distance(lpos2, lpos) * 0.5f + colliderData.ext.z - 0.5f);

                                if (cachedInserter.otherIsBelt)
                                {
                                    if (cachedInserter.incoming)
                                    {
                                        colliderData.pos.z -= 0.4f;
                                        colliderData.ext.z += 0.4f;
                                    }
                                    else
                                    {
                                        colliderData.pos.z += 0.4f;
                                        colliderData.ext.z += 0.4f;
                                    }
                                }

                                if (colliderData.ext.z < 0.1f)
                                {
                                    colliderData.ext.z = 0.1f;
                                }
                                colliderData.pos = pose.position + pose.rotation * colliderData.pos;
                                colliderData.q   = pose.rotation * colliderData.q;


                                //int mask = 165888;// the following is equivalent but explicitly states layers affected
                                int mask            = Convert.ToInt32(LayerMaskConstants.layer12 | LayerMaskConstants.layer16 | LayerMaskConstants.layer18);
                                int collisionsFound = Physics.OverlapBoxNonAlloc(colliderData.pos, colliderData.ext, _tmp_cols, colliderData.q, mask, QueryTriggerInteraction.Collide);

                                int collisionLimit = cachedInserter.otherIsBelt ? 0 : 1;

                                if (collisionsFound > collisionLimit)
                                {
                                    PlanetPhysics physics2 = ___player.planetData.physics;
                                    for (int j = 0; j < collisionsFound; j++)
                                    {
                                        physics2.GetColliderData(_tmp_cols[j], out ColliderData colliderData2);
                                        if (colliderData2.objId != 0)
                                        {
                                            if (colliderData2.usage == EColliderUsage.Build)
                                            {
                                                positionData.condition = EBuildCondition.Collide;
                                            }
                                        }
                                    }
                                }
                            }

                            bp.condition = (EBuildCondition)positionData.condition;

                            if (bp.condition == EBuildCondition.Ok)
                            {
                                if (cachedInserter.incoming)
                                {
                                    bp.inputObjId = positionData.otherId;
                                }
                                else
                                {
                                    bp.outputObjId = positionData.otherId;
                                }
                            }

                            __instance.AddBuildPreview(bp);
                        }
                    }
                }

                SwapPositionCache();
            }
        }
Пример #8
0
            public static bool DetermineDestructPreviewsPatch(PlayerAction_Build __instance,
                                                              ref NearColliderLogic ___nearcdLogic,
                                                              ref PlanetFactory ___factory)
            {
                if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.LeftControl))
                {
                    if (!VFInput.onGUI)
                    {
                        UICursor.SetCursor(ECursor.Delete);
                    }
                }
                else
                {
                    return(true);
                }

                if (!Utils.CheckIfInBuildDistance(__instance.cursorTarget))
                {
                    // Out of reach
                    UnityEngine.Debug.Log("out of reach");
                    return(true);
                }

                List <EntityData> deleteEntitiesList = new List <EntityData>();
                ItemProto         itemProto          = Traverse.Create(__instance).Method(
                    "GetItemProto", __instance.castObjId).GetValue <ItemProto>();

                __instance.ClearBuildPreviews();

                if (VFInput.reformMinusKey.onDown)
                {
                    if (__instance.reformSize >= 0)
                    {
                        __instance.reformSize--;
                    }
                }

                if (VFInput.reformPlusKey.onDown)
                {
                    if (__instance.reformSize < _configDisassemblingRadiusMax.Value)
                    {
                        __instance.reformSize++;
                    }
                }

                // Management of the sphere delete
                if (Input.GetKey(KeyCode.LeftControl))
                {
                    int[] buildingIdsToDelete = new int[_maxArrayOfBuildingSize.Value];
                    if (itemProto != null)
                    {
                        ___nearcdLogic.GetBuildingsInAreaNonAlloc(__instance.castObjPos, __instance.reformSize, buildingIdsToDelete);
                    }
                    else
                    {
                        ___nearcdLogic.GetBuildingsInAreaNonAlloc(__instance.cursorTarget, __instance.reformSize, buildingIdsToDelete);
                    }

                    List <int> listBuildingIdsToDelete = new List <int>();

                    foreach (var id in buildingIdsToDelete)
                    {
                        if (id != 0)
                        {
                            listBuildingIdsToDelete.Add(id);
                        }
                    }

                    foreach (var item in listBuildingIdsToDelete)
                    {
                        deleteEntitiesList.Add(___factory.entityPool[item]);
                    }

                    // Management of both
                    if (Input.GetKey(KeyCode.LeftShift))
                    {
                        if (itemProto != null)
                        {
                            deleteEntitiesList = Utils.GetEntitySortedByTypeAndRadius(itemProto, deleteEntitiesList,
                                                                                      __instance.castObjPos, __instance.reformSize);
                        }
                    }
                }

                // Management of the Mass Item delete
                if (Input.GetKey(KeyCode.LeftShift) && !Input.GetKey(KeyCode.LeftControl))
                {
                    __instance.previewPose.position = Vector3.zero;
                    __instance.previewPose.rotation = Quaternion.identity;
                    if ((uint)__instance.castObjId > 0U)
                    {
                        if (itemProto != null)
                        {
                            if ((uint)___factory.entityPool[__instance.castObjId].beltId > 0U)
                            {
                                CargoPath pathByBeltId = Utils.GetPathWithBeltId(___factory,
                                                                                 ___factory.entityPool[__instance.castObjId].beltId);
                                deleteEntitiesList = Utils.GetBeltsEntitiesByCargoPathBuildRange(___factory, pathByBeltId);
                            }
                            else
                            {
                                // deleteEntitiesList = Utils.GetEntitiesByProtoBuildRange(___factory, itemProto);
                                return(true);
                            }
                        }
                    }
                }


                foreach (var entityData in deleteEntitiesList)
                {
                    __instance.AddBuildPreview(new BuildPreview());
                }

                // Common Code
                for (int index = 0; index < __instance.buildPreviews.Count; ++index)
                {
                    BuildPreview buildPreview = __instance.buildPreviews[index];
                    ItemProto    itemProto2   = Traverse.Create(__instance).Method(
                        "GetItemProto", deleteEntitiesList[index].id).GetValue <ItemProto>();
                    buildPreview.item  = itemProto2;
                    buildPreview.desc  = itemProto2.prefabDesc;
                    buildPreview.lpos  = deleteEntitiesList[index].pos;
                    buildPreview.lrot  = deleteEntitiesList[index].rot;
                    buildPreview.objId = deleteEntitiesList[index].id;
                    int num = buildPreview.desc.lodCount <= 0
                        ? 0
                        : ((Object)buildPreview.desc.lodMeshes[0] != (Object)null ? 1 : 0);
                    buildPreview.needModel  = num != 0;
                    buildPreview.isConnNode = true;
                    if (buildPreview.desc.isInserter)
                    {
                        Pose pose = Traverse.Create(__instance).Method("GetObjectPose2", buildPreview.objId)
                                    .GetValue <Pose>();
                        buildPreview.lpos2 = pose.position;
                        buildPreview.lrot2 = pose.rotation;
                    }

                    if ((buildPreview.lpos - __instance.player.position).sqrMagnitude >
                        __instance.player.mecha.buildArea *
                        __instance.player.mecha.buildArea)
                    {
                        buildPreview.condition   = EBuildCondition.OutOfReach;
                        __instance.cursorText    = "目标超出范围".Translate();
                        __instance.cursorWarning = true;
                    }
                    else
                    {
                        buildPreview.condition = EBuildCondition.Ok;
                        __instance.cursorText  = "拆除".Translate() + buildPreview.item.name;
                    }

                    if (buildPreview.desc.multiLevel)
                    {
                        ___factory.ReadObjectConn(buildPreview.objId, 15, out bool _, out int otherObjId,
                                                  out int _);
                        if ((uint)otherObjId > 0U)
                        {
                            buildPreview.condition = EBuildCondition.Covered;
                            __instance.cursorText  = buildPreview.conditionText;
                        }
                    }
                }


                return(false);
            }
Пример #9
0
        public static void FlattenTerrainReformALT(this PlanetFactory planetFactory, Vector3 center, float radius, int reformSize, bool veinBuried, float localRadius, float realRadius, float fade0 = 3f)
        {
            if (planetFactory.tmp_ids == null)
            {
                planetFactory.tmp_ids = new int[1024];
            }
            if (planetFactory.tmp_entity_ids == null)
            {
                planetFactory.tmp_entity_ids = new int[1024];
            }
            Array.Clear(planetFactory.tmp_ids, 0, planetFactory.tmp_ids.Length);
            Array.Clear(planetFactory.tmp_entity_ids, 0, planetFactory.tmp_entity_ids.Length);
            Vector3       zero = Vector3.zero;
            PlanetRawData data = planetFactory.planet.data;

            ushort[] heightData = data.heightData;
            float    f          = ((float)(int)heightData[data.QueryIndex(center)] - realRadius * 100f + 20f) * 0.01f * 2f;

            //f = Mathf.Min(9f, Mathf.Abs(f));
            f      = Mathf.Min(5f, Mathf.Abs(f));
            fade0 += f;
            float areaRadius = radius + fade0;
            short num        = (short)(realRadius * 100f + 20f);
            bool  levelized  = planetFactory.planet.levelized;
            int   num2       = Mathf.RoundToInt((center.magnitude - 0.2f - realRadius) / 1.33333325f);
            int   num3       = num2 * 133 + num - 60;
            float num4       = localRadius * 100f - 20f;

            foreach (KeyValuePair <int, int> tmp_levelChange in planetFactory.tmp_levelChanges)
            {
                /*
                 * if (tmp_levelChange.Value <= 0)
                 * {
                 *  continue;
                 * }
                 */
                ushort num5 = heightData[tmp_levelChange.Key];
                if (levelized)
                {
                    if (num5 >= num3)
                    {
                        if (data.GetModLevel(tmp_levelChange.Key) < 3)
                        {
                            data.SetModPlane(tmp_levelChange.Key, num2);
                        }
                        planetFactory.planet.AddHeightMapModLevel(tmp_levelChange.Key, tmp_levelChange.Value);
                    }
                }
                else
                {
                    Debug.Log("AddHeightMapModLevel: " + tmp_levelChange.Key + ", " + tmp_levelChange.Value);
                    planetFactory.planet.AddHeightMapModLevelALT(tmp_levelChange.Key, tmp_levelChange.Value);
                }
                if ((float)(int)num5 < num4)
                {
                    planetFactory.planet.landPercentDirty = true;
                }
            }
            if (planetFactory.planet.UpdateDirtyMeshes())
            {
                planetFactory.RenderLocalPlanetHeightmap();
            }
            radius -= (float)reformSize * 0.15f;
            NearColliderLogic nearColliderLogic = planetFactory.planet.physics.nearColliderLogic;
            int   vegetablesInAreaNonAlloc      = nearColliderLogic.GetVegetablesInAreaNonAlloc(center, areaRadius, planetFactory.tmp_ids);
            float num6 = radius * radius;

            for (int i = 0; i < vegetablesInAreaNonAlloc; i++)
            {
                int num7 = planetFactory.tmp_ids[i];
                zero  = planetFactory.vegePool[num7].pos;
                zero -= center;
                if (zero.x * zero.x + zero.y * zero.y + zero.z * zero.z <= num6 + 2.2f)
                {
                    planetFactory.RemoveVegeWithComponents(num7);
                    continue;
                }
                float num8 = data.QueryModifiedHeight(planetFactory.vegePool[num7].pos) - 0.03f;
                planetFactory.vegePool[num7].pos = planetFactory.vegePool[num7].pos.normalized * num8;
                GameMain.gpuiManager.AlterModel(planetFactory.vegePool[num7].modelIndex, planetFactory.vegePool[num7].modelId, num7, planetFactory.vegePool[num7].pos, planetFactory.vegePool[num7].rot, setBuffer: false);
            }
            float num9 = 50f;

            vegetablesInAreaNonAlloc = ((!veinBuried) ? nearColliderLogic.GetVeinsInOceanInAreaNonAlloc(center, areaRadius, planetFactory.tmp_ids) : nearColliderLogic.GetVeinsInAreaNonAlloc(center, areaRadius, planetFactory.tmp_ids));
            for (int j = 0; j < vegetablesInAreaNonAlloc; j++)
            {
                int num10 = planetFactory.tmp_ids[j];
                zero = planetFactory.veinPool[num10].pos;
                float   num11  = realRadius + 0.2f;
                Vector3 vector = zero.normalized * num11 - center;
                if (!(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z <= num6 + 2f))
                {
                    continue;
                }
                PlanetPhysics physics      = planetFactory.planet.physics;
                int           colliderId   = planetFactory.veinPool[num10].colliderId;
                ColliderData  colliderData = physics.GetColliderData(colliderId);
                if (veinBuried)
                {
                    num11 -= num9;
                }
                else
                {
                    Vector3 center2 = zero.normalized * num11;
                    int     entitiesInAreaWhenReformNonAlloc = nearColliderLogic.GetEntitiesInAreaWhenReformNonAlloc(center2, colliderData.radius, planetFactory.tmp_entity_ids);
                    if (entitiesInAreaWhenReformNonAlloc > 0)
                    {
                        num11 = zero.magnitude;
                    }
                }
                Vector3 pos   = colliderData.pos.normalized * num11;
                int     num12 = colliderId >> 20;
                colliderId &= 0xFFFFF;
                physics.colChunks[num12].colliderPool[colliderId].pos = pos;
                planetFactory.veinPool[num10].pos = zero.normalized * num11;
                physics.RefreshColliders();
                GameMain.gpuiManager.AlterModel(planetFactory.veinPool[num10].modelIndex, planetFactory.veinPool[num10].modelId, num10, planetFactory.veinPool[num10].pos, setBuffer: false);
            }
            planetFactory.tmp_levelChanges.Clear();
            Array.Clear(planetFactory.tmp_ids, 0, planetFactory.tmp_ids.Length);
            Array.Clear(planetFactory.tmp_ids, 0, planetFactory.tmp_entity_ids.Length);
            GameMain.gpuiManager.SyncAllGPUBuffer();
        }
Пример #10
0
        public static void FlattenTerrainReform(PlanetFactory factory, List <Vector3> points, Vector3 center)
        {
            if (factory.tmp_ids == null)
            {
                factory.tmp_ids = new int[1024];
            }

            if (factory.tmp_entity_ids == null)
            {
                factory.tmp_entity_ids = new int[1024];
            }

            Array.Clear(factory.tmp_ids, 0, factory.tmp_ids.Length);
            Array.Clear(factory.tmp_entity_ids, 0, factory.tmp_entity_ids.Length);

            PlanetRawData data = factory.planet.data;

            ushort[] heightData = data.heightData;
            short    h          = (short)(factory.planet.realRadius * 100f + 20f);
            bool     levelized  = factory.planet.levelized;
            int      step       = Mathf.RoundToInt((center.magnitude - 0.2f - factory.planet.realRadius) / 1.3333333f);
            int      heightT    = step * 133 + h - 60;
            float    heightT2   = factory.planet.radius * 100f - 20f;

            foreach (KeyValuePair <int, int> keyValuePair in tmpLevelChanges)
            {
                if (keyValuePair.Value > 0)
                {
                    ushort height = heightData[keyValuePair.Key];
                    if (levelized)
                    {
                        if (height >= heightT)
                        {
                            if (data.GetModLevel(keyValuePair.Key) < 3)
                            {
                                data.SetModPlane(keyValuePair.Key, step);
                            }

                            factory.planet.AddHeightMapModLevel(keyValuePair.Key, keyValuePair.Value);
                        }
                    }
                    else
                    {
                        factory.planet.AddHeightMapModLevel(keyValuePair.Key, keyValuePair.Value);
                    }

                    if (height < heightT2)
                    {
                        factory.planet.landPercentDirty = true;
                    }
                }
            }

            if (factory.planet.UpdateDirtyMeshes())
            {
                factory.RenderLocalPlanetHeightmap();
            }

            foreach (Vector3 point in points)
            {
                NearColliderLogic nearColliderLogic = factory.planet.physics.nearColliderLogic;
                int num7 = nearColliderLogic.GetVegetablesInAreaNonAlloc(point, 1, factory.tmp_ids);
                for (int i = 0; i < num7; i++)
                {
                    int     id       = factory.tmp_ids[i];
                    Vector3 position = factory.vegePool[id].pos;
                    position -= point;
                    if (position.sqrMagnitude <= 9f)
                    {
                        factory.RemoveVegeWithComponents(id);
                    }
                    else
                    {
                        float d = data.QueryModifiedHeight(factory.vegePool[id].pos) - 0.03f;
                        factory.vegePool[id].pos = factory.vegePool[id].pos.normalized * d;
                        GameMain.gpuiManager.AlterModel(factory.vegePool[id].modelIndex, factory.vegePool[id].modelId, id,
                                                        factory.vegePool[id].pos, factory.vegePool[id].rot, false);
                    }
                }
            }


            tmpLevelChanges.Clear();
            Array.Clear(factory.tmp_ids, 0, factory.tmp_ids.Length);
            Array.Clear(factory.tmp_ids, 0, factory.tmp_entity_ids.Length);
            GameMain.gpuiManager.SyncAllGPUBuffer();
        }
Пример #11
0
        public static void CreatePrebuildsRequest(CreatePrebuildsRequest packet)
        {
            PlanetData planet = GameMain.galaxy.PlanetById(packet.PlanetId);

            if (planet.factory == null)
            {
                if (FactoryManager.EventFromServer)
                {
                    // We only execute the code if the client has loaded the factory at least once.
                    // Else it will get it once it goes to the planet for the first time.
                    return;
                }
                Log.Warn($"planet.factory was null create new one");
                planet.factory = GameMain.data.GetOrCreateFactory(planet);
            }

            PlayerAction_Build pab = GameMain.mainPlayer.controller != null ? GameMain.mainPlayer.controller.actionBuild : null;

            BuildTool[] buildTools = pab.tools;
            BuildTool   buildTool  = null;

            for (int i = 0; i < buildTools.Length; i++)
            {
                if (buildTools[i].GetType().ToString() == packet.BuildToolType)
                {
                    buildTool = buildTools[i];
                    break;
                }
            }
            if (pab != null && buildTool != null)
            {
                FactoryManager.TargetPlanet = packet.PlanetId;
                FactoryManager.PacketAuthor = packet.AuthorId;

                PlanetFactory     tmpFactory       = null;
                NearColliderLogic tmpNearcdLogic   = null;
                PlanetPhysics     tmpPlanetPhysics = null;
                bool loadExternalPlanetData        = GameMain.localPlanet != planet;

                if (loadExternalPlanetData)
                {
                    //Make backup of values that are overwritten
                    tmpFactory       = buildTool.factory;
                    tmpNearcdLogic   = buildTool.actionBuild.nearcdLogic;
                    tmpPlanetPhysics = buildTool.actionBuild.planetPhysics;
                }

                //Create Prebuilds from incoming packet and prepare new position
                List <BuildPreview> tmpList = new List <BuildPreview>();
                tmpList.AddRange(buildTool.buildPreviews); buildTool.buildPreviews.Clear();
                buildTool.buildPreviews.AddRange(packet.GetBuildPreviews());
                FactoryManager.EventFactory = planet.factory;

                //Check if some mandatory variables are missing
                if (planet.physics == null || planet.physics.colChunks == null)
                {
                    planet.physics = new PlanetPhysics(planet);
                    planet.physics.Init();
                }
                if (planet.aux == null)
                {
                    planet.aux = new PlanetAuxData(planet);
                }

                //Set temporary Local Planet / Factory data that are needed for original methods CheckBuildConditions() and CreatePrebuilds()
                buildTool.factory    = planet.factory;
                pab.factory          = planet.factory;
                pab.noneTool.factory = planet.factory;
                if (FactoryManager.EventFromClient)
                {
                    // Only the server needs to set these
                    pab.planetPhysics = planet.physics;
                    pab.nearcdLogic   = planet.physics.nearColliderLogic;
                }

                //Check if prebuilds can be build (collision check, height check, etc)
                bool canBuild = false;
                if (FactoryManager.EventFromClient)
                {
                    GameMain.mainPlayer.mecha.buildArea = float.MaxValue;
                    canBuild = CheckBuildingConnections(buildTool.buildPreviews, planet.factory.entityPool, planet.factory.prebuildPool);
                }

                Debug.Log(buildTool.buildPreviews[0].condition);

                if (canBuild || FactoryManager.EventFromServer)
                {
                    if (FactoryManager.EventFromClient)
                    {
                        CheckAndFixConnections(buildTool, planet);
                    }

                    if (packet.BuildToolType == typeof(BuildTool_Click).ToString())
                    {
                        ((BuildTool_Click)buildTool).CreatePrebuilds();
                    }
                    else if (packet.BuildToolType == typeof(BuildTool_Path).ToString())
                    {
                        ((BuildTool_Path)buildTool).CreatePrebuilds();
                    }
                    else if (packet.BuildToolType == typeof(BuildTool_Inserter).ToString())
                    {
                        ((BuildTool_Inserter)buildTool).CreatePrebuilds();
                    }
                }

                //Revert changes back to the original planet
                if (loadExternalPlanetData)
                {
                    buildTool.factory    = tmpFactory;
                    pab.factory          = tmpFactory;
                    pab.noneTool.factory = tmpFactory;

                    if (FactoryManager.EventFromClient)
                    {
                        pab.planetPhysics = tmpPlanetPhysics;
                        pab.nearcdLogic   = tmpNearcdLogic;
                        planet.physics.Free();
                        planet.physics = null;
                    }
                }

                GameMain.mainPlayer.mecha.buildArea = Configs.freeMode.mechaBuildArea;
                FactoryManager.EventFactory         = null;

                buildTool.buildPreviews.Clear();
                buildTool.buildPreviews.AddRange(tmpList);

                FactoryManager.TargetPlanet = FactoryManager.PLANET_NONE;
                FactoryManager.PacketAuthor = -1;
            }
        }
Пример #12
0
        public static void CreatePrebuildsRequest(CreatePrebuildsRequest packet)
        {
            PlanetData planet = GameMain.galaxy.PlanetById(packet.PlanetId);

            if (planet.factory == null)
            {
                if (FactoryManager.IsIncomingRequest)
                {
                    // We only execute the code if the client has loaded the factory at least once.
                    // Else it will get it once it goes to the planet for the first time.
                    return;
                }
                Log.Warn($"planet.factory was null create new one");
                planet.factory = GameMain.data.GetOrCreateFactory(planet);
            }

            PlayerAction_Build pab = GameMain.mainPlayer.controller != null ? GameMain.mainPlayer.controller.actionBuild : null;

            BuildTool[] buildTools = pab.tools;
            BuildTool   buildTool  = null;

            for (int i = 0; i < buildTools.Length; i++)
            {
                if (buildTools[i].GetType().ToString() == packet.BuildToolType)
                {
                    buildTool = buildTools[i];
                    break;
                }
            }

            if (pab != null && buildTool != null)
            {
                FactoryManager.TargetPlanet = packet.PlanetId;
                FactoryManager.PacketAuthor = packet.AuthorId;

                PlanetFactory     tmpFactory       = null;
                NearColliderLogic tmpNearcdLogic   = null;
                PlanetPhysics     tmpPlanetPhysics = null;
                bool loadExternalPlanetData        = GameMain.localPlanet?.id != planet.id;

                if (loadExternalPlanetData)
                {
                    //Make backup of values that are overwritten
                    tmpFactory       = buildTool.factory;
                    tmpNearcdLogic   = buildTool.actionBuild.nearcdLogic;
                    tmpPlanetPhysics = buildTool.actionBuild.planetPhysics;
                    FactoryManager.AddPlanetTimer(packet.PlanetId);
                }

                bool incomingBlueprintEvent = packet.BuildToolType == typeof(BuildTool_BlueprintPaste).ToString();

                //Create Prebuilds from incoming packet and prepare new position
                List <BuildPreview> tmpList = new List <BuildPreview>();
                if (!incomingBlueprintEvent)
                {
                    tmpList.AddRange(buildTool.buildPreviews);
                    buildTool.buildPreviews.Clear();
                    buildTool.buildPreviews.AddRange(packet.GetBuildPreviews());
                }

                FactoryManager.EventFactory = planet.factory;

                //Set temporary Local Planet / Factory data that are needed for original methods CheckBuildConditions() and CreatePrebuilds()
                buildTool.factory    = planet.factory;
                pab.factory          = planet.factory;
                pab.noneTool.factory = planet.factory;
                if (FactoryManager.IsIncomingRequest)
                {
                    // Only the server needs to set these
                    pab.planetPhysics = planet.physics;
                    pab.nearcdLogic   = planet.physics.nearColliderLogic;
                }

                //Check if prebuilds can be build (collision check, height check, etc)
                bool canBuild = false;
                if (FactoryManager.IsIncomingRequest)
                {
                    GameMain.mainPlayer.mecha.buildArea = float.MaxValue;
                    canBuild = CheckBuildingConnections(buildTool.buildPreviews, planet.factory.entityPool, planet.factory.prebuildPool);
                }

                if (canBuild || FactoryManager.IsIncomingRequest)
                {
                    if (FactoryManager.IsIncomingRequest)
                    {
                        CheckAndFixConnections(buildTool, planet);
                    }

                    if (packet.BuildToolType == typeof(BuildTool_Click).ToString())
                    {
                        ((BuildTool_Click)buildTool).CreatePrebuilds();
                    }
                    else if (packet.BuildToolType == typeof(BuildTool_Path).ToString())
                    {
                        ((BuildTool_Path)buildTool).CreatePrebuilds();
                    }
                    else if (packet.BuildToolType == typeof(BuildTool_Inserter).ToString())
                    {
                        ((BuildTool_Inserter)buildTool).CreatePrebuilds();
                    }
                    else if (incomingBlueprintEvent)
                    {
                        BuildTool_BlueprintPaste bpTool = buildTool as BuildTool_BlueprintPaste;

                        // Cache the current data before performing the requested CreatePrebuilds();
                        int            previousCursor = bpTool.bpCursor;
                        BuildPreview[] previousPool   = bpTool.bpPool;

                        // Perform the requested CreatePrebuilds();
                        List <BuildPreview> incomingPreviews = packet.GetBuildPreviews();
                        bpTool.bpCursor = incomingPreviews.Count;
                        bpTool.bpPool   = incomingPreviews.ToArray();
                        bpTool.CreatePrebuilds();

                        // Revert to previous data
                        bpTool.bpCursor = previousCursor;
                        bpTool.bpPool   = previousPool;
                    }
                }

                //Revert changes back to the original planet
                if (loadExternalPlanetData)
                {
                    buildTool.factory    = tmpFactory;
                    pab.factory          = tmpFactory;
                    pab.noneTool.factory = tmpFactory;
                    pab.planetPhysics    = tmpPlanetPhysics;
                    pab.nearcdLogic      = tmpNearcdLogic;
                }

                GameMain.mainPlayer.mecha.buildArea = Configs.freeMode.mechaBuildArea;
                FactoryManager.EventFactory         = null;

                if (!incomingBlueprintEvent)
                {
                    buildTool.buildPreviews.Clear();
                    buildTool.buildPreviews.AddRange(tmpList);
                }

                FactoryManager.TargetPlanet = FactoryManager.PLANET_NONE;
                FactoryManager.PacketAuthor = FactoryManager.AUTHOR_NONE;
            }
        }