public static Bitmap TerrainBitmap(TileSet tileset, Map map, bool actualSize = false) { var isDiamond = map.TileShape == TileShape.Diamond; var b = map.Bounds; // Fudge the heightmap offset by adding as much extra as we need / can. // This tries to correct for our incorrect assumption that MPos == PPos var heightOffset = Math.Min(map.MaximumTerrainHeight, map.MapSize.Y - b.Bottom); var width = b.Width; var height = b.Height + heightOffset; var bitmapWidth = width; if (isDiamond) bitmapWidth = 2 * bitmapWidth - 1; if (!actualSize) bitmapWidth = height = Exts.NextPowerOf2(Math.Max(bitmapWidth, height)); var terrain = new Bitmap(bitmapWidth, height); var bitmapData = terrain.LockBits(terrain.Bounds(), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); var mapTiles = map.MapTiles.Value; unsafe { var colors = (int*)bitmapData.Scan0; var stride = bitmapData.Stride / 4; for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var uv = new MPos(x + b.Left, y + b.Top); var type = tileset.GetTileInfo(mapTiles[uv]); var leftColor = type != null ? type.LeftColor : Color.Black; if (isDiamond) { // Odd rows are shifted right by 1px var dx = uv.V & 1; var rightColor = type != null ? type.RightColor : Color.Black; if (x + dx > 0) colors[y * stride + 2 * x + dx - 1] = leftColor.ToArgb(); if (2 * x + dx < stride) colors[y * stride + 2 * x + dx] = rightColor.ToArgb(); } else colors[y * stride + x] = leftColor.ToArgb(); } } } terrain.UnlockBits(bitmapData); return terrain; }
public FrozenActor(Actor self, MPos[] footprint, CellRegion footprintRegion, Shroud shroud) { actor = self; isVisibleTest = shroud.IsVisibleTest(footprintRegion); Footprint = footprint; CenterPosition = self.CenterPosition; Bounds = self.Bounds; UpdateVisibility(); }
public FrozenActor(Actor self, MPos[] footprint, Shroud shroud) { actor = self; this.shroud = shroud; // Consider all cells inside the map area (ignoring the current map bounds) Footprint = footprint .Where(m => shroud.Contains(m)) .ToArray(); CenterPosition = self.CenterPosition; Bounds = self.Bounds; UpdateVisibility(); }
public void Update(MPos uv, Sprite sprite, float2 pos) { if (sprite != null) { if (sprite.Sheet != Sheet) throw new InvalidDataException("Attempted to add sprite from a different sheet"); if (sprite.BlendMode != BlendMode) throw new InvalidDataException("Attempted to add sprite with a different blend mode"); } else sprite = emptySprite; var offset = rowStride * uv.V + 4 * uv.U; Util.FastCreateQuad(vertices, pos, sprite, palette.TextureIndex, offset, sprite.Size); dirtyRows.Add(uv.V); }
static void ReadWaypoints(Map map, IniFile file, int2 fullSize) { var waypointsSection = file.GetSection("Waypoints", true); foreach (var kv in waypointsSection) { var pos = int.Parse(kv.Value); var ry = pos / 1000; var rx = pos - ry * 1000; var dx = rx - ry + fullSize.X - 1; var dy = rx + ry - fullSize.X - 1; var cell = new MPos(dx / 2, dy).ToCPos(map); var ar = new ActorReference((!int.TryParse(kv.Key, out var wpindex) || wpindex > 7) ? "waypoint" : "mpspawn"); ar.Add(new LocationInit(cell)); ar.Add(new OwnerInit("Neutral")); map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); } }
public void Update(MPos uv, Sprite sprite, float3 pos) { if (sprite != null) { //if (sprite.Sheet != Sheet) // throw new InvalidDataException("Attempted to add sprite from a different sheet"); if (sprite.BlendMode != BlendMode) { throw new InvalidDataException("Attempted to add sprite with a different blend mode"); } } else { sprite = emptySprite; } // The vertex buffer does not have geometry for cells outside the map if (!map.Tiles.Contains(uv)) { return; } SamplerPointer textslot; var offset = TerrainFullRowLenInVertexRowsNums * uv.V + 6 * uv.U; if (sprite.SpriteType == 4) //megaTexture { Sprite tmpsp = new Sprite(sprite.Sheet, new Rectangle((int)pos.X, (int)pos.Y, 16, 16), TextureChannel.RGBA); tmpsp.SpriteType = 4; textslot = SetRenderStateForSprite(tmpsp); Util.FastCreateQuad(vertices, pos, tmpsp, textslot, palette.TextureIndex, offset, tmpsp.Size); } else { textslot = SetRenderStateForSprite(sprite); Util.FastCreateQuad(vertices, pos, sprite, textslot, palette.TextureIndex, offset, sprite.Size); } dirtyRows.Add(uv.V); }
static void ReadTiles(Map map, IniFile file, int2 fullSize) { var terrainInfo = (ITemplatedTerrainInfo)Game.ModData.DefaultTerrainInfo[map.Tileset]; var mapSection = file.GetSection("IsoMapPack5"); var data = Convert.FromBase64String(mapSection.Aggregate(string.Empty, (a, b) => a + b.Value)); var cells = (fullSize.X * 2 - 1) * fullSize.Y; var lzoPackSize = cells * 11 + 4; // last 4 bytes contains a lzo pack header saying no more data is left var isoMapPack = new byte[lzoPackSize]; UnpackLZO(data, isoMapPack); var mf = new MemoryStream(isoMapPack); for (var i = 0; i < cells; i++) { var rx = mf.ReadUInt16(); var ry = mf.ReadUInt16(); var tilenum = mf.ReadUInt16(); /*var zero1 = */ mf.ReadInt16(); var subtile = mf.ReadUInt8(); var z = mf.ReadUInt8(); /*var zero2 = */ mf.ReadUInt8(); var dx = rx - ry + fullSize.X - 1; var dy = rx + ry - fullSize.X - 1; var mapCell = new MPos(dx / 2, dy); var cell = mapCell.ToCPos(map); if (map.Tiles.Contains(cell)) { if (!terrainInfo.Templates.ContainsKey(tilenum)) { tilenum = subtile = 0; } map.Tiles[cell] = new TerrainTile(tilenum, subtile); map.Height[cell] = z; } } }
public ChatBox(CPos position) { panel = new TextPanel(new MPos(8120, 2048), FontManager.Default, "stone") { Position = position }; input = new TextBox("wooden", 45) { Position = position + new CPos(-800, 2048 + 512, 0), OnEnter = SendText }; send = new Button("send", "wooden", SendText) { Position = position + new CPos(8120 - 800, 2048 + 512, 0) }; Position += new CPos(0, 5632, 0); Bounds = new MPos(8120 + 512, 4096); SelectableBounds = Bounds; }
void UpdateTint(MPos uv) { var offset = rowStride * uv.V + 6 * uv.U; if (ignoreTint[offset]) { var noTint = float3.Ones; for (var i = 0; i < 6; i++) { var v = vertices[offset + i]; vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, noTint); } return; } // Allow the terrain tint to vary linearly across the cell to smooth out the staircase effect // This is done by sampling the lighting the corners of the sprite, even though those pixels are // transparent for isometric tiles var tl = worldRenderer.TerrainLighting; var pos = map.CenterOfCell(uv.ToCPos(map)); var step = map.Grid.Type == MapGridType.RectangularIsometric ? 724 : 512; var weights = new[] { tl.TintAt(pos + new WVec(-step, -step, 0)), tl.TintAt(pos + new WVec(step, -step, 0)), tl.TintAt(pos + new WVec(step, step, 0)), tl.TintAt(pos + new WVec(-step, step, 0)) }; // Apply tint directly to the underlying vertices // This saves us from having to re-query the sprite information, which has not changed for (var i = 0; i < 6; i++) { var v = vertices[offset + i]; vertices[offset + i] = new Vertex(v.X, v.Y, v.Z, v.S, v.T, v.U, v.V, palette.TextureIndex, v.C, weights[CornerVertexMap[i]]); } dirtyRows.Add(uv.V); }
public ShroudLayer(MPos bounds) { Bounds = bounds * new MPos(2, 2); Shroud = new Shroud[Bounds.X, Bounds.Y]; for (int x = 0; x < Bounds.X; x++) { for (int y = 0; y < Bounds.Y; y++) { var shroud = new Shroud(new MPos(x, y)); var list = new List <Shroud>() { shroud }; Shroud[x, y] = shroud; listenerPositions.Add(new MPos(x, y), list); } } }
CellRegion CalculateVisibleCells(bool insideBounds) { var map = worldRenderer.World.Map; // Calculate the viewport corners in "projected wpos" (at ground level), and // this to an equivalent projected cell for the two corners var tl = map.CellContaining(worldRenderer.ProjectedPosition(TopLeft)).ToMPos(map); var br = map.CellContaining(worldRenderer.ProjectedPosition(BottomRight)).ToMPos(map); // Diamond tile shapes don't have straight edges, and so we need // an additional cell margin to include the cells that are half // visible on each edge. if (map.TileShape == TileShape.Diamond) { tl = new MPos(tl.U - 1, tl.V - 1); br = new MPos(br.U + 1, br.V + 1); } // Clamp to the visible map bounds, if requested if (insideBounds) { tl = map.Clamp(tl); br = map.Clamp(br); } // Cells can be pushed up from below if they have non-zero height. // Each height step is equivalent to 512 WDist units, which is // one MPos step for diamond cells, but only half a MPos step // for classic cells. Doh! var heightOffset = map.TileShape == TileShape.Diamond ? map.MaximumTerrainHeight : map.MaximumTerrainHeight / 2; br = new MPos(br.U, br.V + heightOffset); // Finally, make sure that this region doesn't extend outside the map area. tl = map.MapHeight.Value.Clamp(tl); br = map.MapHeight.Value.Clamp(br); return(new CellRegion(map.TileShape, tl.ToCPos(map), br.ToCPos(map))); }
void Initialize(string mapFile) { mapSize = new Size(stream.ReadUInt16(), stream.ReadUInt16()); tileSet = rules.TileSets["ARRAKIS"]; map = new Map(tileSet, mapSize.Width + 2 * MapCordonWidth, mapSize.Height + 2 * MapCordonWidth) { Title = Path.GetFileNameWithoutExtension(mapFile), Author = "Westwood Studios" }; var tl = new MPos(MapCordonWidth, MapCordonWidth); var br = new MPos(MapCordonWidth + mapSize.Width - 1, MapCordonWidth + mapSize.Height - 1); map.SetBounds(tl, br); // Get all templates from the tileset YAML file that have at least one frame and an Image property corresponding to the requested tileset // Each frame is a tile from the Dune 2000 tileset files, with the Frame ID being the index of the tile in the original file tileSetsFromYaml = tileSet.Templates.Where(t => t.Value.Frames != null && t.Value.Images[0].ToLower() == tilesetName.ToLower()).Select(ts => ts.Value).ToList(); }
public Tooltip(string title, params string[] text) { font = FontManager.Default; this.title = new TextLine(CPos.Zero, font); this.title.WriteText(title); this.text = new TextBlock(CPos.Zero, font, TextOffset.LEFT); this.text.Add(text); var width = font.GetWidth(this.title.Text); if (text.Length != 0) { var textWidth = this.text.Bounds.X; if (textWidth > width) { width = textWidth; } } bounds = new MPos(2 * margin + width, 2 * margin + (text.Length + 1) * font.MaxHeight / 2 + text.Length * font.HeightGap / 2); }
// NOTE: pos required to be in map bounds bool AnyActorsAt(MPos uv, CPos a, CellLayer <InfluenceNode> layer, SubCell sub, bool checkTransient) { var always = sub == SubCell.FullCell || sub == SubCell.Any; for (var i = layer[uv]; i != null; i = i.Next) { if (always || i.SubCell == sub || i.SubCell == SubCell.FullCell) { if (checkTransient) { return(true); } var pos = i.Actor.TraitOrDefault <IPositionable>(); if (pos == null || !pos.IsLeavingCell(a, i.SubCell)) { return(true); } } } return(false); }
static void ReadTerrainActors(Map map, IniFile file, int2 fullSize) { var terrainSection = file.GetSection("Terrain", true); foreach (var kv in terrainSection) { var pos = int.Parse(kv.Key); var ry = pos / 1000; var rx = pos - ry * 1000; var dx = rx - ry + fullSize.X - 1; var dy = rx + ry - fullSize.X - 1; var cell = new MPos(dx / 2, dy).ToCPos(map); var name = kv.Value.ToLowerInvariant(); var ar = new ActorReference(name); ar.Add(new LocationInit(cell)); ar.Add(new OwnerInit("Neutral")); if (!map.Rules.Actors.ContainsKey(name)) Console.WriteLine("Ignoring unknown actor type: `{0}`".F(name)); else map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); } }
public void Update(MPos uv, Sprite sprite, float3 pos, bool ignoreTint) { if (sprite != null) { if (sprite.Sheet != Sheet) { throw new InvalidDataException("Attempted to add sprite from a different sheet"); } if (sprite.BlendMode != BlendMode) { throw new InvalidDataException("Attempted to add sprite with a different blend mode"); } } else { sprite = emptySprite; } // The vertex buffer does not have geometry for cells outside the map if (!map.Tiles.Contains(uv)) { return; } var offset = rowStride * uv.V + 6 * uv.U; Util.FastCreateQuad(vertices, pos, sprite, int2.Zero, palette.TextureIndex, offset, sprite.Size, float3.Ones); if (worldRenderer.TerrainLighting != null) { this.ignoreTint[offset] = ignoreTint; UpdateTint(uv); } dirtyRows.Add(uv.V); }
public static bool IsVisibleIgnoringBounds(MPos position) { if (map == null) { return(false); } if (shroud.RevealAll) { return(true); } if (!position.InRange(lastCameraPosition, lastCameraPosition + lastCameraZoom)) { return(false); } if (position.X < 0) { position = new MPos(0, position.Y); } else if (position.X >= mapBounds.X) { position = new MPos(mapBounds.X - 1, position.Y); } if (position.Y < 0) { position = new MPos(position.X, 0); } else if (position.Y >= mapBounds.Y) { position = new MPos(position.X, mapBounds.Y - 1); } return(visible[position.X, position.Y]); }
List <Triangle> getTriangles(World world, CPos position, int height, MPos shroudPos, int radius) { var outerRadius = MathF.Sqrt(2) * radius * 1024; var pos1 = (shroudPos - new MPos(radius, radius)) / new MPos(2, 2); // Why MPos(1, 1) here? -> when 7/2=3, but we want 4. Thus (7+1)/2=4. (8+1)/2=4 so works for this case as well. var pos2 = (shroudPos + new MPos(radius, radius) + new MPos(1, 1)) / new MPos(2, 2); var triangles = new List <Triangle>(); var walls = world.WallLayer.GetRange(pos1, pos2); foreach (var wall in walls) { if (wall.Physics.IsEmpty || wall.Type.IsTransparent) { continue; } if (height > wall.Type.Height) { continue; } var angleA = (position - wall.EndPointA).FlatAngle; var angleB = (position - wall.EndPointB).FlatAngle; var pointC = position + CPos.FromFlatAngle(angleA, outerRadius); var pointD = position + CPos.FromFlatAngle(angleB, outerRadius); triangles.Add(new Triangle(wall.EndPointA, wall.EndPointB, pointC)); triangles.Add(new Triangle(wall.EndPointB, pointC, pointD)); } return(triangles); }
void UpdateTerrainColor(MPos uv) { var colorPair = playerRadarTerrain != null && playerRadarTerrain.IsInitialized ? playerRadarTerrain[uv] : PlayerRadarTerrain.GetColor(world.Map, radarTerrainLayers, uv); var leftColor = colorPair.Left; var rightColor = colorPair.Right; var stride = radarSheet.Size.Width; unsafe { fixed(byte *colorBytes = &radarData[0]) { var colors = (int *)colorBytes; if (isRectangularIsometric) { // Odd rows are shifted right by 1px var dx = uv.V & 1; if (uv.U + dx > 0) { colors[uv.V * stride + 2 * uv.U + dx - 1] = leftColor; } if (2 * uv.U + dx < stride) { colors[uv.V * stride + 2 * uv.U + dx] = rightColor; } } else { colors[uv.V * stride + uv.U] = leftColor; } } } }
public static void PrintGeneratorMap(MPos bounds, NoiseMap noise, bool[,] dirty, int id) { using var image = new Bitmap(bounds.X, bounds.Y); for (int x = 0; x < bounds.X; x++) { for (int y = 0; y < bounds.Y; y++) { System.Drawing.Color color = Color.Red; if (!dirty[x, y]) { var value = (int)(noise[x, y] * 255); color = System.Drawing.Color.FromArgb(value, value, value); } image.SetPixel(x, y, color); } } var path = FileExplorer.Logs + "debugMaps/"; checkDirectory(path); image.Save(path + $"generator{id}.png"); }
static void ReadTerrainActors(Map map, IniFile file, int2 fullSize) { var terrainSection = file.GetSection("Terrain", true); foreach (var kv in terrainSection) { var pos = int.Parse(kv.Key); var ry = pos / 1000; var rx = pos - ry * 1000; var dx = rx - ry + fullSize.X - 1; var dy = rx + ry - fullSize.X - 1; var cell = new MPos(dx / 2, dy).ToCPos(map); var name = kv.Value.ToLowerInvariant(); if (ReplaceActors.ContainsKey(name)) { name = ReplaceActors[name]; } var ar = new ActorReference(name); ar.Add(new LocationInit(cell)); ar.Add(new OwnerInit("Neutral")); if (!map.Rules.Actors.ContainsKey(name)) Console.WriteLine("Ignoring unknown actor type: `{0}`".F(name)); else map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); } }
public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits, int refundableValue) { if (IsTraitDisabled || IsTraitPaused) { return(false); } var info = (ProductionAirdropInfo)Info; var owner = self.Owner; var map = owner.World.Map; var aircraftInfo = self.World.Map.Rules.Actors[info.ActorType].TraitInfo <AircraftInfo>(); CPos startPos; CPos endPos; WAngle spawnFacing; if (info.BaselineSpawn) { var bounds = map.Bounds; var center = new MPos(bounds.Left + bounds.Width / 2, bounds.Top + bounds.Height / 2).ToCPos(map); var spawnVec = owner.HomeLocation - center; startPos = owner.HomeLocation + spawnVec * (Exts.ISqrt((bounds.Height * bounds.Height + bounds.Width * bounds.Width) / (4 * spawnVec.LengthSquared))); endPos = startPos; var spawnDirection = new WVec((self.Location - startPos).X, (self.Location - startPos).Y, 0); spawnFacing = spawnDirection.Yaw; } else { // Start a fixed distance away: the width of the map. // This makes the production timing independent of spawnpoint var loc = self.Location.ToMPos(map); startPos = new MPos(loc.U + map.Bounds.Width, loc.V).ToCPos(map); endPos = new MPos(map.Bounds.Left, loc.V).ToCPos(map); spawnFacing = info.Facing; } // Assume a single exit point for simplicity var exit = self.Info.TraitInfos <ExitInfo>().First(); foreach (var tower in self.TraitsImplementing <INotifyDelivery>()) { tower.IncomingDelivery(self); } owner.World.AddFrameEndTask(w => { if (!self.IsInWorld || self.IsDead) { owner.PlayerActor.Trait <PlayerResources>().GiveCash(refundableValue); return; } var actor = w.CreateActor(info.ActorType, new TypeDictionary { new CenterPositionInit(w.Map.CenterOfCell(startPos) + new WVec(WDist.Zero, WDist.Zero, aircraftInfo.CruiseAltitude)), new OwnerInit(owner), new FacingInit(spawnFacing) }); var exitCell = self.Location + exit.ExitCell; actor.QueueActivity(new Land(actor, Target.FromActor(self), WDist.Zero, WVec.Zero, info.Facing, clearCells: new CPos[1] { exitCell })); actor.QueueActivity(new CallFunc(() => { if (!self.IsInWorld || self.IsDead) { owner.PlayerActor.Trait <PlayerResources>().GiveCash(refundableValue); return; } foreach (var cargo in self.TraitsImplementing <INotifyDelivery>()) { cargo.Delivered(self); } self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, productionType, inits)); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName); TextNotificationsManager.AddTransientLine(info.ReadyTextNotification, self.Owner); })); actor.QueueActivity(new FlyOffMap(actor, Target.FromCell(w, endPos))); actor.QueueActivity(new RemoveSelf()); }); return(true); }
public PhysicsSector[] GetSectors(SimplePhysics physics) { if (physics.IsEmpty) { return(new PhysicsSector[0]); } var position = physics.Position - Map.Offset; // Add margin to be sure. var radiusX = physics.Type.RadiusX + 10; var radiusY = physics.Type.RadiusY + 10; var points = new MPos[4]; // Corner points points[0] = new MPos(position.X + radiusX, position.Y + radiusY); // Sector 1 ( x| y) points[1] = new MPos(position.X + radiusX, position.Y - radiusY); // Sector 2 ( x|-y) points[2] = new MPos(position.X - radiusX, position.Y - radiusY); // Sector 3 (-x|-y) points[3] = new MPos(position.X - radiusX, position.Y + radiusY); // Sector 4 (-x| y) // Corner sectors var sectorPositions = new MPos[4]; for (int i = 0; i < 4; i++) { var point = points[i]; var x = point.X / (SectorSize * 1024f); if (x < 0) { x = 0; } if (x >= Bounds.X) { x = Bounds.X - 1; } var y = point.Y / (SectorSize * 1024f); if (y < 0) { y = 0; } if (y >= Bounds.Y) { y = Bounds.Y - 1; } sectorPositions[i] = new MPos((int)Math.Floor(x), (int)Math.Floor(y)); } // Determine Size of the Sector field to enter and the sector with the smallest value (sector 3) var startPosition = sectorPositions[2]; // Difference plus one to have the field (e.g. 1 and 2 -> diff. 1 + 1 = 2 fields) var xSize = (sectorPositions[1].X - sectorPositions[2].X) + 1; var ySize = (sectorPositions[3].Y - sectorPositions[2].Y) + 1; var sectors = new List <PhysicsSector>(); for (int x = 0; x < xSize; x++) { for (int y = 0; y < ySize; y++) { var sector = Sectors[startPosition.X + x, startPosition.Y + y]; if (!sectors.Contains(sector)) { sectors.Add(sector); } } } return(sectors.ToArray()); }
public void WorldLoaded(World w, WorldRenderer wr) { /* based on SmudgeLayer.cs */ var first = sideSprites.First().Value.First(); var sheet = first.Sheet; if (sideSprites.Values.Any(sprites => sprites.Any(s => s.Sheet != sheet))) throw new InvalidDataException("Resource sprites span multiple sheets. Try loading their sequences earlier."); var blendMode = first.BlendMode; if (sideSprites.Values.Any(sprites => sprites.Any(s => s.BlendMode != blendMode))) throw new InvalidDataException("Smudges specify different blend modes. " + "Try using different smudge types for smudges that use different blend modes."); render = new TerrainSpriteLayer(w, wr, sheet, blendMode, wr.Palette(Info.Palette), wr.World.Type != WorldType.Editor); var tilesLayer = w.Map.Tiles; for (var v = 0; v < tilesLayer.Size.Height; v++) { for (var u = 0; u < tilesLayer.Size.Width; u++) { var mpos = new MPos(u, v); var tile = tilesLayer[mpos]; if (tile.Type == 143) { ClearSides clear = ClearSides.None; if (u > 0) { var leftPos = new MPos(u - 1, v); var leftTile = tilesLayer[leftPos]; if (!(leftTile.Type >= 126 && leftTile.Type <= 143) && !(leftTile.Type >= 160 && leftTile.Type <= 175)) { clear |= ClearSides.Left; } } if (v > 0) { var topPos = new MPos(u, v - 1); var topTile = tilesLayer[topPos]; if (!(topTile.Type >= 126 && topTile.Type <= 143) && !(topTile.Type >= 160 && topTile.Type <= 175)) { clear |= ClearSides.Top; } } if (u < tilesLayer.Size.Width - 1) { var rightPos = new MPos(u + 1, v); var rightTile = tilesLayer[rightPos]; if (!(rightTile.Type >= 126 && rightTile.Type <= 143) && !(rightTile.Type >= 160 && rightTile.Type <= 175)) { clear |= ClearSides.Right; } } if (v < tilesLayer.Size.Height - 1) { var bottomPos = new MPos(u, v + 1); var bottomTile = tilesLayer[bottomPos]; if (!(bottomTile.Type >= 126 && bottomTile.Type <= 143) && !(bottomTile.Type >= 160 && bottomTile.Type <= 175)) { clear |= ClearSides.Bottom; } } if (clear != ClearSides.None) { CPos cpos = mpos.ToCPos(w.Map); Sprite sprite = sideSprites["rock"][SpriteMap[clear]]; render.Update(cpos, sprite); } } if (tile.Type == 175) { ClearSides clear = ClearSides.None; if (u > 0) { var leftPos = new MPos(u - 1, v); var leftTile = tilesLayer[leftPos]; if (!(leftTile.Type >= 160 && leftTile.Type <= 175)) { clear |= ClearSides.Left; } } if (v > 0) { var topPos = new MPos(u, v - 1); var topTile = tilesLayer[topPos]; if (!(topTile.Type >= 160 && topTile.Type <= 175)) { clear |= ClearSides.Top; } } if (u < tilesLayer.Size.Width - 1) { var rightPos = new MPos(u + 1, v); var rightTile = tilesLayer[rightPos]; if (!(rightTile.Type >= 160 && rightTile.Type <= 175)) { clear |= ClearSides.Right; } } if (v < tilesLayer.Size.Height - 1) { var bottomPos = new MPos(u, v + 1); var bottomTile = tilesLayer[bottomPos]; if (!(bottomTile.Type >= 160 && bottomTile.Type <= 175)) { clear |= ClearSides.Bottom; } } if (clear != ClearSides.None) { CPos cpos = mpos.ToCPos(w.Map); Sprite sprite = sideSprites["rough"][SpriteMap[clear]]; render.Update(cpos, sprite); } } if (tile.Type == 159) { ClearSides clear = ClearSides.None; if (u > 0) { var leftPos = new MPos(u - 1, v); var leftTile = tilesLayer[leftPos]; if (!(leftTile.Type >= 144 && leftTile.Type <= 159)) { clear |= ClearSides.Left; } } if (v > 0) { var topPos = new MPos(u, v - 1); var topTile = tilesLayer[topPos]; if (!(topTile.Type >= 144 && topTile.Type <= 159)) { clear |= ClearSides.Top; } } if (u < tilesLayer.Size.Width - 1) { var rightPos = new MPos(u + 1, v); var rightTile = tilesLayer[rightPos]; if (!(rightTile.Type >= 144 && rightTile.Type <= 159)) { clear |= ClearSides.Right; } } if (v < tilesLayer.Size.Height - 1) { var bottomPos = new MPos(u, v + 1); var bottomTile = tilesLayer[bottomPos]; if (!(bottomTile.Type >= 144 && bottomTile.Type <= 159)) { clear |= ClearSides.Bottom; } } if (clear != ClearSides.None) { CPos cpos = mpos.ToCPos(w.Map); Sprite sprite = sideSprites["dune"][SpriteMap[clear]]; render.Update(cpos, sprite); } } } } }
public byte[] SavePreview() { var tileset = Rules.TileSet; var resources = Rules.Actors["world"].TraitInfos<ResourceTypeInfo>() .ToDictionary(r => r.ResourceType, r => r.TerrainType); using (var stream = new MemoryStream()) { var isRectangularIsometric = Grid.Type == MapGridType.RectangularIsometric; // Fudge the heightmap offset by adding as much extra as we need / can. // This tries to correct for our incorrect assumption that MPos == PPos var heightOffset = Math.Min(Grid.MaximumTerrainHeight, MapSize.Y - Bounds.Bottom); var width = Bounds.Width; var height = Bounds.Height + heightOffset; var bitmapWidth = width; if (isRectangularIsometric) bitmapWidth = 2 * bitmapWidth - 1; using (var bitmap = new Bitmap(bitmapWidth, height)) { var bitmapData = bitmap.LockBits(bitmap.Bounds(), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); unsafe { var colors = (int*)bitmapData.Scan0; var stride = bitmapData.Stride / 4; Color leftColor, rightColor; for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var uv = new MPos(x + Bounds.Left, y + Bounds.Top); var resourceType = Resources[uv].Type; if (resourceType != 0) { // Cell contains resources string res; if (!resources.TryGetValue(resourceType, out res)) continue; leftColor = rightColor = tileset[tileset.GetTerrainIndex(res)].Color; } else { // Cell contains terrain var type = tileset.GetTileInfo(Tiles[uv]); leftColor = type != null ? type.LeftColor : Color.Black; rightColor = type != null ? type.RightColor : Color.Black; } if (isRectangularIsometric) { // Odd rows are shifted right by 1px var dx = uv.V & 1; if (x + dx > 0) colors[y * stride + 2 * x + dx - 1] = leftColor.ToArgb(); if (2 * x + dx < stride) colors[y * stride + 2 * x + dx] = rightColor.ToArgb(); } else colors[y * stride + x] = leftColor.ToArgb(); } } } bitmap.UnlockBits(bitmapData); bitmap.Save(stream, ImageFormat.Png); } return stream.ToArray(); } }
public bool Contains(MPos uv) { return Bounds.Contains(uv.U, uv.V); }
void PostInit() { rules = Exts.Lazy(() => { try { return Game.ModData.RulesetCache.LoadMapRules(this); } catch (Exception e) { InvalidCustomRules = true; Log.Write("debug", "Failed to load rules for {0} with error {1}", Title, e.Message); } return Game.ModData.DefaultRules; }); cachedTileSet = Exts.Lazy(() => Rules.TileSets[Tileset]); var tl = new MPos(Bounds.Left, Bounds.Top).ToCPos(this); var br = new MPos(Bounds.Right - 1, Bounds.Bottom - 1).ToCPos(this); Cells = new CellRegion(TileShape, tl, br); CustomTerrain = new CellLayer<byte>(this); foreach (var uv in Cells.MapCoords) CustomTerrain[uv] = byte.MaxValue; var leftDelta = TileShape == TileShape.Diamond ? new WVec(-512, 0, 0) : new WVec(-512, -512, 0); var topDelta = TileShape == TileShape.Diamond ? new WVec(0, -512, 0) : new WVec(512, -512, 0); var rightDelta = TileShape == TileShape.Diamond ? new WVec(512, 0, 0) : new WVec(512, 512, 0); var bottomDelta = TileShape == TileShape.Diamond ? new WVec(0, 512, 0) : new WVec(-512, 512, 0); CellCorners = CellCornerHalfHeights.Select(ramp => new WVec[] { leftDelta + new WVec(0, 0, 512 * ramp[0]), topDelta + new WVec(0, 0, 512 * ramp[1]), rightDelta + new WVec(0, 0, 512 * ramp[2]), bottomDelta + new WVec(0, 0, 512 * ramp[3]) }).ToArray(); }
static void ReadActors(Map map, IniFile file, string type, int2 fullSize) { var structuresSection = file.GetSection(type, true); foreach (var kv in structuresSection) { var isDeployed = false; var entries = kv.Value.Split(','); var name = entries[1].ToLowerInvariant(); if (DeployableActors.ContainsKey(name)) { name = DeployableActors[name]; isDeployed = true; } if (ReplaceActors.ContainsKey(name)) { name = ReplaceActors[name]; } var health = short.Parse(entries[2]); var rx = int.Parse(entries[3]); var ry = int.Parse(entries[4]); var facing = byte.Parse(entries[5]) + 96; var dx = rx - ry + fullSize.X - 1; var dy = rx + ry - fullSize.X - 1; var cell = new MPos(dx / 2, dy).ToCPos(map); var ar = new ActorReference(name) { new LocationInit(cell), new OwnerInit("Neutral") }; if (health != 256) { ar.Add(new HealthInit(100 * health / 256)); } if (facing != 96) { ar.Add(new FacingInit(facing)); } if (isDeployed) { ar.Add(new DeployStateInit(DeployState.Deployed)); } if (!map.Rules.Actors.ContainsKey(name)) { Console.WriteLine("Ignoring unknown actor type: `{0}`".F(name)); } else { map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); } } }
static void ReadOverlay(Map map, IniFile file, int2 fullSize) { var overlaySection = file.GetSection("OverlayPack"); var overlayCompressed = Convert.FromBase64String(string.Concat(overlaySection.Select(kvp => kvp.Value))); var overlayPack = new byte[1 << 18]; var temp = new byte[1 << 18]; UnpackLCW(overlayCompressed, overlayPack, temp); var overlayDataSection = file.GetSection("OverlayDataPack"); var overlayDataCompressed = Convert.FromBase64String(string.Concat(overlayDataSection.Select(kvp => kvp.Value))); var overlayDataPack = new byte[1 << 18]; UnpackLCW(overlayDataCompressed, overlayDataPack, temp); var overlayIndex = new CellLayer <int>(map); overlayIndex.Clear(0xFF); for (var y = 0; y < fullSize.Y; y++) { for (var x = fullSize.X * 2 - 2; x >= 0; x--) { var dx = (ushort)x; var dy = (ushort)(y * 2 + x % 2); var uv = new MPos(dx / 2, dy); var rx = (ushort)((dx + dy) / 2 + 1); var ry = (ushort)(dy - rx + fullSize.X + 1); if (!map.Resources.Contains(uv)) { continue; } overlayIndex[uv] = rx + 512 * ry; } } foreach (var cell in map.AllCells) { var overlayType = overlayPack[overlayIndex[cell]]; if (overlayType == 0xFF) { continue; } if (OverlayToActor.TryGetValue(overlayType, out var actorType)) { if (string.IsNullOrEmpty(actorType)) { continue; } var shape = new Size(1, 1); if (OverlayShapes.TryGetValue(overlayType, out shape)) { // Only import the top-left cell of multi-celled overlays var aboveType = overlayPack[overlayIndex[cell - new CVec(1, 0)]]; if (shape.Width > 1 && aboveType != 0xFF) { if (OverlayToActor.TryGetValue(aboveType, out var a) && a == actorType) { continue; } } var leftType = overlayPack[overlayIndex[cell - new CVec(0, 1)]]; if (shape.Height > 1 && leftType != 0xFF) { if (OverlayToActor.TryGetValue(leftType, out var a) && a == actorType) { continue; } } } // Fix position of vein hole actors var location = cell; if (actorType == "veinhole") { location -= new CVec(1, 1); } var ar = new ActorReference(actorType) { new LocationInit(location), new OwnerInit("Neutral") }; if (OverlayToHealth.TryGetValue(overlayType, out var damageState)) { var health = 100; if (damageState == DamageState.Critical) { health = 25; } else if (damageState == DamageState.Heavy) { health = 50; } else if (damageState == DamageState.Medium) { health = 75; } if (health != 100) { ar.Add(new HealthInit(health)); } } map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); continue; } // TS maps encode the non-harvestable border tiles as overlay // Only convert to vein resources if the overlay data specifies non-border frames if (overlayType == 0x7E) { var frame = overlayDataPack[overlayIndex[cell]]; if (frame < 48 || frame > 60) { continue; } // Pick half or full density based on the frame map.Resources[cell] = new ResourceTile(3, (byte)(frame == 52 ? 1 : 2)); continue; } var resourceType = ResourceFromOverlay .Where(kv => kv.Value.Contains(overlayType)) .Select(kv => kv.Key) .FirstOrDefault(); if (resourceType != 0) { map.Resources[cell] = new ResourceTile(resourceType, overlayDataPack[overlayIndex[cell]]); continue; } Console.WriteLine("{0} unknown overlay {1}", cell, overlayType); } }
static void ReadOverlay(Map map, IniFile file, int2 fullSize) { var overlaySection = file.GetSection("OverlayPack"); var overlayCompressed = Convert.FromBase64String(overlaySection.Aggregate(string.Empty, (a, b) => a + b.Value)); var overlayPack = new byte[1 << 18]; var temp = new byte[1 << 18]; UnpackLCW(overlayCompressed, overlayPack, temp); var overlayDataSection = file.GetSection("OverlayDataPack"); var overlayDataCompressed = Convert.FromBase64String(overlayDataSection.Aggregate(string.Empty, (a, b) => a + b.Value)); var overlayDataPack = new byte[1 << 18]; UnpackLCW(overlayDataCompressed, overlayDataPack, temp); var overlayIndex = new CellLayer<int>(map); overlayIndex.Clear(0xFF); for (var y = 0; y < fullSize.Y; y++) { for (var x = fullSize.X * 2 - 2; x >= 0; x--) { var dx = (ushort)x; var dy = (ushort)(y * 2 + x % 2); var uv = new MPos(dx / 2, dy); var rx = (ushort)((dx + dy) / 2 + 1); var ry = (ushort)(dy - rx + fullSize.X + 1); if (!map.Resources.Contains(uv)) continue; overlayIndex[uv] = rx + 512 * ry; } } foreach (var cell in map.AllCells) { var overlayType = overlayPack[overlayIndex[cell]]; if (overlayType == 0xFF) continue; string actorType; if (OverlayToActor.TryGetValue(overlayType, out actorType)) { var shape = new Size(1, 1); if (OverlayShapes.TryGetValue(overlayType, out shape)) { // Only import the top-left cell of multi-celled overlays var aboveType = overlayPack[overlayIndex[cell - new CVec(1, 0)]]; if (shape.Width > 1 && aboveType != 0xFF) { string a; if (OverlayToActor.TryGetValue(aboveType, out a) && a == actorType) continue; } var leftType = overlayPack[overlayIndex[cell - new CVec(0, 1)]]; if (shape.Height > 1 && leftType != 0xFF) { string a; if (OverlayToActor.TryGetValue(leftType, out a) && a == actorType) continue; } } var ar = new ActorReference(actorType) { new LocationInit(cell), new OwnerInit("Neutral") }; DamageState damageState; if (OverlayToHealth.TryGetValue(overlayType, out damageState)) { var health = 100; if (damageState == DamageState.Critical) health = 25; else if (damageState == DamageState.Heavy) health = 50; else if (damageState == DamageState.Medium) health = 75; if (health != 100) ar.Add(new HealthInit(health)); } map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); continue; } var resourceType = ResourceFromOverlay .Where(kv => kv.Value.Contains(overlayType)) .Select(kv => kv.Key) .FirstOrDefault(); if (resourceType != 0) { map.Resources[cell] = new ResourceTile(resourceType, overlayDataPack[overlayIndex[cell]]); continue; } Console.WriteLine("{0} unknown overlay {1}", cell, overlayType); } }
static void ReadWaypoints(Map map, IniFile file, int2 fullSize) { var waypointsSection = file.GetSection("Waypoints", true); foreach (var kv in waypointsSection) { var pos = int.Parse(kv.Value); var ry = pos / 1000; var rx = pos - ry * 1000; var dx = rx - ry + fullSize.X - 1; var dy = rx + ry - fullSize.X - 1; var cell = new MPos(dx / 2, dy).ToCPos(map); int wpindex; var ar = new ActorReference((!int.TryParse(kv.Key, out wpindex) || wpindex > 7) ? "waypoint" : "mpspawn"); ar.Add(new LocationInit(cell)); ar.Add(new OwnerInit("Neutral")); map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); } }
static void ReadTiles(Map map, IniFile file, int2 fullSize) { var tileset = Game.ModData.DefaultTileSets[map.Tileset]; var mapSection = file.GetSection("IsoMapPack5"); var data = Convert.FromBase64String(mapSection.Aggregate(string.Empty, (a, b) => a + b.Value)); int cells = (fullSize.X * 2 - 1) * fullSize.Y; int lzoPackSize = cells * 11 + 4; // last 4 bytes contains a lzo pack header saying no more data is left var isoMapPack = new byte[lzoPackSize]; UnpackLZO(data, isoMapPack); var mf = new MemoryStream(isoMapPack); for (var i = 0; i < cells; i++) { var rx = mf.ReadUInt16(); var ry = mf.ReadUInt16(); var tilenum = mf.ReadUInt16(); /*var zero1 = */mf.ReadInt16(); var subtile = mf.ReadUInt8(); var z = mf.ReadUInt8(); /*var zero2 = */mf.ReadUInt8(); int dx = rx - ry + fullSize.X - 1; int dy = rx + ry - fullSize.X - 1; var mapCell = new MPos(dx / 2, dy); var cell = mapCell.ToCPos(map); if (map.Tiles.Contains(cell)) { if (!tileset.Templates.ContainsKey(tilenum)) tilenum = subtile = 0; map.Tiles[cell] = new TerrainTile(tilenum, subtile); map.Height[cell] = z; } } }
public void SetBounds(MPos tl, MPos br) { // The tl and br coordinates are inclusive, but the Rectangle // is exclusive. Pad the right and bottom edges to match. Bounds = Rectangle.FromLTRB(tl.U, tl.V, br.U + 1, br.V + 1); CellsInsideBounds = new CellRegion(TileShape, tl.ToCPos(this), br.ToCPos(this)); // Directly calculate the projected map corners in world units avoiding unnecessary // conversions. This abuses the definition that the width of the cell is always // 1024 units, and that the height of two rows is 2048 for classic cells and 1024 // for diamond cells. var wtop = tl.V * 1024; var wbottom = (br.V + 1) * 1024; if (TileShape == TileShape.Diamond) { wtop /= 2; wbottom /= 2; } ProjectedTopLeft = new WPos(tl.U * 1024, wtop, 0); ProjectedBottomRight = new WPos(br.U * 1024 - 1, wbottom - 1, 0); }
// editor magic. public void Resize(int width, int height) { var oldMapTiles = MapTiles.Value; var oldMapResources = MapResources.Value; var oldMapHeight = MapHeight.Value; var newSize = new Size(width, height); MapTiles = Exts.Lazy(() => CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[MPos.Zero])); MapResources = Exts.Lazy(() => CellLayer.Resize(oldMapResources, newSize, oldMapResources[MPos.Zero])); MapHeight = Exts.Lazy(() => CellLayer.Resize(oldMapHeight, newSize, oldMapHeight[MPos.Zero])); MapSize = new int2(newSize); var tl = new MPos(0, 0).ToCPos(this); var br = new MPos(MapSize.X - 1, MapSize.Y - 1).ToCPos(this); AllCells = new CellRegion(TileShape, tl, br); }
public MPos Clamp(MPos uv) { var bounds = new Rectangle(Bounds.X, Bounds.Y, Bounds.Width - 1, Bounds.Height - 1); return uv.Clamp(bounds); }
public bool IsVisible(MPos uv) { if (!visibleCount.Contains(uv)) return false; foreach (var puv in map.ProjectedCellsCovering(uv)) if (IsVisible(puv)) return true; return false; }
public bool IsExplored(MPos uv) { if (!map.Contains(uv)) return false; foreach (var puv in map.ProjectedCellsCovering(uv)) if (IsExplored(puv)) return true; return false; }
static void ReadActors(Map map, IniFile file, string type, int2 fullSize) { var structuresSection = file.GetSection(type, true); foreach (var kv in structuresSection) { var isDeployed = false; var entries = kv.Value.Split(','); var name = entries[1].ToLowerInvariant(); if (DeployableActors.ContainsKey(name)) { name = DeployableActors[name]; isDeployed = true; } if (ReplaceActors.ContainsKey(name)) { name = ReplaceActors[name]; } var health = short.Parse(entries[2]); var rx = int.Parse(entries[3]); var ry = int.Parse(entries[4]); var facing = (byte)(byte.Parse(entries[5]) + 96); var dx = rx - ry + fullSize.X - 1; var dy = rx + ry - fullSize.X - 1; var cell = new MPos(dx / 2, dy).ToCPos(map); var ar = new ActorReference(name) { new LocationInit(cell), new OwnerInit("Neutral"), new HealthInit(100 * health / 256), new FacingInit(facing), }; if (isDeployed) ar.Add(new DeployStateInit(DeployState.Deployed)); if (!map.Rules.Actors.ContainsKey(name)) Console.WriteLine("Ignoring unknown actor type: `{0}`".F(name)); else map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); } }
public void Update(MPos uv, Sprite sprite, PaletteReference palette, in float3 pos, float scale, float alpha, bool ignoreTint)
public bool Contains(MPos uv) { // The first check ensures that the cell is within the valid map region, avoiding // potential crashes in deeper code. All CellLayers have the same geometry, and // CustomTerrain is convenient. return CustomTerrain.Contains(uv) && ContainsAllProjectedCellsCovering(uv); }
public void ResizeCordon(int left, int top, int right, int bottom) { Bounds = Rectangle.FromLTRB(left, top, right, bottom); var tl = new MPos(Bounds.Left, Bounds.Top).ToCPos(this); var br = new MPos(Bounds.Right - 1, Bounds.Bottom - 1).ToCPos(this); Cells = new CellRegion(TileShape, tl, br); }
// Add the static resources defined in the map; if the map lives // in a world use AddCustomTerrain instead static Bitmap AddStaticResources(TileSet tileset, Map map, Ruleset resourceRules, Bitmap terrainBitmap) { var terrain = new Bitmap(terrainBitmap); var isDiamond = map.TileShape == TileShape.Diamond; var b = map.Bounds; // Fudge the heightmap offset by adding as much extra as we need / can // This tries to correct for our incorrect assumption that MPos == PPos var heightOffset = Math.Min(map.MaximumTerrainHeight, map.MapSize.Y - b.Bottom); var width = b.Width; var height = b.Height + heightOffset; var resources = resourceRules.Actors["world"].Traits.WithInterface<ResourceTypeInfo>() .ToDictionary(r => r.ResourceType, r => r.TerrainType); var bitmapData = terrain.LockBits(terrain.Bounds(), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); unsafe { var colors = (int*)bitmapData.Scan0; var stride = bitmapData.Stride / 4; for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var uv = new MPos(x + b.Left, y + b.Top); if (map.MapResources.Value[uv].Type == 0) continue; string res; if (!resources.TryGetValue(map.MapResources.Value[uv].Type, out res)) continue; var color = tileset[tileset.GetTerrainIndex(res)].Color.ToArgb(); if (isDiamond) { // Odd rows are shifted right by 1px var dx = uv.V & 1; if (x + dx > 0) colors[y * stride + 2 * x + dx - 1] = color; if (2 * x + dx < stride) colors[y * stride + 2 * x + dx] = color; } else colors[y * stride + x] = color; } } } terrain.UnlockBits(bitmapData); return terrain; }
public bool IsExplored(MPos uv) { if (!map.Contains(uv)) return false; return map.ProjectedCellsCovering(uv).Any(isExploredTest); }
public void Resize(int width, int height) { var oldMapTiles = Tiles; var oldMapResources = Resources; var oldMapHeight = Height; var newSize = new Size(width, height); Tiles = CellLayer.Resize(oldMapTiles, newSize, oldMapTiles[MPos.Zero]); Resources = CellLayer.Resize(oldMapResources, newSize, oldMapResources[MPos.Zero]); Height = CellLayer.Resize(oldMapHeight, newSize, oldMapHeight[MPos.Zero]); MapSize = new int2(newSize); var tl = new MPos(0, 0); var br = new MPos(MapSize.X - 1, MapSize.Y - 1); AllCells = new CellRegion(Grid.Type, tl.ToCPos(this), br.ToCPos(this)); SetBounds(new PPos(tl.U + 1, tl.V + 1), new PPos(br.U - 1, br.V - 1)); }
// NOTE: pos required to be in map bounds bool AnyActorsAt(MPos uv, CellLayer <InfluenceNode> layer) { return(layer[uv] != null); }
public const int ChunkSize = 8; // 8x8 chunks ==> 192x192 bitmaps. Bitmap RenderChunk(int u, int v) { var bitmap = new Bitmap(ChunkSize * TileSetRenderer.TileSize, ChunkSize * TileSetRenderer.TileSize); var data = bitmap.LockBits(bitmap.Bounds(), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); unsafe { var p = (int*)data.Scan0.ToPointer(); var stride = data.Stride >> 2; for (var i = 0; i < ChunkSize; i++) for (var j = 0; j < ChunkSize; j++) { var ui = u * ChunkSize + i; var vj = v * ChunkSize + j; var uv = new MPos(ui, vj); if (uv.U >= Map.MapSize.X || uv.V >= Map.MapSize.Y) { for (var x = 0; x < TileSetRenderer.TileSize; x++) for (var y = 0; y < TileSetRenderer.TileSize; y++) p[(j * TileSetRenderer.TileSize + y) * stride + i * TileSetRenderer.TileSize + x] = 0; continue; } var tr = Map.MapTiles.Value[uv]; var tile = TileSetRenderer.Data(tr.Type); if (tile == null) continue; var index = (tr.Index < tile.Length) ? tr.Index : (byte)0; var rawImage = tile[index]; for (var x = 0; x < TileSetRenderer.TileSize; x++) for (var y = 0; y < TileSetRenderer.TileSize; y++) p[(j * TileSetRenderer.TileSize + y) * stride + i * TileSetRenderer.TileSize + x] = Palette.GetColor(rawImage[x + TileSetRenderer.TileSize * y]).ToArgb(); if (Map.MapResources.Value[uv].Type != 0) { var resourceImage = ResourceTemplates[Map.MapResources.Value[uv].Type].Bitmap; var srcdata = resourceImage.LockBits(resourceImage.Bounds(), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); var q = (int*)srcdata.Scan0.ToPointer(); var srcstride = srcdata.Stride >> 2; for (var x = 0; x < TileSetRenderer.TileSize; x++) for (var y = 0; y < TileSetRenderer.TileSize; y++) { var c = q[y * srcstride + x]; if ((c & 0xff000000) != 0) /* quick & dirty, i cbf doing real alpha */ p[(j * TileSetRenderer.TileSize + y) * stride + i * TileSetRenderer.TileSize + x] = c; } resourceImage.UnlockBits(srcdata); } } } bitmap.UnlockBits(data); if (ShowGrid) { using (var g = SGraphics.FromImage(bitmap)) { var ts = Game.ModData.Manifest.TileSize; var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); ControlPaint.DrawGrid(g, rect, new Size(2, ts.Height), Color.DarkRed); ControlPaint.DrawGrid(g, rect, new Size(ts.Width, 2), Color.DarkRed); ControlPaint.DrawGrid(g, rect, new Size(ts.Width, ts.Height), Color.Red); } } return bitmap; }
void place() { if (currentSelected != Selected.WALL && !game.World.IsInWorld(MouseInput.GamePosition)) { return; } var pos = MouseInput.GamePosition; var mpos = pos.ToMPos(); var bounds = game.World.Map.Bounds; switch (currentSelected) { case Selected.ACTOR: if (actorWidget.CurrentType == null) { return; } if (actorWidget.RelativeHP == 0) { return; } pos = actorWidget.Rasterization ? rasterizedPosition(pos) : pos; var team = Math.Max(actorWidget.Team, (byte)0); var actor = ActorCache.Create(game.World, actorWidget.CurrentType, pos, team, actorWidget.Bot, false, actorWidget.RelativeHP); actor.Angle = actorWidget.RelativeFacing * Angle.MaxRange; game.World.Add(actor); break; case Selected.TILE: if (terrainWidget.CurrentType == null) { return; } if (mpos.X < 0 || mpos.Y < 0 || mpos.X >= bounds.X || mpos.Y >= bounds.Y) { return; } if (game.World.TerrainLayer.Terrain[mpos.X, mpos.Y].Type == terrainWidget.CurrentType) { return; } var terrain = TerrainCache.Create(game.World, mpos, terrainWidget.CurrentType.ID); game.World.TerrainLayer.Set(terrain); WorldRenderer.CheckTerrainAround(mpos, true); break; case Selected.WALL: if (wallWidget.CurrentType == null) { return; } if (mpos.X < 0 || mpos.Y < 0 || mpos.X > bounds.X || mpos.Y > bounds.Y) { return; } mpos = new MPos(mpos.X * 2 + (wallWidget.Horizontal ? 0 : 1), mpos.Y); var wallLayer = game.World.WallLayer; if (mpos.X >= wallLayer.Bounds.X - 2) { return; } if (mpos.Y >= wallLayer.Bounds.Y - 2 && wallWidget.Horizontal) { return; } var type = wallWidget.CurrentType; var plannedHealth = (int)(type.Health * wallWidget.RelativeHP); if (plannedHealth == 0 && type.Health != 0) { return; } var currentWall = wallLayer.Walls[mpos.X, mpos.Y]; if (currentWall != null && currentWall.Type.ID == type.ID && currentWall.Health == plannedHealth) { return; } var wall = WallCache.Create(mpos, wallLayer, type.ID); wall.Health = plannedHealth; wallLayer.Set(wall); break; } }
public Shroud(MPos pos) { Listener = pos; graphicPosition = new CPos(pos.X * 512 - 256, pos.Y * 512 - 256, 0); }
public PhysicsSector(MPos position) { Position = position; }
static void ReadOverlay(Map map, IniFile file, int2 fullSize) { var overlaySection = file.GetSection("OverlayPack"); var overlayCompressed = Convert.FromBase64String(overlaySection.Aggregate(string.Empty, (a, b) => a + b.Value)); var overlayPack = new byte[1 << 18]; var temp = new byte[1 << 18]; UnpackLCW(overlayCompressed, overlayPack, temp); var overlayDataSection = file.GetSection("OverlayDataPack"); var overlayDataCompressed = Convert.FromBase64String(overlayDataSection.Aggregate(string.Empty, (a, b) => a + b.Value)); var overlayDataPack = new byte[1 << 18]; UnpackLCW(overlayDataCompressed, overlayDataPack, temp); var overlayIndex = new CellLayer <int>(map); overlayIndex.Clear(0xFF); for (var y = 0; y < fullSize.Y; y++) { for (var x = fullSize.X * 2 - 2; x >= 0; x--) { var dx = (ushort)x; var dy = (ushort)(y * 2 + x % 2); var uv = new MPos(dx / 2, dy); var rx = (ushort)((dx + dy) / 2 + 1); var ry = (ushort)(dy - rx + fullSize.X + 1); if (!map.Resources.Contains(uv)) { continue; } overlayIndex[uv] = rx + 512 * ry; } } foreach (var cell in map.AllCells) { var overlayType = overlayPack[overlayIndex[cell]]; if (overlayType == 0xFF) { continue; } if (OverlayToActor.TryGetValue(overlayType, out var actorType)) { var shape = new Size(1, 1); if (OverlayShapes.TryGetValue(overlayType, out shape)) { // Only import the top-left cell of multi-celled overlays var aboveType = overlayPack[overlayIndex[cell - new CVec(1, 0)]]; if (shape.Width > 1 && aboveType != 0xFF) { if (OverlayToActor.TryGetValue(aboveType, out var a) && a == actorType) { continue; } } var leftType = overlayPack[overlayIndex[cell - new CVec(0, 1)]]; if (shape.Height > 1 && leftType != 0xFF) { if (OverlayToActor.TryGetValue(leftType, out var a) && a == actorType) { continue; } } } var ar = new ActorReference(actorType) { new LocationInit(cell), new OwnerInit("Neutral") }; if (OverlayToHealth.TryGetValue(overlayType, out var damageState)) { var health = 100; if (damageState == DamageState.Critical) { health = 25; } else if (damageState == DamageState.Heavy) { health = 50; } else if (damageState == DamageState.Medium) { health = 75; } if (health != 100) { ar.Add(new HealthInit(health)); } } map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); continue; } var resourceType = ResourceFromOverlay .Where(kv => kv.Value.Contains(overlayType)) .Select(kv => kv.Key) .FirstOrDefault(); if (resourceType != 0) { map.Resources[cell] = new ResourceTile(resourceType, overlayDataPack[overlayIndex[cell]]); continue; } Console.WriteLine("{0} unknown overlay {1}", cell, overlayType); } }
public bool IsVisible(MPos uv) { if (!visibleCount.Contains(uv)) return false; return map.ProjectedCellsCovering(uv).Any(isVisibleTest); }
void notifyNeighbors(MPos pos, bool added, bool ignoresNearby) { byte s = 0; bool left = pos.X >= 2; bool right = pos.X < Bounds.X - 3; bool top = pos.Y > 0; bool bottom = pos.Y < Bounds.Y - 2; /* * * HORIZONTAL |0 |3 | | | 1 | | 4 | --------o-------o-------- | | | | | 2| |5 | | VERTICAL |1 | | 0 | 2 | --------o-------- | | | | --------o-------- | 3 | 5 | | 4| | | BYTE 012 345 67 | 000 000 -- */ Wall wall; if (pos.X % 2 != 0) { // Horizontal if (top) { wall = Walls[pos.X - 1, pos.Y - 1]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b00000100, added); } s |= 0b10000000; } } if (left) { wall = Walls[pos.X - 2, pos.Y]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b00001000, added); } s |= 0b01000000; } } wall = Walls[pos.X - 1, pos.Y]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b00100000, added); } s |= 0b00100000; } if (right) { if (top) { wall = Walls[pos.X + 1, pos.Y - 1]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b00010000, added); } s |= 0b00010000; } } wall = Walls[pos.X + 2, pos.Y]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b01000000, added); } s |= 0b00001000; } wall = Walls[pos.X + 1, pos.Y]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b10000000, added); } s |= 0b00000100; } } } else { // Vertical if (left) { wall = Walls[pos.X - 1, pos.Y]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b00000100, added); } s |= 0b10000000; } } if (top) { wall = Walls[pos.X, pos.Y - 1]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b00001000, added); } s |= 0b01000000; } } wall = Walls[pos.X + 1, pos.Y]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b00100000, added); } s |= 0b00100000; } if (bottom) { if (left) { wall = Walls[pos.X - 1, pos.Y + 1]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b00010000, added); } s |= 0b00010000; } } wall = Walls[pos.X, pos.Y + 1]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b01000000, added); } s |= 0b00001000; } wall = Walls[pos.X + 1, pos.Y + 1]; if (wall != null && !wall.Type.IgnoreForNearby) { if (!ignoresNearby) { wall.SetNeighborState(0b10000000, added); } s |= 0b00000100; } } } if (added) { Walls[pos.X, pos.Y].SetNeighborState(s, true); } }
void CellTerrainColorChanged(MPos uv) { UpdateTerrainColor(uv); }
int VertexArrayOffset(MPos uv) { return(4 * (uv.V * map.MapSize.X + uv.U)); }
CellRegion CalculateVisibleCells(bool insideBounds) { var map = worldRenderer.World.Map; // Calculate the viewport corners in "projected wpos" (at ground level), and // this to an equivalent projected cell for the two corners var tl = map.CellContaining(worldRenderer.ProjectedPosition(TopLeft)).ToMPos(map); var br = map.CellContaining(worldRenderer.ProjectedPosition(BottomRight)).ToMPos(map); // Diamond tile shapes don't have straight edges, and so we need // an additional cell margin to include the cells that are half // visible on each edge. if (map.TileShape == TileShape.Diamond) { tl = new MPos(tl.U - 1, tl.V - 1); br = new MPos(br.U + 1, br.V + 1); } // Clamp to the visible map bounds, if requested if (insideBounds) { tl = map.Clamp(tl); br = map.Clamp(br); } // Cells can be pushed up from below if they have non-zero height. // Each height step is equivalent to 512 WDist units, which is // one MPos step for diamond cells, but only half a MPos step // for classic cells. Doh! var heightOffset = map.TileShape == TileShape.Diamond ? map.MaximumTerrainHeight : map.MaximumTerrainHeight / 2; br = new MPos(br.U, br.V + heightOffset); // Finally, make sure that this region doesn't extend outside the map area. tl = map.MapHeight.Value.Clamp(tl); br = map.MapHeight.Value.Clamp(br); return new CellRegion(map.TileShape, tl.ToCPos(map), br.ToCPos(map)); }
public PPos[] ProjectedCellsCovering(MPos uv) { if (!initializedCellProjection) InitializeCellProjection(); if (!cellProjection.Contains(uv)) return NoProjectedCells; return cellProjection[uv]; }
public NewMapLogic(Action onExit, Action<string> onSelect, Ruleset modRules, Widget widget, World world) { panel = widget; panel.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () => { Ui.CloseWindow(); onExit(); }; var tilesetDropDown = panel.Get<DropDownButtonWidget>("TILESET"); var tilesets = modRules.TileSets.Select(t => t.Key).ToList(); Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) => { var item = ScrollItemWidget.Setup(template, () => tilesetDropDown.Text == option, () => { tilesetDropDown.Text = option; }); item.Get<LabelWidget>("LABEL").GetText = () => option; return item; }; tilesetDropDown.Text = tilesets.First(); tilesetDropDown.OnClick = () => tilesetDropDown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, tilesets, setupItem); var widthTextField = panel.Get<TextFieldWidget>("WIDTH"); var heightTextField = panel.Get<TextFieldWidget>("HEIGHT"); panel.Get<ButtonWidget>("CREATE_BUTTON").OnClick = () => { int width, height; int.TryParse(widthTextField.Text, out width); int.TryParse(heightTextField.Text, out height); // Require at least a 2x2 playable area so that the // ground is visible through the edge shroud width = Math.Max(2, width); height = Math.Max(2, height); var maxTerrainHeight = Game.ModData.Manifest.MaximumTerrainHeight; var tileset = modRules.TileSets[tilesetDropDown.Text]; var map = new Map(tileset, width + 2, height + maxTerrainHeight + 2); var tl = new MPos(1, 1); var br = new MPos(width, height + maxTerrainHeight); map.SetBounds(tl, br); map.PlayerDefinitions = new MapPlayers(map.Rules, map.SpawnPoints.Value.Length).ToMiniYaml(); map.FixOpenAreas(modRules); Action<string> afterSave = uid => { // HACK: Work around a synced-code change check. // It's not clear why this is needed here, but not in the other places that load maps. Game.RunAfterTick(() => { ConnectionLogic.Connect(System.Net.IPAddress.Loopback.ToString(), Game.CreateLocalServer(uid), "", () => Game.LoadEditor(uid), () => { Game.CloseServer(); onExit(); }); }); Ui.CloseWindow(); onSelect(uid); }; Ui.OpenWindow("SAVE_MAP_PANEL", new WidgetArgs() { { "onSave", afterSave }, { "onExit", () => { Ui.CloseWindow(); onExit(); } }, { "map", map }, { "playerDefinitions", map.PlayerDefinitions }, { "actorDefinitions", map.ActorDefinitions } }); }; }
static void ReadActors(Map map, IniFile file, string type, int2 fullSize) { var structuresSection = file.GetSection(type, true); foreach (var kv in structuresSection) { var isDeployed = false; var entries = kv.Value.Split(','); var name = entries[1].ToLowerInvariant(); if (DeployableActors.ContainsKey(name)) { name = DeployableActors[name]; isDeployed = true; } if (ReplaceActors.ContainsKey(name)) { name = ReplaceActors[name]; } var health = short.Parse(entries[2]); var rx = int.Parse(entries[3]); var ry = int.Parse(entries[4]); var facing = (byte)(224 - byte.Parse(entries[type == "Infantry" ? 7 : 5])); var dx = rx - ry + fullSize.X - 1; var dy = rx + ry - fullSize.X - 1; var cell = new MPos(dx / 2, dy).ToCPos(map); var ar = new ActorReference(name) { new LocationInit(cell), new OwnerInit(CreepActors.Contains(entries[1]) ? "Creeps" : "Neutral") }; if (type == "Infantry") { var subcell = 0; switch (byte.Parse(entries[5])) { case 2: subcell = 3; break; case 3: subcell = 1; break; case 4: subcell = 2; break; } if (subcell != 0) { ar.Add(new SubCellInit((SubCell)subcell)); } } if (health != 256) { ar.Add(new HealthInit(100 * health / 256)); } ar.Add(new FacingInit(WAngle.FromFacing(facing))); if (isDeployed) { ar.Add(new DeployStateInit(DeployState.Deployed)); } if (!map.Rules.Actors.ContainsKey(name)) { Console.WriteLine("Ignoring unknown actor type: `{0}`".F(name)); } else { map.ActorDefinitions.Add(new MiniYamlNode("Actor" + map.ActorDefinitions.Count, ar.Save())); } } }
bool ContainsAllProjectedCellsCovering(MPos uv) { if (Grid.MaximumTerrainHeight == 0) return Contains((PPos)uv); // If the cell has no valid projection, then we're off the map. var projectedCells = ProjectedCellsCovering(uv); if (projectedCells.Length == 0) return false; foreach (var puv in projectedCells) if (!Contains(puv)) return false; return true; }