cdedc32fa534d9692340fcf7c88cf572e533afa0
[nit.git] / lib / bucketed_game.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
3 # Copyright 2011-2013 Alexis Laferrière <alexis.laf@xymus.net>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # Provides basic game logic utilities using buckets to coordinate and
18 # optimize actions on game turn ends. Supports both action at each
19 # end of turn as well as actions on some end of turns.
20 #
21 # Allows for fast support of a large number of entities with rare actions,
22 # such as a forest with many individual trees.
23 module bucketed_game
24
25 # Something acting on the game
26 class Turnable[G: Game]
27 fun do_turn(turn: GameTurn[G]) is abstract
28 end
29
30 # Something acting on the game from time to time
31 class Bucketable[G: Game]
32 super Turnable[G]
33 private var act_at: Int = 0
34 end
35
36 # Optiomized organization of `Bucketable` instances
37 class Buckets[G: Game]
38 super Turnable[G]
39 type BUCKET: HashSet[Bucketable[G]]
40
41 private var buckets: Array[BUCKET] is noinit
42
43 private var next_bucket: nullable BUCKET = null
44 private var current_bucket_key: Int = -1
45
46 init
47 do
48 var n_buckets = 100
49 buckets = new Array[BUCKET].with_capacity(n_buckets)
50
51 for b in [0 .. n_buckets [do
52 buckets[b] = new HashSet[Bucketable[G]]
53 end
54 end
55
56 fun add_at(e: Bucketable[G], at_tick: Int)
57 do
58 var at_key = key_for_tick(at_tick)
59
60 if at_key == current_bucket_key then
61 next_bucket.as(not null).add(e)
62 else
63 buckets[at_key].add(e)
64 end
65
66 e.act_at = at_tick
67 end
68
69 private fun key_for_tick(at_tick: Int): Int
70 do
71 return at_tick % buckets.length
72 end
73
74 redef fun do_turn(turn: GameTurn[G])
75 do
76 current_bucket_key = key_for_tick(turn.tick)
77 var current_bucket = buckets[current_bucket_key]
78
79 next_bucket = new HashSet[Bucketable[G]]
80
81 for e in current_bucket do
82 if e.act_at == turn.tick then
83 e.do_turn(turn)
84 else if e.act_at > turn.tick and
85 key_for_tick(e.act_at) == current_bucket_key
86 then
87 next_bucket.as(not null).add(e)
88 end
89 end
90
91 buckets[current_bucket_key] = next_bucket.as(not null)
92 end
93 end
94
95 # Game related event
96 interface GameEvent
97 fun apply( game : ThinGame ) is abstract
98 end
99
100 # Event raised at the first turn
101 class FirstTurnEvent
102 super GameEvent
103 end
104
105 # Game logic on the client
106 class ThinGame
107 var tick: Int protected writable = 0
108 end
109
110 # Game turn on the client
111 class ThinGameTurn[G: ThinGame]
112 var tick: Int protected writable = 0
113
114 var events: List[GameEvent] protected writable = new List[GameEvent]
115
116 init (t: Int) do tick = t
117 end
118
119 # Game turn on the full logic
120 class GameTurn[G: Game]
121 super ThinGameTurn[G]
122 var game: G
123
124 init (g: G)
125 do
126 super(g.tick)
127 game = g
128 end
129
130 fun act_next(e: Bucketable[G])
131 do
132 game.buckets.add_at(e, tick + 1)
133 end
134
135 fun act_in(e: Bucketable[G], t: Int)
136 do
137 game.buckets.add_at(e, tick + t)
138 end
139
140 fun add_event( event : GameEvent )
141 do
142 event.apply( game )
143 events.add( event )
144 end
145 end
146
147 # Full game logic
148 class Game
149 super ThinGame
150 type G: Game
151
152 var buckets: Buckets[G] = new Buckets[G]
153
154 # Last turn executed in this game
155 # Can be used to consult the latest events (by the display for example),
156 # but cannot be used to add new Events.
157 var last_turn: nullable ThinGameTurn[G] = null
158
159 init do end
160
161 fun do_turn: GameTurn[G]
162 do
163 var turn = new GameTurn[G](self)
164
165 do_pre_turn(turn)
166 buckets.do_turn(turn)
167 do_post_turn(turn)
168
169 last_turn = turn
170
171 tick += 1
172
173 return turn
174 end
175
176 fun do_pre_turn(turn: GameTurn[G]) do end
177 fun do_post_turn(turn: GameTurn[G]) do end
178 end