module Interp_types:sig..end
Interp.Interp.typeposition =float * float
(x, y).type'atable =('a Bulletml.Syntax.id * 'a) list
actionRef, etc).type 'a linear_map = {
|
frame_start : |
|
frame_end : |
|
val_start : |
|
val_end : |
frame_start, its value is val_start.frame_end, its value is val_end.type opcode =
| |
OpRepeatE of |
(* | Repeat times the same Syntax.action | *) |
| |
OpWaitE of |
(* | Wait frames (unevaluated form) | *) |
| |
OpWaitN of |
(* | Wait frames (evaluated form) | *) |
| |
OpFire of |
(* | Fork execution by creating a new Interp_types.obj | *) |
| |
OpSpdE of |
(* | Change speed (unevaluated form) | *) |
| |
OpSpdN of |
(* | Change speed (evaluated form) | *) |
| |
OpDirE of |
(* | Change direction (unevaluated form) | *) |
| |
OpDirN of |
(* | Change direction (evaluated form) | *) |
| |
OpAccelE of |
(* | Accelerate (unevaluated form) | *) |
| |
OpAccelN of |
(* | Accelerate (unevaluated form): h, v, term | *) |
| |
OpVanish |
(* | Let the bullet disappear | *) |
| |
OpCall of |
(* | Call an indirect action with parameters | *) |
Interp_types.obj.
Some cases have an both an unevaluated form and an evaluated form. This is because it is necessary to delay the evaluation as late as possible, and having a convenient form for computations in progress.
For example, OpWaitX. Evaluating Wait (Param 1) will create a OpWaitE
(Param 1) (as is in the opcode list). When it is time to handle the opcode, it
will be replaced by OpWaitN 3 (if we are in a context where $1 = 3). At the
next frame it will be OpWaitN 2, etc.
Some cases have a "term", it is the number of frames in which the evolution
will be done in the corresponding Interp_types.linear_map.
type obj = {
|
prog : |
(* | Behaviour | *) |
|
speed : |
(* | In pixels/frame | *) |
|
dir : |
(* | In degrees. Top is 0, clockwise. Strange, I know | *) |
|
children : |
(* | Interp_types.objs created by this one | *) |
|
pos : |
(* | Where to draw it | *) |
|
prev_dir : |
(* | Used for interpreting DirSeq e | *) |
|
prev_speed : |
(* | Used for interpreting SpdSeq e | *) |
|
vanished : |
(* | If true, don't draw this bullet | *) |
type env = {
|
frame : |
(* | Frame number. Usually starts at 1, but only deltas are significant | *) |
|
ship_pos : |
(* | Where patterns can aim | *) |
|
screen_w : |
(* | Screen width in pixels | *) |
|
screen_h : |
(* | Screen height in pixels | *) |
|
actions : |
(* | Definitions of Syntax.actions | *) |
|
bullets : |
(* | Definitions of Syntax.bullets | *) |
|
fires : |
(* | Definitions of Syntax.fires | *) |
type init_params = {
|
p_ship : |
(* | Where is the ship. The one that is aimed | *) |
|
p_enemy : |
(* | Where is the enemy. The one that shoots | *) |
|
p_screen_w : |
(* | Screen width in pixels | *) |
|
p_screen_h : |
(* | Screen height in pixels | *) |
type ('g, 'l, 'r) interpreter = {
|
make_global_ctx : |
(* | (once) create the global, persistent context | *) |
|
make_local_ctx : |
(* | (each frame) create a local context | *) |
|
clear : |
(* | clear frame | *) |
|
draw : |
(* | draw the object and its descendants | *) |
|
run_cont : |
(* | run the next frame. When in doubt, fun _ k -> k () works | *) |
Interp.main_loop.
It is polymorphic and quite parametric, to allow for different kind of interpreters.
'g is the global context, something that lasts during all the game. For
example, a window object.'l is the local context, something created at each frame.'r is the return type. Typically unit, but who knows, you might be
writing a monadic interpreter.