// Duplication of HBS code, avoiding prefix=true for now. public static void Postfix(ref BehaviorTreeResults __result, string ___name, BehaviorTree ___tree, AbstractActor ___unit) { Mod.Log.Info?.Write("CJMCN:T - entered"); Mech mech = ___unit as Mech; if (mech != null && mech.WorkingJumpjets > 0) { string stayInsideRegionGUID = RegionUtil.GetStayInsideRegionGUID(___unit); float acceptableHeat = AIUtil.GetAcceptableHeatLevelForMech(mech); float currentHeat = (float)mech.CurrentHeat; Mod.Log.Info?.Write($"CJMCN:T - === actor:{CombatantUtils.Label(mech)} has currentHeat:{currentHeat} and acceptableHeat:{acceptableHeat}"); List <PathNode> sampledPathNodes = ___unit.JumpPathing.GetSampledPathNodes(); Mod.Log.Info?.Write($"CJMCN:T - calculating {sampledPathNodes.Count} nodes"); for (int i = 0; i < sampledPathNodes.Count; i++) { Vector3 candidatePos = sampledPathNodes[i].Position; float distanceBetween2D = AIUtil.Get2DDistanceBetweenVector3s(candidatePos, ___unit.CurrentPosition); float distanceBetween3D = Vector3.Distance(candidatePos, ___unit.CurrentPosition); Mod.Log.Info?.Write($"CJMCN:T - calculated distances 2D:'{distanceBetween2D}' 3D:'{distanceBetween3D} "); if (distanceBetween2D >= 1f) { float magnitude = (candidatePos - ___unit.CurrentPosition).magnitude; float jumpHeat = (float)mech.CalcJumpHeat(magnitude); Mod.Log.Info?.Write($"CJMCN:T - calculated jumpHeat:'{jumpHeat}' from magnitude:'{magnitude}. "); Mod.Log.Info?.Write($"CJMCN:T - comparing heat: [jumpHeat:'{jumpHeat}' + currentHeat:'{currentHeat}'] <= acceptableHeat:'{acceptableHeat}. "); if (jumpHeat + (float)mech.CurrentHeat <= acceptableHeat) { if (stayInsideRegionGUID != null) { MapTerrainDataCell cellAt = ___unit.Combat.MapMetaData.GetCellAt(candidatePos); if (cellAt != null) { MapEncounterLayerDataCell mapEncounterLayerDataCell = cellAt.MapEncounterLayerDataCell; if (mapEncounterLayerDataCell != null && mapEncounterLayerDataCell.regionGuidList != null && !mapEncounterLayerDataCell.regionGuidList.Contains(stayInsideRegionGUID)) { // Skip this loop iteration if Mod.Log.Info?.Write($"CJMCN:T - candidate outside of constraint region, ignoring."); goto CANDIDATE_OUTSIDE_REGION; } } } Mod.Log.Info?.Write($"CJMCN:T - adding candidate position:{candidatePos}"); ___tree.movementCandidateLocations.Add(new MoveDestination(sampledPathNodes[i], MoveType.Jumping)); } } CANDIDATE_OUTSIDE_REGION :; } } // Should already be set by prefix method //__result = BehaviorTreeResults(BehaviorNodeState.Success); }
private void RegenerateRegion(GameObject regionGo) { CombatGameState combatState = UnityGameInstance.BattleTechGame.Combat; RegionGameLogic regionGameLogic = regionGo.GetComponent <RegionGameLogic>(); List <Vector3> meshPoints = new List <Vector3>(); // Get all region points and fix the y height foreach (Transform t in regionGo.transform) { if (t.gameObject.name.StartsWith("RegionPoint")) { Vector3 position = t.position; float height = combatState.MapMetaData.GetLerpedHeightAt(position); Vector3 fixedHeightPosition = new Vector3(position.x, height, position.z); t.position = fixedHeightPosition; meshPoints.Add(t.localPosition); } } // Create new mesh from points and set to collider and mesh filter MeshCollider collider = regionGo.GetComponent <MeshCollider>(); MeshFilter mf = regionGo.GetComponent <MeshFilter>(); Mesh mesh = MeshTools.CreateHexigon(REGION_RADIUS, meshPoints); collider.sharedMesh = mesh; mf.mesh = mesh; List <MapEncounterLayerDataCell> cells = SceneUtils.GetMapEncounterLayerDataCellsWithinCollider(regionGo); for (int i = 0; i < cells.Count; i++) { MapEncounterLayerDataCell cell = cells[i]; cell.AddRegion(regionGameLogic); } }
public static bool PointInRegion(CombatGameState combat, Vector3 point, string regionGUID) { MapEncounterLayerDataCell cell = combat.EncounterLayerData.GetCellAt(point); List <string> cellRegionGUIDList = cell.GetRegionGuids(); return((cellRegionGUIDList == null) ? false : cellRegionGUIDList.Contains(regionGUID)); }
public static void SpawnAmbush(Vector3 originPos) { if (!Mod.Config.ExplosionAmbush.Enabled) { return; } // Determine the number of blasts that will occurs int numBlasts = Mod.Random.Next(ModState.ExplosionAmbushDefForContract.MinSpawns, ModState.ExplosionAmbushDefForContract.MaxSpawns); Mod.Log.Info?.Write($"Explosion ambush will apply {numBlasts} blasts."); // Determine the positions for the blasts Vector3 originHex = ModState.Combat.HexGrid.GetClosestPointOnGrid(originPos); List <Vector3> blastPositions = new List <Vector3> { originHex }; List <Vector3> adjacentHexes = ModState.Combat.HexGrid.GetGridPointsAroundPointWithinRadius(originPos, 2); Mod.Log.Debug?.Write($"Found origin hex: {originHex} with {adjacentHexes.Count} adjacent hexes within {2} hexes"); for (int i = 0; i < numBlasts - 1; i++) { if (adjacentHexes.Count == 0) { break; } int randIdx = Mod.Random.Next(0, adjacentHexes.Count); Vector3 newHexPos = adjacentHexes[randIdx]; Mod.Log.Debug?.Write($" Adding additional blast position: {newHexPos}"); EncounterLayerData encounterLayerData = ModState.Combat.EncounterLayerData; Point cellPoint = new Point( ModState.Combat.MapMetaData.GetXIndex(newHexPos.x), ModState.Combat.MapMetaData.GetZIndex(newHexPos.z)); MapEncounterLayerDataCell melDataCell = encounterLayerData.mapEncounterLayerDataCells[cellPoint.Z, cellPoint.X]; Mod.Log.Debug?.Write($" TerrainCell cached height: {melDataCell.relatedTerrainCell.cachedHeight}"); blastPositions.Add(new Vector3(newHexPos.x, melDataCell.relatedTerrainCell.cachedHeight, newHexPos.z)); adjacentHexes.RemoveAt(randIdx); } // Load the weapons we'll use in the blast List <AOEBlastDef> blastsForAttack = RandomizeBlastDefs(numBlasts); Mod.Log.Debug?.Write($"Sending AddSequence message for ambush explosion with {blastsForAttack.Count} weapons and {blastPositions.Count} blasts"); try { ExplosionAmbushSequence ambushSequence = new ExplosionAmbushSequence(ModState.Combat, blastPositions, blastsForAttack, ModState.AmbushTeam); ModState.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(ambushSequence)); } catch (Exception e) { Mod.Log.Error?.Write(e, "Failed to create AES sequence due to error!"); } }
public static void RemoveRegion(this MapEncounterLayerDataCell layerDataCell, RegionGameLogic regionGameLogic) { if (layerDataCell.regionGuidList == null) { return; } if (layerDataCell.regionGuidList.Contains(regionGameLogic.encounterObjectGuid)) { layerDataCell.regionGuidList.Remove(regionGameLogic.encounterObjectGuid); } }
static void Prefix(MapEncounterLayerDataCell __instance) { if (MissionControl.Instance.IsCustomContractType) { // Since we copy data from other encounters, they have dirty regions in them. Clear them so we're fresh. // TODO: When we add our regions for the custom type ensure we don't clear those if (__instance.regionGuidList != null) { __instance.regionGuidList.Clear(); } } }
public static void Regenerate(this RegionGameLogic regionGameLogic) { GameObject regionGo = regionGameLogic.gameObject; CombatGameState combatState = UnityGameInstance.BattleTechGame.Combat; List <Vector3> meshPoints = new List <Vector3>(); // Remove old region location from layer data cells List <MapEncounterLayerDataCell> beforeCells = SceneUtils.GetMapEncounterLayerDataCellsWithinCollider(regionGo); for (int i = 0; i < beforeCells.Count; i++) { MapEncounterLayerDataCell cell = beforeCells[i]; cell.RemoveRegion(regionGameLogic); } // Get all region points and fix the y height foreach (Transform t in regionGo.transform) { if (t.gameObject.name.StartsWith("RegionPoint")) { Vector3 position = t.position; float height = combatState.MapMetaData.GetLerpedHeightAt(position); Vector3 fixedHeightPosition = new Vector3(position.x, height, position.z); t.position = fixedHeightPosition; meshPoints.Add(t.localPosition); } } // Create new mesh from points and set to collider and mesh filter MeshCollider collider = regionGo.GetComponent <MeshCollider>(); MeshFilter mf = regionGo.GetComponent <MeshFilter>(); Mesh mesh = MeshTools.CreateHexigon(regionGameLogic.radius, meshPoints); collider.sharedMesh = mesh; mf.mesh = mesh; List <MapEncounterLayerDataCell> afterCells = SceneUtils.GetMapEncounterLayerDataCellsWithinCollider(regionGo); for (int i = 0; i < afterCells.Count; i++) { MapEncounterLayerDataCell cell = afterCells[i]; cell.AddRegion(regionGameLogic); } }
public static MapEncounterLayerDataCell GetOrCreateEncounterLayerDataCell(int x, int z) { // Add a safe get cell if (MissionControl.MissionControl.Instance.EncounterLayerData.IsWithinBounds(x, z)) { MapEncounterLayerDataCell encounterLayerDataCell = MissionControl.MissionControl.Instance.EncounterLayerData.GetSafeCellAt(x, z); if (encounterLayerDataCell == null) { encounterLayerDataCell = new MapEncounterLayerDataCell(); } if (encounterLayerDataCell.relatedTerrainCell == null) { encounterLayerDataCell.relatedTerrainCell = UnityGameInstance.BattleTechGame.Combat.MapMetaData.SafeGetCellAt(x, z); } // Seems the x, z are reversed in the BT source, so better keep it the same MissionControl.MissionControl.Instance.EncounterLayerData.mapEncounterLayerDataCells[z, x] = encounterLayerDataCell; return(encounterLayerDataCell); } return(null); }
public static List <MapEncounterLayerDataCell> GetMapEncounterLayerDataCellsWithinCollider(GameObject regionGo) { MeshCollider collider = regionGo.GetComponent <MeshCollider>(); RegionGameLogic regionGameLogic = regionGo.GetComponent <RegionGameLogic>(); List <MapEncounterLayerDataCell> cells = new List <MapEncounterLayerDataCell>(); Vector3 colliderExtents = collider.bounds.extents; Vector3 colliderCenter = collider.bounds.center; EncounterLayerData encounterLayerData = MissionControl.MissionControl.Instance.EncounterLayerData; int cellX = encounterLayerData.GetXIndex(colliderCenter.x); int cellZ = encounterLayerData.GetZIndex(colliderCenter.z); // Add center MapEncounterLayerDataCell layerDataCell = GetOrCreateEncounterLayerDataCell(cellX, cellZ); cells.Add(layerDataCell); float bottom = colliderCenter.x - colliderExtents.x; float top = colliderCenter.x + colliderExtents.x; float left = colliderCenter.z - colliderExtents.z; float right = colliderCenter.z + colliderExtents.z; for (float i = bottom; i < top; i += 0.5f) { for (float j = left; j < right; j += 0.5f) { cellX = encounterLayerData.GetXIndex(i); cellZ = encounterLayerData.GetZIndex(j); layerDataCell = GetOrCreateEncounterLayerDataCell(cellX, cellZ); if (layerDataCell != null) { cells.Add(layerDataCell); } } } return(cells); }
public static AbstractActor SpawnAmbushTurret(Team team, Lance ambushLance, BattleTech.Building building, Vector3 ambushOrigin) { // Randomly determine one of the spawnpairs from the current ambushdef List <TurretAndPilotDef> shuffledSpawns = new List <TurretAndPilotDef>(); shuffledSpawns.AddRange(ModState.InfantryAmbushDefForContract.SpawnPool); shuffledSpawns.Shuffle(); TurretAndPilotDef ambushDef = shuffledSpawns[0]; PilotDef pilotDef = ModState.Combat.DataManager.PilotDefs.Get(ambushDef.PilotDefId); TurretDef turretDef = ModState.Combat.DataManager.TurretDefs.GetOrCreate(ambushDef.TurretDefId); turretDef.Refresh(); // determine a position somewhere up the building's axis EncounterLayerData encounterLayerData = ModState.Combat.EncounterLayerData; Point cellPoint = new Point( ModState.Combat.MapMetaData.GetXIndex(building.CurrentPosition.x), ModState.Combat.MapMetaData.GetZIndex(building.CurrentPosition.z)); MapEncounterLayerDataCell melDataCell = encounterLayerData.mapEncounterLayerDataCells[cellPoint.Z, cellPoint.X]; float buildingHeight = melDataCell.GetBuildingHeight(); float terrainHeight = ModState.Combat.MapMetaData.GetLerpedHeightAt(building.CurrentPosition, true); float heightDelta = (buildingHeight - terrainHeight) * 0.7f; float adjustedY = terrainHeight + heightDelta; Mod.Log.Debug?.Write($"At building position, terrain height is: {terrainHeight} while buildingHeight is: {buildingHeight}. " + $" Calculated 70% of building height + terrain as {adjustedY}."); Vector3 newPosition = building.GameRep.transform.position; newPosition.y = adjustedY; Mod.Log.Debug?.Write($"Changing transform position from: {building.GameRep.transform.position} to {newPosition}"); /// Rotate to face the ambush origin Vector3 spawnDirection = Vector3.RotateTowards(building.CurrentRotation.eulerAngles, ambushOrigin, 1f, 0f); Quaternion spawnRotation = Quaternion.LookRotation(spawnDirection); // Create the turret Turret turret = ActorFactory.CreateTurret(turretDef, pilotDef, team.EncounterTags, ModState.Combat, team.GetNextSupportUnitGuid(), "", null); turret.Init(newPosition, spawnRotation.eulerAngles.y, true); turret.InitGameRep(null); if (turret == null) { Mod.Log.Error?.Write($"Failed to spawn turretDefId: {ambushDef.TurretDefId} + pilotDefId: {ambushDef.PilotDefId} !"); } Mod.Log.Debug?.Write($" Spawned trap turret, adding to team."); team.AddUnit(turret); turret.AddToTeam(team); turret.AddToLance(ambushLance); turret.BehaviorTree = BehaviorTreeFactory.MakeBehaviorTree(ModState.Combat.BattleTechGame, turret, BehaviorTreeIDEnum.CoreAITree); Mod.Log.Debug?.Write("Updated turret behaviorTree"); ModState.AmbushBuildingGUIDToTurrets.Add(building.GUID, turret); ModState.AmbushTurretGUIDtoBuilding.Add(turret.GUID, building); // Associate the building withe the team building.AddToTeam(team); building.BuildingRep.IsTargetable = true; building.BuildingRep.SetHighlightColor(ModState.Combat, team); building.BuildingRep.RefreshEdgeCache(); // Increase the building's health to the current value + turret structure float combinedStructure = (float)Math.Ceiling(building.CurrentStructure + turret.GetCurrentStructure(BuildingLocation.Structure)); Mod.Log.Debug?.Write($"Setting ambush structure to: {combinedStructure} = building.currentStructure: {building.CurrentStructure} + " + $"turret.currentStructure: {turret.GetCurrentStructure(BuildingLocation.Structure)}"); building.StatCollection.Set <float>("Structure", combinedStructure); // Finally notify others UnitSpawnedMessage message = new UnitSpawnedMessage("CJ_TRAP", turret.GUID); ModState.Combat.MessageCenter.PublishMessage(message); // Finally force the turret to be fully visible turret.OnPlayerVisibilityChanged(VisibilityLevel.LOSFull); return(turret); }
public void GenerateEncounterLayerBuildingData() { Main.Logger.LogDebug($"[EncounterDataManager.GenerateEncounterLayerBuildingData] Generating building data"); Terrain terrain = Terrain.activeTerrain; float terrainZSize = terrain.terrainData.size.z; float terrainXSize = terrain.terrainData.size.x; Vector3 terrainPosition = terrain.transform.position; float halfMapCellSize = (float)MapMetaDataExporter.cellSize / 2f; int halfMapCellSizeInt = MapMetaDataExporter.cellSize / 2; EncounterLayerParent encounterLayerParent = MissionControl.Instance.EncounterLayerParent; EncounterLayerData encounterLayerData = MissionControl.Instance.EncounterLayerData; MapMetaDataExporter mapMetaExporter = encounterLayerParent.GetComponent <MapMetaDataExporter>(); MapMetaData mapMetaData = UnityGameInstance.BattleTechGame.Combat.MapMetaData; Vector3 raycastOrigin = new Vector3(0f, 1000f, 0f); // Lookups Dictionary <string, int> regionRaycastHits = new Dictionary <string, int>(); List <RegionGameLogic> regionGameObjectList = new List <RegionGameLogic>(); List <ObstructionGameLogic> obstructionGameObjectList = new List <ObstructionGameLogic>(); // Marks only the ObstructionGameLogic objects for ray tracing for performance reasons AccessTools.Method(typeof(MapMetaDataExporter), "MarkCellsForRaycasting").Invoke(mapMetaExporter, new object[] { mapMetaData.mapTerrainDataCells, (int)terrain.transform.position.x, (int)terrain.transform.position.z }); // TODO: Maybe wipe region building lists. Not sure if I really need/want this yet RegionGameLogic[] componentsInChildren = encounterLayerData.GetComponentsInChildren <RegionGameLogic>(); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].InitRegionForRayCasting(); } // Iterate over the Z cell range for (float i = halfMapCellSize; i < terrainZSize; i += (float)MapMetaDataExporter.cellSize) { int zCellIndex = (int)i / MapMetaDataExporter.cellSize; // Iterate over the X cell range for (float j = halfMapCellSize; j < terrainXSize; j += (float)MapMetaDataExporter.cellSize) { int xCellIndex = (int)j / MapMetaDataExporter.cellSize; MapEncounterLayerDataCell mapEncounterLayerDataCell = new MapEncounterLayerDataCell(); if (mapMetaData.mapTerrainDataCells[zCellIndex, xCellIndex].doRayCast) { int hitIndex = 0; regionRaycastHits.Clear(); regionGameObjectList.Clear(); obstructionGameObjectList.Clear(); for (int k = -halfMapCellSizeInt; k < halfMapCellSizeInt; k++) { raycastOrigin.z = i + (float)k + terrainPosition.z; for (int l = -halfMapCellSizeInt; l < halfMapCellSizeInt; l++) { raycastOrigin.x = j + (float)l + terrainPosition.x; RaycastHit[] raycastHits = Physics.RaycastAll(raycastOrigin, Vector3.down); List <ObstructionGameLogic> list3 = new List <ObstructionGameLogic>(); // Go through all the raycasts at Z,X of the terrain by cell size (middle of cell) // Then find any regions hit, record the number of hits/cells the region has for (int m = 0; m < raycastHits.Length; m++) { RegionGameLogic regionGameLogic = raycastHits[m].transform.GetComponent <RegionGameLogic>(); if (regionGameLogic != null) { if (!regionRaycastHits.ContainsKey(regionGameLogic.encounterObjectGuid)) { regionRaycastHits[regionGameLogic.encounterObjectGuid] = 1; } else { string encounterObjectGuid = regionGameLogic.encounterObjectGuid; regionRaycastHits[encounterObjectGuid]++; } // Cache the region in the lookup if (!regionGameObjectList.Contains(regionGameLogic)) { regionGameObjectList.Add(regionGameLogic); } } ObstructionGameLogic obstructionGameLogicInParent = raycastHits[m].transform.GetComponentInParent <ObstructionGameLogic>(); if (obstructionGameLogicInParent != null && raycastHits[m].point.y > mapMetaData.mapTerrainDataCells[zCellIndex, xCellIndex].terrainHeight) { if (obstructionGameLogicInParent.IsBuildingHitAddWorthy) { if (!obstructionGameObjectList.Contains(obstructionGameLogicInParent)) { obstructionGameObjectList.Add(obstructionGameLogicInParent); } } Vector3 normal = raycastHits[m].normal; BuildingRaycastHit buildingRaycastHit = new BuildingRaycastHit { buildingSteepness = 90f - 57.29578f * Mathf.Atan2(normal.y, Mathf.Sqrt(normal.x * normal.x + normal.z * normal.z)), buildingHeight = raycastHits[m].point.y, buildingGuid = obstructionGameLogicInParent.encounterObjectGuid, hitIndex = hitIndex }; mapEncounterLayerDataCell.AddBuildingHit(buildingRaycastHit); } } hitIndex++; } } // For all the regions detected, if it exists in 10 or more cells - add the region to the map encounter layer data (this is vanilla... why?!) // And all all obstruction games logics to the region foreach (RegionGameLogic regionGameLogic in regionGameObjectList) { if (regionRaycastHits[regionGameLogic.encounterObjectGuid] >= 10) { mapEncounterLayerDataCell.AddRegion(regionGameLogic); foreach (ObstructionGameLogic obstructionGameLogic in obstructionGameObjectList) { regionGameLogic.AddBuildingGuidToRegion(obstructionGameLogic.encounterObjectGuid); } } } mapEncounterLayerDataCell.AverageTheBuildingHits(); mapEncounterLayerDataCell.SortBuildingListByHeight(); } encounterLayerData.mapEncounterLayerDataCells[zCellIndex, xCellIndex] = mapEncounterLayerDataCell; encounterLayerData.mapEncounterLayerDataCells[zCellIndex, xCellIndex].relatedTerrainCell = mapMetaData.mapTerrainDataCells[zCellIndex, xCellIndex]; mapMetaData.mapTerrainDataCells[zCellIndex, xCellIndex].MapEncounterLayerDataCell = mapEncounterLayerDataCell; } } }