From 805087d36a4c90ca961af15c4364695e64e156e5 Mon Sep 17 00:00:00 2001 From: iProgramInCpp Date: Wed, 6 Dec 2023 21:20:14 +0200 Subject: [PATCH] * Add PathfinderMob base * Normalize tab style in source/CMakeLists.txt --- .../projects/Client/Client.vcxproj.filters | 114 ++++---- .../windows/projects/World/World.vcxproj | 8 + .../projects/World/World.vcxproj.filters | 36 ++- source/CMakeLists.txt | 101 +++---- source/world/entity/Animal.cpp | 0 source/world/entity/Animal.hpp | 0 source/world/entity/Mob.cpp | 7 +- source/world/entity/Mob.hpp | 1 + source/world/entity/Monster.cpp | 0 source/world/entity/Monster.hpp | 0 source/world/entity/PathfinderMob.cpp | 252 ++++++++++++++++++ source/world/entity/PathfinderMob.hpp | 39 +++ source/world/entity/WaterAnimal.cpp | 0 source/world/entity/WaterAnimal.hpp | 0 source/world/level/path/Path.hpp | 25 ++ 15 files changed, 471 insertions(+), 112 deletions(-) create mode 100644 source/world/entity/Animal.cpp create mode 100644 source/world/entity/Animal.hpp create mode 100644 source/world/entity/Monster.cpp create mode 100644 source/world/entity/Monster.hpp create mode 100644 source/world/entity/PathfinderMob.cpp create mode 100644 source/world/entity/PathfinderMob.hpp create mode 100644 source/world/entity/WaterAnimal.cpp create mode 100644 source/world/entity/WaterAnimal.hpp diff --git a/platforms/windows/projects/Client/Client.vcxproj.filters b/platforms/windows/projects/Client/Client.vcxproj.filters index 4094f75..ec15a1c 100644 --- a/platforms/windows/projects/Client/Client.vcxproj.filters +++ b/platforms/windows/projects/Client/Client.vcxproj.filters @@ -353,9 +353,6 @@ Header Files\Player\Input - - Header Files - Header Files\Player\Input @@ -371,60 +368,6 @@ Header Files\Model - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files\Renderer @@ -434,6 +377,63 @@ Header Files\GUI\Components + + Header Files\Renderer\Entity + + + Header Files\Renderer\Entity + + + Header Files\Renderer\Entity + + + Header Files\Renderer\Entity + + + Header Files\Renderer\Entity + + + Header Files\Renderer\Entity + + + Header Files\Renderer\Entity + + + Header Files\Renderer\Entity + + + Header Files\Renderer\Entity + + + Header Files\Model + + + Header Files\Model + + + Header Files\Model + + + Header Files\Model + + + Header Files\Model + + + Header Files\Model + + + Header Files\Model + + + Header Files\Model + + + Header Files\Model + + + Header Files\Model + diff --git a/platforms/windows/projects/World/World.vcxproj b/platforms/windows/projects/World/World.vcxproj index 264824c..bf78c01 100644 --- a/platforms/windows/projects/World/World.vcxproj +++ b/platforms/windows/projects/World/World.vcxproj @@ -336,6 +336,10 @@ + + + + @@ -435,6 +439,10 @@ + + + + diff --git a/platforms/windows/projects/World/World.vcxproj.filters b/platforms/windows/projects/World/World.vcxproj.filters index 66e9db2..43fb7c8 100644 --- a/platforms/windows/projects/World/World.vcxproj.filters +++ b/platforms/windows/projects/World/World.vcxproj.filters @@ -422,9 +422,6 @@ Source Files\Tile - - Source Files - Source Files\Level\Path @@ -434,6 +431,21 @@ Source Files\Level\Path + + Source Files\Level\Path + + + Source Files\Entity + + + Source Files\Entity + + + Source Files\Entity + + + Source Files\Entity + @@ -715,9 +727,6 @@ Header Files\Tile - - Header Files - Header Files\Level\Path @@ -727,5 +736,20 @@ Header Files\Level\Path + + Header Files\Level\Path + + + Header Files\Entity + + + Header Files\Entity + + + Header Files\Entity + + + Header Files\Entity + \ No newline at end of file diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index c61efeb..bd6cde6 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 3.16.0) project(reminecraftpe-core) if(NOT ANDROID) - set(INCLUDES . .. ../thirdparty/zlib) + set(INCLUDES . .. ../thirdparty/zlib) else() - set(INCLUDES . ..) + set(INCLUDES . ..) endif() # Build @@ -155,10 +155,10 @@ add_library(reminecraftpe-core STATIC world/level/levelgen/synth/Synth.cpp world/level/levelgen/synth/ImprovedNoise.cpp world/level/levelgen/synth/PerlinNoise.cpp - world/level/path/Node.cpp - world/level/path/Path.cpp - world/level/path/BinaryHeap.cpp - world/level/path/PathFinder.cpp + world/level/path/Node.cpp + world/level/path/Path.cpp + world/level/path/BinaryHeap.cpp + world/level/path/PathFinder.cpp world/phys/HitResult.cpp world/phys/Vec3.cpp world/phys/AABB.cpp @@ -173,6 +173,10 @@ add_library(reminecraftpe-core STATIC world/entity/FallingTile.cpp world/entity/TripodCamera.cpp world/entity/ItemEntity.cpp + world/entity/PathfinderMob.cpp + world/entity/Animal.cpp + world/entity/WaterAnimal.cpp + world/entity/Monster.cpp world/level/Dimension.cpp world/level/Material.cpp world/level/LevelListener.cpp @@ -189,9 +193,10 @@ add_library(reminecraftpe-core STATIC world/level/storage/LevelSource.cpp world/level/storage/MemoryChunkStorage.cpp world/level/storage/ExternalFileLevelStorageSource.cpp - world/level/path/Node.cpp - world/level/path/Path.cpp - world/level/path/PathFinder.cpp + world/level/path/Node.cpp + world/level/path/Path.cpp + world/level/path/PathFinder.cpp + world/level/path/BinaryHeap.cpp world/level/levelgen/feature/BirchFeature.cpp world/level/levelgen/feature/LargeFeature.cpp world/level/levelgen/feature/Feature.cpp @@ -279,48 +284,48 @@ target_link_libraries(reminecraftpe-core PUBLIC raknet) # zlib - Android builds with its own version # Hack - If we're not building for Android, surely we're building with SDL if(USE_SDL) - add_subdirectory(../thirdparty/zlib zlib) - target_link_libraries(reminecraftpe-core PUBLIC zlib) - - # SDL - add_library(SDL INTERFACE) - if(EMSCRIPTEN) - set(SDL_FLAG -sUSE_SDL=2) - target_compile_options(SDL INTERFACE "${SDL_FLAG}") - target_link_options(SDL INTERFACE "${SDL_FLAG}") - else() - find_package(SDL2 REQUIRED) - target_link_libraries(SDL INTERFACE SDL2::SDL2) - endif() - target_link_libraries(reminecraftpe-core PUBLIC SDL) - - # OpenGL - if(NOT EMSCRIPTEN) - option(USE_GLES1_COMPATIBILITY_LAYER "Whether To Enable The GLESv1_CM Compatibility Layer" TRUE) - else() - set(USE_GLES1_COMPATIBILITY_LAYER TRUE) - endif() - if(USE_GLES1_COMPATIBILITY_LAYER) - set(GLES_COMPATIBILITY_LAYER_USE_SDL TRUE CACHE BOOL "" FORCE) - set(GLES_COMPATIBILITY_LAYER_DEPENDENCY SDL CACHE STRING "" FORCE) - add_subdirectory(../thirdparty/gles-compatibility-layer gles-compatibility-layer) - target_link_libraries(reminecraftpe-core PUBLIC gles-compatibility-layer) - target_compile_definitions(reminecraftpe-core PUBLIC USE_GLES1_COMPATIBILITY_LAYER) - if(EMSCRIPTEN) - target_link_options(reminecraftpe-core PUBLIC -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2) - endif() - else() - find_package(OpenGL REQUIRED) - target_link_libraries(reminecraftpe-core PUBLIC OpenGL::OpenGL OpenGL::GLU) - endif() + add_subdirectory(../thirdparty/zlib zlib) + target_link_libraries(reminecraftpe-core PUBLIC zlib) + + # SDL + add_library(SDL INTERFACE) + if(EMSCRIPTEN) + set(SDL_FLAG -sUSE_SDL=2) + target_compile_options(SDL INTERFACE "${SDL_FLAG}") + target_link_options(SDL INTERFACE "${SDL_FLAG}") + else() + find_package(SDL2 REQUIRED) + target_link_libraries(SDL INTERFACE SDL2::SDL2) + endif() + target_link_libraries(reminecraftpe-core PUBLIC SDL) + + # OpenGL + if(NOT EMSCRIPTEN) + option(USE_GLES1_COMPATIBILITY_LAYER "Whether To Enable The GLESv1_CM Compatibility Layer" TRUE) + else() + set(USE_GLES1_COMPATIBILITY_LAYER TRUE) + endif() + if(USE_GLES1_COMPATIBILITY_LAYER) + set(GLES_COMPATIBILITY_LAYER_USE_SDL TRUE CACHE BOOL "" FORCE) + set(GLES_COMPATIBILITY_LAYER_DEPENDENCY SDL CACHE STRING "" FORCE) + add_subdirectory(../thirdparty/gles-compatibility-layer gles-compatibility-layer) + target_link_libraries(reminecraftpe-core PUBLIC gles-compatibility-layer) + target_compile_definitions(reminecraftpe-core PUBLIC USE_GLES1_COMPATIBILITY_LAYER) + if(EMSCRIPTEN) + target_link_options(reminecraftpe-core PUBLIC -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2) + endif() + else() + find_package(OpenGL REQUIRED) + target_link_libraries(reminecraftpe-core PUBLIC OpenGL::OpenGL OpenGL::GLU) + endif() elseif(ANDROID) - # Add Android related stuff here. + # Add Android related stuff here. endif() # Sound Data if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../sound_data/sounds.h") - if(NOT DEFINED ENV{CI}) - message(WARNING "Missing sound data! Did you run tools/grabsounds.py?") - endif() - target_compile_definitions(reminecraftpe-core PRIVATE MISSING_SOUND_DATA) + if(NOT DEFINED ENV{CI}) + message(WARNING "Missing sound data! Did you run tools/grabsounds.py?") + endif() + target_compile_definitions(reminecraftpe-core PRIVATE MISSING_SOUND_DATA) endif() diff --git a/source/world/entity/Animal.cpp b/source/world/entity/Animal.cpp new file mode 100644 index 0000000..e69de29 diff --git a/source/world/entity/Animal.hpp b/source/world/entity/Animal.hpp new file mode 100644 index 0000000..e69de29 diff --git a/source/world/entity/Mob.cpp b/source/world/entity/Mob.cpp index ffdeb53..827d878 100644 --- a/source/world/entity/Mob.cpp +++ b/source/world/entity/Mob.cpp @@ -1,3 +1,4 @@ +#include "Mob.hpp" /******************************************************************** Minecraft: Pocket Edition - Decompilation Project Copyright (C) 2023 iProgramInCpp @@ -547,7 +548,6 @@ void Mob::travel(float a2, float a3) if (onLadder()) { - m_distanceFallen = 0.0f; if (m_vel.y < -0.15f) @@ -873,6 +873,11 @@ std::string Mob::getDeathSound() return "random.hurt"; } +float Mob::getWalkingSpeedModifier() +{ + return 0.7f; +} + void Mob::defineSynchedData() { diff --git a/source/world/entity/Mob.hpp b/source/world/entity/Mob.hpp index 653b90d..de20a1b 100644 --- a/source/world/entity/Mob.hpp +++ b/source/world/entity/Mob.hpp @@ -75,6 +75,7 @@ public: virtual std::string getAmbientSound(); virtual std::string getHurtSound(); virtual std::string getDeathSound(); + virtual float getWalkingSpeedModifier(); virtual void defineSynchedData(); float rotlerp(float, float, float); diff --git a/source/world/entity/Monster.cpp b/source/world/entity/Monster.cpp new file mode 100644 index 0000000..e69de29 diff --git a/source/world/entity/Monster.hpp b/source/world/entity/Monster.hpp new file mode 100644 index 0000000..e69de29 diff --git a/source/world/entity/PathfinderMob.cpp b/source/world/entity/PathfinderMob.cpp new file mode 100644 index 0000000..570c952 --- /dev/null +++ b/source/world/entity/PathfinderMob.cpp @@ -0,0 +1,252 @@ +/******************************************************************** + 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 "PathfinderMob.hpp" +#include "world/level/Level.hpp" + +PathfinderMob::PathfinderMob(Level* pLevel) : Mob(pLevel) +{ + field_BA0 = false; + field_BA4 = 0; +} + +Entity* PathfinderMob::getAttackTarget() +{ + return m_pAttackTarget; +} + +void PathfinderMob::setAttackTarget(Entity* pEnt) +{ + m_pAttackTarget = pEnt; +} + +Entity* PathfinderMob::findAttackTarget() +{ + return nullptr; +} + +bool PathfinderMob::checkHurtTarget(Entity* pEnt, float f) +{ + return false; +} + +void PathfinderMob::checkCantSeeTarget(Entity* pEnt, float f) +{ + if (f > 32.0f) + setAttackTarget(nullptr); +} + +float PathfinderMob::getWalkTargetValue(int, int, int) +{ + return 0.0f; +} + +bool PathfinderMob::shouldHoldGround() +{ + return false; +} + +void PathfinderMob::findRandomStrollLocation() +{ + bool foundLocation = false; + + float maxDist = -99999.0f; + int dx = -1, dy = -1, dz = -1; + + for (int i = 0; i < 10; i++) + { + int testX = Mth::floor(float(m_pos.x + m_random.nextInt(13)) - 6.0f); + int testY = Mth::floor(float(m_pos.y + m_random.nextInt(7)) - 3.0f); + int testZ = Mth::floor(float(m_pos.z + m_random.nextInt(13)) - 6.0f); + + float wtv = getWalkTargetValue(testX, testY, testZ); + if (maxDist < wtv) + { + maxDist = wtv; + dx = testX; + dy = testY; + dz = testZ; + foundLocation = true; + } + } + + if (foundLocation) + { + m_pLevel->findPath(&m_path, this, dx, dy, dz, 10.0f); + } +} + +float PathfinderMob::getWalkingSpeedModifier() +{ + float mod = Mob::getWalkingSpeedModifier(); + + if (field_BA4 > 0) + mod *= 2.0f; + + return mod; +} + +bool PathfinderMob::canSpawn() +{ + if (!Mob::canSpawn()) + return false; + + return getWalkTargetValue(Mth::floor(m_pos.x), Mth::floor(m_hitbox.min.y), Mth::floor(m_pos.z)) >= 0.0f; +} + +static Vec3 GetNodePosition(Node* pNode, Entity* pEnt) +{ + float offset = float(int(pEnt->field_88 + 1.0f)) * 0.5f; + float fx = float(pNode->m_x) + offset; + float fz = float(pNode->m_z) + offset; + float fy = float(pNode->m_y); + return Vec3(fx, fy, fz); +} + +void PathfinderMob::updateAi() +{ + if (field_BA4 > 0) + field_BA4--; + + field_BA0 = shouldHoldGround(); + + if (m_pAttackTarget) + { + if (!m_pAttackTarget->isAlive()) + { + m_pAttackTarget = nullptr; + } + else + { + float dist = m_pAttackTarget->distanceTo(this); + + if (canSee(m_pAttackTarget)) + checkHurtTarget(m_pAttackTarget, dist); + else + checkCantSeeTarget(m_pAttackTarget, dist); + } + } + else + { + m_pAttackTarget = findAttackTarget(); + if (m_pAttackTarget) + m_pLevel->findPath(&m_path, this, m_pAttackTarget, 16.0f); + } + + // @TODO: fix gotos + + if (!field_BA0) + { + if (m_pAttackTarget) + { + if (m_path.empty() || m_random.nextInt(20) == 0 || m_random.nextInt(20) == 0) + { + m_pLevel->findPath(&m_path, this, m_pAttackTarget, 16.0f); + goto label_1; + } + + if (field_BA0) + goto label_1; + } + + if ((m_path.empty() && m_random.nextInt(180) == 0) || m_random.nextInt(120) == 0 || (field_BA4 > 0 && (field_BA4 & 7) == 1)) + { + if (field_AFC < 100) + findRandomStrollLocation(); + } + } + +label_1: + m_pitch = 0.0f; + + if (m_path.empty() || m_random.nextInt(100) == 0) + { + Mob::updateAi(); + return; + } + + // follow path + Node* pCurrNode = m_path.getCurrentNode(); + Vec3 nodePos = GetNodePosition(pCurrNode, this); + + // Skip all nodes that we are inside of. + float sqdbl = (field_88 * 2.0f) * (field_88 * 2.0f); + while (sqdbl > nodePos.distanceToSqr(Vec3(m_pos.x, nodePos.y, m_pos.z))) + { + m_path.advance(); + + if (m_path.endOfPath()) + { + m_path.clear(); + pCurrNode = nullptr; + break; + } + + nodePos = GetNodePosition(pCurrNode, this); + } + + field_B0C = false; + + bool inWater = isInWater(); + bool inLava = isInLava(); + + if (!pCurrNode) + { + float ang = Mth::atan2(nodePos.z - m_pos.z, nodePos.x - m_pos.x) * 180.0f / float(M_PI) - 90.0f; + float heightDiff = nodePos.y - Mth::floor(m_hitbox.min.y + 0.5f); + + float angDiff = ang - m_yaw; + while (angDiff < -180.0f) angDiff += 360.0f; + while (angDiff >= 180.0f) angDiff -= 360.0f; + + if (angDiff > +30.0f) angDiff = +30.0f; + if (angDiff < -30.0f) angDiff = -30.0f; + + float oldYaw = m_yaw; + + m_yaw += angDiff; + + if (field_BA0 && m_pAttackTarget) + { + float ang2 = Mth::atan2(m_pAttackTarget->m_pos.z - m_pos.z, m_pAttackTarget->m_pos.x - m_pos.x) * 180.0f / float(M_PI) - 90.0f; + m_yaw = ang2; + + float thing = ((((angDiff + oldYaw) - ang2) + 90.0f) * float(M_PI)) / 180.0f; + + // @NOTE: Using old field_B04 value + field_B00 = -field_B04 * Mth::sin(thing); + field_B04 = field_B04 * Mth::cos(thing); + } + + field_B04 = field_B14; + + if (heightDiff > 0.0f) + field_B0C = true; + } + + if (m_pAttackTarget) + lookAt(m_pAttackTarget, 30.0f, 30.0f); + + // if we hit a wall while moving + if (field_7D && !isPathFinding()) + field_B0C = true; + + // if we're in water, try to swim up + if (m_random.nextFloat() < 0.8f && (inWater || inLava)) + field_B0C = true; +} + +void PathfinderMob::setPath(Path& path) +{ + // @TODO: Not sure if it calls the destructor here. In original Minecraft, it doesn't. + m_path = path; +} + +bool PathfinderMob::isPathFinding() +{ + return !m_path.empty(); +} diff --git a/source/world/entity/PathfinderMob.hpp b/source/world/entity/PathfinderMob.hpp new file mode 100644 index 0000000..e81933c --- /dev/null +++ b/source/world/entity/PathfinderMob.hpp @@ -0,0 +1,39 @@ +/******************************************************************** + 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 + ********************************************************************/ +#pragma once + +#include "Mob.hpp" +#include "world/level/path/Path.hpp" + +class PathfinderMob : public Mob +{ +public: + PathfinderMob(Level* pLevel); + + virtual Entity* getAttackTarget(); + virtual void setAttackTarget(Entity*); + virtual Entity* findAttackTarget(); + virtual bool checkHurtTarget(Entity*, float); + virtual void checkCantSeeTarget(Entity*, float); + virtual float getWalkTargetValue(int, int, int); + virtual bool shouldHoldGround(); + virtual void findRandomStrollLocation(); + + float getWalkingSpeedModifier() override; + bool canSpawn() override; + void updateAi() override; + + void setPath(Path& path); + bool isPathFinding(); + +private: + Entity* m_pAttackTarget; + bool field_BA0; + int field_BA4; + Path m_path; +}; diff --git a/source/world/entity/WaterAnimal.cpp b/source/world/entity/WaterAnimal.cpp new file mode 100644 index 0000000..e69de29 diff --git a/source/world/entity/WaterAnimal.hpp b/source/world/entity/WaterAnimal.hpp new file mode 100644 index 0000000..e69de29 diff --git a/source/world/level/path/Path.hpp b/source/world/level/path/Path.hpp index 83344ad..292573c 100644 --- a/source/world/level/path/Path.hpp +++ b/source/world/level/path/Path.hpp @@ -32,6 +32,31 @@ public: field_8 = 0; } + bool empty() + { + return m_numNodes == 0 || m_pNodes == nullptr; + } + + Node* getCurrentNode() + { + return m_pNodes[field_8]; + } + + void advance() + { + field_8++; + } + + void rewind() + { + field_8 = 0; + } + + bool endOfPath() + { + return field_8 >= m_numNodes; + } + private: int m_numNodes; Node** m_pNodes;