Skip to content
This repository
Browse code

Some work on comments, to make docco a bit happier.

  • Loading branch information...
commit 9acf3599d05722cfb5ebdb29da625441a403acff 1 parent 98491d6
Stéphan Kochen authored
8 src/client/base64.coffee
@@ -31,17 +31,17 @@ decodeBase64 = (input) ->
31 31 else if cc == 47 then 63 # /
32 32 else if cc == 61 then -1 # Padding
33 33 else throw new Error "Invalid base64 input character: #{c}"
34   - # Did we complete a quad?
35   - continue unless quadIndex == 3
36 34
37   - # Calculate the octet values.
  35 + # Did we complete a quad? If so, calculate the octet values and add them to the output.
38 36 # We take bits from the character values as follows: 000000 001111 111122 222222
  37 + continue unless quadIndex == 3
39 38 output[outputIndex++] = ((quad[0] & 0x3F) << 2) + ((quad[1] & 0x30) >> 4)
40 39 output[outputIndex++] = ((quad[1] & 0x0F) << 4) + ((quad[2] & 0x3C) >> 2) unless quad[2] == -1
41 40 output[outputIndex++] = ((quad[2] & 0x03) << 6) + ((quad[3] & 0x3F) ) unless quad[3] == -1
42 41
  42 + # Return output.
43 43 output
44 44
45 45
46   -# Exports.
  46 +#### Exports
47 47 exports.decodeBase64 = decodeBase64
9 src/client/brequire.coffee
@@ -2,6 +2,8 @@
2 2 # This version is slightly modified, and rewritten in CoffeeScript.
3 3
4 4
  5 +# The require function loads the module on-demand, or returns the existing `exports` object in case
  6 +# the module is already loaded.
5 7 require = (path) ->
6 8 unless m = require.modules[path]
7 9 throw "Couldn't find module for: #{path}"
@@ -12,8 +14,11 @@ require = (path) ->
12 14
13 15 m.exports
14 16
  17 +# Our index of modules.
15 18 require.modules = {}
16 19
  20 +# Helper used to create the `require` function used in the inner scope of the module. It takes
  21 +# care of making paths relative to the current module work as expected.
17 22 require.bind = (path, directory) ->
18 23 (p) ->
19 24 return require(p) unless p.charAt(0) == '.'
@@ -27,6 +32,8 @@ require.bind = (path, directory) ->
27 32
28 33 require cwd.join('/')
29 34
  35 +# The function used to define a module. Each module that is loaded into the browser should be
  36 +# wrapped with a call to this function.
30 37 require.module = (path, directory, fn) ->
31 38 fn.directory =
32 39 if typeof(directory) == 'boolean'
@@ -38,5 +45,5 @@ require.module = (path, directory, fn) ->
38 45 require.modules[path] = fn
39 46
40 47
41   -# Exports.
  48 +#### Exports
42 49 window.require = require
23 src/client/index.coffee
@@ -22,6 +22,9 @@ DefaultRenderer = require './renderer/offscreen_2d'
22 22 EverardIsland = require './everard'
23 23
24 24
  25 +
  26 +#### Common logic
  27 +
25 28 class BaseGame
26 29 constructor: ->
27 30 # Setup the key handlers.
@@ -51,12 +54,11 @@ class BaseGame
51 54 @renderer = new DefaultRenderer(@resources.images, @sim)
52 55 @sim.map.setView(@renderer)
53 56
54   - # Game loop.
  57 + ##### Game loop.
55 58
56 59 start: ->
57 60 return if @gameTimer?
58 61
59   - # Are we networked or not?
60 62 @tick()
61 63 @lastTick = Date.now()
62 64
@@ -76,7 +78,7 @@ class BaseGame
76 78 @lastTick += TICK_LENGTH_MS
77 79 @renderer.draw()
78 80
79   - # Abstract methods.
  81 + ##### Abstract methods.
80 82
81 83 # Called after resources are loaded.
82 84 startup: ->
@@ -89,6 +91,9 @@ class BaseGame
89 91 handleKeyup: (e) ->
90 92
91 93
  94 +
  95 +#### Local game simulation
  96 +
92 97 class LocalGame extends BaseGame
93 98 startup: ->
94 99 map = SimulationMap.load decodeBase64(EverardIsland)
@@ -100,7 +105,7 @@ class LocalGame extends BaseGame
100 105 tick: ->
101 106 @sim.tick()
102 107
103   - # Key press handlers.
  108 + ##### Key press handlers.
