templates.ecal 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import "./const.ecal" as const
  2. import "./helper.ecal" as hlp
  3. /*
  4. newGameWorld creates a new game world datastructure.
  5. */
  6. func newGameWorld(name) {
  7. let ret := hlp.copyMap(DefaultGameWorld)
  8. ret["key"] := name
  9. ret["kind"] := const.ObjectKinds.ConfigurationObject
  10. return ret
  11. }
  12. DefaultGameWorld := {
  13. "screenWidth" : 1280,
  14. "screenHeight" : 1024,
  15. "screenElementWidth" : 1280,
  16. "screenElementHeight" : 1024,
  17. "backdrop" : "background_nebular"
  18. }
  19. /*
  20. newSpriteNode creates a new general sprite datastructure.
  21. */
  22. func newSpriteNode(id, kind, x, y, dim=20, rot=0, speed=0) {
  23. let ret := hlp.copyMap(DefaultSpriteState)
  24. ret["key"] := id
  25. ret["kind"] := kind
  26. ret["id"] := id
  27. ret["x"] := x
  28. ret["y"] := y
  29. ret["dim"] := dim
  30. ret["rot"] := rot
  31. ret["speed"] := speed
  32. return ret
  33. }
  34. DefaultSpriteState := {
  35. /* A unique ID */
  36. "id" : "",
  37. /* Sprite x position */
  38. "x" : 20,
  39. /* Sprite y position */
  40. "y" : 20,
  41. /* Dimensions of the sprite (box) */
  42. "dim" : 20,
  43. /* Flag if the sprite is moving or static */
  44. "isMoving" : true,
  45. /*
  46. Flag if the sprite is kept in the display or if it should be
  47. destroyed once it is outside of the visible area
  48. */
  49. "displayLoop" : true,
  50. /* Turning direction (-1 for left, 1 for right, 0 no turning) */
  51. "dir" : 0,
  52. /* Angle of rotation */
  53. "rot" : 0,
  54. /* Rotation speed for each step (in radians) */
  55. "rotSpeed" : math.Pi / 180,
  56. /* Moving direction (1 forward, -1 backwards, 0 no movement) */
  57. "speed" : 0,
  58. /* Strafing direction of sprite (-1 left, 1 right, 0 no movement) */
  59. "strafe" : 0,
  60. /* Move speed for each step */
  61. "moveSpeed" : 0.21,
  62. /* Action handler funcion */
  63. "doAction" : func (entity, action, engine) {
  64. },
  65. /* Collision handler funcion */
  66. "collision" : func (entity, otherEntity) {
  67. return []
  68. }
  69. }
  70. PlayerState := {
  71. "lastBounce" : 0,
  72. /* Collision handler funcion */
  73. "collision" : func (entity, otherEntity, engine) {
  74. return [entity]
  75. },
  76. /* Action handler funcion */
  77. "doAction" : func (entity, action, engine) {
  78. if action == "fire" {
  79. let sx := entity.x + math.cos(entity.rot) * 20
  80. let sy := entity.y + math.sin(entity.rot) * 20
  81. let sprites := engine.gameState[engine.part].sprites
  82. let sprite := newShot("shot-{{entity.id}}-{{math.floor(rand() * 1000)}}", sx, sy, entity.rot)
  83. sprite.displayLoop := false
  84. sprite.owner := entity.id
  85. mutex GameStateMutex {
  86. engine.gameState[engine.part].sprites := add(sprites, sprite)
  87. }
  88. sendAudioEvent({"audioEvent" : "shot", "player" : entity.id}, engine)
  89. }
  90. }
  91. }
  92. /*
  93. newPlayer creates a new player datastructure.
  94. */
  95. func newPlayer(id, x, y) {
  96. base := newSpriteNode(id, const.ObjectKinds.Player, x, y)
  97. return hlp.copyMap(PlayerState, base)
  98. }
  99. ShotState := {
  100. /* Collision handler funcion */
  101. "collision" : func (entity, otherEntity, engine) {
  102. if otherEntity.kind == const.ObjectKinds.Asteroid {
  103. addEvent("changescore", "main.gamescore", {
  104. "id" : entity.owner,
  105. "part" : engine.part,
  106. "changeFunc" : func (s) {
  107. s.score := s.score + 100
  108. }
  109. })
  110. }
  111. /* A shot colliding with anything removes the shot */
  112. return [entity]
  113. }}
  114. /*
  115. newShot creates a new shot datastructure.
  116. */
  117. func newShot(id, x, y, rot) {
  118. base := newSpriteNode(id, const.ObjectKinds.Shot, x, y, 10, rot, 0.05)
  119. return hlp.copyMap(ShotState, base)
  120. }
  121. AsteroidState := {
  122. "lastBounce" : 0,
  123. /* Collision handler funcion */
  124. "collision" : func (entity, otherEntity, engine) {
  125. let ret := []
  126. if otherEntity.kind == const.ObjectKinds.Asteroid {
  127. /*
  128. Asteroids bounce off each other
  129. */
  130. if now() - entity.lastBounce > 1000000 {
  131. entity.rot := entity.rot + math.Pi
  132. if otherEntity.kind == const.ObjectKinds.Asteroid {
  133. otherEntity.rot := otherEntity.rot + math.Pi
  134. }
  135. /* Prevent further bouncing for some time on both objects */
  136. entity.lastBounce := now()
  137. if otherEntity.kind == const.ObjectKinds.Asteroid {
  138. otherEntity.lastBounce := now()
  139. }
  140. }
  141. } elif otherEntity.kind == const.ObjectKinds.Shot {
  142. let sprites := engine.gameState[engine.part].sprites
  143. let newParticleAsteroid := func (counter) {
  144. /*
  145. Create a particle asteroid after a bigger asteroid has been shot
  146. */
  147. let rot := counter % 3 * 45 + math.floor(rand() * 45)
  148. let dim := math.floor(entity.dim * 0.5)
  149. let radius := 10 + math.floor(rand() * 10)
  150. let speed := entity.speed + math.floor(2 + rand() * 3) / 1000
  151. let newX := math.floor(entity.x + math.cos(rot) * radius)
  152. let newY := math.floor(entity.y + math.sin(rot) * radius)
  153. return newAsteroid("{{entity.id}}-{{counter}}", newX, newY, 5 + dim, rot, speed)
  154. }
  155. if entity.dim > 35 {
  156. for i in range(1, 4) {
  157. let sprite := newParticleAsteroid(i)
  158. sprite.lastBounce := now()
  159. sprites := add(sprites, sprite)
  160. }
  161. mutex GameStateMutex {
  162. engine.gameState[engine.part].sprites := sprites
  163. }
  164. sendAudioEvent({"audioEvent" : "explosion"}, engine)
  165. } else {
  166. sendAudioEvent({"audioEvent" : "vanish"}, engine)
  167. }
  168. ret := [entity]
  169. }
  170. return ret
  171. },
  172. /* Action handler funcion */
  173. "doAction" : func (entity, action, engine) {
  174. }
  175. }
  176. /*
  177. newAsteroid creates a new asteroid datastructure.
  178. */
  179. func newAsteroid(id, x, y, dim=20, rot=0, speed=0) {
  180. base := newSpriteNode(id, const.ObjectKinds.Asteroid, x, y, dim, rot, speed)
  181. return hlp.copyMap(AsteroidState, base)
  182. }
  183. /*
  184. sendAudioEvent sends an audio event to the frontend.
  185. */
  186. func sendAudioEvent(payload, engine) {
  187. for [commID, data] in engine.websocket {
  188. if data.gamename == engine.part {
  189. addEvent("AudioEvent", "db.web.sock.msg", {"commID" : commID, "payload" : payload})
  190. }
  191. }
  192. }