<?xml version="1.0" encoding="utf-8"?>
<!--
  xmlgameengine.xsd

  What a game file is made of. The rule of thumb:

    * An attribute NAMES or PICKS something: name, class, object, variable,
      state, action, button, edge, direction, unless, burn, sprite. These are labels, and
      they cannot be computed.
    * Everything else is the content of an element. Wherever the content is a
      number (valueType below) it is either a math expression written out as
      text - window.width.center - title.width / 2 - or a single value tag that
      makes the number, such as <random min="-7" max="7" />. To add a new kind
      of value, add it to the valueTag group and teach game_xml and game_expr
      about it; every place that takes a value takes it.
    * What an object DOES is a list of command tags (the command group below):
      <bounce />, <inc variable="paddle1.score" />, <push state="playing" />.
      They run in the order written.

  Shapes, commands and random numbers are tags, never function calls; only
  plain arithmetic is left to be written out as text.
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified">

  <!-- The words an attribute may hold. -->
  <xs:simpleType name="edgeType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="left" />
      <xs:enumeration value="right" />
      <xs:enumeration value="top" />
      <xs:enumeration value="bottom" />
      <xs:enumeration value="horizontal" />
      <xs:enumeration value="vertical" />
      <xs:enumeration value="all" />
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="directionType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="up" />
      <xs:enumeration value="down" />
      <xs:enumeration value="left" />
      <xs:enumeration value="right" />
    </xs:restriction>
  </xs:simpleType>

  <!-- The two ways an object with a heading can turn. -->
  <xs:simpleType name="turnDirectionType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="left" />
      <xs:enumeration value="right" />
    </xs:restriction>
  </xs:simpleType>

  <xs:simpleType name="flipType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="horizontal" />
      <xs:enumeration value="vertical" />
    </xs:restriction>
  </xs:simpleType>

  <!-- Value tags: the ways, besides writing an expression, to give a number. -->
  <xs:group name="valueTag">
    <xs:choice>
      <xs:element name="random">
        <xs:complexType>
          <xs:attribute name="min" type="xs:string" use="required" />
          <xs:attribute name="max" type="xs:string" use="required" />
        </xs:complexType>
      </xs:element>
    </xs:choice>
  </xs:group>

  <!-- A number: text (an expression) or one value tag, not both. -->
  <xs:complexType name="valueType" mixed="true">
    <xs:sequence>
      <xs:group ref="valueTag" minOccurs="0" />
    </xs:sequence>
  </xs:complexType>

  <!-- A number with a name, for <variable name="score">0</variable>. -->
  <xs:complexType name="variableType" mixed="true">
    <xs:sequence>
      <xs:group ref="valueTag" minOccurs="0" />
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" use="required" />
  </xs:complexType>

  <!-- Two numbers: <position>, <velocity> and a grid's <padding>. -->
  <xs:complexType name="vector2Type">
    <xs:sequence>
      <xs:element name="x" type="valueType" />
      <xs:element name="y" type="valueType" />
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="emptyType" />

  <!-- Sprites: one shape, and optionally a grid of copies of it. -->
  <xs:group name="shape">
    <xs:choice>
      <xs:element name="circle">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="radius" type="valueType" />
            <xs:element name="color" type="xs:string" minOccurs="0" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="rectangle">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="width" type="valueType" />
            <xs:element name="height" type="valueType" />
            <xs:element name="color" type="xs:string" minOccurs="0" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="text">
        <xs:complexType>
          <xs:sequence>
            <xs:choice>
              <!-- a fixed label -->
              <xs:element name="content" type="xs:string" />
              <!-- a live number: an owner.variable stays up to date as it changes -->
              <xs:element name="number" type="valueType" />
            </xs:choice>
            <xs:element name="size" type="valueType" />
            <xs:element name="color" type="xs:string" minOccurs="0" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="image">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="path" type="xs:string" />
            <xs:element name="flip" type="flipType" minOccurs="0" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <!--
        A picture in rows of text, one character to a pixel: a period is a clear
        pixel and an asterisk a solid one, drawn in the color. Every character
        becomes scale by scale real pixels (1 if left out), so a few characters
        make a chunky sprite. The rows must all be the same width. Like a
        drawing of lines it is made into a picture when the game loads.
      -->
      <xs:element name="bitmap">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="row" type="xs:string" maxOccurs="unbounded" />
            <xs:element name="scale" type="valueType" minOccurs="0" />
            <xs:element name="color" type="xs:string" minOccurs="0" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:choice>
  </xs:group>

  <!--
    One straight line of a drawing: where from and to, in pixels from the
    sprite's top left, and optionally its color and how many pixels thick it is
    (1 if left out). A sprite of one or more of these is drawn once, when the
    game loads, into the picture that is shown and that a collision of type
    pixel looks at.
  -->
  <xs:complexType name="lineType">
    <xs:sequence>
      <xs:element name="from" type="vector2Type" />
      <xs:element name="to" type="vector2Type" />
      <xs:element name="color" type="xs:string" minOccurs="0" />
      <xs:element name="thickness" type="valueType" minOccurs="0" />
    </xs:sequence>
  </xs:complexType>

  <!-- A sprite is named when an object has several, so an animation can pick them out. -->
  <xs:complexType name="spriteType">
    <xs:choice>
      <xs:group ref="shape" />
      <xs:element name="line" type="lineType" maxOccurs="unbounded" />
      <xs:element name="grid">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="columns" type="valueType" />
            <xs:element name="rows" type="valueType" />
            <xs:element name="padding" type="vector2Type" minOccurs="0" />
            <xs:group ref="shape" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:choice>
    <xs:attribute name="name" type="xs:string" use="optional" />
  </xs:complexType>

  <!--
    An animation: how many seconds each picture is shown, and the sprites it
    shows in turn, round and round, each picked out by the name of one of the
    object's own sprites. At least two frames; the same sprite may come up more
    than once. The frames must all be pictures (bitmaps or drawings of lines)
    of the same size.
  -->
  <xs:complexType name="animationType">
    <xs:sequence>
      <xs:element name="interval" type="valueType" />
      <xs:element name="frame" maxOccurs="unbounded">
        <xs:complexType>
          <xs:attribute name="sprite" type="xs:string" use="required" />
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>

  <!-- Commands: what a collision rule, an object action, a key or a condition does. -->
  <!-- inc and dec: which variable, and by how much (1 when there is nothing between the tags). -->
  <xs:complexType name="variableReferenceType" mixed="true">
    <xs:sequence>
      <xs:group ref="valueTag" minOccurs="0" />
    </xs:sequence>
    <xs:attribute name="variable" type="xs:string" use="required" />
  </xs:complexType>

  <!-- move and hop: a direction, and how far. -->
  <xs:complexType name="stepType" mixed="true">
    <xs:sequence>
      <xs:group ref="valueTag" minOccurs="0" />
    </xs:sequence>
    <xs:attribute name="direction" type="directionType" use="required" />
  </xs:complexType>

  <!-- accelerate: a direction, how much to add to the velocity each frame, and optionally the variable it burns. -->
  <xs:complexType name="thrustType" mixed="true">
    <xs:sequence>
      <xs:group ref="valueTag" minOccurs="0" />
    </xs:sequence>
    <xs:attribute name="direction" type="directionType" use="required" />
    <xs:attribute name="burn" type="xs:string" use="optional" />
  </xs:complexType>

  <!-- turn: left or right, and how many degrees a frame while the key is held. -->
  <xs:complexType name="turnType" mixed="true">
    <xs:sequence>
      <xs:group ref="valueTag" minOccurs="0" />
    </xs:sequence>
    <xs:attribute name="direction" type="turnDirectionType" use="required" />
  </xs:complexType>

  <!-- thrust: how much to add to the velocity each frame along the heading, and optionally the variable it burns. -->
  <xs:complexType name="aheadType" mixed="true">
    <xs:sequence>
      <xs:group ref="valueTag" minOccurs="0" />
    </xs:sequence>
    <xs:attribute name="burn" type="xs:string" use="optional" />
  </xs:complexType>

  <!-- release: the objects (a pool, a group) to put back in play, and how many (one if left out). -->
  <xs:complexType name="releaseType" mixed="true">
    <xs:sequence>
      <xs:group ref="valueTag" minOccurs="0" />
    </xs:sequence>
    <xs:attribute name="object" type="xs:string" use="required" />
  </xs:complexType>

  <xs:group name="command">
    <xs:choice>
      <xs:element name="bounce" type="emptyType" />
      <xs:element name="stick" type="emptyType" />
      <xs:element name="wrap" type="emptyType" />
      <xs:element name="carry" type="emptyType" />
      <xs:element name="die" type="emptyType" />
      <xs:element name="stop" type="emptyType" />
      <xs:element name="reset">
        <xs:complexType>
          <xs:attribute name="object" type="xs:string" use="optional" />
        </xs:complexType>
      </xs:element>
      <xs:element name="inc" type="variableReferenceType" />
      <xs:element name="dec" type="variableReferenceType" />
      <xs:element name="move" type="stepType" />
      <xs:element name="hop" type="stepType" />
      <xs:element name="accelerate" type="thrustType" />
      <xs:element name="turn" type="turnType" />
      <xs:element name="thrust" type="aheadType" />
      <xs:element name="release" type="releaseType" />
      <xs:element name="push">
        <xs:complexType>
          <xs:attribute name="state" type="xs:string" use="required" />
        </xs:complexType>
      </xs:element>
      <xs:element name="pop" type="emptyType" />
      <xs:element name="fire">
        <xs:complexType>
          <xs:attribute name="object" type="xs:string" use="required" />
        </xs:complexType>
      </xs:element>
      <xs:element name="trigger">
        <xs:complexType>
          <xs:attribute name="object" type="xs:string" use="required" />
          <xs:attribute name="action" type="xs:string" use="required" />
        </xs:complexType>
      </xs:element>
    </xs:choice>
  </xs:group>

  <!--
    A collision rule. With edge it is about a screen edge; without, it is about
    another object, narrowed by class and/or object (neither: anything), and
    passed over while the object is touching something of the unless class. A
    rule about another object may also begin with slower and/or faster, which
    hold it to when the object's own speed is under (slower) or at least
    (faster) that number.
  -->
  <xs:complexType name="collisionType">
    <xs:sequence>
      <xs:element name="slower" type="valueType" minOccurs="0" />
      <xs:element name="faster" type="valueType" minOccurs="0" />
      <xs:group ref="command" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
    <xs:attribute name="edge" type="edgeType" use="optional" />
    <xs:attribute name="class" type="xs:string" use="optional" />
    <xs:attribute name="object" type="xs:string" use="optional" />
    <xs:attribute name="unless" type="xs:string" use="optional" />
  </xs:complexType>

  <!-- A named thing an object can do, run by a <trigger>. -->
  <xs:complexType name="actionType">
    <xs:sequence>
      <xs:group ref="command" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" use="required" />
  </xs:complexType>

  <!-- What pressing a key does in a state. -->
  <xs:complexType name="inputType">
    <xs:sequence>
      <xs:group ref="command" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
    <xs:attribute name="button" type="xs:string" use="required" />
  </xs:complexType>

  <!--
    Checked every frame. One test, then the commands to run when it holds:
    atleast / atmost read the variable named by the variable attribute off
    every object that matches class and/or object; remaining counts the
    matching objects still in play.
  -->
  <xs:complexType name="conditionType">
    <xs:sequence>
      <xs:choice>
        <xs:element name="atleast" type="valueType" />
        <xs:element name="atmost" type="valueType" />
        <xs:element name="remaining" type="valueType" />
      </xs:choice>
      <xs:group ref="command" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
    <xs:attribute name="class" type="xs:string" use="optional" />
    <xs:attribute name="object" type="xs:string" use="optional" />
    <xs:attribute name="variable" type="xs:string" use="optional" />
  </xs:complexType>

  <!-- Some of two numbers: a group's or a member's <position> and <velocity>. -->
  <xs:complexType name="partialVector2Type">
    <xs:sequence>
      <xs:element name="x" type="valueType" minOccurs="0" />
      <xs:element name="y" type="valueType" minOccurs="0" />
    </xs:sequence>
  </xs:complexType>

  <!-- How an object's shape is tested: as a box (the default), or by the pixels drawn. -->
  <xs:simpleType name="collisionShapeType">
    <xs:restriction base="xs:string">
      <xs:enumeration value="box" />
      <xs:enumeration value="pixel" />
    </xs:restriction>
  </xs:simpleType>

  <!-- Whether collisions are on, whether the object moves in lockstep with the others of its grid or group, how its shape is tested, then the rules. -->
  <xs:complexType name="collisionsType">
    <xs:sequence>
      <xs:element name="enabled" type="xs:boolean" />
      <xs:element name="lockstep" type="xs:boolean" minOccurs="0" />
      <xs:element name="type" type="collisionShapeType" minOccurs="0" />
      <xs:element name="collision" type="collisionType" minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="actionsType">
    <xs:sequence>
      <xs:element name="action" type="actionType" maxOccurs="unbounded" />
    </xs:sequence>
  </xs:complexType>

  <!-- The variables an object owns. -->
  <xs:complexType name="variablesType">
    <xs:sequence>
      <xs:element name="variable" type="variableType" maxOccurs="unbounded" />
    </xs:sequence>
  </xs:complexType>

  <!-- Anything that can be drawn, moved or collided with. -->
  <xs:complexType name="objectType">
    <xs:sequence>
      <xs:element name="sprite" type="spriteType" maxOccurs="unbounded" />
      <xs:element name="animation" type="animationType" minOccurs="0" />
      <xs:element name="position" type="vector2Type" />
      <xs:element name="velocity" type="vector2Type" />
      <xs:element name="acceleration" type="vector2Type" minOccurs="0" />
      <xs:element name="heading" type="valueType" minOccurs="0" />
      <xs:element name="drag" type="valueType" minOccurs="0" />
      <xs:element name="hidden" type="xs:boolean" minOccurs="0" />
      <xs:element name="collisions" type="collisionsType" />
      <xs:element name="actions" type="actionsType" minOccurs="0" />
      <xs:element name="variables" type="variablesType" minOccurs="0" />
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" use="required" />
    <xs:attribute name="class" type="xs:string" use="optional" />
  </xs:complexType>

  <!--
    Objects that share a description: a lane of logs or cars, the pads at the
    top of Frogger, a row of debris. The group says what its members have in
    common (all of it optional) and each member says only what is its own; what
    a member leaves out it takes from its group, and after that it must be as
    complete as an object. A member's <position> and <velocity> can give just
    an <x> or a <y>. The group's class is every member's. A member is called
    name="..." if it says so, otherwise the group's name, a dot, and its number
    counting from 1 (lane3.2), and the group's name means all of them.
  -->
  <xs:complexType name="memberType">
    <xs:sequence>
      <xs:element name="sprite" type="spriteType" minOccurs="0" maxOccurs="unbounded" />
      <xs:element name="animation" type="animationType" minOccurs="0" />
      <xs:element name="position" type="partialVector2Type" minOccurs="0" />
      <xs:element name="velocity" type="partialVector2Type" minOccurs="0" />
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" use="optional" />
  </xs:complexType>

  <xs:complexType name="groupType">
    <xs:sequence>
      <xs:element name="sprite" type="spriteType" minOccurs="0" maxOccurs="unbounded" />
      <xs:element name="animation" type="animationType" minOccurs="0" />
      <xs:element name="position" type="partialVector2Type" minOccurs="0" />
      <xs:element name="velocity" type="partialVector2Type" minOccurs="0" />
      <xs:element name="hidden" type="xs:boolean" minOccurs="0" />
      <xs:element name="collisions" type="collisionsType" />
      <xs:element name="actions" type="actionsType" minOccurs="0" />
      <xs:element name="variables" type="variablesType" minOccurs="0" />
      <xs:element name="member" type="memberType" maxOccurs="unbounded" />
    </xs:sequence>
    <xs:attribute name="name" type="xs:string" use="required" />
    <xs:attribute name="class" type="xs:string" use="optional" />
  </xs:complexType>

  <xs:element name="game">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="window">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="width" type="xs:unsignedShort" />
              <xs:element name="height" type="xs:unsignedShort" />
              <xs:element name="background" type="xs:string" />
              <xs:element name="fullscreen" type="xs:boolean" />
              <xs:element name="framerate" type="xs:unsignedByte" />
            </xs:sequence>
            <xs:attribute name="name" type="xs:string" use="required" />
          </xs:complexType>
        </xs:element>
        <xs:element name="variables">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="variable" type="variableType" maxOccurs="unbounded" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="objects">
          <xs:complexType>
            <xs:choice maxOccurs="unbounded">
              <xs:element name="object" type="objectType" />
              <xs:element name="group" type="groupType" />
            </xs:choice>
          </xs:complexType>
        </xs:element>
        <xs:element name="states">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="state" maxOccurs="unbounded">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="shows">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="show" maxOccurs="unbounded">
                            <xs:complexType>
                              <xs:attribute name="object" type="xs:string" use="required" />
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                    <xs:element name="inputs">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="input" type="inputType" maxOccurs="unbounded" />
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                    <xs:element name="conditions" minOccurs="0">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="condition" type="conditionType" maxOccurs="unbounded" />
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                  <xs:attribute name="name" type="xs:string" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>
