package TerrainUtils import NoWurst import Item import Rect import MapBounds import Wurstunit import Colors // Terrain API public function setWaterBaseColor(colorA color) SetWaterBaseColor(color.red, color.green, color.blue, color.alpha) public function vec2.setTerrainType(int ttype, int variation, int area, int shape) SetTerrainType(this.x, this.y, ttype, variation, area, shape) public function vec2.getTerrainType() returns int return GetTerrainType(this.x, this.y) public function vec2.getTerrainVariance() returns int return GetTerrainVariance(this.x, this.y) /** Applies a single terrain deformation to closest tile. Is not immediate. Reports shows that this may cause desync with Mac players. */ public function vec2.addTerrainHeight(real val) returns terraindeformation return TerrainDeformCrater(this.x, this.y, 63, -val, 1, true) /** Fills 32x32 rect around vec2 with given pathing type */ public function vec2.setTerrainPathable(pathingtype ttype, bool flag) SetTerrainPathable(this.x, this.y, ttype, flag) /** Returns if a specific pathingtype is set at the location. Note: Returns true if the pathingtype is *not* set, false if it *is* set. */ public function vec2.isTerrainPathable(pathingtype ttype) returns bool return IsTerrainPathable(this.x, this.y, ttype) /** Returns center of the Tile which given vec2 belongs to. */ public function vec2.toTileCenter() returns vec2 return vec2((this.x / 128.).round() * 128., (this.y / 128.).round() * 128.) /** Returns the (not normalised) terrain-normal at the given point */ public function vec2.getTerrainNormal(real sampleRadius) returns vec3 return 2 * sampleRadius * vec3((this.add(-sampleRadius, 0).getTerrainZ() - this.add(sampleRadius, 0).getTerrainZ()), (this.add(-sampleRadius, 0).getTerrainZ() - this.add(sampleRadius, 0).getTerrainZ()), 2 * sampleRadius) public function vec2.isTerrainDeepWater() returns boolean return not this.isTerrainPathable(PATHING_TYPE_FLOATABILITY) and this.isTerrainPathable(PATHING_TYPE_WALKABILITY) public function vec2.isTerrainShallowWater() returns boolean return not this.isTerrainPathable(PATHING_TYPE_FLOATABILITY) and not this.isTerrainPathable(PATHING_TYPE_WALKABILITY) and this.isTerrainPathable(PATHING_TYPE_BUILDABILITY) public function vec2.isTerrainLand() returns boolean return this.isTerrainPathable(PATHING_TYPE_FLOATABILITY) public function vec2.isTerrainPlatform() returns boolean return not this.isTerrainPathable(PATHING_TYPE_FLOATABILITY) and not this.isTerrainPathable(PATHING_TYPE_WALKABILITY) and not this.isTerrainPathable(PATHING_TYPE_BUILDABILITY) constant MAX_RANGE_SQ = 10. .squared() constant DUMMY_ITEM_ID = 'wolg' let dummyItem = createItem(DUMMY_ITEM_ID, ZERO2)..setVisible(false) let itemSearchRect = Rect(0., 0., 128., 128.) item array hiddenItems int hiddenItemsCount = 0 public function vec2.isTerrainWalkable() returns boolean // Hide any items in the area to avoid conflicts with our item itemSearchRect.moveTo(this) EnumItemsInRect(itemSearchRect, null) -> if GetEnumItem().isVisible() hiddenItems[hiddenItemsCount] = GetEnumItem() hiddenItems[hiddenItemsCount].setVisible(false) hiddenItemsCount++ // Try to move the test item and get its coords dummyItem.setPos(this) // Unhides the item let tempPos = dummyItem.getPos() dummyItem.setVisible(false) // Hide it again // Unhide any items hidden at the start while hiddenItemsCount > 0 hiddenItemsCount-- hiddenItems[hiddenItemsCount].setVisible(true) hiddenItems[hiddenItemsCount] = null return (tempPos.x - this.x) * (tempPos.x - this.x) + (tempPos.y-this.y) * (tempPos.y - this.y) <= MAX_RANGE_SQ and not this.isTerrainPathable(PATHING_TYPE_WALKABILITY) // Tile API /** represents a single terrain tile can also be used as vec2 <-> int conversion */ public tuple tile(int id) function tileToIndex(tile t) returns int return t.id function tileFromIndex(int i) returns tile return tile(i) // coordinate public constant TILES_X = (boundMax.x - boundMin.x).toInt() div 128 + 1 public constant TILES_Y = (boundMax.y - boundMin.y).toInt() div 128 + 1 public constant TILES_COUNT = TILES_X * TILES_Y public function tile(int x, int y) returns tile if ((x < 0) or (x >= TILES_X) or (y < 0) or (y >= TILES_Y)) return tile(-1) return tile(y * TILES_X + x) public function tile(real x, real y) returns tile let rx = (x - boundMin.x + 64).toInt() div 128 let ry = (y - boundMin.y + 64).toInt() div 128 if ((rx < 0) or (rx >= TILES_X) or (ry < 0) or (ry >= TILES_Y)) return tile(-1) return tile(ry * TILES_X + rx) public function tile.getX() returns real return (this.id mod TILES_X) * 128. + boundMin.x public function tile.getY() returns real return (this.id div TILES_X) * 128. + boundMin.y // vec2 conversion /** Returns the tile which given vec2 belongs to. */ public function vec2.getTile() returns tile return tile(this.x, this.y) public function tile.toVec2() returns vec2 return vec2(this.getX(), this.getY()) // functionality public function tile.setType(int ttype, int variation) this.toVec2().setTerrainType(ttype, variation, 1, 1) public function tile.setType(int ttype) this.toVec2().setTerrainType(ttype, -1, 1, 1) public function tile.getHeight() returns real return this.toVec2().getTerrainZ() public function tile.addHeight(real val) returns terraindeformation return this.toVec2().addTerrainHeight(val) public function tile.getType() returns int return this.toVec2().getTerrainType() public function tile.getVariance() returns int return this.toVec2().getTerrainVariance() /** Fills the entire tile (128x128 rect around tile pos) with given type */ public function tile.setPathing(pathingtype ttype, bool flag) for i = 0 to 3 for j = 0 to 3 (this.toVec2() + vec2(-48. + i * 32,-48. + j * 32)).setTerrainPathable(ttype, flag) @test function testMapCorners() boundMin.getTile().id.assertEquals(0) boundMax.getTile().id.assertEquals(TILES_COUNT - 1) tile(boundMax.x, boundMin.y).id.assertEquals(TILES_X - 1) tile(boundMin.x, boundMax.y).id.assertEquals(TILES_X * (TILES_Y - 1)) @test function testUpperBound() (boundMin + vec2(1,0)).getTile().id.assertEquals(0) (boundMin + vec2(63.9999,0)).getTile().id.assertEquals(0) (boundMin + vec2(64,0)).getTile().id.assertEquals(1) (boundMin + vec2(128,0)).getTile().id.assertEquals(1) (boundMin + vec2(63.9999,63.9999)).getTile().id.assertEquals(0) (boundMin + vec2(64,64)).getTile().id.assertEquals(TILES_X + 1) @test function testLowerBound() (boundMax - vec2(1,0)).getTile().id.assertEquals(TILES_COUNT - 1) (boundMax - vec2(64,0)).getTile().id.assertEquals(TILES_COUNT - 1) (boundMax - vec2(64.0002,0)).getTile().id.assertEquals(TILES_COUNT - 2) (boundMax - vec2(128,0)).getTile().id.assertEquals(TILES_COUNT - 2) (boundMax - vec2(64,64)).getTile().id.assertEquals(TILES_COUNT - 1) (boundMax - vec2(64.0002,64.0002)).getTile().id.assertEquals(TILES_COUNT - 1 - (TILES_X + 1)) // Some notes: // tile(0,0) bounds: // x: [-64; 64) // y: [-64; 64) // Tile mapping example on a map sized 256x256 pts. // Total tile count: 9 // Last tile index: 8 == - 1 // 6 7 8 // 3 4 5 // 0 1 2