🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Progress Report

Published May 03, 2006
Advertisement
I've missed a lot of MoE work due to focus on job hunting and interviews. Yesterday's interview went fairly well I think, though it was interesting to note how much more comfortable I am now with actually doing Unix work rather than Windows [since my sys-admin experience was primarily on the windows side].

Because of that, and because I have a coding interview tomarrow, I wanted to get some work on MoE done today. Thankfully motivation decided to join me, and I got a bit of work done this morning. Behold!



Tiles now recenter if clicked upon, and the little UI windows will display (placeholder) tile and unit info as the centering changes.


Also, to avoid being a bit of a hypocrit, I'd like to take a little time to discuss the inner-workings of this setup since it seems to work particularly well for me so far, and because I said myself I like to see these things.

So, MoE's map structure is a little uncommon, since [a few revisions ago] I wanted two primary design goals for it. One, the client was to have as little knowledge as possible to the maps' size and or shape. Two, I wanted maps to be customizable. That is, allow users to specify a hex, 4way square, 8way square, or more exotic maps and have the game deal with everything generically and nicely. This second requirement has been dropped/relaxed in favor of getting stuff done. Still it's influenced the development of map rendering.

The rendering seen above is done via a Layering approach. One class exists to do tile layout. In other words, it takes a single reference tile/position pair and builds other tile/position pairs from them. Other classes then take that layout information and build the visual representation of the map from it. In the screenshot above there are 4 layers active: One to draw the squares, one to draw units, one to draw coordinates, and one to add 'mousetarget's for recentering.

        public interface MapLayer {            void Add(TileLocation newtile);            void Remove(TileLocation oldtile);            void Clear();            void render();            string Name { get;}            MapRenderer Src { get;set;}        }        public abstract class MapRenderingLayer : rms.basero, MapLayer {            #region MapLayer Members            // Re-Abstraction            #endregion        }                public class MapRenderer:basero {            protected List       Layers = new List();            protected basero                        LastLayer;            protected Tile                          ReferenceTile;            protected System.Drawing.Rectangle      size;            protected int                           bordersize;            protected int                           xscroll = 0;            protected int                           yscroll = 0;            protected dynamic_rect                  ReferenceRect;            protected Dictionary ScreenPositioning = new Dictionary();            public override void render() {                if (visible > 0) {                    rendertree();                }            }            private System.Drawing.Rectangle Sizing(){return(size);}            protected virtual TileLocation[] FanLeftRight(UInt32 x, UInt32 y, UInt32 z, rect StartLocation){                //                 // Snip. Loops to look left/right from tile                 //  and generate positioning pairs.                //                // Rects are tied, so that moving one moves its                //  neighbors as well.                //            }            protected virtual TileLocation[] FanUpDown(TileLocation Center){                //                // Fan up and down from the center tile, returning the tiles                //  Generated this way.                //                // X X X                //  X X X                // X X X                //                //                // Snip. Move up/down from reference tile, adding                 //  each, and calling FanLeftRight on each.                //            }            public virtual void Generate() {                //                // Generate map from nothing. Assume reference Tile and rect                //  are set. Called for a Center, new Map, or catastrophic change.                //                if (ReferenceTile == null || ReferenceRect == null) {                    return;                }                ScreenPositioning.Clear();                foreach (MapLayer ml in Layers) {                    ml.Clear();                }                xscroll = 0;                yscroll = 0;                basero newro = image_manager.manager.Load("solid");                newro.rerect(ReferenceRect);                TileLocation[] rtn = FanUpDown(new TileLocation(ReferenceTile, newro.tblr));                foreach (TileLocation tl in rtn) {                    if (!ScreenPositioning.ContainsKey(tl.MapTile)) {                        ScreenPositioning.Add(tl.MapTile, tl);                        foreach (MapLayer ml in Layers) {                            ml.Add(tl);                        }                    }                }                                                    }            public virtual void Scroll(int x, int y) {                xscroll += x;                yscroll += y;                if (System.Math.Abs(xscroll) > Width || System.Math.Abs(yscroll)>Height) {                    Generate();                    return;                } else {                    System.Drawing.Rectangle r = ReferenceRect.fetch();                    r.Offset(x, y);                    // TODO: If Ref-rect off screen, choose a new refrect                    //        or reset scrolling.                    ReferenceRect.rerect(r);                }                            }            public override basero rerect(rect newr) {                base.rerect(newr);                Generate();                return (this);            }            public virtual void Center(Tile tile) {                System.Drawing.Rectangle newref = Sizing();                newref.Offset(tblr.fetch().X + (tblr.fetch().Width - newref.Width) / 2,                    tblr.fetch().Y + (tblr.fetch().Height - newref.Height) / 2);                ReferenceRect = new dynamic_rect(newref);                ReferenceTile = tile;                Generate();            }            public int Height{                get{                    return(size.Height);                }                set{                    size.Height=value;                    if (ReferenceRect != null) {                        System.Drawing.Rectangle r = ReferenceRect.fetch();                        r.Height = value;                        ReferenceRect.rerect(r);                    }                    Generate();                }            }            public int Width{                get{                    return(size.Width);                }                set{                    size.Width=value;                    if (ReferenceRect != null) {                        System.Drawing.Rectangle r = ReferenceRect.fetch();                        r.Width = value;                        ReferenceRect.rerect(r);                    }                    Generate();                }            }            public int TileSize{                set{                    size.Width=value;                    size.Height=value;                    if (ReferenceRect != null) {                        System.Drawing.Rectangle r = ReferenceRect.fetch();                        r.Width = value;                        r.Height = value;                        ReferenceRect.rerect(r);                    }                    Generate();                }            }                               public int BorderSize{                get{                    return(bordersize);                }                set{                    if(bordersize!=value && value>=0){                        bordersize=value;                        Generate();                    }                }            }            public virtual void Add(MapRenderingLayer ml) {                Layers.Add(ml);                if (LastLayer == null) {                    this.push_bottom(ml);                    LastLayer = ml;                } else {                    LastLayer.push_top(ml);                    LastLayer.placement = LastLayer.placement + 1;                    LastLayer = ml;                }                foreach (TileLocation tl in ScreenPositioning.Values) {                    ml.Add(tl);                }            }            public MapRenderer(bool Scrollable)                : base() {                if (Scrollable) {                    std_input_handler ih = new std_input_handler();                    ih.kbBind(System.Windows.Forms.Keys.Left, new keyinput.kb_function(                        delegate() {                            Scroll(-8, 0);                        }), input_modifiers.ims.IM_NORMAL);                    ih.kbBind(System.Windows.Forms.Keys.Up, new keyinput.kb_function(                        delegate() {                            Scroll(0, -8);                        }), input_modifiers.ims.IM_NORMAL);                    ih.kbBind(System.Windows.Forms.Keys.Right, new keyinput.kb_function(                        delegate() {                            Scroll(8, 0);                        }), input_modifiers.ims.IM_NORMAL);                    ih.kbBind(System.Windows.Forms.Keys.Down, new keyinput.kb_function(                        delegate() {                            Scroll(0, 8);                        }), input_modifiers.ims.IM_NORMAL);                    set_handler(ih);                }            }            public MapRenderer() : this(true) { }                    }        //         // Snip. Example Layer. Others follow this basic pattern        //  for implementing the common abstract base class.        //        public class CoordLayer : MapRenderingLayer {            private Dictionary LookupTable = new Dictionary();            private MapRenderer MapParent;                        public override void Add(TileLocation newtile) {                ro_text rot=font_manager.manager.Load("Arial12") as ro_text;                rot.SrcText=new StoredText(newtile.MapTile.x +","+newtile.MapTile.y);                rot.color=System.Drawing.Color.Red.ToArgb();                rot.rerect(new identical_rect(newtile.Location));                rot.push_onto(this);                LookupTable.Add(newtile.MapTile,rot);            }            public override void Remove(TileLocation oldtile) {                if (LookupTable.ContainsKey(oldtile.MapTile)) {                    LookupTable[oldtile.MapTile].Close();                }                LookupTable.Remove(oldtile.MapTile);            }            public override void Clear() {                foreach (basero br in LookupTable.Values) {                    br.Close();                }                LookupTable.Clear();            }            public override string Name {                get {                     return("Coords");                }            }            public override MapRenderer Src {                get {                    return (MapParent);                }                set {                    if (MapParent != value) {                        Clear();                        MapParent = value;                    }                }            }            public override void render() {                if (visible > 0) {                    rendertree();                }            }            public CoordLayer(MapRenderer Map):base() {                MapParent = Map;                tblr = new identical_rect(Map.tblr);            }                    }


So in this way, I'll be able to reuse the map classes in different areas [say for a town view], and simply modify the functionality by changing the layers. Setting a "select" or "MoveTo" mode will be as simple as overlaying a new layer onto the map. And should I decide to allow or change the tile orientation minimal changes will need to be made to the layers, since almost all of that positioning is done in the base MapRenderer class.

Downside is that tiles are treated independantly, so dealing with things that require neighbor info like visual tile transitions will be painful and slow. It also perhaps isn't the speediest thing in town since each tile is its own [and usually a half-dozen or more overlayed] quads.

[edit: 6:15 CST - Late Afternoon Work]
- Tracked down and fixed a bug that prevented ChatBoxes from catching update messages.
- Modified the ChatBox class to better interact with StatusGroups [for switching active textentry fields]
- Added code to transfer the common ChatDisplay to the main window. Escape and 't' currently exit and enter chatting respectively.
Previous Entry Caramel Armadillos
Next Entry Progress Report
0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement