private List <BeyondGroup> findCloseGroups(BeyondComponent bc, out BeyondGroup closestGroup) { List <BeyondGroup> result = new List <BeyondGroup>(); // Find the groups that are close to this BeyondComponent Collider[] collidersInGroup = Physics.OverlapBox(bc.transform.position, bc.template.castBox + new Vector3(1f, 1f, 1f) * groupSnapTolerance, bc.transform.rotation, ConstraintController.getBuildingsMask()); closestGroup = null; float minDistance = 0; foreach (Collider c in collidersInGroup) { BeyondComponent collided_bc = c.transform.GetComponent <BeyondComponent>(); if (collided_bc != null && collided_bc.beyondGroup != null) { result.Add(collided_bc.beyondGroup); float distance = Vector3.Distance(bc.transform.position, collided_bc.transform.position); if (closestGroup == null || distance < minDistance) { minDistance = distance; closestGroup = collided_bc.beyondGroup; } } } UIController.Instance.SetClosestGroup(closestGroup); return(result); }
public bool addBeyondComponent(BeyondComponent bc) { // On-demand creation of componentList (for deserialization) if (componentList == null) { componentList = new List <BeyondComponent>(); } if (componentList.Contains(bc)) { return(false); } else { componentList.Add(bc); // On-demand creation of group GameObject (for deserialization) if (groupObject == null) { CreateGroupObject(); } bc.transform.SetParent(groupObject.transform, false); return(true); } }
public bool CheckAvoidCollision(GameObject go) { BeyondComponent bc = go.GetComponent <BeyondComponent>(); //TO DO : Can't I make this layer mask better ? Creating a list each time I check collisions is inefficient List <string> layerList = new List <string>(); foreach (string layerName in Operands) { layerList.Add(layerName); } LayerMask mask = LayerMask.GetMask(layerList.ToArray()); // 2 cases here: // 1 - If colliding with object in different group -> always fail // 2 - If collidind with object in same group -> don't fail , but another test somewhere must make sure my position in the group is valid (has enough neigbours in the right positions, is not isolated from the group, etc) Collider[] hitColliders = Physics.OverlapBox(go.transform.position, bc.Template.CastBox, go.transform.rotation, mask); foreach (Collider col in hitColliders) { BeyondComponent bcCol = col.gameObject.GetComponent <BeyondComponent>(); if ((bcCol == null) || (bcCol.Group != bc.Group)) { return(false); } } // So this below used to be enough: //return (hitColliders.Length == 0); // But now I need to go through all objects I collided with and fail only if objects are not in the same group, or don't have beyond components (should not happen though, since they are in the building layer) return(true); }
void StartDrag() { ForwardOffsetBeforeDragging = UIController.Instance.forwardOffset; if (ConstraintController.CanPlace(currentBC)) { string templateName = currentBC.template.name; string name = templateName + "_" + (nbObjectsPlaced++); // Place the first Ghost = still green, still not collidin' TemplateController.PlaceObject(currentBC, name, BC_State.Ghost); draggedBC.Add(currentBC); draggingGroup = currentBC.beyondGroup; lastGroupPosition = currentBC.groupPosition; currentBC = null; CreateNewPlaceableObject(templateName); // TODO : another hardcoded position. Not good. Yet, I must move the currentBC to where I started dragging or else I'm going to drag to some unknown place. currentBC.transform.position = draggedBC[0].transform.position; currentBC.transform.rotation = draggedBC[0].transform.rotation; // Instantiate a big bunch of placeable object based on what we are currently dragging for (int i = 0; i < MaxDraggedObjectCount; i++) { BeyondComponent bc = TemplateController.CreateObject(templateName); //TODO Better names, please name = templateName + "_Ghost" + i; bc.SetBCinGroup(draggingGroup, lastGroupPosition); TemplateController.PlaceObject(bc, name, BC_State.Ghost); bc.gameObject.SetActive(false); draggedBC.Add(bc); } } }
/* * SUPERCEEDED by dragging (or is it ?) * public void TryPlacingBlueprint() * { * //TO DO : SHould check if we are on UI, don't click if we are * if (ActiveBlueprint!=null) * { * if ( Constraint.CheckRootConstraint(ActiveBlueprint)) * { * GameObject PlacedObject = Instantiate(ActiveBlueprint , ActiveBlueprint.transform.position , ActiveBlueprint.transform.rotation); * * // Copy BeyondComponent values from the blueprint to the PlacedObject * BeyondComponent blueprintBC = ActiveBlueprint.GetComponent<BeyondComponent>(); * BeyondComponent bc = PlacedObject.GetComponent<BeyondComponent>(); * bc.SetValues(blueprintBC.Template, State.Ghost , blueprintBC.BuildingMaterials); * * // Remove Outline, disable "isTrigger", set layer, name object * PlacedObject.GetComponent<BoxCollider>().isTrigger = false; * PlacedObject.layer = LayerMask.NameToLayer("Buildings"); * PlacedObject.name = bc.Template.Name; * CreateNewBeyondGroup(bc); * } * DestroyActiveBlueprint(); * } * } */ public void StartDragging() { if (ActiveBlueprint != null) { if (Constraint.CheckRootConstraint(ActiveBlueprint)) { GameObject_DragFrom = Instantiate(ActiveBlueprint, ActiveBlueprint.transform.position, ActiveBlueprint.transform.rotation); BeyondComponent bc = ActiveBlueprint.GetComponent <BeyondComponent>(); GameObject_DragFrom.GetComponent <BeyondComponent>().CopyValues(bc); dragDirections = Utility.RotatedAxes(GameObject_DragFrom.transform.rotation); // Clearing & initialising the pool of DraggedObjects for (int i = 0; i < MaxDraggedObjects; i++) { GameObject go = Instantiate(ActiveBlueprint); go.SetActive(false); // I need to initialise the BC of each dragged object so I know what template they are. Instantiating them was not enough to copy the ActiveBlueprint's BC go.GetComponent <BeyondComponent>().CopyValues(bc); draggedObjects.Add(go); } ActiveBlueprint.SetActive(false); } } Debug.Log("Start Dragging initialised draggedObjects: " + draggedObjects.Count); }
public void AddObject(GameObject go) { if (go == null) { throw new BeyondException("GameObject is null"); } if (GroupObject == null) { throw new BeyondException("GroupObject is null"); } BeyondComponent bc = go.GetComponent <BeyondComponent>(); if (bc == null) { throw new BeyondException("No Beyond Component attached to GameObject"); } // Set BC's group position Vector3Int groupPosition = Vector3Int.RoundToInt(Utility.RotateAroundPoint(go.transform.position - Position, Vector3.zero, Rotation)); //TO DO : check if this position is already occupied, which is tricky since some objects (like walls or cables) can co-exist in the same cell bc.SetGroupPosition(this, groupPosition); go.transform.SetParent(GroupObject.transform); ComponentList.Add(bc); }
// Is the bottom inside terrain by enough ? private static bool BaseInTerrain(BeyondComponent bc) { Vector3 BoxCast = bc.template.castBox; Vector3 p = bc.transform.gameObject.transform.position; Quaternion q = bc.transform.gameObject.transform.rotation; // Cast 4 boxes at each corner of the bottom of the object // Their centres are: p + BoxCast in both X and Z, plus FoundationInTerrainBy / 2 for (float i = -1; i <= 2; i += 2) { for (float j = -1; j <= 2; j += 2) { Vector3 point = (p + new Vector3((BoxCast.x - FoundationInTerrainBy / 2) * i, -BoxCast.y + FoundationInTerrainBy / 2, (BoxCast.z - FoundationInTerrainBy / 2) * j)); point = Utility.RotateAroundPoint(point, p, q); //Debug.DrawLine(point + (Vector3.down * YOffset), new Vector3(point.x , point.y- FoundationInTerrainBy/2 , point.z) , Color.yellow , 0.25f); // The height of the boxes' starting point must be offset by the template's half height (BoxCast.y) if (Physics.BoxCast(point - (Vector3.down * BoxCast.y), BoxCast, Vector3.down, q, Mathf.Infinity, getTerrainMask())) { return(false); } } } return(true); }
private static bool NeedsAll(BeyondComponent bc, BeyondGroup bg, List <string> templatesList, List <Vector3Int> offsetsList, List <cellSide> cellSides, Vector3Int?optionalGroupPos = null) { if (bg == null) { return(false); } Vector3Int groupPos = (optionalGroupPos != null) ? (Vector3Int)optionalGroupPos : bc.groupPosition; foreach (string templateName in templatesList) { foreach (Vector3Int offset in offsetsList) { bool foundOne = false; foreach (cellSide side in cellSides) { if (IsTemplatePresentHere(bg, groupPos + offset, templateName, side)) { foundOne = true; } } if (!foundOne) { return(false); } } } return(true); }
void Update() { if (UIController.Instance.gameMode == gameMode.build) { //All this should only happens when gameMode=build HandleNewObjectHotkey(); if (currentBC != null) { //bool wasRotated = RotateFromMouseWheel(); RotateFromMouseWheel(); //if (Input.mousePosition!=mousePosition || wasRotated) //{ // only move placeable object when mouse has moved mousePosition = Input.mousePosition; MovePlaceableObjectToMouse(); //} // Make the placeable red or green based on whether it can be placed ConstraintController.SetCanPlaceObjectColour(currentBC); Drag(); PlaceOnClic(); } } else if (currentBC != null) { // Always destroy placeable object when we leave build mode Destroy(currentBC.gameObject); Debug.Log("Going off build mode destroyed placeable object"); currentBC = null; } }
public static void SetCanPlaceObjectColour(BeyondComponent bc) { Renderer r = bc.gameObject.GetComponent <Renderer>(); r.material.color = (ConstraintController.CanPlace(bc) ? Color.green : Color.red); //Debug.Log("Setting can place colour on " + bc.name + " in group " + (bc.beyondGroup==null ? "null" : bc.beyondGroup.name) + " at position "+bc.groupPosition + " = "+ ConstraintController.CanSnapTo(bc, bc.beyondGroup, bc.groupPosition)); //r.material.color = (ConstraintController.CanSnapTo(bc, bc.beyondGroup , bc.groupPosition) ? Color.green : Color.red); }
void SetBlueprintFromGhost(GameObject go) { go.GetComponent <BoxCollider>().isTrigger = false; BeyondComponent bc = go.GetComponent <BeyondComponent>(); go.name = bc.Template.Name; bc.SetState(State.Blueprint); EffectManager.UpdateBlueprintVisuals(go); }
public bool removeBeyondComponent(BeyondComponent bc) { if (componentList.Contains(bc)) { componentList.Remove(bc); return(true); } return(false); }
//TODO : All this is currently hardcoded but should just do CheckConstraints(bc) public static bool CanPlace(BeyondComponent bc) { // Won't work on a NULL BeyondComponent if (bc == null) { return(false); } // can't already have a the same object at the same place & position if (IsTemplatePresentHere(bc.beyondGroup, bc.groupPosition, bc.template.name, bc.side)) { return(false); } // Object can't collide with other objects in different group if (bc.collidingWithBuilding()) { return(false); } if (bc.template.name == "Foundation") { // 1 - Foundations must be partially inside terrain, but their top must not be covered by it if (!BaseInTerrain(bc)) { return(false); } if (!TopClear(bc, getTreesMask())) { return(false); } if (AllClear(bc, ConstraintController.getTreesMask())) { return(true); } } else { // 2 -All non-foundations objects must be clear of terrain and trees- return false immediately if they're not if (AllClear(bc, getTerrainAndTreesMask())) { return(true); } } //3 - All non-foundations must be snapped to another building part if (bc.template.name != "Foundation" && bc.beyondGroup == null) { // TO DO : I must check if I can snap here, but how to do that with dragged ghosts ? return(false); } return(false); //5 - Think of moveable objects later (they're not in groups, so they don't snap = constraints are easier) }
private static bool AndConstraints(BeyondComponent bc, BeyondGroup bg, List <Constraints> lc, Vector3Int?optionalGroupPos = null) { foreach (Constraints c in lc) { if (!CheckConstraints(bc, c, bg, optionalGroupPos)) { return(false); } } return(true); }
private static bool TopClear(BeyondComponent bc, LayerMask mask) { Vector3 castFrom = bc.transform.position; castFrom.y = PlaceController.Instance.place.Height; // Cast from the highest possible altitude RaycastHit hitInfo; float rayLength = PlaceController.Instance.place.Height - bc.transform.position.y - bc.template.castBox.y * 2; bool result = Physics.BoxCast(castFrom, bc.template.castBox, Vector3.down, out hitInfo, bc.transform.rotation, rayLength, mask); //if (result) Debug.Log(bc.gameObject.name+" top is not clear"); return(!result); }
private static bool FirstInGroup(BeyondComponent bc, BeyondGroup bg) { if (bg == null) { return(true); // By definition, if there's no group, the bc fulfills this constraint } if (bg.componentList.Count == 1) { return(true); } return(false); }
// TODO : should this really be hardcoded this badly ? I might not need to even put that in the templates but just have a list somewhere // OR: not even needed since all elemetns should be more or less above terrain ? /* * public static bool ShowOnTerrain(Template t) * { * return t.name == "Foundation" ; * } */ //IMPORTANT : go.transform.position cannot be used since we're trying to place the GameObject through this method //rotation is fine (even if we just created the go, rotation will just be Quaternion.Identity) public static Vector3 PlaceGhost(BeyondComponent bc, Vector3 onPoint, LayerMask layerMask) { if (bc == null) { return(onPoint); } // As a rule, the result is the same as the pointOnTerrain, we are just applying some filter below Vector3 result = onPoint; result = GetPointOnLayer(bc, onPoint, layerMask); return(result); }
private void CreateNewPlaceableObject(string templateName) { if (currentBC == null) { //TemplateController.CreateObject(templateName , ref currentPlaceableObject , ref currentBC) ; // refactored into : currentBC = TemplateController.CreateObject(templateName); } else { Destroy(currentBC.gameObject); Debug.Log("CreateNewPlaceableobject destroyed placeable object"); } }
public void StopDragging() { List <GameObject> objectsPlaced = new List <GameObject>(); GameObject FirstObject = Instantiate(GameObject_DragFrom); BeyondComponent BC_DragFrom = GameObject_DragFrom.GetComponent <BeyondComponent>(); FirstObject.GetComponent <BeyondComponent>().CopyValues(BC_DragFrom); SetBlueprintFromGhost(FirstObject); objectsPlaced.Add(FirstObject); Destroy(GameObject_DragFrom); for (int i = 0; i < draggedObjects.Count; i++) // Note: a for loop is better than a foreach that can miss some objects { // TO DO : We need to check a bit more than that: can't have a bunch of objects split in several groups because they failed constraint. All draggedObject should form 1 block // And, as stated in the AvoidCollision Constraint, if adding to an existing group we should check the object is in a valid group position: // As above: no isolated objects, but also no 2 same objects in the same cell, all objects have at least one Snaptarget, etc if (draggedObjects[i].activeSelf && Constraint.CheckRootConstraint(draggedObjects[i])) { GameObject ThisObject = Instantiate(draggedObjects[i]); BeyondComponent BC_ThisObject = draggedObjects[i].GetComponent <BeyondComponent>(); ThisObject.GetComponent <BeyondComponent>().CopyValues(BC_ThisObject); SetBlueprintFromGhost(ThisObject); objectsPlaced.Add(ThisObject); } Destroy(draggedObjects[i]); } draggedObjects.Clear(); DestroyActiveBlueprint(); // When snapping, the Active blueprint is already in a group, which we should use, otherwise create a new one BeyondGroup bg = null; if (SnappedToObject != null) { bg = SnappedToObject.GetComponent <BeyondComponent>().Group; //Debug.Log("I should add all these to the group of the object I was snapped to: "+bg.Name); SnappedToObject = null; } // Create BeyondGroup, add all objects to it foreach (GameObject go in objectsPlaced) { if (bg == null) { bg = CreateNewBeyondGroup(go); } bg.AddObject(go); // Only set the layer at the end, so objects don't collide into each other and fail the constraint check go.layer = LayerMask.NameToLayer("Buildings"); } }
public void CopyValues(BeyondComponent bc) { Template = bc.Template; PosInCell = bc.PosInCell; State = bc.State; if (BuildingMaterials == null) { BuildingMaterials = new List <BuildingMaterial>(); } foreach (BuildingMaterial gm in bc.BuildingMaterials) { BuildingMaterials.Add(gm); } }
public static BeyondComponent CreateObject(string templateName) { Template template = TemplateController.Instance.templates[templateName]; GameObject go = Instantiate(template.prefab); //TODO: Un-hardcode this shit go.layer = 0; //TODO : need to experiment with BoxColldier & trigger go.GetComponent <BoxCollider>().enabled = true; BeyondComponent bc = go.AddComponent <BeyondComponent>(); bc.setTemplate(template); return(bc); }
// For object with a BC ans State=Ghost, show them green or red based on whether they can be placed public static void UpdateGhostVisuals(GameObject go) { BeyondComponent bc = go.GetComponent <BeyondComponent>(); if (bc == null) { return; } if (bc.State == State.Ghost) { go.GetComponent <Renderer>().material = Constraint.CheckRootConstraint(go) ? BuildController.instance.GhostGreen : BuildController.instance.GhostRed; } }
public SavedComponent(BeyondComponent bc) { template = bc.template.name; state = bc.state; group = bc.beyondGroup; side = bc.side; groupPosition = bc.groupPosition; cells = bc.cells; position = bc.transform.position; rotation = bc.transform.rotation; name = bc.transform.gameObject.name; layer = bc.transform.gameObject.layer; isTrigger = bc.transform.gameObject.GetComponent <BoxCollider>().isTrigger; enabled = bc.transform.gameObject.GetComponent <BoxCollider>().enabled; }
public static GameObject SnapToObject(GameObject ObjectSnapping, out SnapTarget TargetSnapping) { GameObject result = null; TargetSnapping = null; float d = -1; float dMin = -1; // Given an ObjectSnapping, go through all the ObjectSnapping's snaptargets // For each snap target, check overlap sphere, return objects that have a centre close enough to that snap target // As we go through potential SnapToObjects and corresponding ObjectSnapping targets, always keep the closest only BeyondComponent BCSnapping = ObjectSnapping.GetComponent <BeyondComponent>(); // We want to snap an object that has a Beyond Component if (BCSnapping != null) { // Go through each SnapTarget of the ObjectSnapping foreach (SnapTarget st in BCSnapping.Template.SnapTargets) { Vector3 stCoords = st.GetToCentre(ObjectSnapping); // Go through all objects that overlap with a sphere centered around that SnapTarget foreach (Collider col in Physics.OverlapSphere(stCoords, _distanceToSnap)) { // Can't snap to yourself if (col.gameObject != ObjectSnapping) { BeyondComponent BCSnapped = col.gameObject.GetComponent <BeyondComponent>(); // We want to snap to an object that has a BeyondComponent, that has one of the target object's ToTags and is in the snaptarget's ToPos if ((BCSnapped != null) && (BCSnapped.Template.ContainsTag(st.ToTags)) && (BCSnapped.PosInCell == st.ToPos)) { // The snapping object must be in the snaptarget's Posincell if (BCSnapping.PosInCell == st.FromPos) { // Distance between the snapTarget and the snapped Object's cell centre d = Vector3.Distance(stCoords, BCSnapped.Template.GetCellCentre(col.gameObject)); if (dMin == -1 || d < dMin) { dMin = d; result = col.gameObject; TargetSnapping = st; } } } } } } } return(result); }
public void CreateNewBeyondGroup(BeyondComponent bc, string name = null) { if (name == null) { // Auto give name name = String.Format("Group {0:0000}", place.beyondGroups.Count); } // bc.transform.position - bc.template.pivotOffset : THIS IS ESSENTIAL - This allows us to properly set the pivot of the group BeyondGroup group = new BeyondGroup(name, bc.transform.position - bc.template.pivotOffset, bc.transform.rotation); if (bc != null) { group.addBeyondComponent(bc); // Vector3Int.zero because the first object in a group is at position [0,0,0] bc.SetBCinGroup(group, Vector3Int.zero, true); } place.beyondGroups.Add(group); }
/* * ============================================================ * CONSTRAINTS CHECKING * ============================================================ */ // Check this BC's constraints (optionally in a group) public static bool CheckConstraints(BeyondComponent bc, Constraints c = null, BeyondGroup bg = null, Vector3Int?optionalGroupPos = null) { Constraints constraints = (c == null ? bc.template.constraints : c); //Debug.Log(String.Format("Checking constraint on {0} of group {1}: {2}" , bc.name , (bg==null ? "null" : bg.name) , Constraints.ShowConstraints(constraints))); //int i=0; switch (constraints.operation) { case "OR": //if (i++>1000) {Debug.Log("OR just exploded"); return false;} return(OrConstraints(bc, bg, constraints.constraintsList, optionalGroupPos)); case "AND": //if (i++>1000) {Debug.Log("AND just exploded"); return false;} return(AndConstraints(bc, bg, constraints.constraintsList, optionalGroupPos)); case "TOPCLEAR": //if (i++>1000) {Debug.Log("TOPCLEAR just exploded"); return false;} return(TopClear(bc, constraints.mask)); case "ALLCLEAR": //if (i++>1000) {Debug.Log("ALLCLEAR just exploded"); return false;} return(AllClear(bc, constraints.mask)); case "BASEIN": //if (i++>1000) {Debug.Log("BASEIN just exploded"); return false;} return(BaseInTerrain(bc)); case "NEEDSONE": //if (i++>1000) {Debug.Log("NEEDSONE just exploded"); return false;} return(NeedsOne(bc, bg, constraints.templateNamesList, constraints.offsetsList, constraints.cellSides, optionalGroupPos)); case "NEEDSALL": //if (i++>1000) {Debug.Log("BEEDSALL just exploded"); return false;} return(NeedsAll(bc, bg, constraints.templateNamesList, constraints.offsetsList, constraints.cellSides, optionalGroupPos)); case "FIRSTINGROUP": //if (i++>1000) {Debug.Log("FIRSTINGROUP just exploded"); return false;} return(FirstInGroup(bc, bg)); default: //if (i++>1000) {Debug.Log("CHeckConstraints just exploded"); return false;} return(false); } }
public static Constraint GetGameObjectConstraint(GameObject go) { BeyondComponent bc = go.GetComponent <BeyondComponent>(); if (bc == null) { Debug.LogError("Attempting to get constraints on an Object without a BeyondComponent"); return(null); } Template t = bc.Template; if (t == null) { Debug.LogError("Attempting to get constraints on an Object without a Template in its BeyondComponent"); return(null); } return(t.Constraint); }
public void CreateBlueprint(GameAction blueprint, List <BuildingMaterial> materials) { // Instantiate a prefab of that blueprint // Show it floating 5 units away from where the FPS is looking Template t = GameManager.instance.GetTemplate(blueprint.Name); if (t != null) { DestroyActiveBlueprint(); ActiveBlueprint = Instantiate(t.Prefab, FPSCharacter.transform, false); BeyondComponent bc = ActiveBlueprint.AddComponent <BeyondComponent>(); bc.SetValues(t, State.Ghost, materials); ActiveBlueprint.transform.Translate(new Vector3(0, 0, blueprintDistanceFromCamera)); ActiveBlueprint.name = string.Format("Ghost ({0})", t.Name); } else { Debug.LogError("Attempting to create null template"); } }
public void Load(SavedGame game) { place = game.place; // I need to initiate the componentList of the group. It doesn't exist as it can't be serialised foreach (SavedComponent data in game.components) { GameObject go = Instantiate(TemplateController.Instance.templates[data.template].prefab); BeyondComponent bc = go.AddComponent <BeyondComponent>(); bc.LoadComponent(data); go.SetActive(false); go.transform.position = data.position; go.transform.rotation = data.rotation; go.name = data.name; go.layer = data.layer; go.GetComponent <BoxCollider>().isTrigger = data.isTrigger; go.GetComponent <BoxCollider>().enabled = data.enabled; go.SetActive(true); place.beyondGroups.Find(group => group == data.group).addBeyondComponent(bc); } }
// given a point, get an object's postion above or below it so that the object is exactly on the Terrain public static Vector3 GetPointOnLayer(BeyondComponent bc, Vector3 point, LayerMask layerMask) { if (bc == null) { return(point); } Vector3 result = point; result.y = PlaceController.Instance.place.Height; // Cast from the highest possible altitude RaycastHit hitInfo; Physics.BoxCast(result, bc.template.castBox, Vector3.down, out hitInfo, bc.transform.rotation, Mathf.Infinity, layerMask); //TODO - is this better ? : Physics.BoxCast(point, bc.template.castBox, Vector3.down, out hitInfo, go.transform.rotation); // Half the height of the object is bc.template.castBox.y //TODO : am I sure of that ? We need an offset for Foundations, by how much they can be insde terrain result.y = hitInfo.point.y + bc.template.castBox.y; if (bc.template.name == "Foundation") { result.y += FoundationInTerrainBy - bc.template.castBox.y * 2; //FoundationInTerrainBy } return(result); }