/// <summary> /// This method should be called only by LandClaimSystem. /// </summary> internal static void AddArea(ILogicObject area) { if (StateSubscriptionStorages.ContainsKey(area)) { return; } // register for group change event var stateSubscriptionStorage = new StateSubscriptionStorage(); StateSubscriptionStorages[area] = stateSubscriptionStorage; var areaPublicState = LandClaimArea.GetPublicState(area); areaPublicState.ClientSubscribe( o => o.LandClaimAreasGroup, newValue => { //Api.Logger.Dev($"Received LandClaimAreasGroup changed: {newValue} for {area}"); OnAreaModified(area); }, stateSubscriptionStorage); // register area RendererManagerGraceAreas.RegisterArea(area); var renderer = LandClaimSystem.ClientIsOwnedArea(area) ? RendererManagerOwnedByPlayer : RendererManagerNotOwnedByPlayer; renderer.RegisterArea(area); AreaAdded?.Invoke(area); }
public string Execute( double chargePercent = 100, [CurrentCharacterIfNull] ICharacter character = null) { var chargeFraction = MathHelper.Clamp(chargePercent / 100, min: 0, max: 1); using var tempLandClaims = Api.Shared.GetTempList <ILogicObject>(); LandClaimSystem.SharedGetAreasInBounds( new RectangleInt(character.TilePosition, (1, 1)), tempLandClaims, addGracePadding: false); var landClaim = tempLandClaims.AsList().FirstOrDefault(); if (landClaim is null) { return("No power grid exist near " + character.Name); } var landClaimAreasGroup = LandClaimArea.GetPublicState(landClaim).LandClaimAreasGroup; var powerGrid = LandClaimAreasGroup.GetPrivateState(landClaimAreasGroup).PowerGrid; var powerGridState = PowerGrid.GetPublicState(powerGrid); powerGridState.ElectricityAmount = powerGridState.ElectricityCapacity * chargeFraction; return($"Charge amount of the power grid modified to {chargeFraction * 100}%"); }
private void RefreshSafeStorageAndPowerGrid() { var objectPublicState = this.landClaimWorldObject.GetPublicState <ObjectLandClaimPublicState>(); var area = objectPublicState.LandClaimAreaObject; var areasGroup = LandClaimArea.GetPublicState(area).LandClaimAreasGroup; var areasGroupPrivateState = LandClaimAreasGroup.GetPrivateState(areasGroup); // setup power grid var powerGrid = areasGroupPrivateState.PowerGrid; var oldViewModelPowerGridState = this.ViewModelPowerGridState; this.ViewModelPowerGridState = new ViewModelPowerGridState(PowerGrid.GetPublicState(powerGrid)); oldViewModelPowerGridState?.Dispose(); // setup safe storage this.DisposeViewModelItemsContainerExchange(); this.ViewModelItemsContainerExchange = new ViewModelItemsContainerExchange( areasGroupPrivateState.ItemsContainer, callbackTakeAllItemsSuccess: () => { }, enableShortcuts: this.IsSafeStorageAvailable) { IsContainerTitleVisible = false, }; this.ViewModelItemsContainerExchange.Container.SlotsCountChanged += this.SafeStorageSlotsChangedHandler; this.ViewModelItemsContainerExchange.Container.ItemsReset += this.SafeStorageSlotsChangedHandler; }
private static bool AppendInfoAboutPlayerLandClaims(ICharacter player, StringBuilder result) { var ownedLandClaimAreas = PlayerCharacter.GetPrivateState(player) .OwnedLandClaimAreas; if (ownedLandClaimAreas is null || ownedLandClaimAreas.Count == 0) { return(false); } var worldBoundsOffset = Server.World.WorldBounds.Offset; result.Append("Owned land claims by ") .Append(player) .Append(":"); foreach (var ownedLandClaimArea in ownedLandClaimAreas) { result.AppendLine(); var publicState = LandClaimArea.GetPublicState(ownedLandClaimArea); result.AppendFormat(" * {0} at {1}", publicState.ProtoObjectLandClaim.ShortId, publicState.LandClaimCenterTilePosition - worldBoundsOffset); } return(true); }
public void Register(ILogicObject area) { Controller controller; foreach (var pair in this.groupControllers) { controller = pair.Value; if (controller.Areas.Contains(area)) { throw new Exception("Already has area registered: " + area); } } var areasGroup = LandClaimArea.GetPublicState(area).LandClaimAreasGroup; if (areasGroup is null) { return; } if (!this.groupControllers.TryGetValue(areasGroup, out controller)) { controller = new Controller(areasGroup, this.worldMapController); this.groupControllers[areasGroup] = controller; } controller.Areas.Add(area); }
private static void ServerLoadSystem() { const string key = nameof(LandClaimAreaManager); if (Server.Database.TryGet(key, key, out ILogicObject savedManager)) { Server.World.DestroyObject(savedManager); } serverLandClaimManagerInstance = Server.World.CreateLogicObject <LandClaimAreaManager>(); var publicState = LandClaimAreaManager.GetPublicState(serverLandClaimManagerInstance); publicState.LandClaimAreas = new NetworkSyncList <ILogicObject>(); Server.Database.Set(key, key, serverLandClaimManagerInstance); sharedLandClaimAreas = LandClaimAreaManager.GetPublicState(serverLandClaimManagerInstance) .LandClaimAreas; foreach (var area in sharedLandClaimAreas) { var areaPrivateState = LandClaimArea.GetPrivateState(area); var areaPublicState = LandClaimArea.GetPublicState(area); areaPublicState.SetupAreaProperties(areaPrivateState); } }
private static void ServerTransferAreasGroupToFactionOwnership( ILogicObject faction, ICharacter byCharacter, ILogicObject areasGroup) { var areas = LandClaimAreasGroup.GetPrivateState(areasGroup).ServerLandClaimsAreas; foreach (var area in areas) { ServerUnregisterArea(area); } var publicState = LandClaimAreasGroup.GetPublicState(areasGroup); publicState.ServerSetFaction(faction); var centerTilePosition = new Vector2Ushort( (ushort)areas.Average(a => LandClaimArea.GetPublicState(a).LandClaimCenterTilePosition.X), (ushort)areas.Average(a => LandClaimArea.GetPublicState(a).LandClaimCenterTilePosition.Y)); Logger.Important( $"Transferred land claim areas group to the faction ownership: {areasGroup} at {centerTilePosition}"); FactionSystem.ServerOnLandClaimExpanded(faction, centerTilePosition, byCharacter); foreach (var area in areas) { ServerRegisterArea(area); } }
public static void ServerOnObjectLandClaimBuilt( ICharacter byCharacter, IStaticWorldObject landClaimStructure) { if (!(landClaimStructure?.ProtoStaticWorldObject is IProtoObjectLandClaim)) { throw new Exception("Not a land claim structure: " + landClaimStructure); } // create new area for this land claim structure var area = Api.Server.World.CreateLogicObject <LandClaimArea>(); var areaPrivateState = LandClaimArea.GetPrivateState(area); var areaPublicState = LandClaimArea.GetPublicState(area); var founderName = byCharacter.Name; // setup it areaPrivateState.ServerLandClaimWorldObject = landClaimStructure; areaPrivateState.LandClaimFounder = founderName; areaPrivateState.LandOwners = new NetworkSyncList <string>() { founderName }; areaPublicState.Title = founderName; areaPublicState.SetupAreaProperties(areaPrivateState); // set this area to the structure public state landClaimStructure.GetPublicState <ObjectLandClaimPublicState>() .LandClaimAreaObject = area; ServerOnAddLandOwner(area, byCharacter, notify: false); Logger.Important("Land claim area added: " + area); }
public static RectangleInt SharedGetLandClaimAreaBounds(ILogicObject area) { var publicState = LandClaimArea.GetPublicState(area); return(SharedCalculateLandClaimAreaBounds( publicState.LandClaimCenterTilePosition, publicState.LandClaimSize)); }
public string Execute(byte minOwnersNumber = 1) { if (minOwnersNumber < 1) { minOwnersNumber = 1; } var result = new StringBuilder("List of all land claims with their access lists: (with at least ") .Append(minOwnersNumber) .Append(" owner(s))") .AppendLine(); var worldBoundsOffset = Server.World.WorldBounds.Offset; foreach (var area in LandClaimSystem.SharedEnumerateAllAreas()) { var privateState = LandClaimArea.GetPrivateState(area); var landClaimOwners = privateState.ServerGetLandOwners(); var ownersCount = landClaimOwners.Count(); if (ownersCount < minOwnersNumber) { continue; } var publicState = LandClaimArea.GetPublicState(area); result.AppendLine() .Append(publicState.ProtoObjectLandClaim.ShortId) .Append(" at ") .Append(publicState.LandClaimCenterTilePosition - worldBoundsOffset) .Append(" — "); var factionClanTag = LandClaimSystem.SharedGetAreaOwnerFactionClanTag(area); if (!string.IsNullOrEmpty(factionClanTag)) { result.AppendFormat("faction [{0}] — ", factionClanTag); } result.Append(ownersCount) .Append(" owner(s)"); foreach (var ownerName in landClaimOwners) { result.AppendLine() .Append(" * ") .Append(ownerName); } } return(result.ToString()); }
private static void ServerAreasGroupChangedHandler( ILogicObject area, [CanBeNull] ILogicObject areasGroupFrom, [CanBeNull] ILogicObject areasGroupTo) { var byMember = ServerPlayerCharacterCurrentActionStateContext.CurrentCharacter ?? (ServerRemoteContext.IsRemoteCall ? ServerRemoteContext.Character : null); if (areasGroupTo is not null) { var clanTag = LandClaimAreasGroup.GetPublicState(areasGroupTo).FactionClanTag; if (string.IsNullOrEmpty(clanTag)) { return; } var faction = FactionSystem.ServerGetFactionByClanTag(clanTag); var centerTilePosition = LandClaimArea.GetPublicState(area).LandClaimCenterTilePosition; Logger.Important( string.Format("Faction-owned land claim areas group expanded with a new claim area: {0} at {1}", areasGroupTo, centerTilePosition)); FactionSystem.ServerOnLandClaimExpanded(faction, centerTilePosition, byMember: byMember); } else if (areasGroupFrom is not null) { var clanTag = LandClaimAreasGroup.GetPublicState(areasGroupFrom).FactionClanTag; if (string.IsNullOrEmpty(clanTag)) { return; } var faction = FactionSystem.ServerGetFactionByClanTag(clanTag); var centerTilePosition = LandClaimArea.GetPublicState(area).LandClaimCenterTilePosition; Logger.Important( string.Format("Faction-owned land claim areas group removed: {0} at {1}", areasGroupFrom, centerTilePosition)); FactionSystem.ServerOnLandClaimRemoved(faction, centerTilePosition, byMember: byMember); } }
private static bool IsBaseMusicShouldPlay(ICharacter character) { using var tempListAreasNearby = Api.Shared.GetTempList <ILogicObject>(); using var tempListOwnedAreaGroupsNearby = Api.Shared.GetTempList <ILogicObject>(); LandClaimSystem.SharedGetAreasInBounds( new RectangleInt(character.TilePosition.X, character.TilePosition.Y, 1, 1).Inflate(2), tempListAreasNearby, addGracePadding: true); // find owned bases (area groups) nearby foreach (var area in tempListAreasNearby.AsList()) { if (LandClaimSystem.SharedIsOwnedArea(area, character, requireFactionPermission: false)) { var areasGroup = LandClaimSystem.SharedGetLandClaimAreasGroup(area); tempListOwnedAreaGroupsNearby.AddIfNotContains(areasGroup); } } if (tempListOwnedAreaGroupsNearby.Count == 0) { return(false); } var allAreas = (List <ILogicObject>)LandClaimSystem.SharedEnumerateAllAreas(); // check every owned base whether it has at least single T2 or higher tier land claim foreach (var areasGroup in tempListOwnedAreaGroupsNearby.AsList()) { foreach (var area in allAreas) { var areaPublicState = LandClaimArea.GetPublicState(area); if (areaPublicState.LandClaimAreasGroup == areasGroup && areaPublicState.LandClaimTier > 1) { // found an area on the base with land claim tier > 1 return(true); } } } return(false); }
public string Execute( ShieldProtectionStatus status, [CurrentCharacterIfNull] ICharacter character = null) { if (!LandClaimShieldProtectionConstants.SharedIsEnabled) { return("S.H.I.E.L.D. protection is not available"); } if (status == ShieldProtectionStatus.Active) { status = ShieldProtectionStatus.Activating; } using var tempLandClaims = Api.Shared.GetTempList <ILogicObject>(); LandClaimSystem.SharedGetAreasInBounds( new RectangleInt(character.TilePosition, (1, 1)), tempLandClaims, addGracePadding: false); var landClaim = tempLandClaims.AsList().FirstOrDefault(); if (landClaim is null) { return("No base exist near " + character.Name); } var landClaimAreasGroup = LandClaimArea.GetPublicState(landClaim).LandClaimAreasGroup; LandClaimShieldProtectionSystem.SharedGetShieldProtectionMaxStatsForBase(landClaimAreasGroup, out _, out _); var privateState = LandClaimAreasGroup.GetPrivateState(landClaimAreasGroup); privateState.ShieldProtectionCooldownExpirationTime = 0; var publicState = LandClaimAreasGroup.GetPublicState(landClaimAreasGroup); publicState.Status = status; publicState.ShieldActivationTime = Server.Game.FrameTime; return($"Status of the S.H.I.E.L.D. changed to {status}."); }
public static IStaticWorldObject ServerUpgrade( IStaticWorldObject oldStructure, IProtoObjectStructure upgradeStructure, ICharacter character) { if (!(oldStructure?.ProtoStaticWorldObject is IProtoObjectLandClaim)) { throw new Exception("Not a land claim structure: " + oldStructure); } var tilePosition = oldStructure.TilePosition; var area = ServerGetLandClaimArea(oldStructure); // release area oldStructure.GetPublicState <ObjectLandClaimPublicState>().LandClaimAreaObject = null; // destroy old structure ServerWorld.DestroyObject(oldStructure); // create new structure var upgradedObject = ServerWorld.CreateStaticWorldObject(upgradeStructure, tilePosition); // get area for the old land claim structure var areaPrivateState = LandClaimArea.GetPrivateState(area); var areaPublicState = LandClaimArea.GetPublicState(area); // update it to use upgraded land claim structure areaPrivateState.ServerLandClaimWorldObject = upgradedObject; areaPublicState.SetupAreaProperties(areaPrivateState); // set this area to the structure public state upgradedObject.GetPublicState <ObjectLandClaimPublicState>() .LandClaimAreaObject = area; Logger.Important($"Successfully upgraded: {oldStructure} to {upgradedObject}", character); Instance.CallClient( Server.Characters.EnumerateAllPlayerCharacters(onlyOnline: true), _ => _.ClientRemote_OnLandClaimUpgraded(area)); return(upgradedObject); }
private void UpdateCallback() { var areasGroup = LandClaimArea.GetPublicState(this.area).LandClaimAreasGroup; var status = LandClaimShieldProtectionSystem.SharedGetShieldPublicStatus(areasGroup); switch (status) { case ShieldProtectionStatus.Active: this.markControl.IsUnderShield = true; break; // ReSharper disable once CompareOfFloatsByEqualityOperator case ShieldProtectionStatus.Activating: // flicker the icon for half a second this.markControl.IsUnderShield = Math.Round(Api.Client.Core.ClientRealTime % 1.0) == 0.0; break; default: this.markControl.IsUnderShield = false; break; } }
public string Execute( double chargePercent = 100, [CurrentCharacterIfNull] ICharacter character = null) { if (!LandClaimShieldProtectionConstants.SharedIsEnabled) { return("S.H.I.E.L.D. protection is not available"); } var chargeFraction = MathHelper.Clamp(chargePercent / 100, min: 0, max: 1); using var tempLandClaims = Api.Shared.GetTempList <ILogicObject>(); LandClaimSystem.SharedGetAreasInBounds( new RectangleInt(character.TilePosition, (1, 1)), tempLandClaims, addGracePadding: false); var landClaim = tempLandClaims.AsList().FirstOrDefault(); if (landClaim is null) { return("No base exist near " + character.Name); } var landClaimAreasGroup = LandClaimArea.GetPublicState(landClaim).LandClaimAreasGroup; LandClaimShieldProtectionSystem.SharedGetShieldProtectionMaxStatsForBase(landClaimAreasGroup, out _, out var electricityCapacity); var privateState = LandClaimAreasGroup.GetPrivateState(landClaimAreasGroup); privateState.ShieldProtectionCurrentChargeElectricity = electricityCapacity * chargeFraction; privateState.ShieldProtectionCooldownExpirationTime = 0; // reset the cooldown return($"Charge amount of the S.H.I.E.L.D. modified to {chargeFraction * 100}% and cooldown reset."); }
/// <summary> /// This check prevents the abuse of the shield mechanic for this layout: /// * * * /// * X * /// * * * /// Where X is the land claim surrounded by 8 other * land claims (of max tier level), /// but not connected to them so normally it should provide its own shield. /// </summary> public static bool SharedIsLandClaimInsideAnotherBase(ILogicObject areasGroup) { var groupAreas = Api.IsServer ? LandClaimAreasGroup.GetPrivateState(areasGroup).ServerLandClaimsAreas : LandClaimSystem.ClientGetKnownAreasForGroup(areasGroup); if (groupAreas.Count() > 1) { // definitely cannot be inside another base as the base size is limited to 3*3 bases return(false); } // Check whether this area is surrounded by other land claims. // We will use a flood fill approach. // First, create and fill an array of tiles occupied by other land claims // for the max land claim area bounds. var centerArea = groupAreas.First(); var centerAreaState = LandClaimArea.GetPublicState(centerArea); var landClaimCenterTilePosition = centerAreaState.LandClaimCenterTilePosition; var newAreaBounds = LandClaimSystem.SharedCalculateLandClaimAreaBounds( landClaimCenterTilePosition, (ushort)(LandClaimSystem.MaxLandClaimSizeWithGraceArea.Value // reduce the outer bounds as it's the buffer area - LandClaimSystem.MinPaddingSizeOneDirection * 2)); using var tempListAreas = Api.Shared.GetTempList <ILogicObject>(); { using var tempList = Api.Shared.GetTempList <ILogicObject>(); LandClaimSystem.SharedGetAreasInBounds(newAreaBounds, tempList, addGracePadding: true); foreach (var area in tempList.AsList()) { tempListAreas.AddIfNotContains(area); } } if (tempListAreas.Count == 1) { // there is only a single area return(false); } var arraySizeX = newAreaBounds.Width + 2; var arraySizeY = newAreaBounds.Height + 2; var arrayOffset = new Vector2Int(newAreaBounds.X - 1, newAreaBounds.Y - 1); var arrayCoverage = new bool[arraySizeX, arraySizeY]; foreach (var otherArea in tempListAreas.AsList()) { if (ReferenceEquals(centerArea, otherArea)) { continue; } var bounds = LandClaimSystem.SharedGetLandClaimAreaBounds(otherArea, addGracePadding: false); FillArray(new RectangleInt(bounds.X - arrayOffset.X, bounds.Y - arrayOffset.Y, bounds.Width, bounds.Height)); } WriteArrayToLog(); // now we have a filled array, detect whether it represents an enclosed space by filling it from the center var isEnclosed = TestIsEnclosed((arraySizeX / 2, arraySizeY / 2)); //Api.Logger.Dev("Result: " + (isEnclosed ? "Enclosed" : "Not enclosed")); WriteArrayToLog(); return(isEnclosed); void FillArray(RectangleInt bounds) { var fromX = Math.Max(0, bounds.Left); var fromY = Math.Max(0, bounds.Bottom); var toX = Math.Min(arraySizeX, bounds.Right); var toY = Math.Min(arraySizeY, bounds.Top); for (var y = fromY; y < toY; y++) { for (var x = fromX; x < toX; x++) { arrayCoverage[x, y] = true; } } } bool TestIsEnclosed(Vector2Int startPosition) { var stack = new Stack <Vector2Int>(); stack.Push(startPosition); while (stack.Count > 0) { var pos = stack.Pop(); if (pos.X < 0 || pos.Y < 0 || pos.X >= arraySizeX || pos.Y >= arraySizeY) { // reached the bound - not enclosed return(false); } if (arrayCoverage[pos.X, pos.Y]) { // another land claim there or already visited continue; } arrayCoverage[pos.X, pos.Y] = true; // fill it stack.Push((pos.X - 1, pos.Y)); stack.Push((pos.X + 1, pos.Y)); stack.Push((pos.X, pos.Y - 1)); stack.Push((pos.X, pos.Y + 1)); } // was unable to reach the bound (filled in the enclosed space) return(true); } void WriteArrayToLog() { // we don't need this debug data but it might be useful someday return; var sb = new StringBuilder("Coverage array: (Y is reversed)").AppendLine(); for (var y = 0; y < arraySizeY; y++) { for (var x = 0; x < arraySizeX; x++) { sb.Append(arrayCoverage[x, y] ? "x" : "."); } sb.AppendLine(); } Api.Logger.Dev(sb); } }