Objects can be created in non-walkable areas; that creates a big problem

For my eco-system placeables are being created in random locations, but from some reason they are often created in non-walkable places such as inside walls or hovering over a chasm. I suppose I can create various “safe respawn zones” and make sure the createobject operates within that radius, but that would be quite difficult to manage and rehaul. Does anyone have an idea how to solve? Note that Mushrooms have X survival time, but they can only self-destruct and respawn in another place if Y creature interacts with them.

Also,
In ActionForceMoveToObject according to NWNLexicon: “The action subject will attempt to move to within fRange meters of oMoveTo. The creature will try for fTimeout seconds to reach the waypoint and will teleport to the location no matter if it found a path in the meantime or not.”

Can I define how long is fTimeout? It seems to be lasting forever, even in valid locations.

Are you working with EE or 1.69?
Anyway, here is an answer: https://forums.beamdog.com/discussion/71306/getislocationvalid

Sure, you can pass any value to fTimeout that suits best for your needs.

void ActionForceMoveToObject(
    object oMoveTo,
    int bRun = FALSE,
    float fRange = 1.0f,
    float fTimeout = 30.0f
);
1 Like

Have a look at my HR base module (it is saved under development version(BD patch version), let me know if you need stable build version to open the developer module.) it has some custom functions just for this.
Video link
Video Link 2

Base

Let me know if you have any questions.

1 Like

When spawning placeables in area with multiple elevations, you may want to land them on the ground first (i.e. so they don’t hover over streams), then check if their new location is valid using solution provided by other posters.

// Returns location on the ground below (or above) lLocation which
// may or may not be valid. It exploits the fact that traps (and
// items) align themselves vertically with tile geometry.
location Land(location lLocation)
{
    object oTrap; // friendly (?), hard-to-detect trap
    oTrap = CreateTrapAtLocation(TRAP_BASE_TYPE_EPIC_SONIC,
        lLocation, 1.0, "", STANDARD_FACTION_COMMONER);
    lLocation = GetLocation(oTrap);
    DestroyObject(oTrap);
    return lLocation;
}

Sample usage:

location lInput = Location(oArea, Vector(11.0, 12.0, 5.0), 0.0);
location lOutput = Land(lInput);

Assuming there is flat ground at x=11, y=12, GetPositionFromLocation(lOutput).z should be 0.0 instead of 5.0.

Hmm, all of these solutions aren’t very clear-cut to me, but NWShacker’s reply did give me an idea when commenting about checking if the location is valid-- The “JumpToLocation” function has a “valid location” check built-in, so I’ll just get a random area location and then jump that placeable to the nearest object… I believe that should significantly cut down on the glitches I’m getting now. I’ll try that soon.

@ShadowM
Wow, well done! These are some awesome systems. Are you using it for a PW ?

@Aqvilinus
Thanks for the fTimeout answer :slight_smile:

That’s probably not going to work. Consider the following:

Creatures:

  • React to JumpToLocation (unless dead)
  • Must always be in walkable position within map space
  • Are subject to gravity (will land on ground when created mid-air)

Placeables:

  • Do NOT react to JumpToLocation (in 1.69 must be destroyed and re-created to “move”)
  • Can be placed at any location, walkable or not, in or outside level geom, or outside of its boundaries (i.e. position <1000, 1000, 1000> is fine)
  • Will always retain their Z-coordinate (hover in the air or be dug in ground)

@Proleric suggested to spawn an invisible creature where you’d like your placeable to be. If the creature’s resulting X or Y is different, you know that location is invalid (you can use Z for later) Creatures will always be moved to nearest valid location (EDIT: this will also detect whether a location is valid but already occupied). Then you delete the creature. Be warned however, that spawning creatures may cause OnPerception events to fire and generally has highest CreateObject overhead.

My code above lands the placeable on ground, so you don’t need to worry about their Z-corrdinate when spawning. It may not be required if you got that creature’s position, but if you want to avoid using creatures… See picture for visual description: left - incorrect “hovershrooms”, right - correct “watershrooms”. All made with random X, Y and Z = 0.0.

With this method you can also put placeables in chasms, at the top of cliffs, first story segments of buildings, etc:

use GetSurfaceMaterial, values 0,2,7,8,15,16,17 are not walkable
use GetGroundHeight to get the Z offset where the location is walkable

3 Likes

Yes, JumpToLocation was a bad idea, so I resolved to do something similar with “invisible creatures” except I’m simply using waypoints and the creatures/placeables are spawned in all those pre-determined places. However, I’m planning to spam many WP’s so it should be as good as random.

In case anyone wants to see the working location function it’s:

location RandomSpawn()
{
    int itotal;
    object oTarget = GetObjectByTag("SpawnPoint");
    while (oTarget != OBJECT_INVALID)
        {
        oTarget = GetObjectByTag("SpawnPoint", ++itotal);
        }
       int iRandom = Random(itotal);
       object oSpawnPoint = GetObjectByTag("SpawnPoint", iRandom);
       location lLocation = GetLocation(oSpawnPoint);
       return lLocation;
}

I appreciate the screenshots and the alternative solutions! I wanted to keep it as simple as possible and my current script seems to be doing the trick.

That’s one way to do it. But then you may need a lot of waypoints, or the positions will look pretty deterministic. Perhaps spawning within some radius around the waypoints (in a circle) would look better?

location RandomSpawnNear(string sTag, int iRadius=5) // 5.0 is half of tile
{
    // Get total number of spawn points
    int iTotal;
    while(GetObjectByTag(sTag, iTotal) != OBJECT_INVALID) { iTotal++; }
    // Get random spawn point
    object oSpawnPoint = GetObjectByTag(sTag, Random(iTotal));
    // Get unit vector of random facing
    vector vPosition = AngleToVector(IntToFloat(Random(360)));
    // Get displacement radius between 0 and iRadius
    float fRadius = IntToFloat(iRadius) * IntToFloat(Random(32768)) / 32768.0;
    // iRadius could also be pulled from local variable of oSpawnPoint
    // It would allow larger and smaller circles
    vPosition.x *= fRadius;
    vPosition.y *= fRadius;
    // Get random location
    location lLocation = Location(
        GetArea(oSpawnPoint),
        GetPosition(oSpawnPoint) + vPosition,
        IntToFloat(Random(360))); // random rotation too

    // Here insert optional location management code

    return lLocation;
}

Unless you don’t consider this simple anymore. It is also trivial to change this code to spawn in a square centered at spawn point.

EDIT: alternative, but will generate locations exactly fRadius away.

#include "x0_i0_position"
location RandomSpawnNear(string sTag, float fRadius=5.0) // 5.0 is half of tile
{
    // Get total number of spawn points
    int iTotal;
    while(GetObjectByTag(sTag, iTotal) != OBJECT_INVALID) { iTotal++; }
    // Get random spawn point
    object oSpawnPoint = GetObjectByTag(sTag, Random(iTotal));
    // Get random location fRadius away from spawn point
    return GetRandomLocation(GetArea(oSpawnPoint), oSpawnPoint, fRadius);
}

Yep, that what I used in my base custom functions and work perfectly. Shadooow beat me to saying it. Sorry life busy.

@Val
It 3.5 D&D base module that can be used to make single player, multi-player(lan) or PW. The group that doing testing on it is doing Server for their testing right now.