104 109
105 110 handleKeydown: (e) ->
106 111 switch e.which
@@ -122,6 +127,8 @@ class LocalGame extends BaseGame
122 127 e.preventDefault()
123 128
124 129
  130 +#### Networked game simulation
  131 +
125 132 class NetworkGame extends BaseGame
126 133 constructor: ->
127 134 @heartbeatTimer = 0
@@ -155,7 +162,7 @@ class NetworkGame extends BaseGame
155 162 @heartbeatTimer = 0
156 163 @ws.send('')
157 164
158   - # Key press handlers.
  165 + ##### Key press handlers.
159 166
160 167 handleKeydown: (e) ->
161 168 return unless @ws?
@@ -179,7 +186,7 @@ class NetworkGame extends BaseGame
179 186 else return
180 187 e.preventDefault()
181 188
182   - # Network message handlers.
  189 + ##### Network message handlers.
183 190
184 191 handleMessage: (e) ->
185 192 @netctx.authoritative = yes
@@ -238,6 +245,8 @@ class NetworkGame extends BaseGame
238 245 -1
239 246
240 247
  248 +#### Entry point
  249 +
241 250 game = null
242 251
243 252 init = ->
@@ -247,7 +256,7 @@ init = ->
247 256 game = new NetworkGame()
248 257
249 258
250   -# Exports.
  259 +#### Exports
251 260 exports.init = init
252 261 exports.start = -> game.start()
253 262 exports.stop = -> game.stop()
7 src/client/loader.coffee
@@ -2,8 +2,9 @@
2 2 # firing a single event when everything is complete, and exposing resources in a tidy structure.
3 3
4 4
5   -# FIXME: Add audio file support. Needs to be smart about supported formats.
6   -# FIXME: Implement progress notification.
  5 +# FIXME:
  6 +# * Add audio file support. Needs to be smart about supported formats.
  7 +# * Implement progress notification.
7 8
8 9 class Loader
9 10 constructor: ->
@@ -52,5 +53,5 @@ class Loader
52 53 onError: ->
53 54
54 55
55   -# Exports.
  56 +#### Exports
56 57 module.exports = Loader
2  src/client/renderer/common_2d.coffee
@@ -118,5 +118,5 @@ class Common2dRenderer extends BaseRenderer
118 118 @ctx.restore()
119 119
120 120
121   -# Exports.
  121 +#### Exports
122 122 module.exports = Common2dRenderer
2  src/client/renderer/direct_2d.coffee
@@ -35,5 +35,5 @@ class Direct2dRenderer extends Common2dRenderer
35 35 , stx, sty, etx, ety
36 36
37 37
38   -# Exports
  38 +#### Exports
39 39 module.exports = Direct2dRenderer
6 src/client/renderer/index.coffee
@@ -39,7 +39,7 @@ class BaseRenderer
39 39 # Inherited from MapView.
40 40 onRetile: (cell, tx, ty) ->
41 41
42   - # Common functions.
  42 + #### Common functions.
43 43
44 44 # Draw a single frame.
45 45 draw: ->
@@ -67,6 +67,8 @@ class BaseRenderer
67 67 # Update all DOM HUD elements.
68 68 @updateHud()
69 69
  70 + #### HUD elements
  71 +
70 72 # Draw HUD elements that overlay the map. These are elements that need to be drawn in regular
71 73 # game coordinates, rather than screen coordinates.
72 74 drawOverlay: ->
@@ -126,5 +128,5 @@ class BaseRenderer
126 128 $(node).attr('status', 'neutral')
127 129
128 130
129   -# Exports.
  131 +#### Exports
130 132 module.exports = BaseRenderer
18 src/client/renderer/offscreen_2d.coffee
@@ -20,7 +20,9 @@ MAP_SIZE_SEGMENTS = MAP_SIZE_TILES / SEGMENT_SIZE_TILES
20 20 SEGMENT_SIZE_PIXEL = SEGMENT_SIZE_TILES * TILE_SIZE_PIXELS
21 21
22 22
23   -# This class represents a single segment.
  23 +#### Cached segment
  24 +
  25 +# This class represents a single map segment.
24 26 class CachedSegment
25 27 constructor: (@renderer, x, y) ->
26 28 # Tile bounds
@@ -71,31 +73,34 @@ class CachedSegment
71 73 cell.x * TILE_SIZE_PIXELS, cell.y * TILE_SIZE_PIXELS, @ctx
72 74
73 75
  76 +#### Renderer
  77 +
  78 +# The off-screen renderer keeps a 2D array of instances of MapSegment.
