/******************************************************************** Minecraft: Pocket Edition - Decompilation Project Copyright (C) 2023 iProgramInCpp The following code is licensed under the BSD 1 clause license. SPDX-License-Identifier: BSD-1-Clause ********************************************************************/ #include "Explosion.hpp" Explosion::Explosion(Level* level, Entity* entity, float x, float y, float z, float power) { field_20 = 0; m_bIsFiery = false; m_pos = Vec3(x, y, z); m_power = power; m_pEntity = entity; m_pLevel = level; assert(m_power != 0.0f); } // This doesn't do a complete explosion, instead, it only adds tiles that should be blown up. // addParticles() completes the explosion. void Explosion::explode() { for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { for (int k = 0; k < 16; k++) { // what if (i != 0 && j != 0 && k != 0 && i != 15 && j != 15 && k != 15) continue; float rayX = float(i) / 15.0f * 2.0f - 1.0f; float rayY = float(j) / 15.0f * 2.0f - 1.0f; float rayZ = float(k) / 15.0f * 2.0f - 1.0f; // normalize the ray vector float length = sqrtf(rayX * rayX + rayY * rayY + rayZ * rayZ); rayX /= length; rayY /= length; rayZ /= length; float mult = m_power * (0.7f + 0.6f * m_pLevel->m_random.nextFloat()); float posX = m_pos.x; float posY = m_pos.y; float posZ = m_pos.z; while (true) { if (mult < 0) break; int pxf = Mth::floor(posX); int pyf = Mth::floor(posY); int pzf = Mth::floor(posZ); TileID tile = m_pLevel->getTile(pxf, pyf, pzf); if (tile > 0) mult -= 0.3f * (0.3f + Tile::tiles[tile]->getExplosionResistance(m_pEntity)); if (mult > 0) m_tiles.insert(TilePos(pxf, pyf, pzf)); mult -= 0.225f; if (mult < 0) break; posX += rayX * 0.3f; posY += rayY * 0.3f; posZ += rayZ * 0.3f; } } } } m_power *= 2; AABB aabb( (float)Mth::floor(m_pos.x - m_power - 1.0f), (float)Mth::floor(m_pos.y - m_power - 1.0f), (float)Mth::floor(m_pos.z - m_power - 1.0f), (float)Mth::floor(m_pos.x + m_power + 1.0f), (float)Mth::floor(m_pos.y + m_power + 1.0f), (float)Mth::floor(m_pos.z + m_power + 1.0f) ); EntityVector* pEnts = m_pLevel->getEntities(m_pEntity, aabb); for (int i = 0; i < pEnts->size(); i++) { Entity* entity = pEnts->at(i); float distPowerRatio = entity->distanceTo(m_pos.x, m_pos.y, m_pos.z) / m_power; if (distPowerRatio > 1.0f) continue; float deltaX = entity->m_pos.x - m_pos.x; float deltaY = entity->m_pos.y - m_pos.y; float deltaZ = entity->m_pos.z - m_pos.z; // @NOTE: They used it here, but not when normalizing the 16*16*16=4096 rays shot before... float normInv = Mth::invSqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ); float hurtPercent = m_pLevel->getSeenPercent(m_pos, entity->m_hitbox) * (1.0f - distPowerRatio); entity->hurt(m_pEntity, int((hurtPercent * hurtPercent + hurtPercent) / 2.0f * 8.0f * this->m_power + 1.0f)); entity->m_vel.x += deltaX * normInv * hurtPercent; entity->m_vel.y += deltaY * normInv * hurtPercent; entity->m_vel.z += deltaZ * normInv * hurtPercent; } std::vector vec; // @NOTE: Could avoid this copy if m_bFiery is false vec.insert(vec.begin(), m_tiles.begin(), m_tiles.end()); if (!m_bIsFiery) return; for (int i = int(vec.size() - 1); i >= 0; i--) { TilePos tp = vec[i]; TileID tile = m_pLevel->getTile(tp.x, tp.y, tp.z), tileBelow = m_pLevel->getTile(tp.x, tp.y - 1, tp.z); if (tile == TILE_AIR && Tile::solid[tileBelow] && m_random.nextInt(3) == 0) m_pLevel->setTile(tp.x, tp.y, tp.z, Tile::fire->m_ID); } } void Explosion::addParticles() { m_pLevel->playSound(m_pos.x, m_pos.y, m_pos.z, "random.explode", 4.0f, 0.7f * (1.0f + 0.2f * (m_pLevel->m_random.nextFloat() - m_pLevel->m_random.nextFloat()))); std::vector vec; vec.insert(vec.begin(), m_tiles.begin(), m_tiles.end()); for (int i = int(vec.size() - 1); i >= 0; i--) { TilePos tp = vec[i]; TileID tile = m_pLevel->getTile(tp.x, tp.y, tp.z); // Spawn a particle only for every 8th tile. Weird if ((i & 0x7) == 0) { float x2, y2, z2, vx, vy, vz, mult; x2 = float(tp.x) + m_pLevel->m_random.nextFloat(); y2 = float(tp.y) + m_pLevel->m_random.nextFloat(); z2 = float(tp.z) + m_pLevel->m_random.nextFloat(); float dX, dY, dZ; dX = x2 - m_pos.x; dY = y2 - m_pos.y; dZ = z2 - m_pos.z; // @NOTE: Can use Mth::invSqrt float dist = Mth::sqrt(dX * dX + dY * dY + dZ * dZ); vx = dX / dist; vy = dY / dist; vz = dZ / dist; // @HUH: Dividing by the inverse is the same as multiplying. Thanks, IDA! :) float power1 = m_power / (1.0f / dist) + 0.1f; mult = ((m_pLevel->m_random.nextFloat() * m_pLevel->m_random.nextFloat()) + 0.3f) * (0.5f / power1); m_pLevel->addParticle("explode", (x2 + m_pos.x) / 2, (y2 + m_pos.y) / 2, (z2 + m_pos.z) / 2, vx * mult, vy * mult, vz * mult); m_pLevel->addParticle("smoke", x2, y2, z2, vx * mult, vy * mult, vz * mult); } if (tile > 0) { Tile::tiles[tile]->spawnResources(m_pLevel, tp.x, tp.y, tp.z, m_pLevel->getData(tp.x, tp.y, tp.z), 0.3f); m_pLevel->setTile(tp.x, tp.y, tp.z, TILE_AIR); Tile::tiles[tile]->wasExploded(m_pLevel, tp.x, tp.y, tp.z); } } }