/// <summary> /// Implements building stuff /// </summary> /// <param name="camPos">The camera position OR the character position in a 3rd person controller</param>"> protected virtual void DoBuild(Vector3 camPos, Vector3 forward, Vector3 hintedPlacePos) { if (player.selectedItemIndex < 0 || player.selectedItemIndex >= player.items.Count) { return; } InventoryItem inventoryItem = player.GetSelectedItem(); ItemDefinition currentItem = inventoryItem.item; switch (currentItem.category) { case ItemCategory.Voxel: // Basic placement rules bool canPlace = crosshairOnBlock; Voxel existingVoxel = crosshairHitInfo.voxel; VoxelDefinition existingVoxelType = existingVoxel.type; Vector3 placePos; if (currentItem.voxelType.renderType == RenderType.Water && !canPlace) { canPlace = true; // water can be poured anywhere placePos = camPos + forward * 3f; } else { placePos = crosshairHitInfo.voxelCenter + crosshairHitInfo.normal; if (canPlace && crosshairHitInfo.normal.y == 1) { // Make sure there's a valid voxel under position (ie. do not build a voxel on top of grass) canPlace = (existingVoxelType != null && existingVoxelType.renderType != RenderType.CutoutCross && (existingVoxelType.renderType != RenderType.Water || currentItem.voxelType.renderType == RenderType.Water)); } } VoxelDefinition placeVoxelType = currentItem.voxelType; // Check voxel promotion bool isPromoting = false; if (canPlace) { if (existingVoxelType == currentItem.voxelType) { if (existingVoxelType.promotesTo != null) { // Promote existing voxel //env.VoxelDestroy (crosshairHitInfo.voxelCenter); placePos = crosshairHitInfo.voxelCenter; placeVoxelType = existingVoxelType.promotesTo; isPromoting = true; } else if (crosshairHitInfo.normal.y > 0 && existingVoxelType.biomeDirtCounterpart != null) { env.VoxelPlace(crosshairHitInfo.voxelCenter, existingVoxelType.biomeDirtCounterpart); } } } // Compute rotation int textureRotation = 0; if (placeVoxelType.placeFacingPlayer && placeVoxelType.renderType.supportsTextureRotation()) { // Orient voxel to player if (Mathf.Abs(forward.x) > Mathf.Abs(forward.z)) { if (forward.x > 0) { textureRotation = 1; } else { textureRotation = 3; } } else if (forward.z < 0) { textureRotation = 2; } } // Final check, does it overlap existing geometry? if (canPlace && !isPromoting) { Quaternion rotationQ = Quaternion.Euler(0, Voxel.GetTextureRotationDegrees(textureRotation), 0); canPlace = !env.VoxelOverlaps(placePos, placeVoxelType, rotationQ, 1 << env.layerVoxels); if (!canPlace) { PlayCancelSound(); } } #if UNITY_EDITOR else if (env.constructorMode) { placePos = hintedPlacePos; placeVoxelType = currentItem.voxelType; canPlace = true; } #endif // Finally place the voxel if (canPlace) { // Consume item first if (!env.buildMode) { player.ConsumeItem(); } // Place it float amount = inventoryItem.quantity < 1f ? inventoryItem.quantity : 1f; env.VoxelPlace(placePos, placeVoxelType, true, placeVoxelType.tintColor, amount, textureRotation); // Moves back character controller if voxel is put just on its position const float minDist = 0.5f; float distSqr = Vector3.SqrMagnitude(camPos - placePos); if (distSqr < minDist * minDist) { MoveTo(transform.position + crosshairHitInfo.normal); } } break; case ItemCategory.Torch: if (crosshairOnBlock) { GameObject torchAttached = env.TorchAttach(crosshairHitInfo, currentItem); if (!env.buildMode && torchAttached != null) { player.ConsumeItem(); } } break; case ItemCategory.Model: if (!modelBuildInProgress) { if (modelBuildPreview) { ModelPreviewCancel(); // check if building position is in frustum, otherwise cancel building Vector3 viewportPos = env.cameraMain.WorldToViewportPoint(modelBuildPreviewPosition); if (viewportPos.x < 0 || viewportPos.x > 1f || viewportPos.y < 0 || viewportPos.y > 1f || viewportPos.z < 0) { return; } modelBuildInProgress = true; env.ModelPlace(modelBuildPreviewPosition, currentItem.model, currentItem.model.buildDuration, modelBuildRotation, 1f, true, FinishBuilding); player.ConsumeItem(); } else { modelBuildPreview = true; modelBuildItem = currentItem.model; ModelBuildPreviewUpdate(); } } break; case ItemCategory.General: ThrowCurrentItem(camPos, forward); break; } }