74 79 class Offscreen2dRenderer extends Common2dRenderer
75 80 constructor: (images, sim) ->
76 81 super
77 82
78   - # Build a 2D array of map segments.
79 83 @cache = new Array(MAP_SIZE_SEGMENTS)
80 84 for y in [0...MAP_SIZE_SEGMENTS]
81 85 row = @cache[y] = new Array(MAP_SIZE_SEGMENTS)
82 86 for x in [0...MAP_SIZE_SEGMENTS]
83 87 row[x] = new CachedSegment(this, x, y)
84 88
  89 + # When a cell is retiled, we store the tile index and update the segment.
85 90 onRetile: (cell, tx, ty) ->
86   - # Remember the tilemap index.
87 91 cell.tile = [tx, ty]
88 92
89   - # Notify the segment, so it can update it's buffer if it has one.
90 93 segx = floor(cell.x / SEGMENT_SIZE_TILES)
91 94 segy = floor(cell.y / SEGMENT_SIZE_TILES)
92 95 @cache[segy][segx].onRetile(cell, tx, ty)
93 96
  97 + # Drawing the map is a matter of iterating the map segments that are on-screen, and blitting
  98 + # the off-screen canvas to the main canvas. The segments are prepared on-demand from here, and
  99 + # extra care is taken to only build one segment per frame.
94 100 drawMap: (sx, sy, w, h) ->
95 101 ex = sx + w - 1
96 102 ey = sy + h - 1
97 103
98   - # Iterate all cache segments.
99 104 alreadyBuiltOne = no
100 105 for row in @cache
101 106 for segment in row
@@ -106,7 +111,6 @@ class Offscreen2dRenderer extends Common2dRenderer
106 111
107 112 # Make sure the segment buffer is available.
108 113 unless segment.canvas
109   - # Let's only draw one segment per frame, to keep things smooth.
110 114 continue if alreadyBuiltOne
111 115 segment.build()
112 116 alreadyBuiltOne = yes
@@ -119,5 +123,5 @@ class Offscreen2dRenderer extends Common2dRenderer
119 123 return
120 124
121 125
122   -# Exports
  126 +#### Exports
123 127 module.exports = Offscreen2dRenderer
92 src/client/renderer/webgl.coffee
... ... @@ -1,8 +1,8 @@
1 1 # The WebGL renderer works much like the Direct2D renderer, but uses WebGL to accomplish it.
2 2 # The advantage is that we can draw individual tiles, but actually feed them in large batches to
3   -# the graphics hardware using Vertex Buffer Objects. Another advantage is that we can do all the
4   -# styling we need in a fragment shader.
5   -
  3 +# the graphics hardware using Vertex Buffer Objects (VBO). Another advantage is that we can do all
  4 +# the styling we need in a fragment shader.
  5 +#
6 6 # All in all, this is the least CPU intensive drawing method, but strangely not the smoothest.
7 7
8 8
@@ -13,6 +13,10 @@ BaseRenderer = require '.'
13 13 TEAM_COLORS = require '../../team_colors'
14 14
15 15
  16 +
  17 +#### Shaders
  18 +
  19 +# The vertex shader simply applies the transformation matrix, and interpolates texture coordinates.
16 20 VERTEX_SHADER =
17 21 '''
18 22 /* Input variables. */
@@ -30,6 +34,8 @@ VERTEX_SHADER =
30 34 }
31 35 '''
32 36
  37 +# The fragment shader makes the decision which tilemap to sample from, and combines the styled
  38 +# tilemap with the styling overlay. Three texture units are used.
33 39 FRAGMENT_SHADER =
34 40 '''
35 41 #ifdef GL_ES
@@ -68,7 +74,6 @@ FRAGMENT_SHADER =
68 74 }
69 75 '''
70 76
71   -
72 77 # Helper function that is used to compile the above shaders.
73 78 compileShader = (ctx, type, source) ->
74 79 shader = ctx.createShader type
@@ -79,6 +84,8 @@ compileShader = (ctx, type, source) ->
79 84 shader
80 85
81 86
  87 +#### Renderer
  88 +
82 89 class WebglRenderer extends BaseRenderer
83 90 constructor: (images, sim) ->
84 91 super
@@ -175,8 +182,9 @@ class WebglRenderer extends BaseRenderer
175 182 @handleResize()
176 183 $(window).resize => @handleResize()
177 184
  185 + # On resize, we update the canvas size, and recalculate the translation matrix. Because this is
  186 + # called at convenient times, we also check the GL error state at this point.
