protected void CheckFootfalls() { if (!isGrounded && !isInWater) { Vector3 curPos = transform.position; int x = (int)curPos.x; int y = (int)curPos.y; int z = (int)curPos.z; if (x != lastPosX || y != lastPosY || z != lastPosZ) { lastPosX = x; lastPosY = y; lastPosZ = z; VoxelIndex index = env.GetVoxelUnderIndex(curPos, true); if (index.typeIndex != lastVoxelTypeIndex) { lastVoxelTypeIndex = index.typeIndex; if (lastVoxelTypeIndex != 0) { VoxelDefinition vd = index.type; SetFootstepSounds(vd.footfalls, vd.landingSound, vd.jumpSound); if (vd.triggerWalkEvent && OnVoxelWalk != null) { OnVoxelWalk(index.chunk, index.voxelIndex); } CheckDamage(vd); } } } } }
void GetCrumblyVoxelRecursive(Vector3 originalPosition, Vector3 position, int amount, List <VoxelIndex> voxelIndices) { if (tempVoxelIndicesCount >= amount) { return; } VoxelChunk chunk; int voxelIndex; int c = 0; bool dummy; for (int k = 0; k < collapsingOffsets.Length; k += 3) { Vector3 pos = position; pos.x += collapsingOffsets [k]; pos.y += collapsingOffsets [k + 1]; pos.z += collapsingOffsets [k + 2]; float dx = pos.x > originalPosition.x ? pos.x - originalPosition.x : originalPosition.x - pos.x; float dz = pos.z > originalPosition.z ? pos.z - originalPosition.z : originalPosition.z - pos.z; if (dx > 8 || dz > 8) { continue; } if (!tempVoxelPositions.TryGetValue(pos, out dummy)) { tempVoxelPositions [pos] = true; if (GetVoxelIndex(pos, out chunk, out voxelIndex, false) && chunk.voxels [voxelIndex].hasContent == 1 && chunk.voxels [voxelIndex].opaque >= 3 && voxelDefinitions [chunk.voxels [voxelIndex].typeIndex].willCollapse) { VoxelIndex vi = new VoxelIndex(); vi.chunk = chunk; vi.voxelIndex = voxelIndex; vi.position = pos; voxelIndices.Add(vi); tempVoxelIndicesCount++; c++; if (tempVoxelIndicesCount >= amount) { break; } } } } int lastCount = tempVoxelIndicesCount; for (int k = 1; k <= c; k++) { GetCrumblyVoxelRecursive(originalPosition, tempVoxelIndices [lastCount - k].position, amount, voxelIndices); } }
int DamageAreaFast(Vector3 origin, int damage, int damageRadius = 1, bool distanceAttenuation = true, bool addParticles = true, List <VoxelIndex> results = null) { bool hasResults = results != null; if (hasResults) { results.Clear(); } if (damageRadius < 0 || damage < 1) { return(0); } int damagedCount = 0; Vector3 direction = Misc.vector3zero; VoxelHitInfo hitInfo; GetVoxelIndices(origin, damageRadius, tempVoxelIndices); int count = tempVoxelIndices.Count; for (int k = 0; k < count; k++) { VoxelIndex vi = tempVoxelIndices [k]; VoxelChunk otherChunk = vi.chunk; int otherIndex = vi.voxelIndex; int dam = damage; if (distanceAttenuation && vi.sqrDistance > 1) { dam = (int)(damage * damageRadius * damageRadius / vi.sqrDistance); } if (dam > 0 && GetVoxelVisibility(otherChunk, otherIndex)) { FastVector.NormalizedDirection(ref origin, ref vi.position, ref direction); if (RayCastFast(origin, direction, out hitInfo, damageRadius, false, 5)) { int damageTaken = DamageVoxelFast(ref hitInfo, dam, addParticles, false); if (hasResults) { VoxelIndex di = vi; di.damageTaken = damageTaken; results.Add(di); damagedCount++; } } } } return(damagedCount); }
void CreateTree(Vector3 position, ModelDefinition tree, int rotation) { if ((object)tree == null) { return; } if (OnTreeBeforeCreate != null) { if (!OnTreeBeforeCreate(position)) { return; } } Vector3 pos; treeChunkRefreshRequests.Clear(); VoxelChunk lastChunk = null; int modelOneYRow = tree.sizeZ * tree.sizeX; int modelOneZRow = tree.sizeX; int halfSizeX = tree.sizeX / 2; int halfSizeZ = tree.sizeZ / 2; int tmp; VoxelIndex index = new VoxelIndex(); bool informIndices = false; if (OnTreeAfterCreate != null) { informIndices = true; tempVoxelIndices.Clear(); } for (int b = 0; b < tree.bits.Length; b++) { int bitIndex = tree.bits [b].voxelIndex; int py = bitIndex / modelOneYRow; int remy = bitIndex - py * modelOneYRow; int pz = remy / modelOneZRow; int px = remy - pz * modelOneZRow; // Random rotation switch (rotation) { case 0: tmp = px; px = halfSizeZ - pz; pz = tmp - halfSizeX; break; case 1: tmp = px; px = pz - halfSizeZ; pz = tmp - halfSizeX; break; case 2: tmp = px; px = pz - halfSizeZ; pz = halfSizeX - tmp; break; default: px -= halfSizeX; pz -= halfSizeZ; break; } pos.x = position.x + tree.offsetX + px; pos.y = position.y + tree.offsetY + py; pos.z = position.z + tree.offsetZ + pz; VoxelChunk chunk; int voxelIndex; if (GetVoxelIndex(pos, out chunk, out voxelIndex)) { if (!chunk.modified && (chunk.voxels [voxelIndex].opaque < FULL_OPAQUE || voxelDefinitions [chunk.voxels [voxelIndex].typeIndex].renderType == RenderType.CutoutCross)) { VoxelDefinition treeVoxel = tree.bits [b].voxelDefinition ?? defaultVoxel; chunk.voxels [voxelIndex].Set(treeVoxel, tree.bits [b].finalColor); if (informIndices) { index.chunk = chunk; index.voxelIndex = voxelIndex; index.position = pos; tempVoxelIndices.Add(index); } if (py == 0) { // fills one voxel beneath with tree voxel to avoid the issue of having some trees floating on some edges/corners if (voxelIndex >= ONE_Y_ROW) { if (chunk.voxels [voxelIndex - ONE_Y_ROW].hasContent != 1) { chunk.voxels [voxelIndex - ONE_Y_ROW].Set(treeVoxel, tree.bits [b].finalColor); if (informIndices) { index.chunk = chunk; index.voxelIndex = voxelIndex - ONE_Y_ROW; index.position = pos; index.position.y--; tempVoxelIndices.Add(index); } } } else { VoxelChunk bottom = chunk.bottom; if (bottom != null && !bottom.modified) { int bottomIndex = voxelIndex + (CHUNK_SIZE - 1) * ONE_Y_ROW; if (bottom.voxels [bottomIndex].hasContent != 1) { chunk.voxels [bottomIndex].Set(treeVoxel, tree.bits [b].finalColor); if (informIndices) { index.chunk = bottom; index.voxelIndex = bottomIndex; index.position = pos; index.position.y--; tempVoxelIndices.Add(index); } if (!treeChunkRefreshRequests.Contains(bottom)) { treeChunkRefreshRequests.Add(bottom); } } } } } if (chunk != lastChunk) { lastChunk = chunk; if (!chunk.inqueue && !treeChunkRefreshRequests.Contains(chunk)) { treeChunkRefreshRequests.Add(chunk); } } } } } treesCreated++; if (informIndices) { OnTreeAfterCreate(tempVoxelIndices); } int refreshChunksCount = treeChunkRefreshRequests.Count; for (int k = 0; k < refreshChunksCount; k++) { ChunkRequestRefresh(treeChunkRefreshRequests [k], false, true); } }
void ModelPlace(Vector3 position, ModelDefinition model, ref Bounds bounds, int rotationDegrees = 0, float colorBrightness = 1f, bool fitTerrain = false, List <VoxelIndex> indices = null, int indexStart = -1, int indexEnd = -1) { if (model == null) { return; } if (indexStart < 0) { indexStart = 0; } if (indexEnd < 0) { indexEnd = model.bits.Length - 1; } Vector3 pos; int modelOneYRow = model.sizeZ * model.sizeX; int modelOneZRow = model.sizeX; int halfSizeX = model.sizeX / 2; int halfSizeZ = model.sizeZ / 2; if (rotationDegrees == 360) { switch (UnityEngine.Random.Range(0, 4)) { case 0: rotationDegrees = 90; break; case 1: rotationDegrees = 180; break; case 2: rotationDegrees = 270; break; } } bool indicesProvided = indices != null; if (indicesProvided && indexStart < 0 && indexEnd < 0) { indices.Clear(); } VoxelIndex index = new VoxelIndex(); VoxelChunk lastChunk = null; int tmp; Vector3 min = bounds.min; Vector3 max = bounds.max; for (int b = indexStart; b <= indexEnd; b++) { int bitIndex = model.bits [b].voxelIndex; int py = bitIndex / modelOneYRow; int remy = bitIndex - py * modelOneYRow; int pz = remy / modelOneZRow; int px = remy - pz * modelOneZRow; switch (rotationDegrees) { case 90: tmp = px; px = halfSizeZ - pz; pz = halfSizeX - tmp; break; case 180: px = halfSizeX - px; pz = halfSizeZ - pz; break; case 270: tmp = px; px = pz - halfSizeZ; pz = tmp - halfSizeX; break; default: px -= halfSizeX; pz -= halfSizeZ; break; } pos.x = position.x + model.offsetX + px; pos.y = position.y + model.offsetY + py; pos.z = position.z + model.offsetZ + pz; VoxelChunk chunk; int voxelIndex; if (GetVoxelIndex(pos, out chunk, out voxelIndex)) { Color32 color = model.bits [b].finalColor; VoxelDefinition vd = model.bits [b].voxelDefinition ?? defaultVoxel; bool emptyVoxel = model.bits [b].isEmpty; if (emptyVoxel) { chunk.voxels [voxelIndex] = Voxel.Empty; } else { if (colorBrightness != 1f) { color.r = (byte)(color.r * colorBrightness); color.g = (byte)(color.g * colorBrightness); color.b = (byte)(color.b * colorBrightness); } chunk.voxels [voxelIndex].Set(vd, color); // Add index if (indicesProvided) { index.chunk = chunk; index.voxelIndex = voxelIndex; index.position = pos; indices.Add(index); } if (pos.x < min.x) { min.x = pos.x; } if (pos.y < min.y) { min.y = pos.y; } if (pos.z < min.z) { min.z = pos.z; } if (pos.x > max.x) { max.x = pos.x; } if (pos.y > max.y) { max.y = pos.y; } if (pos.z > max.z) { max.z = pos.z; } } // Prevent tree population chunk.allowTrees = false; chunk.modified = true; if (fitTerrain && !emptyVoxel) { // Fill beneath row 1 if (py == 0) { Vector3 under = pos; under.y -= 1; for (int k = 0; k < 100; k++, under.y--) { VoxelChunk lowChunk; int vindex; GetVoxelIndex(under, out lowChunk, out vindex, false); if (lowChunk != null && lowChunk.voxels [vindex].opaque < FULL_OPAQUE) { lowChunk.voxels [vindex].Set(vd, color); if (lowChunk != lastChunk) { lastChunk = lowChunk; if (!lastChunk.inqueue) { ChunkRequestRefresh(lastChunk, true, true); } } } else { break; } } } } if (chunk != lastChunk) { lastChunk = chunk; if (!lastChunk.inqueue) { lastChunk.MarkAsInconclusive(); ChunkRequestRefresh(lastChunk, true, true); } } } } FastVector.Floor(ref min); FastVector.Ceiling(ref max); bounds.center = (max + min) * 0.5f; bounds.size = max - min; }
void ModelPlace(Vector3 position, ModelDefinition model, ref Bounds bounds, int rotationDegrees = 0, float colorBrightness = 1f, bool fitTerrain = false, List <VoxelIndex> indices = null, int indexStart = -1, int indexEnd = -1, bool useUnpopulatedChunks = false, bool refreshChunks = true) { if (model == null) { return; } if (indexStart < 0) { indexStart = 0; } if (indexEnd < 0) { indexEnd = model.bits.Length - 1; } Vector3 pos; int modelOneYRow = model.sizeZ * model.sizeX; int modelOneZRow = model.sizeX; if (rotationDegrees == 360) { switch (WorldRand.Range(0, 4)) { case 1: rotationDegrees = 90; break; case 2: rotationDegrees = 180; break; case 3: rotationDegrees = 270; break; } } int halfSizeX = model.sizeX / 2; int halfSizeZ = model.sizeZ / 2; Vector3 zeroPos = Quaternion.Euler(0, rotationDegrees, 0) * new Vector3(-halfSizeX, 0, -halfSizeZ); // ensure all voxel definitions are present //bool reloadTextures = false; for (int b = indexStart; b <= indexEnd; b++) { VoxelDefinition vd = model.bits [b].voxelDefinition; if (vd != null && vd.index == 0) { AddVoxelDefinition(vd); //reloadTextures = true; } } //if (reloadTextures) { // TODO: RML (AddVoxelDefinition already marks reload world textures flag) // LoadWorldTextures(); //} bool indicesProvided = indices != null; if (indicesProvided && indexStart < 0 && indexEnd < 0) { indices.Clear(); } VoxelIndex index = new VoxelIndex(); Vector3 min = bounds.min; Vector3 max = bounds.max; for (int b = indexStart; b <= indexEnd; b++) { int bitIndex = model.bits [b].voxelIndex; int py = bitIndex / modelOneYRow; int remy = bitIndex - py * modelOneYRow; int pz = remy / modelOneZRow; int px = remy - pz * modelOneZRow; float wx = zeroPos.x, wz = zeroPos.z; switch (rotationDegrees) { case 90: wx += pz; wz -= px; break; case 180: wx -= px; wz -= pz; break; case 270: wx -= pz; wz += px; break; default: wx += px; wz += pz; break; } pos.x = position.x + model.offsetX + wx; pos.y = position.y + model.offsetY + py; pos.z = position.z + model.offsetZ + wz; VoxelChunk chunk; int voxelIndex; if (useUnpopulatedChunks) { chunk = GetChunkUnpopulated(pos); } if (GetVoxelIndex(pos, out chunk, out voxelIndex)) { bool emptyVoxel = model.bits [b].isEmpty; if (emptyVoxel) { chunk.voxels [voxelIndex] = Voxel.Hole; } else { Color32 color = model.bits [b].finalColor; if (colorBrightness != 1f) { color.r = (byte)(color.r * colorBrightness); color.g = (byte)(color.g * colorBrightness); color.b = (byte)(color.b * colorBrightness); } VoxelDefinition vd = model.bits [b].voxelDefinition ?? defaultVoxel; chunk.voxels [voxelIndex].Set(vd, color); float rotation = (model.bits [b].rotation + 360 + rotationDegrees) % 360; if (rotation != 0) { chunk.voxels [voxelIndex].SetTextureRotation(Voxel.GetTextureRotationFromDegrees(rotation)); } // Add index if (indicesProvided) { index.chunk = chunk; index.voxelIndex = voxelIndex; index.position = pos; indices.Add(index); } if (pos.x < min.x) { min.x = pos.x; } if (pos.y < min.y) { min.y = pos.y; } if (pos.z < min.z) { min.z = pos.z; } if (pos.x > max.x) { max.x = pos.x; } if (pos.y > max.y) { max.y = pos.y; } if (pos.z > max.z) { max.z = pos.z; } if (fitTerrain) { // Fill beneath row 1 if (py == 0) { Vector3 under = pos; under.y -= 1; for (int k = 0; k < 100; k++, under.y--) { VoxelChunk lowChunk; int vindex; GetVoxelIndex(under, out lowChunk, out vindex, false); if (lowChunk != null && lowChunk.voxels [vindex].opaque < FULL_OPAQUE) { lowChunk.voxels [vindex].Set(vd, color); lowChunk.modified = true; if (!lowChunk.inqueue && !useUnpopulatedChunks) { ChunkRequestRefresh(lowChunk, true, true); } } else { break; } } } } } // Prevent tree population chunk.allowTrees = false; chunk.modified = true; if (!chunk.inqueue && !useUnpopulatedChunks) { chunk.MarkAsInconclusive(); if (refreshChunks) { ChunkRequestRefresh(chunk, true, true); } } } } FastVector.Floor(ref min); FastVector.Ceiling(ref max); bounds.center = (max + min) * 0.5f; bounds.size = max - min; }