private void Parse(List <string> rawBlock) { int i = 0; while (i < rawBlock.Count) { string item = rawBlock[i]; if (item.Contains(KeyValDelimiter)) { List <KeyValuePair <string, string> > rawKeyVals = ExtractKeyVals(item); string classname = rawKeyVals.Find(s => s.Key == "classname").Value; foreach (KeyValuePair <string, string> rawKeyVal in rawKeyVals) { Option newOption; if (Definitions.ContainsKey(classname) && Definitions[classname].KeyValsTemplate.ContainsKey(rawKeyVal.Key)) { newOption = new Option(Definitions[classname].KeyValsTemplate[rawKeyVal.Key]); } else { newOption = new Option(); } newOption.Value = rawKeyVal.Value; if (!KeyVals.ContainsKey(rawKeyVal.Key)) { KeyVals.Add(rawKeyVal.Key, newOption); } else { KeyVals[rawKeyVal.Key] = newOption; } } ++i; } // An open delimiter that's the first item in the raw block is just the // start of the top level block; any others indicate children. else if (item == OpenDelimiter && i != 0) { int childOpenBraceIndex = i + 1; var childBlock = new QuakeBlock(rawBlock, childOpenBraceIndex, Definitions); Children.Add(childBlock); i = childBlock.RawStartIndex + childBlock.RawLength; } else if (item.StartsWith("(", StringComparison.OrdinalIgnoreCase)) { string solid = item; int j = i + 1; while (rawBlock[j] != CloseDelimiter) { solid += rawBlock[j]; j++; } Solids.Add(new Solid(ExtractSides(solid))); i += j; } else { i++; } } }
protected override void ExtractRenderables(Block block) { var b = block as QuakeBlock; // Contains brushes. Checking the Solids count allows for both known // and unknown solid entities, which can be treated the same way. if (b.Solids.Count > 0) { foreach (Solid solid in b.Solids) { Renderables.Add(new QuakeBrush(solid)); } } // Known point entity. else if (Definition?.ClassType == ClassType.Point) { if (KeyVals.ContainsKey("origin")) { Position = KeyVals["origin"].Value.ToVector3(); } if (Definition.RenderableSources.ContainsKey(RenderableSource.Key)) { string key = Definition.RenderableSources[RenderableSource.Key]; string path = KeyVals[key].Value; if (path.EndsWith(".map", StringComparison.OrdinalIgnoreCase)) { string oldCwd = Directory.GetCurrentDirectory(); string instancePath; if (Path.IsPathRooted(path)) { instancePath = path; } else { instancePath = Path.Combine(oldCwd, path); } QuakeMap map; using (FileStream stream = File.OpenRead(instancePath)) { map = new QuakeMap(stream, Definition.DefinitionCollection); } map.Parse(); if (Definition.ClassName == "misc_external_map") { map.Prune(ClassType.Point); } // For now just tweak the instance map's renderable // geometry; name fixup and variable replacement will // be easier to accomplish as a separate pass, once all // instance maps have been loaded and parsed. map.Transform(this); UserData = map; foreach (MapObject mo in map.AllObjects) { var modified = new QuakeMapObject(mo); if (mo.KeyVals["classname"].Value == "worldspawn") { modified.Saveability = Saveability.Solids; } Children.Add(modified); } // Create a simple box to mark this instance's origin. Renderable box = new BoxGenerator(Color4.Orange).Generate(); box.Position = Position; box.Transformability = Definition.RenderableTransformability; Renderables.Add(box); } } else if (Definition.RenderableSources.ContainsKey(RenderableSource.Model)) { LoadModel(block as QuakeBlock); } else if (Definition.RenderableSources.ContainsKey(RenderableSource.Size)) { Aabb s = Definition.Size; Renderable box = new BoxGenerator(s.Min, s.Max, Definition.Color).Generate(); box.Position = Position; Renderables.Add(box); } // Known point entity with no predefined size. else { Renderable gem = new GemGenerator(Color4.Lime).Generate(); gem.Position = Position; gem.Transformability = Definition.RenderableTransformability; Renderables.Add(gem); } } // Unknown entity. else if (Definition == null) { Renderable gem = new GemGenerator().Generate(); gem.Position = Position; gem.Transformability = Definition.RenderableTransformability; Renderables.Add(gem); } }