178 187 handleResize: ->
179   - # Update the canvas size.
180 188 @canvas[0].width = window.innerWidth
181 189 @canvas[0].height = window.innerHeight
182 190 @canvas.css(
@@ -185,43 +193,41 @@ class WebglRenderer extends BaseRenderer
185 193 )
186 194 @ctx.viewport(0, 0, window.innerWidth, window.innerHeight)
187 195
188   - # Recalculate the transformation matrix.
189 196 @setTranslation(0, 0)
190 197
191   - # This seems like a good spot to do a quick check for errors.
192 198 @checkError()
193 199
  200 + # This function checks the GL error state and throws an exception if necessary.
194 201 checkError: ->
195 202 gl = @ctx
196 203 unless (err = gl.getError()) == gl.NO_ERROR
197 204 throw "WebGL error: #{err}"
198 205 return
199 206
  207 + # Rebuild the translation matrix. The translation matrix accomplishes the following:
  208 + #
  209 + # A. Apply the requested translation by (px,py).
  210 + # B. The WebGL coordinate space runs from -1 to 1. Scale the pixel coordinates to fit in the
  211 + # range 0 to 2.
  212 + # C. Then translate to fit in the range -1 to 1.
  213 + # D. The WebGL y-axis is inverted compared to what we want. So multiply the y-axis by -1.
  214 + #
  215 + # To chain all this into one matrix, we have to apply these into reverse order. The math then
  216 + # looks as follows:
  217 + #
  218 + # D C B A
  219 + # | 1 0 0 0 | | 1 0 0 -1 | | xt 0 0 0 | | 1 0 0 px |
  220 + # T = | 0 -1 0 0 | x | 0 1 0 -1 | x | 0 xy 0 0 | x | 0 1 0 py |
  221 + # | 0 0 1 0 | | 0 0 1 0 | | 0 0 1 0 | | 0 0 1 0 |
  222 + # | 0 0 0 1 | | 0 0 0 1 | | 0 0 0 1 | | 0 0 0 1 |
  223 + #
  224 + # To top that off, WebGL expects things in column major order. So the array indices should be
  225 + # read as being transposed.
200 226 setTranslation: (px, py) ->
201   - # Rebuild the translation matrix.
202   -
203   - # A. Apply the requested translation by (px,py).
204   - # B. The WebGL coordinate space runs from -1 to 1.
205   - # Scale the pixel coordinates to fit in the range 0 to 2.
206   - # C. Then translate to fit in the range -1 to 1.
207   - # D. The WebGL y-axis is inverted compared to what we want.
208   - # So multiply the y-axis by -1.
209   -
210   - # To chain all this into one matrix, we have to apply these into reverse order.
211   - # The math then looks as follows:
212 227
213 228 xt = 2 / window.innerWidth
214 229 yt = 2 / window.innerHeight
215 230
216   - # D C B A
217   - # | 1 0 0 0 | | 1 0 0 -1 | | xt 0 0 0 | | 1 0 0 px |
218   - # T = | 0 -1 0 0 | x | 0 1 0 -1 | x | 0 xy 0 0 | x | 0 1 0 py |
219   - # | 0 0 1 0 | | 0 0 1 0 | | 0 0 1 0 | | 0 0 1 0 |
220   - # | 0 0 0 1 | | 0 0 0 1 | | 0 0 0 1 | | 0 0 0 1 |
221   -
222   - # To top that off, WebGL expects things in column major order.
223   - # So the array indices below should be read as being transposed.
224   -
225 231 arr = @transformArray
226 232 arr[0] = xt
227 233 arr[5] = -yt
@@ -229,8 +235,8 @@ class WebglRenderer extends BaseRenderer
229 235 arr[13] = py * -yt + 1
230 236 @ctx.uniformMatrix4fv(@uTransform, no, arr)
231 237
  238 + # Apply a translation that centers everything around the given coordinates.
232 239 centerOn: (x, y, cb) ->
233   - # Apply a translation that centers everything around the player.
234 240 {width, height} = @canvas[0]
235 241 left = round(x / PIXEL_SIZE_WORLD - width / 2)
236 242 top = round(y / PIXEL_SIZE_WORLD - height / 2)
@@ -240,8 +246,10 @@ class WebglRenderer extends BaseRenderer
240 246
241 247 @setTranslation(0, 0)
242 248
  249 + # Helper function that adds a tile to an array that is used to prepare the VBO. It takes care
  250 + # of calculating texture coordinates based on tile coordinates `tx` and `ty`, and adds entries
  251 + # for two triangles to the given `buffer` at the given `offset`.
