From aaed341569fa41f210bf0fad03b2e9a0b781bbee Mon Sep 17 00:00:00 2001 From: fgenesis Date: Fri, 23 Jun 2023 15:05:05 +0200 Subject: [PATCH] Cleanup underwater checks, fix rectangular water bubbles Long/thin rectangular nodes were not handled correctly, because it would only check the nearest waterbubble node for overlap, not all on the map. + Lua func: isUnderWater(x, y, radius) --- Aquaria/Entity.cpp | 42 +++++++++++++------------------------ Aquaria/Entity.h | 4 +++- Aquaria/Game.cpp | 33 ++++++++++++++++++++++------- Aquaria/Game.h | 3 +++ Aquaria/GameStructs.h | 8 +++++++ Aquaria/Path.cpp | 17 +++++---------- Aquaria/Path.h | 2 +- Aquaria/ScriptInterface.cpp | 10 +++++++++ BBGE/Rect.h | 2 +- 9 files changed, 71 insertions(+), 50 deletions(-) diff --git a/Aquaria/Entity.cpp b/Aquaria/Entity.cpp index 602829a..46e44d1 100644 --- a/Aquaria/Entity.cpp +++ b/Aquaria/Entity.cpp @@ -277,13 +277,14 @@ bool Entity::checkSplash(const Vector &o) if (!o.isZero()) check = o; bool changed = false; - if (wasUnderWater && !isUnderWater(o)) + bool uw = isUnderWater(o); + if (wasUnderWater && !uw) { sound("splash-outof"); changed = true; wasUnderWater = false; } - else if (!wasUnderWater && isUnderWater(o)) + else if (!wasUnderWater && uw) { sound("splash-into"); changed = true; @@ -1774,34 +1775,21 @@ int Entity::getRandomTargetPoint() return rand()%targetPoints.size(); } -bool Entity::isUnderWater(const Vector &override) +bool Entity::isUnderWater() { - Vector check = position; - if (!override.isZero()) - check = override; - - if (game->useWaterLevel && game->waterLevel.x > 0 && check.y-collideRadius > game->waterLevel.x) - return true; - - - Path *p = game->getNearestPath(position, PATH_WATERBUBBLE); - if (p && p->active && p->isCoordinateInside(position, collideRadius)) - { - waterBubble = p; - return true; - } + return _isUnderWaterPos(position); +} - if (!game->useWaterLevel || game->waterLevel.x == 0) return true; - else - { - if (check.y-collideRadius > game->waterLevel.x) - { - waterBubble = 0; - return true; - } - } +bool Entity::isUnderWater(const Vector& overridePos) +{ + return _isUnderWaterPos(overridePos.isZero() ? position : overridePos); +} - return false; +bool Entity::_isUnderWaterPos(const Vector& pos) +{ + UnderWaterResult res = game->isUnderWater(pos, collideRadius); + waterBubble = res.waterbubble; + return res.uw; } void Entity::push(const Vector &vec, float time, float maxSpeed, float dmg) diff --git a/Aquaria/Entity.h b/Aquaria/Entity.h index 1667ff5..5a3e0b8 100644 --- a/Aquaria/Entity.h +++ b/Aquaria/Entity.h @@ -103,7 +103,9 @@ public: virtual int messageVariadic(lua_State *L, int nparams) { return 0; } virtual int activateVariadic(lua_State *L, int nparams) { return 0; } - bool isUnderWater(const Vector &o=Vector()); + bool isUnderWater(); + bool isUnderWater(const Vector& overridePos); + bool _isUnderWaterPos(const Vector& pos); diff --git a/Aquaria/Game.cpp b/Aquaria/Game.cpp index b36c8c2..811b977 100644 --- a/Aquaria/Game.cpp +++ b/Aquaria/Game.cpp @@ -1262,6 +1262,30 @@ Path *Game::getPathByName(std::string name) return 0; } +Path *Game::getWaterbubbleAt(const Vector& pos, float rad) const +{ + for (Path *p = getFirstPathOfType(PATH_WATERBUBBLE); p; p = p->nextOfType) + if(p->active && p->isCoordinateInside(pos, rad)) + return p; + return NULL; +} + +UnderWaterResult Game::isUnderWater(const Vector& pos, float rad) const +{ + UnderWaterResult ret { false, NULL }; + if (!game->useWaterLevel || game->waterLevel.x == 0 + || (useWaterLevel && waterLevel.x > 0 && pos.y-rad > waterLevel.x)) + { + ret.uw = true; + return ret; + } + + Path *p = game->getWaterbubbleAt(pos, rad); + ret.waterbubble = p; + ret.uw = !!p; + return ret; +} + void Game::toggleOverrideZoom(bool on) { if (avatar) @@ -2536,14 +2560,7 @@ int game_collideParticle(Vector pos) bool inWaterBubble = false; if (!aboveWaterLine) { - Path *p = game->getNearestPath(pos, PATH_WATERBUBBLE); - if (p && p->active) - { - if (p->isCoordinateInside(pos)) - { - inWaterBubble = true; - } - } + inWaterBubble = !!game->getWaterbubbleAt(pos); } if (!inWaterBubble && aboveWaterLine) { diff --git a/Aquaria/Game.h b/Aquaria/Game.h index 42cb8f0..425194b 100644 --- a/Aquaria/Game.h +++ b/Aquaria/Game.h @@ -283,6 +283,9 @@ public: Path *getNearestPath(const Vector &pos, PathType pathType=PATH_NONE); Path *getNearestPath(Path *p, std::string name); + Path *getWaterbubbleAt(const Vector& pos, float rad = 0) const; + UnderWaterResult isUnderWater(const Vector& pos, float rad = 0) const; + SceneEditor sceneEditor; bool isSceneEditorActive() {return sceneEditor.isOn();} diff --git a/Aquaria/GameStructs.h b/Aquaria/GameStructs.h index 3e39094..6bde52b 100644 --- a/Aquaria/GameStructs.h +++ b/Aquaria/GameStructs.h @@ -6,6 +6,8 @@ #include "Vector.h" #include "GameEnums.h" +class Path; + struct ElementEffect { public: @@ -191,5 +193,11 @@ private: typedef std::vector IngredientDatas; +struct UnderWaterResult +{ + bool uw; + Path *waterbubble; +}; + #endif diff --git a/Aquaria/Path.cpp b/Aquaria/Path.cpp index 6359b61..518c15b 100644 --- a/Aquaria/Path.cpp +++ b/Aquaria/Path.cpp @@ -105,20 +105,13 @@ void Path::setActive(bool v) active = v; } -bool Path::isCoordinateInside(const Vector &pos, int radius) +bool Path::isCoordinateInside(const Vector &pos, float radius) const { if (nodes.empty()) return false; - if (pathShape == PATHSHAPE_CIRCLE) - { - Vector diff = pos - nodes[0].position; - return diff.isLength2DIn(this->rect.getWidth()*0.5f - radius); - } - else - { - Vector rel = pos - nodes[0].position; - return rect.isCoordinateInside(rel); - } - return false; + Vector diff = pos - nodes[0].position; + return pathShape == PATHSHAPE_CIRCLE + ? diff.isLength2DIn(rect.getWidth()*0.5f - radius) + : rect.isCoordinateInside(diff, radius); } Vector Path::getEnterNormal() diff --git a/Aquaria/Path.h b/Aquaria/Path.h index 785debf..856223a 100644 --- a/Aquaria/Path.h +++ b/Aquaria/Path.h @@ -96,7 +96,7 @@ public: void setEmitter(const std::string& name); PathNode *getPathNode(size_t idx); - bool isCoordinateInside(const Vector &pos, int rad=0); + bool isCoordinateInside(const Vector &pos, float rad=0) const; void reverseNodes(); diff --git a/Aquaria/ScriptInterface.cpp b/Aquaria/ScriptInterface.cpp index ed1c7a3..2604339 100644 --- a/Aquaria/ScriptInterface.cpp +++ b/Aquaria/ScriptInterface.cpp @@ -7930,6 +7930,15 @@ luaFunc(findWall) luaReturnNum(wall); } +luaFunc(isUnderWater) +{ + float x = lua_tonumber(L, 1); + float y = lua_tonumber(L, 2); + float rad = lua_tonumber(L, 3); + luaReturnBool(game->isUnderWater(Vector(x, y), rad).uw); +} + + luaFunc(setCutscene) { dsq->setCutscene(getBool(L, 1), getBool(L, 2)); @@ -10796,6 +10805,7 @@ static const struct { luaRegister(entity_alpha), luaRegister(findWall), + luaRegister(isUnderWater), luaRegister(overrideZoom), diff --git a/BBGE/Rect.h b/BBGE/Rect.h index 4ae1d5f..4e2547b 100644 --- a/BBGE/Rect.h +++ b/BBGE/Rect.h @@ -59,7 +59,7 @@ public: *x = x1 + ((*w) / 2); *y = y1 + ((*h) / 2); } - bool isCoordinateInside(const Vector &vec, int radius=0) const + bool isCoordinateInside(const Vector &vec, float radius=0) const { return ((vec.x >= x1-radius && vec.x <= x2+radius) && (vec.y >= y1-radius && vec.y <= y2+radius)); }