1 # This file is part of NIT (http://www.nitlanguage.org).
3 # Copyright 2011-2013 Alexis Laferrière <alexis.laf@xymus.net>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Game framework with an emphasis on efficient event coordination
19 # Provides basic game logic entities to manage a game where the logic is executed by turns:
20 # `Game`, `GameTurn`, `GameEvent`, `Turnable`.
21 # Also offers a bucket system to plan future events at a known number of turns in the future:
22 # `Bucketable` and the services `act_next` and `act_in`.
24 # Efficiently support large number of entities with rare or sparse actions,
25 # such as a forest with many individual trees growing slowly.
26 module bucketed_game
is serialize
30 # Something acting on the game
31 abstract class Turnable[G
: Game]
33 # Execute `turn` for this instance.
34 fun do_turn
(turn
: GameTurn[G
]) is abstract
37 # Something acting on the game from time to time
38 abstract class Bucketable[G
: Game]
41 private var act_at
: nullable Int = null
43 # Cancel the previously registered acting turn
45 # Once called, `self.do_turn` will not be invoked until `GameTurn::act_next`
46 # or `GameTurn::act_in` are called again.
47 fun cancel_act
do act_at
= null
50 # Optimized organization of `Bucketable` instances
51 class Buckets[G
: Game]
54 # Bucket type used in this implementation.
55 type BUCKET: HashSet[Bucketable[G
]]
57 private var next_bucket
: nullable BUCKET = null
58 private var current_bucket_key
: Int = -1
60 # Number of `buckets`, default at 100
62 # Must be set prior to using any other methods of this class.
65 private var buckets
: Array[BUCKET] =
66 [for b
in n_buckets
.times
do new HashSet[Bucketable[G
]]] is lazy
68 # Add the Bucketable event `e` at `at_tick`.
69 fun add_at
(e
: Bucketable[G
], at_tick
: Int)
71 var at_key
= key_for_tick
(at_tick
)
73 if at_key
== current_bucket_key
then
74 next_bucket
.as(not null).add
(e
)
76 buckets
[at_key
].add
(e
)
82 private fun key_for_tick
(at_tick
: Int): Int
84 return at_tick
% buckets
.length
87 redef fun do_turn
(turn
: GameTurn[G
])
89 current_bucket_key
= key_for_tick
(turn
.tick
)
90 var current_bucket
= buckets
[current_bucket_key
]
92 var next_bucket
= new HashSet[Bucketable[G
]]
93 buckets
[current_bucket_key
] = next_bucket
94 self.next_bucket
= next_bucket
96 for e
in current_bucket
do
98 if act_at
!= null then
99 if turn
.tick
== act_at
then
101 else if act_at
> turn
.tick
and
102 key_for_tick
(act_at
) == current_bucket_key
114 # Apply `self` to `game` logic.
115 fun apply
( game
: ThinGame ) is abstract
118 # Event raised at the first turn
123 # Game logic on the client
126 # Game tick when `self` should act.
129 var tick
: Int = 0 is protected writable
132 # Game turn on the client
133 class ThinGameTurn[G
: ThinGame]
135 # Game tick when `self` should act.
136 var tick
: Int is protected writable
138 # Game events occurred for `self`.
139 var events
= new Array[GameEvent] is protected writable
142 # Game turn on the full logic
143 class GameTurn[G
: Game]
144 super ThinGameTurn[G
]
146 # Game that `self` belongs to.
149 # Create a new game turn for `game`.
150 init (game
: G
) is old_style_init
do
155 # Insert the Bucketable event `e` to be executed at next tick.
156 fun act_next
(e
: Bucketable[G
]) do game
.buckets
.add_at
(e
, tick
+ 1)
158 # Insert the Bucketable event `e` to be executed at tick `t`.
159 fun act_in
(e
: Bucketable[G
], t
: Int) do game
.buckets
.add_at
(e
, tick
+ t
)
161 # Add and `apply` a game `event`.
162 fun add_event
( event
: GameEvent )
173 # Game type used in this implementation.
176 # Bucket list in this game.
177 var buckets
: Buckets[G
] = new Buckets[G
]
179 # Last turn executed in this game
180 # Can be used to consult the latest events (by the display for example),
181 # but cannot be used to add new Events.
182 var last_turn
: nullable ThinGameTurn[G
] = null
184 # Execute and return a new GameTurn.
186 # This method calls `do_pre_turn` before executing the GameTurn
187 # and `do_post_turn` after.
188 fun do_turn
: GameTurn[G
]
190 var turn
= new GameTurn[G
](self)
193 buckets
.do_turn
(turn
)
203 # Execute something before executing the current GameTurn.
205 # Should be redefined by clients to add a pre-turn behavior.
206 # See `Game::do_turn`.
207 fun do_pre_turn
(turn
: GameTurn[G
]) do end
209 # Execute something after executing the current GameTurn.
211 # Should be redefined by clients to add a post-turn behavior.
212 # See `Game::do_turn`.
213 fun do_post_turn
(turn
: GameTurn[G
]) do end