243 252 bufferTile: (buffer, offset, tx, ty, styled, sdx, sdy) ->
244   - # Calculate texture coordinate bounds for the tile.
245 253 if styled
246 254 stx = tx * @hStyledTileSizeTexture
247 255 sty = ty * @vStyledTileSizeTexture
@@ -253,11 +261,9 @@ class WebglRenderer extends BaseRenderer
253 261 etx = stx + @hTileSizeTexture
254 262 ety = sty + @vTileSizeTexture
255 263
256   - # Calculate pixel coordinate bounds for the destination.
257 264 edx = sdx + TILE_SIZE_PIXELS
258 265 edy = sdy + TILE_SIZE_PIXELS
259 266
260   - # Update the quad array.
261 267 buffer.set([
262 268 sdx, sdy, stx, sty,
263 269 sdx, edy, stx, ety,
@@ -267,45 +273,35 @@ class WebglRenderer extends BaseRenderer
267 273 edx, edy, etx, ety
268 274 ], offset * (6*4))
269 275
  276 + # Draw a single tile, unstyled.
270 277 drawTile: (tx, ty, sdx, sdy) ->
271 278 gl = @ctx
272   -
273   - # Initialize the uniforms, so the shader knows this is an unstyled tile.
274 279 gl.uniform1i(@uUseStyled, 0)
275   -
276   - # Update the quad array.
277 280 @bufferTile(@vertexArray, 0, tx, ty, no, sdx, sdy)
278   -
279   - # Draw.
280 281 gl.bufferData(gl.ARRAY_BUFFER, @vertexArray, gl.DYNAMIC_DRAW)
281 282 gl.drawArrays(gl.TRIANGLES, 0, 6)
282 283
  284 + # Draw a single tile, styled with a team color.
283 285 drawStyledTile: (tx, ty, style, sdx, sdy) ->
284 286 gl = @ctx
285   -
286   - # Initialize the uniforms, to tell the uniform the style color.
287 287 gl.uniform1i(@uUseStyled, 1)
288 288 if color = TEAM_COLORS[style]
289 289 gl.uniform1i(@uIsStyled, 1)
290 290 gl.uniform3f(@uStyleColor, color.r / 255, color.g / 255, color.b / 255)
291 291 else
292 292 gl.uniform1i(@uIsStyled, 0)
293   -
294   - # Update the quad array.
295 293 @bufferTile(@vertexArray, 0, tx, ty, yes, sdx, sdy)
296   -
297   - # Draw.
298 294 gl.bufferData(gl.ARRAY_BUFFER, @vertexArray, gl.DYNAMIC_DRAW)
299 295 gl.drawArrays(gl.TRIANGLES, 0, 6)
300 296
  297 + # When a cell is retiled, we simply store the tile index for the upcoming frames.
301 298 onRetile: (cell, tx, ty) ->
302   - # Simply cache the tile index.
303 299 cell.tile = [tx, ty]
304 300
  301 + # Draw the map.
305 302 drawMap: (sx, sy, w, h) ->
306 303 gl = @ctx
307 304
308   - # Calculate pixel boundaries.
309 305 ex = sx + w - 1
310 306 ey = sy + h - 1
311 307
@@ -326,16 +322,14 @@ class WebglRenderer extends BaseRenderer
326 322 gl.drawArrays(gl.TRIANGLES, 0, arrayTileIndex * 6)
327 323 arrayTileIndex = 0
328 324
329   - # Iterate each tile in view.
  325 + # Only draw unstyled tiles, but build an index of styled tiles by color.
330 326 gl.uniform1i(@uUseStyled, 0)
331 327 @sim.map.each (cell) =>
332 328 if obj = cell.pill || cell.base
333   - # Only draw unstyled tiles, but index styled tiles by color.
334 329 style = obj.owner?.team
335 330 style = 255 unless TEAM_COLORS[style]
336 331 (styledCells[style] ||= []).push(cell)
337 332 else
338   - # Add the tile to the vertex buffer.
339 333 @bufferTile @vertexArray, arrayTileIndex, cell.tile[0], cell.tile[1], no,
340 334 cell.x * TILE_SIZE_PIXELS, cell.y * TILE_SIZE_PIXELS
341 335 if ++arrayTileIndex == maxTiles
@@ -360,5 +354,5 @@ class WebglRenderer extends BaseRenderer
360 354 flushArray()
361 355
362 356
363   -# Exports.
  357 +#### Exports
