1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Pure game logic, independent of gamnit and other display concerns
18 import geometry
::points_and_lines
20 # Root of all entities of a single play
23 # Number of original asteroids per play
26 # Number of parts created when an asteroid explodes
27 var n_asteroid_parts
: Int
29 # Ratio of the world: height / width
30 var ratio_height_width
: Float
32 # Minimum half size of the world, applied either to `half_width` of `half_height`
33 private var min_half_size
= 500.0
36 var half_width
: Float = if ratio_height_width
<= 1.0 then
38 else min_half_size
* ratio_height_width
is lazy
41 var half_height
: Float = if ratio_height_width
>= 1.0 then
43 else min_half_size
/ ratio_height_width
is lazy
46 var ship
= new Ship(self)
48 # All live spacial objects
49 var objects
= new Array[SpacialObject].with_items
(ship
)
52 var asteroids
= new Array[Asteroid]
55 var bullets
= new Array[SpacialObject]
59 for a
in n_asteroids
.times
do
60 var asteroid
= new Asteroid(self, 3)
61 asteroid
.center
.x
= half_width
- 2.0*half_width
.rand
62 asteroid
.center
.y
= half_height
- 2.0*half_height
.rand
63 asteroid
.rotation_inertia
= 0.5 - 1.0.rand
66 asteroids
.add asteroid
70 # Execute a turn that took `dt` seconds
71 fun do_turn
(dt
: Float)
73 for object
in objects
do object
.do_turn dt
75 for object
in objects
.to_a
do if not object
isa Asteroid then
77 for asteroid
in asteroids
.to_a
do
78 var d2
= object
.center
.dist2
(asteroid
.center
)
80 var r
= object
.radius
+ asteroid
.radius
83 if object
== ship
then
84 # The ship is invincible
85 # TODO health and losing
99 # Physical object in space physics
100 abstract class SpacialObject
102 # World in which this object belongs
106 var center
= new Point3d[Float](0.0, 0.0, 0.0)
108 # Position inertia, applied on `center` at each `do_turn`
109 var inertia
= new Point3d[Float](0.0, 0.0, 0.0)
114 # Rotation inertia, applied on `rotation` at each `do_turn`
115 var rotation_inertia
= 0.0
117 # Rotation force, currently applied by the pilot
118 var applied_rotation
= 0.0 is writable
120 # Thrust force, currently applied by the pilot
121 var applied_thrust
= 0.0 is writable
123 # Radius of this object for collision detection
124 var radius
: Float is noinit
126 # New instance copying the data from `other` with an optional `variation`
127 init copy
(other
: SpacialObject, variation
: nullable Float)
131 if variation
== null then variation
= 0.0
133 center
.x
= other
.center
.x
134 center
.y
= other
.center
.y
135 center
.z
= other
.center
.z
137 inertia
.x
= other
.inertia
.x
+ variation
- 2.0*variation
.rand
138 inertia
.y
= other
.inertia
.y
+ variation
- 2.0*variation
.rand
140 rotation
= other
.rotation
141 if variation
!= 0.0 then rotation
= 2.0 * pi
.rand
144 # Apply `thrust` forward on this object
145 fun apply_thrust
(thrust
: Float)
147 inertia
.x
+= thrust
* rotation
.cos
148 inertia
.y
+= thrust
* rotation
.sin
151 # Execute a turn that took `dt` seconds
152 fun do_turn
(dt
: Float)
155 var t
= applied_thrust
* 5.0
156 inertia
.x
+= t
* rotation
.cos
157 inertia
.y
+= t
* rotation
.sin
160 var r
= applied_rotation
* 0.05
163 # Realistic rotation, kept for reference and reality minded individuals
164 #var r = applied_rotation * 0.2
165 #rotation_inertia += r
166 #rotation_inertia = rotation_inertia.min(2.0).max(-2.0)
168 # Inertia to position
169 rotation
+= rotation_inertia
* dt
170 center
.x
+= inertia
.x
* dt
171 center
.y
+= inertia
.y
* dt
172 center
.z
+= inertia
.z
* dt
174 # Wrap objects so they stay in the screen
175 while center
.x
< -world
.half_width
do center
.x
+= 2.0 * world
.half_width
176 while center
.x
> world
.half_width
do center
.x
-= 2.0 * world
.half_width
177 while center
.y
< -world
.half_height
do center
.y
+= 2.0 * world
.half_height
178 while center
.y
> world
.half_height
do center
.y
-= 2.0 * world
.half_height
181 # Destroy this object
182 fun destroy
do world
.objects
.remove
self
189 init do radius
= 20.0
194 var bullet
= new Bullet.copy
(world
.ship
)
195 bullet
.center
.z
= -1.0 # in the background
196 bullet
.apply_thrust
500.0 # give a boost
198 world
.objects
.add bullet
199 world
.bullets
.add bullet
202 # Something hits the ship
206 # Asteroid, the main obstacle in this game
210 # Size of this asteroid, should be greater than 0
213 # Color, or type, on this asteroid
214 var color
: Int = 2.rand
218 rotation_inertia
= 0.5 - 1.0.rand
219 radius
= 22.5 * size
.to_f
222 # New asteroid breaking off from `other`
223 init break_off
(other
: Asteroid)
225 size
= other
.size
- 1
231 # Explode and break off this asteroid
236 world
.asteroids
.remove
self
238 if size
== 1 then return # Do not break off
240 for p
in world
.n_asteroid_parts
.times
do
241 var asteroid
= new Asteroid.break_off
(self)
242 asteroid
.size
= size
- 1
243 asteroid
.color
= color
245 asteroid
.inertia
.x
+= 1.0 - 2.0.rand
246 asteroid
.inertia
.y
+= 1.0 - 2.0.rand
248 world
.objects
.add asteroid
249 world
.asteroids
.add asteroid
254 # Bullet or beam fired from a `Ship`
258 # Time left before this bullet expires
263 redef fun do_turn
(dt
)
268 if ttl
<= 0.0 then destroy
274 world
.bullets
.remove
self