import "./const.ecal" as const import "./helper.ecal" as hlp /* newGameWorld creates a new game world datastructure. */ func newGameWorld(name) { let ret := hlp.copyMap(DefaultGameWorld) ret["key"] := name ret["kind"] := const.ObjectKinds.ConfigurationObject return ret } DefaultGameWorld := { "screenWidth" : 1280, "screenHeight" : 1024, "screenElementWidth" : 1280, "screenElementHeight" : 1024, "backdrop" : "background_nebular" } /* newSpriteNode creates a new general sprite datastructure. */ func newSpriteNode(id, kind, x, y, dim=20, rot=0, speed=0) { let ret := hlp.copyMap(DefaultSpriteState) ret["key"] := id ret["kind"] := kind ret["id"] := id ret["x"] := x ret["y"] := y ret["dim"] := dim ret["rot"] := rot ret["speed"] := speed return ret } DefaultSpriteState := { /* A unique ID */ "id" : "", /* Sprite x position */ "x" : 20, /* Sprite y position */ "y" : 20, /* Dimensions of the sprite (box) */ "dim" : 20, /* Flag if the sprite is moving or static */ "isMoving" : true, /* Flag if the sprite is kept in the display or if it should be destroyed once it is outside of the visible area */ "displayLoop" : true, /* Turning direction (-1 for left, 1 for right, 0 no turning) */ "dir" : 0, /* Angle of rotation */ "rot" : 0, /* Rotation speed for each step (in radians) */ "rotSpeed" : math.Pi / 180, /* Moving direction (1 forward, -1 backwards, 0 no movement) */ "speed" : 0, /* Strafing direction of sprite (-1 left, 1 right, 0 no movement) */ "strafe" : 0, /* Move speed for each step */ "moveSpeed" : 0.21, /* Action handler funcion */ "doAction" : func (entity, action, engine) { }, /* Collision handler funcion */ "collision" : func (entity, otherEntity) { return [] } } PlayerState := { "lastBounce" : 0, /* Collision handler funcion */ "collision" : func (entity, otherEntity, engine) { return [entity] }, /* Action handler funcion */ "doAction" : func (entity, action, engine) { if action == "fire" { let sx := entity.x + math.cos(entity.rot) * 20 let sy := entity.y + math.sin(entity.rot) * 20 let sprites := engine.gameState[engine.part].sprites let sprite := newShot("shot-{{entity.id}}-{{math.floor(rand() * 1000)}}", sx, sy, entity.rot) sprite.displayLoop := false sprite.owner := entity.id mutex GameStateMutex { engine.gameState[engine.part].sprites := add(sprites, sprite) } sendAudioEvent({"audioEvent" : "shot", "player" : entity.id}, engine) } } } /* newPlayer creates a new player datastructure. */ func newPlayer(id, x, y) { base := newSpriteNode(id, const.ObjectKinds.Player, x, y) return hlp.copyMap(PlayerState, base) } ShotState := { /* Collision handler funcion */ "collision" : func (entity, otherEntity, engine) { if otherEntity.kind == const.ObjectKinds.Asteroid { addEvent("changescore", "main.gamescore", { "id" : entity.owner, "part" : engine.part, "changeFunc" : func (s) { s.score := s.score + 100 } }) } /* A shot colliding with anything removes the shot */ return [entity] }} /* newShot creates a new shot datastructure. */ func newShot(id, x, y, rot) { base := newSpriteNode(id, const.ObjectKinds.Shot, x, y, 10, rot, 0.05) return hlp.copyMap(ShotState, base) } AsteroidState := { "lastBounce" : 0, /* Collision handler funcion */ "collision" : func (entity, otherEntity, engine) { let ret := [] if otherEntity.kind == const.ObjectKinds.Asteroid { /* Asteroids bounce off each other */ if now() - entity.lastBounce > 1000000 { entity.rot := entity.rot + math.Pi if otherEntity.kind == const.ObjectKinds.Asteroid { otherEntity.rot := otherEntity.rot + math.Pi } /* Prevent further bouncing for some time on both objects */ entity.lastBounce := now() if otherEntity.kind == const.ObjectKinds.Asteroid { otherEntity.lastBounce := now() } } } elif otherEntity.kind == const.ObjectKinds.Shot { let sprites := engine.gameState[engine.part].sprites let newParticleAsteroid := func (counter) { /* Create a particle asteroid after a bigger asteroid has been shot */ let rot := counter % 3 * 45 + math.floor(rand() * 45) let dim := math.floor(entity.dim * 0.5) let radius := 10 + math.floor(rand() * 10) let speed := entity.speed + math.floor(2 + rand() * 3) / 1000 let newX := math.floor(entity.x + math.cos(rot) * radius) let newY := math.floor(entity.y + math.sin(rot) * radius) return newAsteroid("{{entity.id}}-{{counter}}", newX, newY, 5 + dim, rot, speed) } if entity.dim > 35 { for i in range(1, 4) { let sprite := newParticleAsteroid(i) sprite.lastBounce := now() sprites := add(sprites, sprite) } mutex GameStateMutex { engine.gameState[engine.part].sprites := sprites } sendAudioEvent({"audioEvent" : "explosion"}, engine) } else { sendAudioEvent({"audioEvent" : "vanish"}, engine) } ret := [entity] } return ret }, /* Action handler funcion */ "doAction" : func (entity, action, engine) { } } /* newAsteroid creates a new asteroid datastructure. */ func newAsteroid(id, x, y, dim=20, rot=0, speed=0) { base := newSpriteNode(id, const.ObjectKinds.Asteroid, x, y, dim, rot, speed) return hlp.copyMap(AsteroidState, base) } /* sendAudioEvent sends an audio event to the frontend. */ func sendAudioEvent(payload, engine) { for [commID, data] in engine.websocket { if data.gamename == engine.part { addEvent("AudioEvent", "db.web.sock.msg", {"commID" : commID, "payload" : payload}) } } }