364 358 module.exports = WebglRenderer
2  src/explosion.coffee
@@ -38,5 +38,5 @@ class Explosion
38 38 WorldObject.register Explosion
39 39
40 40
41   -# Exports.
  41 +#### Exports
42 42 module.exports = Explosion
10 src/index.coffee
@@ -17,7 +17,7 @@ class Simulation
17 17
18 18 @spawnMapObjects()
19 19
20   - # Basic object management.
  20 + #### Basic object management.
21 21
22 22 tick: ->
23 23 # Work on a shallow copy of the object list.
@@ -80,7 +80,7 @@ class Simulation
80 80 for i in [obj.idx...@objects.length]
81 81 @objects[i].idx--
82 82
83   - # Serialization
  83 + #### Serialization
84 84
85 85 buildSerializer: (packer) ->
86 86 (specifier, value) ->
@@ -97,7 +97,7 @@ class Simulation
97 97 when 'T' then @tanks[unpacker('B')]
98 98 else unpacker(specifier)
99 99
100   - # Player management.
  100 + #### Player management.
101 101
102 102 addTank: (tank) ->
103 103 tank.tank_idx = @tanks.length
@@ -108,7 +108,7 @@ class Simulation
108 108 @tanks.splice tank.tank_idx, 1
109 109 @resolveMapObjectOwners()
110 110
111   - # Map object management.
  111 + #### Map object management.
112 112
113 113 getAllMapObjects: -> @map.pills.concat @map.bases
114 114
@@ -143,5 +143,5 @@ class Simulation
143 143 obj.cell.retile()
144 144 return
145 145
146   -# Exports.
  146 +#### Exports
147 147 module.exports = Simulation
14 src/map.coffee
@@ -30,7 +30,8 @@ createTerrainMap = ->
30 30 createTerrainMap()
31 31
32 32
33   -# A class to represent a cell on the map.
  33 +#### Cell class
  34 +
34 35 class MapCell
35 36 constructor: (@map, @x, @y) ->
36 37 @type = TERRAIN_TYPES['^']
@@ -354,6 +355,8 @@ class MapCell
354 355 else @setTile 11, 6
355 356
356 357
  358 +#### View class
  359 +
357 360 # This is an interface for map views. Map views are responsible for actually displaying the map on
358 361 # the screen. This class also functions as the do-nothing dummy implementation. You need not
359 362 # inherit from this class, just make sure whatever view object you use responds to the methods
@@ -364,6 +367,8 @@ class MapView
364 367 onRetile: (cell, tx, ty) ->
365 368
366 369
  370 +#### Map objects
  371 +
367 372 # The following are interfaces and dummy default implementations of map objects. If a subclass
368 373 # of `Map` wishes to use different classes for map objects, it simply needs to define new classes
369 374 # with similar constructors and exposing the same attributes.
@@ -378,7 +383,8 @@ class Start
378 383 constructor: (map, @x, @y, @direction) ->
379 384
380 385
381   -# This class holds a complete map.
  386 +#### Map class
  387 +
382 388 class Map
383 389 CellClass: MapCell
384 390 PillboxClass: Pillbox
@@ -436,6 +442,8 @@ class Map
436 442 cell.retile()
437 443 , sx, sy, ex, ey
438 444
  445 + #### Saving and loading
  446 +
439 447 # Dump the map to an array of octets in BMAP format.
440 448 dump: (options) ->
441 449 options ||= {}
@@ -638,7 +646,7 @@ class Map
638 646 child.load = @load unless child.load
639 647
640 648
641   -# Exports.
  649 +#### Exports
642 650 exports.TERRAIN_TYPES = TERRAIN_TYPES
643 651 exports.MapView = MapView
644 652 exports.Map = Map
2  src/net.coffee
@@ -55,7 +55,7 @@ inContext = (ctx, cb) ->
55 55 retval
56 56
57 57
58   -# Exports.
  58 +#### Exports
59 59 exports.inContext = inContext
60 60
61 61 # Delegate the functions used by the simulation to the active context.
10 src/server/index.coffee
@@ -23,7 +23,7 @@ class Game
23 23 @netctx = new ServerContext(@sim)
24 24 @oddTick = no
25 25
26   - # Connection handling.
  26 + #### Connection handling.
