create_kart_properties.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. #!/usr/bin/env python3
  2. #
  3. # SuperTuxKart - a fun racing game with go-kart
  4. # Copyright (C) 2006-2015 SuperTuxKart-Team
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License
  8. # as published by the Free Software Foundation; either version 3
  9. # of the License, or (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. # This script creates code for the characteristics.
  20. # It takes an argument that specifies what the output of the script should be.
  21. # The output options can be seen by running this script without arguments.
  22. # A more convenient version to update the code is to run update_characteristics.py
  23. import sys
  24. # Input data
  25. # Each line contains a topic and the attributes of that topic.
  26. # This model is used for the xml file and to access the kart properties in the code.
  27. characteristics = """Suspension: stiffness, rest, travel, expSpringResponse(bool), maxForce
  28. Stability: rollInfluence, chassisLinearDamping, chassisAngularDamping, downwardImpulseFactor, trackConnectionAccel, smoothFlyingImpulse
  29. Turn: radius(InterpolationArray), timeResetSteer, timeFullSteer(InterpolationArray)
  30. Engine: power, maxSpeed, brakeFactor, brakeTimeIncrease, maxSpeedReverseRatio
  31. Gear: switchRatio(std::vector<float>/floatVector), powerIncrease(std::vector<float>/floatVector)
  32. Mass
  33. Wheels: dampingRelaxation, dampingCompression
  34. Camera: distance, forwardUpAngle, backwardUpAngle
  35. Jump: animationTime
  36. Lean: max, speed
  37. Anvil: duration, weight, speedFactor
  38. Parachute: friction, duration, durationOther, lboundFraction, uboundFraction, maxSpeed
  39. Bubblegum: duration, speedFraction, torque, fadeInTime, shieldDuration
  40. Zipper: duration, force, speedGain, maxSpeedIncrease, fadeOutTime
  41. Swatter: duration, distance, squashDuration, squashSlowdown
  42. Plunger: bandMaxLength, bandForce, bandDuration, bandSpeedIncrease, bandFadeOutTime, inFaceTime
  43. Startup: time(std::vector<float>/floatVector), boost(std::vector<float>/floatVector)
  44. Rescue: duration, vertOffset, height
  45. Explosion: duration, radius, invulnerabilityTime
  46. Nitro: duration, engineForce, consumption, smallContainer, bigContainer, maxSpeedIncrease, fadeOutTime, max
  47. Slipstream: duration, length, width, collectTime, useTime, addPower, minSpeed, maxSpeedIncrease, fadeOutTime
  48. Skid: increase, decrease, max, timeTillMax, visual, visualTime, revertVisualTime, minSpeed, timeTillBonus(std::vector<float>/floatVector), bonusSpeed(std::vector<float>/floatVector), bonusTime(std::vector<float>/floatVector), bonusForce(std::vector<float>/floatVector), physicalJumpTime, graphicalJumpTime, postSkidRotateFactor, reduceTurnMin, reduceTurnMax, enabled(bool)"""
  49. """ A GroupMember is an attribute of a group.
  50. In the xml files, a value will be assigned to it.
  51. If the name of the attribute is 'value', the getter method will only
  52. contain the group name and 'value' will be omitted (e.g. used for mass). """
  53. class GroupMember:
  54. def __init__(self, name, typeC, typeStr):
  55. self.name = name
  56. if name == "value":
  57. self.getName = ""
  58. else:
  59. self.getName = name
  60. self.typeC = typeC
  61. self.typeStr = typeStr
  62. """ E.g. power(std::vector<float>/floatVector)
  63. or speed(InterpolationArray)
  64. The default type is float
  65. The name 'value' is special: Only the group name will be used to access
  66. the member but in the xml file it will be still value (because we
  67. need a name). """
  68. def parse(content):
  69. typeC = "float"
  70. typeStr = typeC
  71. name = content.strip()
  72. pos = content.find("(")
  73. end = content.find(")", pos)
  74. if pos != -1 and end != -1:
  75. name = content[:pos].strip()
  76. pos2 = content.find("/", pos, end)
  77. if pos2 != -1:
  78. typeC = content[pos + 1:pos2].strip()
  79. typeStr = content[pos2 + 1:end].strip()
  80. else:
  81. typeC = content[pos + 1:end].strip()
  82. typeStr = typeC
  83. return GroupMember(name, typeC, typeStr)
  84. """ A Group has a base name and can contain GroupMembers.
  85. In the xml files, a group is a tag. """
  86. class Group:
  87. def __init__(self, baseName):
  88. self.baseName = baseName
  89. self.members = []
  90. """ Parses and adds a member to this group """
  91. def addMember(self, content):
  92. self.members.append(GroupMember.parse(content))
  93. def getBaseName(self):
  94. return self.baseName
  95. """ E.g. engine: power, gears(std::vector<Gear>/Gears)
  96. or mass(float) or only mass """
  97. def parse(content):
  98. pos = content.find(":")
  99. if pos == -1:
  100. group = Group(content)
  101. group.addMember("value")
  102. return group
  103. else:
  104. group = Group(content[:pos].strip())
  105. for m in content[pos + 1:].split(","):
  106. group.addMember(m)
  107. return group
  108. """ Creates a list of words from a titlecase string """
  109. def toList(name):
  110. result = []
  111. cur = ""
  112. for c in name:
  113. if c.isupper() and len(cur) != 0:
  114. result.append(cur)
  115. cur = ""
  116. cur += c.lower()
  117. if len(cur) != 0:
  118. result.append(cur)
  119. return result
  120. """ titleCase: true = result is titlecase
  121. false = result has underscores """
  122. def joinSubName(group, member, titleCase):
  123. words = toList(group.baseName) + toList(member.getName)
  124. first = True
  125. if titleCase:
  126. words = [w.title() for w in words]
  127. return "".join(words)
  128. else:
  129. return "_".join(words)
  130. # Functions to generate code
  131. def createEnum(groups):
  132. for g in groups:
  133. print()
  134. print(" // {0}".format(g.getBaseName().title()))
  135. for m in g.members:
  136. print(" {0},".format(joinSubName(g, m, False).upper()))
  137. def createAcDefs(groups):
  138. for g in groups:
  139. print()
  140. for m in g.members:
  141. nameTitle = joinSubName(g, m, True)
  142. nameUnderscore = joinSubName(g, m, False)
  143. typeC = m.typeC
  144. print(" {0} get{1}() const;".
  145. format(typeC, nameTitle, nameUnderscore))
  146. def createAcGetter(groups):
  147. for g in groups:
  148. for m in g.members:
  149. nameTitle = joinSubName(g, m, True)
  150. nameUnderscore = joinSubName(g, m, False)
  151. typeC = m.typeC
  152. result = "result"
  153. print("""// ----------------------------------------------------------------------------
  154. {3} AbstractCharacteristic::get{1}() const
  155. {{
  156. {0} result;
  157. bool is_set = false;
  158. process({2}, &result, &is_set);
  159. if (!is_set)
  160. Log::fatal("AbstractCharacteristic", "Can't get characteristic %s",
  161. getName({2}).c_str());
  162. return {4};
  163. }} // get{1}
  164. """.format(m.typeC, nameTitle, nameUnderscore.upper(), typeC, result))
  165. def createKpDefs(groups):
  166. for g in groups:
  167. print()
  168. for m in g.members:
  169. nameTitle = joinSubName(g, m, True)
  170. nameUnderscore = joinSubName(g, m, False)
  171. typeC = m.typeC
  172. print(" {0} get{1}() const;".
  173. format(typeC, nameTitle, nameUnderscore))
  174. def createKpGetter(groups):
  175. for g in groups:
  176. for m in g.members:
  177. nameTitle = joinSubName(g, m, True)
  178. nameUnderscore = joinSubName(g, m, False)
  179. typeC = m.typeC
  180. result = "result"
  181. print("""// ----------------------------------------------------------------------------
  182. {1} KartProperties::get{0}() const
  183. {{
  184. return m_cached_characteristic->get{0}();
  185. }} // get{0}
  186. """.format(nameTitle, typeC))
  187. def createGetType(groups):
  188. for g in groups:
  189. for m in g.members:
  190. nameTitle = joinSubName(g, m, True)
  191. nameUnderscore = joinSubName(g, m, False)
  192. print(" case {0}:\n return TYPE_{1};".
  193. format(nameUnderscore.upper(), "_".join(toList(m.typeStr)).upper()))
  194. def createGetName(groups):
  195. for g in groups:
  196. for m in g.members:
  197. nameTitle = joinSubName(g, m, True)
  198. nameUnderscore = joinSubName(g, m, False).upper()
  199. print(" case {0}:\n return \"{0}\";".
  200. format(nameUnderscore))
  201. def createLoadXml(groups):
  202. for g in groups:
  203. print(" if (const XMLNode *sub_node = node->getNode(\"{0}\"))\n {{".
  204. format(g.baseName.lower()))
  205. for m in g.members:
  206. nameUnderscore = joinSubName(g, m, False)
  207. nameMinus = "-".join(toList(m.name))
  208. print(""" sub_node->get(\"{0}\",
  209. &m_values[{1}]);""".
  210. format(nameMinus, nameUnderscore.upper()))
  211. print(" }\n")
  212. # Dicionary that maps an argument string to a tupel of
  213. # a generator function, a help string and a filename
  214. functions = {
  215. "enum": (createEnum, "List the enum values for all characteristics", "karts/abstract_characteristic.hpp"),
  216. "acdefs": (createAcDefs, "Create the header function definitions", "karts/abstract_characteristic.hpp"),
  217. "acgetter": (createAcGetter, "Implement the getters", "karts/abstract_characteristic.cpp"),
  218. "getType": (createGetType, "Implement the getType function", "karts/abstract_characteristic.cpp"),
  219. "getName": (createGetName, "Implement the getName function", "karts/abstract_characteristic.cpp"),
  220. "kpdefs": (createKpDefs, "Create the header function definitions for the getters", "karts/kart_properties.hpp"),
  221. "kpgetter": (createKpGetter, "Implement the getters", "karts/kart_properties.cpp"),
  222. "loadXml": (createLoadXml, "Code to load the characteristics from an xml file", "karts/xml_characteristic.hpp"),
  223. }
  224. def main():
  225. # Find out what to do
  226. if len(sys.argv) != 2:
  227. print("""Usage: ./create_kart_properties.py <operation>
  228. Operations:""")
  229. maxOperationLength = 0
  230. maxDescriptionLength = 0
  231. for o, f in functions.items():
  232. l = len(o)
  233. if l > maxOperationLength:
  234. maxOperationLength = l
  235. l = len(f[1])
  236. if l > maxDescriptionLength:
  237. maxDescriptionLength = l
  238. formatString = " {{0:{0}}} {{1:{1}}} in {{2}}".format(maxOperationLength, maxDescriptionLength)
  239. for o, f in functions.items():
  240. print(formatString.format(o, f[1], f[2]))
  241. return
  242. task = sys.argv[1]
  243. if task not in functions:
  244. print("The wanted operation was not found. Please call this script without arguments to list available arguments.")
  245. return
  246. # Parse properties
  247. groups = [Group.parse(line) for line in characteristics.split("\n")]
  248. # Create the wanted code
  249. functions[task][0](groups)
  250. if __name__ == '__main__':
  251. main()