FurryEskimo Posted December 31, 2020 Share Posted December 31, 2020 I know there are ways to measure the distance between the player and objects, including tiles on the ground. I’m trying to use my distance to the shore to control the player’s speed. Does anyone know how to do this? I’m almost certain I’ve seen code to measure distances, but didn’t understand how it worked. Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/ Share on other sites More sharing options...
CarlZalph Posted December 31, 2020 Share Posted December 31, 2020 This goes into vector math, but the simple form is a 2D case with pythagoras' theorem since the game uses Euclidean geometry. The easy thing to do is to get the origins of the two, subtract one from the other, and use that for the theorem. local x1,y1,z1 = ent1.Transform:GetWorldPosition() local x2,y2,z2 = ent2.Transform:GetWorldPosition() local dx,dy,dz = x2-x1,y2-y1,z2-z1 local dist2d = math.sqrt(dx*dx + dz*dz) local dist3d = math.sqrt(dx*dx + dy*dy + dz*dz) And for tiles you can get the tile's "origin" from any coordinate via: local tx,ty,tz = TheWorld.Map:GetTileCenterPoint(x, 0, z) You could use Klei's vector wrappers and make the code shorter: local dist3d = (ent1.Transform:GetPosition() - ent2.Transform:GetPosition()):Length() local tilevec = ToVector3(TheWorld.Map:GetTileCenterPoint(x, 0, z)) 1 1 Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1411715 Share on other sites More sharing options...
FurryEskimo Posted December 31, 2020 Author Share Posted December 31, 2020 @CarlZalph I think I understood most of that, but I don’t know how to use that code to check the player’s distance to the shoreline. (This is supposed to lower the player’s sleep the further they are from shore, since they can swim. Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1411719 Share on other sites More sharing options...
CarlZalph Posted December 31, 2020 Share Posted December 31, 2020 Ah, I assumed you'd already got that bit calculated with the 'my distance to the shore' as in 'my <function that calculates> distance to the shore'. My mistake. Many ways to go about doing that. The most straight forward way I can think of is to create a periodic timer on the player instance which polls nearby tiles and checks if it's an ocean. You can check if a point is ocean via: TheWorld.Map:IsOceanAtPoint(x, 0, z) Since turfs tiles are fixed 4.0 units away from one another you can simplify the grid aspect by adding 4*tileoffset to the player's position. To get the tileoffsets required to find the shortest distance, either create some sort of mathematical spiral for doing so or pick out the offsets manually and iterate over the ipairs table, bailing on a successful test. After getting the tile location you can then get the tile's center point from: TheWorld.Map:GetTileCenterPoint(x, 0, z) And then pipe that into a function to give you the nearest point to the square if that tile found isn't the player's tile to give you the nearest distance. Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1411731 Share on other sites More sharing options...
FurryEskimo Posted December 31, 2020 Author Share Posted December 31, 2020 @CarlZalph awesome! Thank you! It hat may be enough for me to begin testing, but there’s on weird factor here. The character is literally swimming, so their distance to the nearest ocean tile is, well, small, haha. What I need to know is the distance to the nearest land tile, or maybe it would be simpler to test the nearest non-sea tile? My original plan was to make an invisible wall near the shoreline, so the player could only swim in ‘shallow water’, but it might make more sense if they simply swim slower the further they get from shore, discouraging ocean swimming, but not making it impossible. If they swim Too far from shore, maybe I remove their ‘drown resistance’. Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1411768 Share on other sites More sharing options...
CarlZalph Posted December 31, 2020 Share Posted December 31, 2020 (edited) Ah, well for the land tile the check could use: TheWorld.Map:IsAboveGroundAtPoint(x, 0, z) These functions from the Map are mainly wrapper functions to convert a coordinate into a tile coordinate and checking the map's tile ID. Since your character is based solely on water depth and not a distance from the shore itself, might I suggest getting the ocean tile depth directly? To get a tile ID from a point: local map = TheWorld.Map local tcx, _, tcz = map:GetTileCenterPoint(x, 0, z) local tx, tz = map:GetTileCoordsAtPoint(tcx, 0, tcz) local tileid = map:GetTile(tx, tz) You can get some tile meta information through another Klei wrapper: local tileinfo = GetTileInfo(tileid) The tileinfo table is defined in worldtiledefs.lua, but the important parts for your use case is to test: tileinfo.is_shoreline == true tileinfo.ocean_depth == "SHALLOW" The first one will be true only for the tile adjacent to land, and the second is true for the lighter blue tint water. Other values for ocean_depth are: "NORMAL", "DEEP", and "VERY_DEEP". Edited December 31, 2020 by CarlZalph Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1411806 Share on other sites More sharing options...
FurryEskimo Posted January 2, 2021 Author Share Posted January 2, 2021 @CarlZalph I'm testing the code now. Correct me if I'm wrong, but I need to define the player's position first, right? Those coordinates are used to test the tiles near me? I used this code: --Checking water depth. --Test code print("Testing Water Depth/Distance") local map = TheWorld.Map print("Map Test") local tcx, _, tcz = map:GetTileCenterPoint(x, 0, z) print("txc Test") local tx, tz = map:GetTileCoordsAtPoint(tcx, 0, tcz) print("tx tz Test") local tileid = map:GetTile(tx, tz) print("tileid Test") local tileinfo = GetTileInfo(tileid) print("tileinfo Test") print("Testing Tile info") if tileinfo.is_shoreline == true then print("Tile is shoreline") if tileinfo.ocean_depth == "SHALLOW" then print("SHALLOW WATER") end end The error I received said 'X' was not defined, which it isn't. From what I can tell, this code Can test a tile's properties, but I'm still figuring out how to tell it to test the tiles near the player. I put it in the character's file, and it runs about once every two seconds. Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1412570 Share on other sites More sharing options...
CarlZalph Posted January 2, 2021 Share Posted January 2, 2021 Yeah, 'x' and 'z' are the player's position in your case. local x, y, z = <playerinst>.Transform:GetWorldPosition() Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1412646 Share on other sites More sharing options...
FurryEskimo Posted January 2, 2021 Author Share Posted January 2, 2021 (edited) @CarlZalph Great! I got the code working, but it's a little odd sometimes. I typed: --Checking water depth. --Test code print("Testing Water Depth/Distance") local x, y, z = inst.Transform:GetWorldPosition() print("Player's X, Y, and Y position Test") local map = TheWorld.Map print("Map Test") local tcx, _, tcz = map:GetTileCenterPoint(x, 0, z) print("txc Test") local tx, tz = map:GetTileCoordsAtPoint(tcx, 0, tcz) print("tx tz Test") local tileid = map:GetTile(tx, tz) print("tileid Test") local tileinfo = GetTileInfo(tileid) print("tileinfo Test") print("Testing Tile info") --if tileinfo.is_shoreline == false then -- print("Tile is shoreline") if tileinfo.ocean_depth == "SHALLOW" then print("SHALLOW WATER") elseif tileinfo.ocean_depth == "NORMAL" then print("NORMAL WATER") elseif tileinfo.ocean_depth == "DEEP" then print("DEEP WATER") elseif tileinfo.ocean_depth == "VERY_DEEP" then print("VERY_DEEP WATER") else print("Water's Depth Could Not Be Tested.") end --end if tileinfo.is_shoreline == true then print("Tile is shoreline") elseif tileinfo.is_shoreline == false then print("Tile is Not shoreline") end And the result were often similar to: [00:01:07]: Testing Water Depth/Distance [00:01:07]: Player's X, Y, and Y position Test [00:01:07]: Map Test [00:01:07]: txc Test [00:01:07]: tx tz Test [00:01:07]: tileid Test [00:01:07]: tileinfo Test [00:01:07]: Testing Tile info [00:01:07]: SHALLOW WATER [00:01:07]: Tile is shoreline This is great, but every so often it it says: [00:01:04]: Testing Water Depth/Distance [00:01:04]: Player's X, Y, and Y position Test [00:01:04]: Map Test [00:01:04]: txc Test [00:01:04]: tx tz Test [00:01:04]: tileid Test [00:01:04]: tileinfo Test [00:01:04]: Testing Tile info [00:01:04]: Water's Depth Could Not Be Tested. "Tile is Not shoreline" is never used. It can detect the 'shoreline', which I assume are ocean tiles, but non-ocean tiles aren't registered as non-shoreline? Perhaps the value is 'nil' I suppose. This may be all I need, as I can make the player's speed drop depending on the depth of the water, and possibly remove their drowning resistance in very deep water. I may even be able to use this to limit the player's ability to jump into the water, so they won't jump in when on a boat. That would be a shame. But I can sort of see how this can be used to compare distances. If I wanted to find the nearest land tile (within 30ft for example), would I need to use the player's position and then repeat the testing of tiles all around me until one registered as land? Or was there a way to automatically tell the game to test all the tiles around the player? As I understand it, my code is currently testing the tile I'm standing on (which may actually be all I need, considering it's automatically has a depth rating! Wait, is the depth rating already defining the tiles distance from land? This may be really easy, wow!) Edit: I looked up the difference between the different depths, and they almost seem like biomes. Most of the ocean is shallow water, so I could restrict the player to shallow water, but it won’t be very noticeable. Slowing down as the player gets further from shore would still help convey the sense that swimming is useful, but not heavily encouraged. Edit 2: Ok, after further testing I figured out how to test the player's position and the position of the tiles near them. I can probably figure out a way to manually tell the game to check all the tiles around me, but it won't exactly be fun to code. What took me a little time to figure out was the shoreline statistic. Ocean tiles Very close to the shore are shoreline tiles, but nothing else is. I swam in a tiny river and some of the tiles were ocean, but not shoreline, which was confusing me. I'll probably use the 'tcx, __, tcz test' to get a basic coordinate to test from, then test 3-4 tiles in every direction, covering 9-16 tiles... Lots of testing. If none register as shoreline, maybe I make the player drown or something, idk. I could use the closest tile that lacks a depth statistic to control the player's speed by comparing its coordinates to the player's. There may be multiple close tiles that aren't ocean, so I'd need to test each, compare the distances, and then use the shortest distance.. (sounds like work,,) Edited January 3, 2021 by FurryEskimo Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1412662 Share on other sites More sharing options...
CarlZalph Posted January 3, 2021 Share Posted January 3, 2021 18 hours ago, FurryEskimo said: "Tile is Not shoreline" is never used. It can detect the 'shoreline', which I assume are ocean tiles, but non-ocean tiles aren't registered as non-shoreline? Perhaps the value is 'nil' I suppose. This may be all I need, as I can make the player's speed drop depending on the depth of the water, and possibly remove their drowning resistance in very deep water. I may even be able to use this to limit the player's ability to jump into the water, so they won't jump in when on a boat. That would be a shame. But I can sort of see how this can be used to compare distances. If I wanted to find the nearest land tile (within 30ft for example), would I need to use the player's position and then repeat the testing of tiles all around me until one registered as land? Or was there a way to automatically tell the game to test all the tiles around the player? As I understand it, my code is currently testing the tile I'm standing on (which may actually be all I need, considering it's automatically has a depth rating! Wait, is the depth rating already defining the tiles distance from land? This may be really easy, wow!) Edit: I looked up the difference between the different depths, and they almost seem like biomes. Most of the ocean is shallow water, so I could restrict the player to shallow water, but it won’t be very noticeable. Slowing down as the player gets rather from shore would still help conver the sense that swimming is useful, but not heavily encouraged. Yeah if the field is available then it's there, otherwise it's nil or false. So to check you'd: if field == true then -- Only true else -- false, nil, number, string, etc end This is a map rendered out showing the different ocean tile types: There's the light blue that surrounds the adjacent land tiles, then there's the shallow that is a fairly big area around the land, and then the other depths. The depth rating is based on the tile type, and the tile type is picked by worldgen to have that 'gradient' going from shore to deep land. If the shallow water is too loose for your purposes, then you'll need to add another restriction. For example you say you want to slow them down, well you can have an internal variable keep track how long ago it was that the player was on land or shoreline and scale down the locomotor's speed by that. Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1412820 Share on other sites More sharing options...
FurryEskimo Posted January 3, 2021 Author Share Posted January 3, 2021 (edited) @CarlZalph That could work. I’d make a variable that counts up the longer the player is in the water. I was also thinking, I could simply check for shoreline tiles. If the player is in shallow water they’re fine, but if a shoreline tile isn’t nearby you’re slower, and if you’re in deeper water you have a limited amount of time to turn back or you drown, plus a heavier speed penalty would be applied. That’s much easier to code (I think) Edit: I've been told that a 'raster' may be what I need to effectively check the tiles near the player, but I've yet to find an effective guide on such a technique, but I'll keep looking. For some reason formulas to systematically check a grid aren't that easy to find. I may also just be able to use a repeat function. I used to use those a lot in C++, but I've never coded one in Lua yet. Edit 2: Figured it out. I can use code like this to search the grid around my character. If any tiles test positive, I'll know. --[ local variable definition --] local a = 1 local b = 1 --[ repeat loop execution --] repeat b = 1 repeat print("value of a:", a) print("value of b:", b) b = b + 1 until( b > 15 ) a = a + 1 until( a > 15 ) Edit 3: Took some doing, but I think I got it!! This checks around the player, four tiles in every direction, totally 64 tiles checked, wow! I haven't noticed a spike in lag of any kind, so that's good! If I wanted, I could check each tile's distance to the player, and then compare that to the shortest known distance, and then use the shortest distance to shore, if I wanted to apply a speed penalty that changes smoothly in relation to the player's distance from shore. --Checking water depth. --Test code print("Testing Water Depth/Distance") local x, y, z = inst.Transform:GetWorldPosition() print("Player's X, Y, and Z Position Test") print (x, y, z) local map = TheWorld.Map print("Map Test") --[ local variable definition --] local a = 1 local b = 1 --[ repeat loop execution --] repeat b = 1 repeat print("value of a:", a) print("value of b:", b) local tcx, _, tcz = map:GetTileCenterPoint(x + 16 - (4 * b), 0, z + 16 - (4 * a)) print("tcx, __, tcz Test") print(map:GetTileCenterPoint(x + 16 - (4 * b), 0, z + 16 - (4 * a))) local tx, tz = map:GetTileCoordsAtPoint(tcx, 0, tcz) print("tx tz Test") print(map:GetTileCoordsAtPoint(tcx, 0, tcz)) local tileid = map:GetTile(tx, tz) print("Tile ID Test") print(map:GetTile(tx, tz)) local tileinfo = GetTileInfo(tileid) print("tileinfo Test") print(GetTileInfo(tileid)) print("Testing Tile info") -- if tileinfo.is_shoreline == false then -- print("Tile is shoreline") if tileinfo.ocean_depth == "SHALLOW" then print("SHALLOW WATER") elseif tileinfo.ocean_depth == "NORMAL" then print("NORMAL WATER") elseif tileinfo.ocean_depth == "DEEP" then print("DEEP WATER") elseif tileinfo.ocean_depth == "VERY_DEEP" then print("VERY_DEEP WATER") else print("Water's Depth Could Not Be Tested.") end -- end if tileinfo.is_shoreline == true then print("Tile is shoreline") elseif tileinfo.is_shoreline == false then --Note: This never happens! Non-shoreline tiles probably have a 'ni' value, not 'false'. print("Tile is Not shoreline") elseif tileinfo.is_shoreline == nil then print("Tile is nil") end b = b + 1 until( b > 8 ) a = a + 1 until( a > 8 ) Edited January 3, 2021 by FurryEskimo Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1412831 Share on other sites More sharing options...
FurryEskimo Posted January 4, 2021 Author Share Posted January 4, 2021 @CarlZalph Quick question, what's the difference between "local tcx, _, tcz = map:GetTileCenterPoint(" and "local tx, tz = map:GetTileCoordsAtPoint("? I expected the values to be the same, but they often seem to differ by around a hundred, and I'm not sure why. I've coded the character so their speed drops significantly once no shorelines are detected. I tried testing for shorelines or the lack of an ocean depth (implying land) but for some reason I was detecting tiles that had no depth rating, while out in the ocean. idk. To the point though, the drop in speed is sort of, awkward and sudden, so measuring the distance to shore may help. local dist2d = math.sqrt( (tcx - x)^2 + (tcz - z)^2 ) --Test code. print("Distance between player and tile: ", dist2d) This seems to work, but I wanted your opinion. I know I've messed something up somewhere, because when I measure to the right I'm getting a distance twice as far as when I measure tiles to the character's left, Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1412953 Share on other sites More sharing options...
CarlZalph Posted January 4, 2021 Share Posted January 4, 2021 14 hours ago, FurryEskimo said: Quick question, what's the difference between "local tcx, _, tcz = map:GetTileCenterPoint(" and "local tx, tz = map:GetTileCoordsAtPoint("? I expected the values to be the same, but they often seem to differ by around a hundred, and I'm not sure why. I've coded the character so their speed drops significantly once no shorelines are detected. I tried testing for shorelines or the lack of an ocean depth (implying land) but for some reason I was detecting tiles that had no depth rating, while out in the ocean. idk. To the point though, the drop in speed is sort of, awkward and sudden, so measuring the distance to shore may help. local dist2d = math.sqrt( (tcx - x)^2 + (tcz - z)^2 ) --Test code. print("Distance between player and tile: ", dist2d) This seems to work, but I wanted your opinion. I know I've messed something up somewhere, because when I measure to the right I'm getting a distance twice as far as when I measure tiles to the character's left, First function returns a coordinate in in-game 3D worldspace coordinate system, and the second function returns a tile index relative to the map object as a whole. The second function is used to get the tile coordinates for use in the function: map:GetTile(tx, tz) So for your case you can use the first function alone to get the distance, and yes your dist2d is fine. local dx = 16 - (4 * b) local dz = 16 - (4 * a) local dist2d = math.sqrt(dx*dx + dz*dz) local tcx, _, tcz = map:GetTileCenterPoint(x + dx, 0, z + dz) This is a simplified one since you're already calculating out the deltas by getting the tile spots nearby. While not as accurate as getting the distance to the tile's centers it'll be 'smoother' in that the tile edges will be the transition points on the speed. Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1413150 Share on other sites More sharing options...
FurryEskimo Posted January 4, 2021 Author Share Posted January 4, 2021 (edited) @CarlZalph I did some more testing and decided to graph out the points being tested. Something is definitely wrong. For some reason it's testing too far to the left and down, and the points being tested north to south are super close together. I thought each tile was separated by four units, right? I've reviewed the math a few times now and just can't tell what's wrong. I thought I'd done it perfectly. (Player is at 389, 75) --Checking water depth. --Test code print("Testing Water Depth/Distance") local x, y, z = inst.Transform:GetWorldPosition() print("Player's X, Y, and Z Position Test") print (x, y, z) local map = TheWorld.Map print("Map Test") --[ local variable definition --] local a = 1 local b = 1 local byland = 0 --[ repeat loop execution --] repeat b = 1 repeat print("value of a:", a) print("value of b:", b) local tcx, _, tcz = map:GetTileCenterPoint(x + 12 - (4 * b), 0, z + 12 - (4 * a)) print("tcx, __, tcz Test") print(map:GetTileCenterPoint(x + 12 - (4 * b), 0, z + 12 - (4 * a))) local tx, tz = map:GetTileCoordsAtPoint(tcx, 0, tcz) print("tx tz Test") local dist2d = math.sqrt( (tcx - x)^2 + (tcz - z)^2 ) --Test code. print("Distance between player and tile: ", dist2d) print(map:GetTileCoordsAtPoint(tcx, 0, tcz)) local tileid = map:GetTile(tx, tz) print("Tile ID Test") print(map:GetTile(tx, tz)) local tileinfo = GetTileInfo(tileid) print("tileinfo Test") print(GetTileInfo(tileid)) print("Testing Tile info") -- if tileinfo.is_shoreline == false then -- print("Tile is shoreline") if tileinfo.ocean_depth == "SHALLOW" then print("SHALLOW WATER") elseif tileinfo.ocean_depth == "NORMAL" then print("NORMAL WATER") elseif tileinfo.ocean_depth == "DEEP" then print("DEEP WATER") elseif tileinfo.ocean_depth == "VERY_DEEP" then print("VERY_DEEP WATER") else print("Water's Depth Could Not Be Tested.") -- byland = 1 end -- end if tileinfo.is_shoreline == true then print("Tile is shoreline.") byland = 1 elseif tileinfo.is_shoreline == nil then print("Tile is not shoreline.") end b = b + 1 until( b > 7 or byland == 1) a = a + 1 until( a > 7 or byland == 1) if byland == 0 then print("Not by land.") inst.components.locomotor.walkspeed = inst.components.locomotor.walkspeed / 3 inst.components.locomotor.runspeed = inst.components.locomotor.runspeed / 3 else print("By land.") end Edited January 4, 2021 by FurryEskimo Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1413284 Share on other sites More sharing options...
CarlZalph Posted January 5, 2021 Share Posted January 5, 2021 (edited) 2 hours ago, FurryEskimo said: @CarlZalph I did some more testing and decided to graph out the points being tested. Something is definitely wrong. For some reason it's testing too far to the left and down, and the points being tested north to south are super close together. I thought each tile was separated by four units, right? I've reviewed the math a few times now and just can't tell what's wrong. I thought I'd done it perfectly. If I had to guess, then it's something with the loops or other. I didn't look into too much what you've put together. I do see the value in having such a function existing, so I created one that uses a lookup table of offsets for fast checks. local GenerateGridRadiusLookup = function(r) local rsq = r*r local data = {} local datasorted = {} for x = -r, r do local xsq = x*x for z = -r, r do local zsq = z*z local dsq = xsq + zsq if dsq > 0 and dsq <= rsq then local d = math.sqrt(dsq) local entry = data[dsq] if entry == nil then entry = {} table.insert(datasorted, {d, entry}) end table.insert(entry, {x, z}) data[dsq] = entry end end end table.sort(datasorted, function(a, b) return a[1] < b[1] end) datasorted.maxr = r return datasorted end local GetMinTileDistanceByLookup = function(ent, lookup, testfn) local map = TheWorld.Map local ex, _, ez = ent.Transform:GetWorldPosition() for _,v in ipairs(lookup) do local entries = v[2] for _,v2 in ipairs(entries) do local xo = ex + v2[1]*4 local zo = ez + v2[2]*4 local tx, tz = map:GetTileCoordsAtPoint(xo, 0, zo) local tid = map:GetTile(tx, tz) if testfn(tid) then local tcx, _, tcz = map:GetTileCenterPoint(xo, 0, zo) return v[1]*4, tid, tcx, tcz end end end return nil end local GetMinUnitDistanceByLookup = function(ent, lookup, testfn) local map = TheWorld.Map local ex, _, ez = ent.Transform:GetWorldPosition() for _,v in ipairs(lookup) do local entries = v[2] local valids = {} for _,v2 in ipairs(entries) do local xo = ex + v2[1]*4 local zo = ez + v2[2]*4 local tx, tz = map:GetTileCoordsAtPoint(xo, 0, zo) local tid = map:GetTile(tx, tz) if testfn(tid) then local tcx, _, tcz = map:GetTileCenterPoint(xo, 0, zo) table.insert(valids, {tcx, tcz, tid}) end end local mindsq = lookup.maxr * lookup.maxr * 16 + 1 local mindtid = nil local mindx, mindz = nil, nil for _,v in ipairs(valids) do local tcx, tcz = v[1], v[2] local ctx = ex < tcx - 2 and tcx - 2 or ex > tcx + 2 and tcx + 2 or ex local ctz = ez < tcz - 2 and tcz - 2 or ez > tcz + 2 and tcz + 2 or ez local dx = ex - ctx local dz = ez - ctz local dsq = dx*dx + dz*dz if dsq < mindsq then mindsq = dsq mindtid = v[3] mindx = ctx mindz = ctz end end if mindtid then return math.sqrt(mindsq), mindtid, mindx, mindz end end return nil end local GetTileIDOn = function(ent) local map = TheWorld.Map local ex, _, ez = ent.Transform:GetWorldPosition() local tx, tz = map:GetTileCoordsAtPoint(ex, 0, ez) local tid = map:GetTile(tx, tz) return tid end local GetOceanDepthOf = function(tid) local tinfo = GetTileInfo(tid) return tinfo.ocean_depth, tinfo.is_shoreline end local GridRadiusLookup = GenerateGridRadiusLookup(5) Where '5' is the biggest tile distance away the lookup will generate for faster lookups. Example test code using these: local ptid = GetTileIDOn(ThePlayer) if IsOceanTile(ptid) then local d, tid, nx, nz = GetMinUnitDistanceByLookup(ThePlayer, GridRadiusLookup, IsLandTile) if d then print(string.format("ThePlayer is in the ocean, and the nearest land is %.1f units away and is of type %d. It is at %.1f %.1f.", d, tid, nx, nz)) else print("ThePlayer is in the ocean, and there are no nearby land tiles found.") end local depth, isshore = GetOceanDepthOf(ptid) print(string.format(" The ocean tile ThePlayer is in has depth of {%s}%s.", tostring(depth), isshore and " and is a shoreline" or "")) elseif IsLandTile(ptid) then local d, tid, nx, nz = GetMinUnitDistanceByLookup(ThePlayer, GridRadiusLookup, IsOceanTile) if d then print(string.format("ThePlayer is on land, and the nearest ocean is %.1f units away and is of type %d. It is at %.1f %.1f.", d, tid, nx, nz)) else print("ThePlayer is on land, and there are no nearby ocean tiles found.") end else print("ThePlayer is neither on land nor in the ocean.") -- Could be another terrain type by mods or something else by Klei in the future. end Using the Klei-provided wrapper functions IsLandTile and IsOceanTile defined in ocean_util.lua. I have two functions in here, one for calculating based on tiles so the distance function is more jagged and the other calculates it as a linear unit distance. GetMinUnitDistanceByLookup = Linear, GetMinTileDistanceByLookup = Jagged Both return nil if nothing is found, or 4 return results: MinDistance, TileID, MinDistanceX, MinDistanceZ You could use these coords to do something at the nearest point found, and other stuff depending on its tileID and whatnot. Edited January 5, 2021 by CarlZalph Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1413330 Share on other sites More sharing options...
FurryEskimo Posted January 5, 2021 Author Share Posted January 5, 2021 (edited) @CarlZalph Haha, you'll excuse me if I'm just a little boggled. I can sort-of tell what you've done, and I think I may know how to get the information out of your equation. I'm not a professional coder, but it looks like you made some sort of array of coordinates, then ran the data through some test, comparing it to the existing information, and then that data can be called, yes? I did something similar, just a lot more, noob-ish. I've tried implementing your code a few times now and get an error each time. The first time it was because "GetTileIDOn" wasn't defined (I tried using just the second half of your post.) I tried installing all of your function, just to see what would happen, and it does run, but it claims the "ent" in "local ex, _, ez = ent.Transform:GetWorldPosition()" isn't defined. I'm still trying to figure out what's wrong with my equation, since while noob-sh, I still think it should be able to work, possibly. The main reason I want to get my version working is because I better understand it, and can better diagnose and fix errors. I'm going to retest the equation that generates the scan locations and see if I can't figure out what's wrong with it! Edit: Ha, I think I figured it out, and it's silly and simple. The graph I had didn't have an even scaling, so the values appeared stretched out. I also began testing at a=1 and b=1, so that might have thrown off the results. idk for sure if this will fix it, but I hope so! My evening will be a lot less stressful if this issue was just a silly fluke. Edited January 5, 2021 by FurryEskimo Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1413669 Share on other sites More sharing options...
CarlZalph Posted January 5, 2021 Share Posted January 5, 2021 (edited) 44 minutes ago, FurryEskimo said: @CarlZalph Haha, you'll excuse me if I'm just a little boggled. I can sort-of tell what you've done, and I think I may know how to get the information out of your equation. I'm not a professional coder, but it looks like you made some sort of array of coordinates, then ran the data through some test, comparing it to the existing information, and then that data can be called, yes? I did something similar, just a lot more, noob-ish. I've tried implementing your code a few times now and get an error each time. The first time it was because "GetTileIDOn" wasn't defined (I tried using just the second half of your post.) I tried installing all of your function, just to see what would happen, and it does run, but it claims the "ent" in "local ex, _, ez = ent.Transform:GetWorldPosition()" isn't defined. I'm still trying to figure out what's wrong with my equation, since while noob-sh, I still think it should be able to work, possibly. The main reason I want to get my version working is because I better understand it, and can better diagnose and fix errors. I'm going to retest the equation that generates the scan locations and see if I can't figure out what's wrong with it! Aye the second part requires the first part, and the code is just code ran on a client not the server. To have it on the server, replace ThePlayer with the character prefab's inst on the server-side. Edited January 5, 2021 by CarlZalph Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1413679 Share on other sites More sharing options...
FurryEskimo Posted January 6, 2021 Author Share Posted January 6, 2021 (edited) @CarlZalph Thanks, I'll give that a try. In the meantime I have good news! My code works! It's correctly generating the coordinates of all the tiles around the player! If I tell it to check each tile's distance to the player and remember the shortest distance, this will be a piece of cake! I do worry though that it may generate lag, and the affect on speed will only feel smooth if the equation is run frequently. The easiest solution I can think of is to make a basic test run occasionally, and if the player is standing on ground, do nothing. If the player's in the water, run this equation more often. With time I may learn how to make this test activate and deactivate as the player jumps into and out of the water, but that's a project for a different day. I am curious though, your code seems so much more professional, but more complex too; what's the benefit of using your code vs what I wrote? Edit: This is the formula I'm currently using, and it seems to work. I can edit this pretty easily. --Checking water depth. --Test code print("Testing Water Depth/Distance") local x, y, z = inst.Transform:GetWorldPosition() print("Player's Position: ", x, y, z) local map = TheWorld.Map print("Map Test") --[ local variable definition --] local a = 0 local b = 0 local byland = 0 local shortestdistance = nil --[ repeat loop execution --] repeat b = 0 repeat print("Value of a:", a) print("Value of b:", b) local tcx, _, tcz = map:GetTileCenterPoint(x + 12 - (4 * b), 0, z + 12 - (4 * a)) print("TCX: ", tcx) print("TCZ: ", tcz) local tx, tz = map:GetTileCoordsAtPoint(tcx, 0, tcz) print("TX: ", tx) print("TZ: ", tz) local tileid = map:GetTile(tx, tz) print("Tile ID: ", tileid) local tileinfo = GetTileInfo(tileid) print("Tile Info: ", tileinfo) print("Testing Tile Info.") print("Water Type: ", tileinfo.ocean_depth) --[[ if tileinfo.ocean_depth == "SHALLOW" then print("SHALLOW WATER") elseif tileinfo.ocean_depth == "NORMAL" then print("NORMAL WATER") elseif tileinfo.ocean_depth == "DEEP" then print("DEEP WATER") elseif tileinfo.ocean_depth == "VERY_DEEP" then print("VERY_DEEP WATER") else print("Water's Depth Could Not Be Tested.") -- byland = 1 end --]] local dist2d = math.sqrt( (tcx - x)^2 + (tcz - z)^2 ) --Test code. print("Distance between player and tile: ", dist2d) if tileinfo.is_shoreline == true then print("Tile is shoreline.") byland = 1 if shortestdistance == nil then shortestdistance = dist2d elseif dist2d < shortestdistance then shortestdistance = dist2d end print("Shortest distance to a shoreline tile as of this test: ", shortestdistance) elseif tileinfo.is_shoreline == nil then print("Tile is not shoreline.") end b = b + 1 until( b > 6 ) --or byland == 1) a = a + 1 until( a > 6 ) --or byland == 1) if byland == 0 then print("Not by land.") inst.components.locomotor.walkspeed = inst.components.locomotor.walkspeed / 3 inst.components.locomotor.runspeed = inst.components.locomotor.runspeed / 3 else print("By land.") end Edited January 6, 2021 by FurryEskimo Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1413685 Share on other sites More sharing options...
CarlZalph Posted January 6, 2021 Share Posted January 6, 2021 1 hour ago, FurryEskimo said: @CarlZalph Thanks, I'll give that a try. In the meantime I have good news! My code works! It's correctly generating the coordinates of all the tiles around the player! If I tell it to check each tile's distance to the player and remember the shortest distance, this will be a piece of cake! I do worry though that it may generate lag, and the affect on speed will only feel smooth if the equation is run frequently. The easiest solution I can think of is to make a basic test run occasionally, and if the player is standing on ground, do nothing. If the player's in the water, run this equation more often. With time I may learn how to make this test activate and deactivate as the player jumps into and out of the water, but that's a project for a different day. I am curious though, your code seems so much more professional, but more complex too; what's the benefit of using your code vs what I wrote? Mine'll be faster in the general case as it precalculates out a table of table sorted by distances from the center tile. It does this in a lazy fashion of masking the circle out of a square when it really just needs 1/8th of a circle and reflect the other 8 nodes without having to do calculations, but this is just a one off and not done when the game is simulating. It'll then check out the tiles nearest to the player outward to calculate the minimum distance up to the precalculated maximum, returning the real closest distance the entity has to move to be 'on the tile' via `GetMinUnitDistanceByLookup` or an approximate distance based on tiles via `GetMinTileDistanceByLookup`. The return values also tell you the spot that the entity must be on to make this distance 0 and the tile ID for further parsing and use elsewhere. As for professional this isn't so much professional as I didn't use varnames for the lookup table and chose indexes instead for more performance over code readabilty (indexing versus hashmap lookup). Professionals would say that's bad form as it also makes maintaining the code harder. Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1413703 Share on other sites More sharing options...
FurryEskimo Posted January 9, 2021 Author Share Posted January 9, 2021 (edited) @CarlZalph So I've been testing the code and it works pretty well! There appears to be one weird issue though. The scanning itself seems to cause no lag, and the player's speed seems fine as long as they're by shore, but when the player moves just far enough into the water so that their speed drops to a crawl, on that line they appear to flicker, allowed to move, but then snapping back into place. If I click the player moves the way you'd expect, but trying to control the player yourself causes weird snapping issues. Do you have any clue what might be happening? I change the player's speed constantly, but this is different and weird, and I have no clue why it's happening. Edited January 9, 2021 by FurryEskimo Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1414849 Share on other sites More sharing options...
CarlZalph Posted January 9, 2021 Share Posted January 9, 2021 That sounds like a lag-compensation prediction error where the server thinks that the player is in one spot and the client thinks they're way far away at another spot and then teleports back to the last well known good spot. Perhaps a floating point precision loss is happening where the client still thinks it's moving at some rate while on the server it's actually a lot smaller. I do see you're touching the locomotor's base speed directly instead of using an external speed modifier. Not sure how these two would interfere with each other, will have to test. inst.components.locomotor:SetExternalSpeedMultiplier(inst, "someuniquestringhere", value) This is generally what I see Klei use for speed modifications, perhaps this is the appropriate function that replicates better with clients? I'd also re-check your equation used to calculate out the new speed and note that my functions can return nil. Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1414948 Share on other sites More sharing options...
FurryEskimo Posted January 9, 2021 Author Share Posted January 9, 2021 (edited) @CarlZalph Aha! So there was a default way to change speed! I just made up my own method and it seemed to work just fine. Currently my player's speed is: Default Speed Option: (55/(0.0012*player's temperature^2+10)+2) + 0.5 * √Number of hound followers •When Snowing: + 0.5 •One to three hound followers is recommended. It's honestly kinda silly, but I like it and it works. The player becomes faster as they become colder, faster as they train hounds, and faster in the snow. It's subtle and somehow doesn't seem to cause lag, which was a major concern at first. I've tried a few different methods to change the player's speed in the water, some being Very simple, but it still seems to cause jittering from time to time, but because there's so much math happening, it's hard to look at the server log and figure out when the jittering is happening. if byland == 1 then waterdebuff = 0.6 * (shortestdistance^0.5) -- if waterdebuff < (inst.components.locomotor.walkspeed - 1) then print("By land.") -- inst.components.locomotor.walkspeed = (inst.components.locomotor.walkspeed * 0.75) - waterdebuff -- inst.components.locomotor.runspeed = (inst.components.locomotor.runspeed * 0.75) - waterdebuff inst.components.locomotor.walkspeed = inst.components.locomotor.walkspeed / 2 inst.components.locomotor.runspeed = inst.components.locomotor.runspeed / 2 -- else -- print("By land, but debuff was excessive.") -- inst.components.locomotor.walkspeed = inst.components.locomotor.walkspeed / 8 -- inst.components.locomotor.runspeed = inst.components.locomotor.runspeed / 8 -- end else print("Not by land.") inst.components.locomotor.walkspeed = inst.components.locomotor.walkspeed / 6 inst.components.locomotor.runspeed = inst.components.locomotor.runspeed / 6 end Edited January 9, 2021 by FurryEskimo Link to comment https://forums.kleientertainment.com/forums/topic/125557-measuring-distances/#findComment-1415050 Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now