27 27
28 28 onConnect: (ws) ->
29 29 # In order to create the tank object, we need to be in the networking context.
@@ -108,7 +108,7 @@ class Game
108 108 client.sendMessage(message) unless client.heartbeatTimer > 20
109 109 return
110 110
111   - # Simulation updates.
  111 + #### Simulation updates.
112 112
113 113 tick: ->
114 114 net.inContext @netctx, => @sim.tick()
@@ -135,6 +135,8 @@ class Game
135 135 return
136 136
137 137
  138 +#### HTTP server application
  139 +
138 140 class Application
139 141 constructor: ->
140 142 @games = []
@@ -180,6 +182,8 @@ class Application
180 182 ws.on 'connect', -> handler(ws)
181 183
182 184
  185 +#### Entry point
  186 +
183 187 # Don't export a server directly, but this factory function. Once called, the timer loop will
184 188 # start. I believe it's untidy to have timer loops start after a simple require().
185 189 createBoloServer = ->
@@ -195,5 +199,5 @@ createBoloServer = ->
195 199 server
196 200
197 201
198   -# Exports.
  202 +#### Exports
199 203 module.exports = createBoloServer
2  src/server/net.coffee
@@ -46,5 +46,5 @@ class ServerContext
46 46 packer.finish()
47 47
48 48
49   -# Exports
  49 +#### Exports
50 50 module.exports = ServerContext
2  src/server/websocket.coffee
@@ -214,5 +214,5 @@ class WebSocket extends EventEmitter
214 214 _onClose: (had_error) -> @emit 'close', had_error
215 215
216 216
217   -# Exports
  217 +#### Exports
218 218 module.exports = WebSocket
2  src/shell.coffee
@@ -115,5 +115,5 @@ class Shell
115 115 WorldObject.register Shell
116 116
117 117
118   -# Exports.
  118 +#### Exports
119 119 module.exports = Shell
10 src/simulation_map.coffee
@@ -11,6 +11,8 @@ net = require './net'
11 11 WorldObject = require './world_object'
12 12
13 13
  14 +# Extend `TERRAIN_TYPES` with additional attributes that matter to the simulation.
  15 +
14 16 TERRAIN_TYPE_ATTRIBUTES =
15 17 '|': { tankSpeed: 0, tankTurn: 0.00, manSpeed: 0 }
16 18 ' ': { tankSpeed: 3, tankTurn: 0.25, manSpeed: 0 }
@@ -33,6 +35,8 @@ extendTerrainMap = ->
33 35 extendTerrainMap()
34 36
35 37
  38 +#### Cell class
  39 +
36 40 class SimMapCell extends Map::CellClass
37 41 getTankSpeed: (tank) ->
38 42 # Check for a pillbox.
@@ -74,7 +78,7 @@ class SimMapCell extends Map::CellClass
74 78 net.mapChanged this, oldType, hadMine
75 79
76 80
77   -# Map objects.
  81 +#### Map objects
78 82
79 83 # These implement the interface of each kind of map object, and also implement WorldObject. The
80 84 # latter is so that we may synchronize their state across the network. Drawing is not done like
@@ -165,6 +169,8 @@ class SimBase
165 169 WorldObject.register SimBase
166 170
167 171
  172 +#### Map class
  173 +
168 174 class SimMap extends Map
169 175 CellClass: SimMapCell
170 176 PillboxClass: SimPillbox
@@ -182,5 +188,5 @@ class SimMap extends Map
182 188 @starts[round(random() * (@starts.length - 1))]
183 189
184 190
185   -# Exports.
  191 +#### Exports
186 192 exports.SimulationMap = SimMap
10 src/struct.coffee
@@ -9,6 +9,8 @@
9 9 # deal with arrays of byte values instead.
10 10
11 11
  12 +#### Helpers
  13 +
