// GDnD wiki example (1 of 3: ChemistryRules.cs, ChemistryBody.cs, ElementSource.cs) // Demonstrates: a small "chemistry engine" in the style described for The Legend of Zelda: // Breath of the Wild (Dohta, GDC 2017). Elements (fire, water, ice, electricity, wind) carry // state changes; materials (anything with a ChemistryBody: crates, grass, enemies, the player) // have tags that decide how they react. Three rules, applied the same way to everything: // 1. Elements can change the state of materials (fire sets a Flammable crate burning). // 2. Elements can change each other (water puts out fire, fire melts ice into water). // 3. Materials cannot change each other directly - only through an element they give off. // Physics stays separate: Rigidbody2D moves things; the chemistry code only changes state and // hands over to physics in one place (wind pushes Light bodies, see ChemistryBody). // This file holds the data types and the whole rule table, so every object obeys the same rules. // Related pages: [[breath-of-the-wild]], [[emergent-design]], [[game-atoms]], // [[source-botw-breaking-conventions-gdc-2017]], [[unity-collider2d-and-triggers]] // Unity 6 (6000.x), 2D physics. No input code, so it works with either input backend. using System; namespace GDnD.Systemic.Chemistry { public enum Element { None, Fire, Water, Ice, Electricity, Wind } [Flags] public enum MaterialTags { None = 0, Flammable = 1 << 0, // wood, grass, cloth: catches fire Conductive = 1 << 1, // metal: carries electricity even when dry Freezable = 1 << 2, // can be frozen solid by ice even when dry Light = 1 << 3, // pushed by wind } public enum MaterialState { Normal, Burning, Burnt, Wet, Frozen, Electrified } public static class ChemistryRules { // Rule 2: what an element source becomes when another element reaches it. public static Element Combine(Element present, Element incoming) => (present, incoming) switch { (Element.Fire, Element.Water) => Element.None, // water extinguishes fire (Element.Ice, Element.Fire) => Element.Water, // fire melts ice (Element.Water, Element.Ice) => Element.Ice, // ice freezes water _ => present, // everything else: no change }; // Rule 1: how an element changes a material's state, given its tags and current state. public static MaterialState Apply(Element element, MaterialTags tags, MaterialState current) { if (current == MaterialState.Burnt) return current; // burnt is final return element switch { Element.Fire when current == MaterialState.Frozen => MaterialState.Wet, // thaws Element.Fire when current == MaterialState.Wet => MaterialState.Normal, // dries out Element.Fire when tags.HasFlag(MaterialTags.Flammable) => MaterialState.Burning, Element.Water => MaterialState.Wet, // also puts out Burning Element.Ice when tags.HasFlag(MaterialTags.Freezable) || current == MaterialState.Wet => MaterialState.Frozen, Element.Electricity when tags.HasFlag(MaterialTags.Conductive) || current == MaterialState.Wet => MaterialState.Electrified, _ => current, }; } // The element a body gives off in a given state. Burning things spread fire; that is // the only way one material affects another (rule 3). public static Element Emits(MaterialState state) => state == MaterialState.Burning ? Element.Fire : Element.None; } }