+++ /dev/null
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Pure game logic, independent of gamnit and other display concerns
-module game_logic
-
-import geometry::points_and_lines
-
-# Root of all entities of a single play
-class World
-
- # Number of original asteroids per play
- var n_asteroids: Int
-
- # Number of parts created when an asteroid explodes
- var n_asteroid_parts: Int
-
- # Ratio of the world: height / width
- var ratio_height_width: Float
-
- # Minimum half size of the world, applied either to `half_width` of `half_height`
- private var min_half_size = 500.0
-
- # Width of the world
- var half_width: Float = if ratio_height_width <= 1.0 then
- min_half_size
- else min_half_size * ratio_height_width is lazy
-
- # Height of the world
- var half_height: Float = if ratio_height_width >= 1.0 then
- min_half_size
- else min_half_size / ratio_height_width is lazy
-
- # Player's ship
- var ship = new Ship(self)
-
- # All live spacial objects
- var objects = new Array[SpacialObject].with_items(ship)
-
- # All live asteroids
- var asteroids = new Array[Asteroid]
-
- # All live bullets
- var bullets = new Array[SpacialObject]
-
- init
- do
- for a in n_asteroids.times do
- var asteroid = new Asteroid(self, 3)
- asteroid.center.x = half_width - 2.0*half_width.rand
- asteroid.center.y = half_height - 2.0*half_height.rand
- asteroid.rotation_inertia = 0.5 - 1.0.rand
-
- objects.add asteroid
- asteroids.add asteroid
- end
- end
-
- # Execute a turn that took `dt` seconds
- fun do_turn(dt: Float)
- do
- for object in objects do object.do_turn dt
-
- for object in objects.to_a do if not object isa Asteroid then
-
- for asteroid in asteroids.to_a do
- var d2 = object.center.dist2(asteroid.center)
-
- var r = object.radius + asteroid.radius
- if d2 < r*r then
- # Boom
- if object == ship then
- # The ship is invincible
- # TODO health and losing
- else
- object.destroy
- end
-
- asteroid.destroy
- break
- end
- end
- end
- end
-end
-
-# Physical object in space physics
-abstract class SpacialObject
-
- # World in which this object belongs
- var world: World
-
- # Current position
- var center = new Point3d[Float](0.0, 0.0, 0.0)
-
- # Position inertia, applied on `center` at each `do_turn`
- var inertia = new Point3d[Float](0.0, 0.0, 0.0)
-
- # Current rotation
- var rotation = 0.0
-
- # Rotation inertia, applied on `rotation` at each `do_turn`
- var rotation_inertia = 0.0
-
- # Rotation force, currently applied by the pilot
- var applied_rotation = 0.0 is writable
-
- # Thrust force, currently applied by the pilot
- var applied_thrust = 0.0 is writable
-
- # Radius of this object for collision detection
- var radius: Float is noinit
-
- # New instance copying the data from `other` with an optional `variation`
- init copy(other: SpacialObject, variation: nullable Float)
- do
- init other.world
-
- if variation == null then variation = 0.0
-
- center.x = other.center.x
- center.y = other.center.y
- center.z = other.center.z
-
- inertia.x = other.inertia.x + variation - 2.0*variation.rand
- inertia.y = other.inertia.y + variation - 2.0*variation.rand
-
- rotation = other.rotation
- if variation != 0.0 then rotation = 2.0 * pi.rand
- end
-
- # Apply `thrust` forward on this object
- fun apply_thrust(thrust: Float)
- do
- inertia.x += thrust * rotation.cos
- inertia.y += thrust * rotation.sin
- end
-
- # Execute a turn that took `dt` seconds
- fun do_turn(dt: Float)
- do
- # Forces to inertia
- var t = applied_thrust * 5.0
- inertia.x += t * rotation.cos
- inertia.y += t * rotation.sin
-
- # Arcade rotation
- var r = applied_rotation * 0.05
- rotation += r
-
- # Realistic rotation, kept for reference and reality minded individuals
- #var r = applied_rotation * 0.2
- #rotation_inertia += r
- #rotation_inertia = rotation_inertia.min(2.0).max(-2.0)
-
- # Inertia to position
- rotation += rotation_inertia * dt
- center.x += inertia.x * dt
- center.y += inertia.y * dt
- center.z += inertia.z * dt
-
- # Wrap objects so they stay in the screen
- while center.x < -world.half_width do center.x += 2.0 * world.half_width
- while center.x > world.half_width do center.x -= 2.0 * world.half_width
- while center.y < -world.half_height do center.y += 2.0 * world.half_height
- while center.y > world.half_height do center.y -= 2.0 * world.half_height
- end
-
- # Destroy this object
- fun destroy do world.objects.remove self
-end
-
-# Player's ship
-class Ship
- super SpacialObject
-
- init do radius = 20.0
-
- # Open fire forward
- fun fire
- do
- var bullet = new Bullet.copy(world.ship)
- bullet.center.z = -1.0 # in the background
- bullet.apply_thrust 500.0 # give a boost
-
- world.objects.add bullet
- world.bullets.add bullet
- end
-end
-
-# Asteroid, the main obstacle in this game
-class Asteroid
- super SpacialObject
-
- # Size of this asteroid, should be greater than 0
- var size: Int
-
- # Color, or type, on this asteroid
- var color: Int = 2.rand
-
- init
- do
- rotation_inertia = 0.5 - 1.0.rand
- radius = 22.5 * size.to_f
- end
-
- # New asteroid breaking off from `other`
- init break_off(other: Asteroid)
- do
- size = other.size - 1
- color = other.color
-
- copy(other, 60.0)
- end
-
- # Explode and break off this asteroid
- redef fun destroy
- do
- super
-
- world.asteroids.remove self
-
- if size == 1 then return # Do not break off
-
- for p in world.n_asteroid_parts.times do
- var asteroid = new Asteroid.break_off(self)
- asteroid.size = size - 1
- asteroid.color = color
-
- asteroid.inertia.x += 1.0 - 2.0.rand
- asteroid.inertia.y += 1.0 - 2.0.rand
-
- world.objects.add asteroid
- world.asteroids.add asteroid
- end
- end
-end
-
-# Bullet or beam fired from a `Ship`
-class Bullet
- super SpacialObject
-
- # Time left before this bullet expires
- var ttl = 5.0
-
- init do radius = 0.0
-
- redef fun do_turn(dt)
- do
- super
-
- ttl -= dt
- if ttl <= 0.0 then destroy
- end
-
- redef fun destroy
- do
- super
- world.bullets.remove self
- end
-end