12 14 # The following methods pack Fixnums in an array of bytes, in network byte order (MSB).
13 15
14 16 toUint8 = (n) -> [
@@ -35,6 +37,8 @@ fromUint16 = (d, o) -> (d[o] << 8) + d[o+1]
35 37 fromUint32 = (d, o) -> (d[o] << 24) + (d[o+1] << 16) + (d[o+2] << 8) + d[o+3]
36 38
37 39
  40 +#### Streaming packers
  41 +
38 42 # Return a generator function, that is used to generate binary data. Basic usage is as follows:
39 43 #
40 44 # packer = buildPacker()
@@ -143,7 +147,9 @@ buildUnpacker = (data, offset) ->
143 147 retval
144 148
145 149
146   -# The following are non-streaming variants, that work more like Python's `struct`.
  150 +#### Non-streaming packers
  151 +
  152 +# These work more like Python's `struct`.
147 153
148 154 # The `pack` function takes a format string, and the respective values as its arguments. It then
149 155 # returns the binary data as an array of byte values.
@@ -162,7 +168,7 @@ unpack = (fmt, data, offset) ->
162 168 [values, unpacker.finish()]
163 169
164 170
165   -# Exports
  171 +#### Exports
166 172 exports.buildPacker = buildPacker
167 173 exports.buildUnpacker = buildUnpacker
168 174 exports.pack = pack
4 src/tank.coffee
@@ -113,7 +113,7 @@ class Tank
113 113 # FIXME
114 114
115 115
116   - # The following methods all update the simulation.
  116 + #### Simulation update
117 117
118 118 update: ->
119 119 return if @death()
@@ -288,5 +288,5 @@ class Tank
288 288 WorldObject.register Tank
289 289
290 290
291   -# Exports.
  291 +#### Exports
292 292 module.exports = Tank
2  src/team_colors.coffee
@@ -16,5 +16,5 @@ TEAM_COLORS = [
16 16 ]
17 17
18 18
19   -# Exports.
  19 +#### Exports
20 20 module.exports = TEAM_COLORS
26 src/world_object.coffee
@@ -17,8 +17,6 @@ class WorldObject
17 17 # tilemap. May also be `null`, in which case the object is not drawn at all.
18 18 styled: null
19 19
20   - # Callbacks. All of the following are optional.
21   -
22 20 # These are properties containing the world coordinates of this object. These are actually
23 21 # defined in the constructor. The value `null` for either means that the object is not physical
24 22 # or 'not in the world' at this moment (ie. dead tanks).
@@ -35,13 +33,14 @@ class WorldObject
35 33 # then proceeds as normal with `postInitialize` and further updates.
36 34 constructor: (sim) ->
37 35
  36 + #### Callbacks
  37 +
  38 + # *The following are optional callbacks.*
  39 +
38 40 # Return the (x,y) index in the tilemap (base or styled, selected above) that the object should
39 41 # be drawn with. May be a no-op if the object is never actually drawn.
40 42 getTile: ->
41 43
42   - # The following are optional callbacks, and thus no-ops by default.
43   - # It's not hard to implement more, but these are the ones currently used.
44   -
45 44 # Called after the object has been added to the Simulation, either through normal means or
46 45 # through the network code.
47 46 postInitialize: ->
@@ -61,34 +60,29 @@ class WorldObject
61 60 # or simulated on the client.
62 61 update: ->
63 62
64   - # The following govern serialization, and are normally not overridden.
65   -
66 63 # This method is called to serialize and deserialize an object's state. The parameter `p`
67 64 # is a function which should be repeatedly called for each property of the object. It takes as
68   - # it's first parameter a format specifier for `struct`, and as it's second parameter the current
69   - # value of the property. A special format specifier 'O' may be used to (de-)serialize a reference
70   - # to another WorldObject.
  65 + # its first parameter a format specifier for `struct`, and as it's second parameter the current
  66 + # value of the property. A special format specifier `O` may be used to (de-)serialize a reference
  67 + # to another WorldObject, and `T` may be used for a reference to a Tank.
71 68 #
72 69 # If the function is called to serialize, then parameters are collected to form a packet, and
73 70 # the return value is the same as the value parameter verbatim. If the function is called to
74 71 # deserialize, then the value parameter is ignored, and the return value is the received value.
75 72 serialization: (isCreate, p) ->
76 73
77   - # Static methods.
  74 + #### Static methods
78 75
79 76 # Find a type by character or character code.
80 77 @getType: (c) ->
81 78 c = String.fromCharCode(c) if typeof(c) != 'string'
82 79 types[c]
83 80
84   - # This should be called after a class is defined, as for example `MyObject.register()`.
85   - # FIXME: Would be neat if this were automagic somehow.
  81 + # This should be called after a class is defined, as for example `WorldObject.register MyObject`.
86 82 @register: (type) ->
87   - # Add to the index.
88 83 types[type::charId] = type
89   - # Set the character code, which is the network identifier.
90 84 type::charCodeId = type::charId.charCodeAt(0)
91 85
92 86
93   -# Exports.
  87 +#### Exports
94 88 module.exports = WorldObject

0 comments on commit 9acf359

Please sign in to comment.
Something went wrong with that request. Please try again.