Updated nph

This commit is contained in:
Scott Wadden 2024-02-07 22:30:36 -04:00
parent e91909688e
commit b0e871a329
51 changed files with 809 additions and 846 deletions

View File

@ -1,6 +1,2 @@
# Formatted with nph v0.3-10-g3947a41-dirty
d02b42b06eafd770cc1339459805fb6bf3e19709
# Formatted with nph v0.3-9-gdd146ed-dirty
3f0302c4b68c33aba42de9c657a100dd781de195
# Formatted with nph v0.3-11-gaeceee1
48639ef4af9d86817c8897ed8d78adf480050606
# Formatted with nph v0.4-4-g5e613fb-dirty
b1dd9d43a59785e33b8e778a832cd65edf958292

View File

@ -1,30 +1,29 @@
import nimibook
var book =
init_book_with_toc:
section "Introduction", "intro":
entry "Tutorial", "intro/tutorial"
entry "Install or Build", "intro/building"
entry "Controls and Usage", "intro/controls"
entry "Examples", "intro/examples"
entry "Multiplayer (Experimental)", "intro/multiplayer"
entry "Config", "intro/config"
var book = init_book_with_toc:
section "Introduction", "intro":
entry "Tutorial", "intro/tutorial"
entry "Install or Build", "intro/building"
entry "Controls and Usage", "intro/controls"
entry "Examples", "intro/examples"
entry "Multiplayer (Experimental)", "intro/multiplayer"
entry "Config", "intro/config"
section "Coding Enu", "coding":
entry "Concepts", "coding/concepts"
entry "Built-in Commands", "coding/commands"
entry "Shorthand Commands", "coding/shorthand"
entry "Random Numbers", "coding/random_numbers"
section "Coding Enu", "coding":
entry "Concepts", "coding/concepts"
entry "Built-in Commands", "coding/commands"
entry "Shorthand Commands", "coding/shorthand"
entry "Random Numbers", "coding/random_numbers"
section "Command Loops", "command_loops":
entry "Writing Commands", "command_loops/commands"
entry "Arrows", "command_loops/arrows"
entry "Loops", "command_loops/loops"
entry "Child Loops", "command_loops/child_loops"
entry "More About Command Loops", "command_loops/more"
section "Command Loops", "command_loops":
entry "Writing Commands", "command_loops/commands"
entry "Arrows", "command_loops/arrows"
entry "Loops", "command_loops/loops"
entry "Child Loops", "command_loops/child_loops"
entry "More About Command Loops", "command_loops/more"
entry "Goals", "goals"
entry "Demos", "demos"
entry "TODO", "todo"
entry "Goals", "goals"
entry "Demos", "demos"
entry "TODO", "todo"
nimibook_cli(book)

View File

@ -4,14 +4,16 @@ import enuib
nb_init(theme = use_enu)
nb_text:
"""
# Introduction
Build 3D worlds in Nim.
# Introduction
![Enu Screenshot](assets/screenshot_3.webp)
Build 3D worlds in Nim.
Enu lets you build and explore worlds using a familiar block-building interface
and a Logo inspired API. It tries to make 3D development easier, and will
eventually be able to create standalone games.
"""
![Enu Screenshot](assets/screenshot_3.webp)
Enu lets you build and explore worlds using a familiar block-building interface
and a Logo inspired API. It tries to make 3D development easier, and will
eventually be able to create standalone games.
"""
nb_save

View File

@ -48,8 +48,8 @@ proc add_to_scene(unit: Unit) =
if node.owner != nil:
fail \"{T.name} node shouldn't be owned. unit = {unit.id}"
unit.node.visible =
Visible in unit.global_flags() and
ScriptInitializing notin unit.global_flags()
Visible in unit.global_flags and
(ScriptInitializing notin unit.global_flags)
parent_node.add_child(unit.node)
unit.node.owner = parent_node

View File

@ -95,10 +95,9 @@ proc new_instance(self: Worker, src: Unit, dest: PNode) =
var clone = src.clone(self.active_unit, id)
assert not clone.is_nil
clone.script_ctx =
ScriptCtx.init(
owner = clone, clone_of = src, interpreter = self.interpreter
)
clone.script_ctx = ScriptCtx.init(
owner = clone, clone_of = src, interpreter = self.interpreter
)
self.map_unit(clone, dest)
@ -193,15 +192,14 @@ proc begin_move(
proc sleep_impl(self: Worker, ctx: ScriptCtx, seconds: float) =
var duration = 0.0
ctx.callback =
proc(delta: float, _: MonoTime): TaskStates =
duration += delta
if seconds > 0 and duration < seconds:
Running
elif seconds <= 0 and duration <= 0.5 and ctx.timer > get_mono_time():
Running
else:
Done
ctx.callback = proc(delta: float, _: MonoTime): TaskStates =
duration += delta
if seconds > 0 and duration < seconds:
Running
elif seconds <= 0 and duration <= 0.5 and ctx.timer > get_mono_time():
Running
else:
Done
ctx.last_ran = MonoTime.default
self.pause_script()
@ -342,14 +340,13 @@ proc sees(
return
let future = result
unit.script_ctx.callback =
proc(delta: float, timeout: MonoTime): TaskStates =
let query = unit.sight_query
if ?query.answer:
future.complete(query.answer.get)
result = Done
else:
result = Running
unit.script_ctx.callback = proc(delta: float, timeout: MonoTime): TaskStates =
let query = unit.sight_query
if ?query.answer:
future.complete(query.answer.get)
result = Done
else:
result = Running
unit.script_ctx.last_ran = MonoTime.default
self.pause_script()
@ -428,8 +425,9 @@ proc draw_position_set(self: Build, position: Vector3) =
(position - self.position).local_to(self.parent)
proc save(self: Build, name: string) =
self.save_points[name] =
(self.draw_transform, self.color_value.value, self.drawing)
self.save_points[name] = (
self.draw_transform, self.color_value.value, self.drawing
)
proc restore(self: Build, name: string) =
(self.draw_transform, self.color_value.value, self.drawing) =
@ -486,17 +484,16 @@ proc new_markdown_sign(
size: int,
billboard: bool,
): Unit =
result =
Sign.init(
message,
more = more,
owner = self.active_unit,
transform = drop_transform(unit),
width = width,
height = height,
size = size,
billboard = billboard,
)
result = Sign.init(
message,
more = more,
owner = self.active_unit,
transform = drop_transform(unit),
width = width,
height = height,
size = size,
billboard = billboard,
)
info "creating sign", id = result.id
self.map_unit(result, pnode)

View File

@ -76,51 +76,48 @@ macro bridged_from_vm(
let
symbol = bind_sym($proc_ref)
proc_impl = (if symbol.kind == nnkSym: symbol
else: symbol[0]
).get_impl
else: symbol[0]
).get_impl
proc_name = proc_impl[0].str_val
proc_impl_name = proc_name.replace("=", "_set") & "_impl"
return_node = proc_impl[3][0]
arg_nodes = proc_impl[3][1 ..^ 1]
let args =
collect:
block:
var pos = -1
for ident_def in arg_nodes:
let typ = ident_def[1].str_val
if typ == $Worker.type:
ident"script_engine"
elif typ == "VmArgs":
ident"a"
elif typ == "ScriptCtx":
quote:
script_engine.active_unit.script_ctx
elif typ in ["Unit", "Bot", "Build", "Sign"]:
let getter = "get_" & typ
pos.inc
new_call(
bind_sym(getter), ident"script_engine", ident"a", new_lit(pos)
)
else:
let getter = "get_" & typ
pos.inc
new_call(bind_sym(getter), ident"a", new_lit(pos))
let args = collect:
block:
var pos = -1
for ident_def in arg_nodes:
let typ = ident_def[1].str_val
if typ == $Worker.type:
ident"script_engine"
elif typ == "VmArgs":
ident"a"
elif typ == "ScriptCtx":
quote:
script_engine.active_unit.script_ctx
elif typ in ["Unit", "Bot", "Build", "Sign"]:
let getter = "get_" & typ
pos.inc
new_call(
bind_sym(getter), ident"script_engine", ident"a", new_lit(pos)
)
else:
let getter = "get_" & typ
pos.inc
new_call(bind_sym(getter), ident"a", new_lit(pos))
var call = new_call(proc_ref, args)
if return_node.kind == nnk_sym:
if return_node.str_val in ["Unit", "Bot", "Build", "Sign"]:
call =
new_call(
bind_sym"set_result",
ident"a",
new_call(bind_sym"to_node", ident"script_engine", call),
)
call = new_call(
bind_sym"set_result",
ident"a",
new_call(bind_sym"to_node", ident"script_engine", call),
)
else:
call =
new_call(
bind_sym"set_result", ident"a", new_call(bind_sym"to_result", call)
)
call = new_call(
bind_sym"set_result", ident"a", new_call(bind_sym"to_result", call)
)
elif return_node.kind == nnk_bracket_expr and return_node.len == 2 and
return_node[0].str_val == "Future":
call = new_call(bind_sym"await_future", call, ident"a")

View File

@ -14,23 +14,21 @@ proc init*(
clone_of: Unit = nil,
interpreter: Interpreter,
): ScriptCtx =
result =
ScriptCtx(
module_name: if ?clone_of: clone_of.id else: "",
interpreter: interpreter,
timeout_at: MonoTime.high,
timer: MonoTime.high,
)
result = ScriptCtx(
module_name: if ?clone_of: clone_of.id else: "",
interpreter: interpreter,
timeout_at: MonoTime.high,
timer: MonoTime.high,
)
proc extract_file_info(msg: string): tuple[name: string, info: TLineInfo] =
if msg =~ re"unhandled exception: (.*)\((\d+), (\d+)\)":
result =
(
matches[0],
TLineInfo(
line: matches[1].parse_int.uint16, col: matches[2].parse_int.int16
),
)
result = (
matches[0],
TLineInfo(
line: matches[1].parse_int.uint16, col: matches[2].parse_int.int16
),
)
proc script_error*(self: Worker, unit: Unit, e: ref VMQuit) =
var msg = e.msg
@ -83,48 +81,49 @@ proc init_interpreter*[T](self: Worker, _: T) {.gcsafe.} =
raise (ref VMQuit)(info: info, msg: msg, location: loc)
var count: byte = 0
interpreter.enter_hook =
proc(c: PCtx, pc: int, tos: PStackFrame, instr: TInstr) =
assert ?controller
assert ?controller.active_unit
assert ?controller.active_unit.script_ctx
interpreter.enter_hook = proc(
c: PCtx, pc: int, tos: PStackFrame, instr: TInstr
) =
assert ?controller
assert ?controller.active_unit
assert ?controller.active_unit.script_ctx
let ctx = controller.active_unit.script_ctx
let info = c.debug[pc]
inc count
if count == 255:
# don't call get_mono_time for every instruction for a 5-10% speedup.
count = 0
let now = get_mono_time()
if ctx.timeout_at < now:
let duration = script_timeout
raise (ref VMQuit)(
info: info,
kind: Timeout,
msg:
\"Timeout. Script {ctx.script} executed for too long without " &
\"yielding: {duration}",
)
let ctx = controller.active_unit.script_ctx
let info = c.debug[pc]
inc count
if count == 255:
# don't call get_mono_time for every instruction for a 5-10% speedup.
count = 0
let now = get_mono_time()
if ctx.timeout_at < now:
let duration = script_timeout
raise (ref VMQuit)(
info: info,
kind: Timeout,
msg:
\"Timeout. Script {ctx.script} executed for too long without " &
\"yielding: {duration}",
)
# We don't care about the line info if we're not in our enu script.
# Store the file index the first time we hit our file and only change
# current_line/previous_line if the current instruction has that index.
if ctx.file_index == -1 and info.file_index.int >= 0 and
info.file_index.int < interpreter.config.m.file_infos.len:
let file_name =
interpreter.config.m.file_infos[info.file_index.int].full_path.string
if file_name == ctx.file_name:
ctx.file_index = info.file_index.int
elif ctx.file_index == info.file_index.int:
if ctx.previous_line != info:
(ctx.previous_line, ctx.current_line) = (ctx.current_line, info)
# We don't care about the line info if we're not in our enu script.
# Store the file index the first time we hit our file and only change
# current_line/previous_line if the current instruction has that index.
if ctx.file_index == -1 and info.file_index.int >= 0 and
info.file_index.int < interpreter.config.m.file_infos.len:
let file_name =
interpreter.config.m.file_infos[info.file_index.int].full_path.string
if file_name == ctx.file_name:
ctx.file_index = info.file_index.int
elif ctx.file_index == info.file_index.int:
if ctx.previous_line != info:
(ctx.previous_line, ctx.current_line) = (ctx.current_line, info)
if ctx.pause_requested:
ctx.ctx = c
ctx.pc = pc
ctx.tos = tos
ctx.pause_requested = false
raise VMPause.new_exception("vm paused")
if ctx.pause_requested:
ctx.ctx = c
ctx.pc = pc
ctx.tos = tos
ctx.pause_requested = false
raise VMPause.new_exception("vm paused")
proc load_script*(self: Worker, unit: Unit, timeout = script_timeout) =
let ctx = unit.script_ctx

View File

@ -124,10 +124,9 @@ proc watch_units(
self: Worker,
units: ZenSeq[Unit],
parent: Unit,
body:
proc(unit: Unit, change: Change[Unit], added: bool, removed: bool) {.
gcsafe
.},
body: proc(unit: Unit, change: Change[Unit], added: bool, removed: bool) {.
gcsafe
.},
) {.gcsafe.} =
units.track proc(changes: seq[Change[Unit]]) {.gcsafe.} =
for change in changes:
@ -156,14 +155,13 @@ proc worker_thread(params: (ZenContext, GameState)) {.gcsafe.} =
worker_lock.acquire
var listen_address = main_thread_state.config.listen_address
let worker_ctx =
ZenContext.init(
id = \"work-{generate_id()}",
chan_size = 500,
buffer = false,
listen_address = listen_address,
label = "worker",
)
let worker_ctx = ZenContext.init(
id = \"work-{generate_id()}",
chan_size = 500,
buffer = false,
listen_address = listen_address,
label = "worker",
)
Zen.thread_ctx = worker_ctx
ctx.subscribe(Zen.thread_ctx)
@ -262,18 +260,17 @@ proc worker_thread(params: (ZenContext, GameState)) {.gcsafe.} =
player.script_ctx.interpreter = worker.interpreter
worker.load_script_and_dependents(player)
var sign =
Sign.init(
"",
"",
width = 4,
height = 3.05,
owner = state.player,
size = 244,
billboard = true,
text_only = true,
transform = Transform.init(origin = vec3(0, 4, 0)),
)
var sign = Sign.init(
"",
"",
width = 4,
height = 3.05,
owner = state.player,
size = 244,
billboard = true,
text_only = true,
transform = Transform.init(origin = vec3(0, 4, 0)),
)
state.player.units += sign
sign.global_flags -= Visible

View File

@ -142,12 +142,13 @@ proc wrap*[T](value, min, max: T): float =
when not defined(no_godot):
import pkg/godot
default_chronicles_stream.output.writer =
proc(logLevel: LogLevel, msg: LogOutputStr) {.gcsafe.} =
when defined(release):
godot.print msg
else:
echo msg
default_chronicles_stream.output.writer = proc(
logLevel: LogLevel, msg: LogOutputStr
) {.gcsafe.} =
when defined(release):
godot.print msg
else:
echo msg
# misc
@ -217,9 +218,8 @@ proc update_action_index*(state: GameState, change: int) =
template watch*[T, O](zen: Zen[T, O], unit: untyped, body: untyped) =
when unit is Unit:
mixin thread_ctx
let zid =
zen.changes:
body
let zid = zen.changes:
body
unit.zids.add(zid)
make_discardable(zid)
else:

View File

@ -62,14 +62,16 @@ gdobj Game of Node:
self.stats.text =
\"""
FPS: {fps}
scale_factor: {state.scale_factor}
vram: {vram}
units: {unit_count}
zen objects: {Zen.thread_ctx.len}
level: {state.level_name}
{get_stats()}
"""
FPS: {fps}
scale_factor: {state.scale_factor}
vram: {vram}
units: {unit_count}
zen objects: {Zen.thread_ctx.len}
level: {state.level_name}
{get_stats()}
"""
state.voxel_tasks =
parse_int($get_stats()["tasks"].as_dictionary["main_thread"])
@ -119,13 +121,12 @@ gdobj Game of Node:
var initial_user_config = load_user_config(get_user_data_dir())
Zen.thread_ctx =
ZenContext.init(
id = \"main-{generate_id()}",
chan_size = 2000,
buffer = true,
label = "main",
)
Zen.thread_ctx = ZenContext.init(
id = \"main-{generate_id()}",
chan_size = 2000,
buffer = true,
label = "main",
)
state = GameState.init
state.nodes.game = self
@ -289,10 +290,12 @@ gdobj Game of Node:
if Connecting.added:
state.status_message =
\"""
# Connecting...
Trying to connect to {state.config.connect_address}.
"""
# Connecting...
Trying to connect to {state.config.connect_address}.
"""
elif Connecting.removed:
state.status_message = ""
@ -360,9 +363,9 @@ gdobj Game of Node:
if event of InputEventKey:
let event = InputEventKey(event)
# Left alt support. raw_code is an enu specific addition
if (host_os == "macosx" and event.raw_code == 58) or (
host_os == "windows" and event.raw_code == 56
) or (host_os == "linux" and event.raw_code == 65513):
if (host_os == "macosx" and event.raw_code == 58) or
(host_os == "windows" and event.raw_code == 56) or
(host_os == "linux" and event.raw_code == 65513):
if event.pressed:
state.push_flag CommandMode
else:

View File

@ -45,9 +45,8 @@ proc processModule*(
while true:
syntaxes.openParser(p, fileIdx, s, graph.cache, graph.config)
if not belongsToStdlib(graph, module) or (
belongsToStdlib(graph, module) and module.name.s == "distros"
):
if not belongsToStdlib(graph, module) or
(belongsToStdlib(graph, module) and module.name.s == "distros"):
# XXX what about caching? no processing then? what if I change the
# modules to include between compilation runs? we'd need to track that
# in ROD files. I think we should enable this feature only
@ -129,13 +128,12 @@ proc selectRoutine*(i: Interpreter, name: string, module_name: string): PSym =
## The routine needs to have the export marker ``*``. The only matching
## routine is returned and ``nil`` if it is overloaded.
{.gcsafe.}:
result =
selectUniqueSymbol(
i,
name,
{skTemplate, skMacro, skFunc, skMethod, skProc, skConverter},
moduleName,
)
result = selectUniqueSymbol(
i,
name,
{skTemplate, skMacro, skFunc, skMethod, skProc, skConverter},
moduleName,
)
proc resetModule*(i: Interpreter, moduleName: string) =
for iface in i.graph.ifaces:
@ -254,9 +252,8 @@ proc `enter_hook=`*(
proc `error_hook=`*(
i: Interpreter,
hook:
proc(config: ConfigRef, info: TLineInfo, msg: string, severity: Severity) {.
gcsafe
.},
hook: proc(
config: ConfigRef, info: TLineInfo, msg: string, severity: Severity
) {.gcsafe.},
) =
i.registerErrorHook(hook)

View File

@ -18,12 +18,11 @@ proc init*(_: type Interpreter, script_dir, vmlib: string): Interpreter =
let std_paths = STDLIB_PATHS.map_it join_path(vmlib, "stdlib", it)
let source_paths = std_paths & join_path(vmlib, "enu") & @[script_dir]
{.gcsafe.}:
result =
create_interpreter(
"base_api.nim",
source_paths,
defines = @{"nimscript": "true", "nimconfig": "true"},
)
result = create_interpreter(
"base_api.nim",
source_paths,
defines = @{"nimscript": "true", "nimconfig": "true"},
)
result.config.max_loop_iterations_vm = int.high
proc pause*(ctx: ScriptCtx) =
@ -80,8 +79,8 @@ proc call_proc*(
self.interpreter.select_routine(proc_name, module_name = self.module_name)
if foreign_proc == nil:
raise new_exception(
VMError, \"script does not export a proc of the name: '{proc_name}'"
)
VMError, \"script does not export a proc of the name: '{proc_name}'"
)
result =
try:
{.gcsafe.}:

View File

@ -4,12 +4,11 @@ import core, models/[states, units, colors]
include "bot_code_template.nim.nimf"
method code_template*(self: Bot, imports: string): string =
result =
bot_code_template(
read_file(self.script_ctx.script).encode(safe = true),
self.script_ctx.script,
imports,
)
result = bot_code_template(
read_file(self.script_ctx.script).encode(safe = true),
self.script_ctx.script,
imports,
)
method on_begin_move*(
self: Bot, direction: Vector3, steps: float, moving_mode: int
@ -20,17 +19,16 @@ method on_begin_move*(
moving = -self.transform.basis.z
finish_time = 1.0 / self.speed * steps
result =
proc(delta: float, _: MonoTime): TaskStates =
duration += delta
if duration >= finish_time:
self.velocity_value.touch(vec3())
self.transform_value.origin =
self.transform.origin.snapped(vec3(0.1, 0.1, 0.1))
return Done
else:
self.velocity_value.touch(moving * self.speed)
return Running
result = proc(delta: float, _: MonoTime): TaskStates =
duration += delta
if duration >= finish_time:
self.velocity_value.touch(vec3())
self.transform_value.origin =
self.transform.origin.snapped(vec3(0.1, 0.1, 0.1))
return Done
else:
self.velocity_value.touch(moving * self.speed)
return Running
method on_begin_turn*(
self: Bot, axis: Vector3, degrees: float, lean: bool, move_mode: int
@ -39,19 +37,16 @@ method on_begin_turn*(
let degrees = degrees * -axis.x
var duration = 0.0
var final_basis = self.transform.basis.rotated(UP, deg_to_rad(degrees))
result =
proc(delta: float, _: MonoTime): TaskStates =
duration += delta
self.transform_value.basis =
self.transform.basis.rotated(
UP, deg_to_rad(degrees * delta * self.speed)
)
result = proc(delta: float, _: MonoTime): TaskStates =
duration += delta
self.transform_value.basis =
self.transform.basis.rotated(UP, deg_to_rad(degrees * delta * self.speed))
if duration <= 1.0 / self.speed:
Running
else:
self.transform_value.basis = final_basis
Done
if duration <= 1.0 / self.speed:
Running
else:
self.transform_value.basis = final_basis
Done
proc bot_at*(state: GameState, position: Vector3): Bot =
for unit in state.units:
@ -81,16 +76,15 @@ proc init*(
global = true,
parent: Unit = nil,
): Bot =
var self =
Bot(
id: id,
start_transform: transform,
animation_value: ~"auto",
speed: 1.0,
clone_of: clone_of,
start_color: action_colors[black],
parent: parent,
)
var self = Bot(
id: id,
start_transform: transform,
animation_value: ~"auto",
speed: 1.0,
clone_of: clone_of,
start_color: action_colors[black],
parent: parent,
)
self.init_unit

View File

@ -22,12 +22,11 @@ var
proc draw*(self: Build, position: Vector3, voxel: VoxelInfo) {.gcsafe.}
method code_template*(self: Build, imports: string): string =
result =
build_code_template(
read_file(self.script_ctx.script).encode(safe = true),
self.script_ctx.script,
imports,
)
result = build_code_template(
read_file(self.script_ctx.script).encode(safe = true),
self.script_ctx.script,
imports,
)
proc buffer(position: Vector3): Vector3 =
(position / ChunkSize).floor
@ -210,9 +209,8 @@ proc remove(self: Build) =
state.skip_block_paint = true
draw_normal = self.target_normal
let point =
self.target_point - self.target_normal - (
self.target_normal.inverse_normalized * 0.5
)
self.target_point - self.target_normal -
(self.target_normal.inverse_normalized * 0.5)
skip_point = vec3()
last_point = self.target_point
@ -271,17 +269,16 @@ method on_begin_move*(
finish = self.transform.origin + moving * steps
finish_time = 1.0 / self.speed * steps
result =
proc(delta: float, _: MonoTime): TaskStates =
duration += delta
if duration >= finish_time:
self.transform_value.origin = finish
return Done
else:
self.transform_value.origin =
self.transform.origin + (moving * self.speed * delta)
result = proc(delta: float, _: MonoTime): TaskStates =
duration += delta
if duration >= finish_time:
self.transform_value.origin = finish
return Done
else:
self.transform_value.origin =
self.transform.origin + (moving * self.speed * delta)
return Running
return Running
else:
if self.speed == 0:
self.voxels_per_frame = float.high
@ -290,20 +287,19 @@ method on_begin_move*(
self.voxels_per_frame = self.speed
var count = 0
result =
proc(delta: float, timeout: MonoTime): TaskStates =
while count.float < steps and self.voxels_remaining_this_frame >= 1 and
get_mono_time() < timeout:
if steps < 1:
self.draw_transform =
self.draw_transform.translated(direction * steps)
else:
self.draw_transform = self.draw_transform.translated(direction)
inc count
self.voxels_remaining_this_frame -= 1
self.drop_block()
result = proc(delta: float, timeout: MonoTime): TaskStates =
while count.float < steps and self.voxels_remaining_this_frame >= 1 and
get_mono_time() < timeout:
if steps < 1:
self.draw_transform =
self.draw_transform.translated(direction * steps)
else:
self.draw_transform = self.draw_transform.translated(direction)
inc count
self.voxels_remaining_this_frame -= 1
self.drop_block()
if count.float >= steps: NextTask else: Running
if count.float >= steps: NextTask else: Running
method on_begin_turn*(
self: Build, axis: Vector3, degrees: float, lean: bool, move_mode: int
@ -326,19 +322,17 @@ method on_begin_turn*(
.rotated(axis, deg_to_rad(degrees)).orthonormalized
.scaled(vec3(scale, scale, scale))
result =
proc(delta: float, _: MonoTime): TaskStates =
duration += delta
self.transform_value.basis =
self.transform.basis.rotated(
axis, deg_to_rad(degrees * delta * self.speed)
)
result = proc(delta: float, _: MonoTime): TaskStates =
duration += delta
self.transform_value.basis = self.transform.basis.rotated(
axis, deg_to_rad(degrees * delta * self.speed)
)
if duration <= 1.0 / self.speed:
Running
else:
self.transform = final_transform
Done
if duration <= 1.0 / self.speed:
Running
else:
self.transform = final_transform
Done
else:
let axis = self.draw_transform.basis.xform(axis)
self.draw_transform_value.basis =
@ -402,20 +396,19 @@ proc init*(
bot_collisions = true,
parent: Unit = nil,
): Build =
var self =
Build(
id: id,
chunks: ~(Table[Vector3, Chunk], {SyncLocal, SyncRemote}),
start_transform: transform,
draw_transform_value: ~(Transform.init, flags = {}),
start_color: color,
drawing: true,
bounds_value: ~init_aabb(vec3(), vec3(-1, -1, -1)),
speed: 1.0,
clone_of: clone_of,
bot_collisions: bot_collisions,
parent: parent,
)
var self = Build(
id: id,
chunks: ~(Table[Vector3, Chunk], {SyncLocal, SyncRemote}),
start_transform: transform,
draw_transform_value: ~(Transform.init, flags = {}),
start_color: color,
drawing: true,
bounds_value: ~init_aabb(vec3(), vec3(-1, -1, -1)),
speed: 1.0,
clone_of: clone_of,
bot_collisions: bot_collisions,
parent: parent,
)
self.init_unit
@ -438,16 +431,16 @@ method main_thread_joined*(self: Build) =
root.walk_tree proc(unit: Unit) =
unit.local_flags -= Highlight
if TargetMoved.touched:
let length =
(
self.target_point * self.target_normal -
last_point * self.target_normal
).length
let length = (
self.target_point * self.target_normal - last_point * self.target_normal
).length
if state.skip_block_paint:
state.skip_block_paint = false
elif state.draw_unit_id == self.id and self.target_normal == draw_normal and
length <= 5 and self.target_point != skip_point:
elif (
state.draw_unit_id == self.id and self.target_normal == draw_normal and
length <= 5 and self.target_point != skip_point
):
if SecondaryDown in state.local_flags:
self.remove
elif PrimaryDown in state.local_flags:
@ -495,16 +488,15 @@ method clone*(self: Build, clone_to: Unit, id: string): Unit =
# we need this off for Potato Zombies, but on for the
# tutorials. Make it configurable somehow.
let bot_collisions = true #not (clone_to of Bot)
let clone =
Build.init(
id = id,
transform = transform,
clone_of = self,
global = global,
color = self.start_color,
bot_collisions = bot_collisions,
parent = clone_to,
)
let clone = Build.init(
id = id,
transform = transform,
clone_of = self,
global = global,
color = self.start_color,
bot_collisions = bot_collisions,
parent = clone_to,
)
for loc, info in self.shared.edits[self.id]:
if info.kind != Hole and loc notin clone.shared.edits[clone.id]:

View File

@ -12,12 +12,11 @@ proc fire(self: Ground, append = false) {.gcsafe.} =
let local = point.local_to(add_to)
add_to.draw(local, (Manual, state.selected_color))
else:
add_to =
Build.init(
transform = Transform.init(origin = point),
global = true,
color = state.selected_color,
)
add_to = Build.init(
transform = Transform.init(origin = point),
global = true,
color = state.selected_color,
)
state.units += add_to
add_to.global_flags += Dirty
@ -28,11 +27,10 @@ proc fire(self: Ground, append = false) {.gcsafe.} =
bot.global_flags += Dirty
proc init*(_: type Ground, node: Spatial): Ground =
let self =
Ground(
global_flags: ~set[GlobalModelFlags],
local_flags: ~(set[LocalModelFlags], {SyncLocal}),
)
let self = Ground(
global_flags: ~set[GlobalModelFlags],
local_flags: ~(set[LocalModelFlags], {SyncLocal}),
)
state.local_flags.changes:
if PrimaryDown.added and Hover in self.local_flags:

View File

@ -3,14 +3,13 @@ import godotapi/spatial
import core, models/units
proc init*(_: type Player): Player =
result =
Player(
id: \"player-{Zen.thread_ctx.id}",
rotation_value: ~0.0,
start_transform: Transform.init(origin = vec3(0, 1, 0)),
input_direction_value: ~Vector3,
cursor_position_value: ~((0, 0)),
)
result = Player(
id: \"player-{Zen.thread_ctx.id}",
rotation_value: ~0.0,
start_transform: Transform.init(origin = vec3(0, 1, 0)),
input_direction_value: ~Vector3,
cursor_position_value: ~((0, 0)),
)
result.init_unit(shared = false)
result.global_flags += Global

View File

@ -80,12 +80,11 @@ proc from_json_hook(self: var Transform, json: JsonNode) =
proc from_json_hook(self: var Build, json: JsonNode) =
let color = json["start_color"].json_to(Color)
self =
Build.init(
id = json["id"].json_to(string),
transform = json["start_transform"].json_to(Transform),
color = color,
)
self = Build.init(
id = json["id"].json_to(string),
transform = json["start_transform"].json_to(Transform),
color = color,
)
if load_chunks:
var edit = ~Table[Vector3, VoxelInfo]()
@ -95,11 +94,10 @@ proc from_json_hook(self: var Build, json: JsonNode) =
self.shared.edits.from_json(json["edits"])
proc from_json_hook(self: var Bot, json: JsonNode) =
self =
Bot.init(
id = json["id"].json_to(string),
transform = json["start_transform"].json_to(Transform),
)
self = Bot.init(
id = json["id"].json_to(string),
transform = json["start_transform"].json_to(Transform),
)
if not load_chunks:
self.shared.edits.from_json(json["edits"])
@ -117,16 +115,14 @@ proc `$`(self: tuple[voxel: Vector3, info: VoxelInfo]): string =
\"[{self.voxel}, [{int self.info.kind}, {self.info.color}]]"
proc `$`(self: ZenTable[string, ZenTable[Vector3, VoxelInfo]]): string =
let edits =
collect:
for id, edit in self.value:
let json =
collect:
for voxel, info in edit.value:
$(voxel, info)
if json.len > 0:
let elements = json.join(",\n").indent(2)
\"\"{id}\": [\n{elements}\n]"
let edits = collect:
for id, edit in self.value:
let json = collect:
for voxel, info in edit.value:
$(voxel, info)
if json.len > 0:
let elements = json.join(",\n").indent(2)
\"\"{id}\": [\n{elements}\n]"
result = edits.join(",\n")
proc `$`(self: Unit): string =
@ -134,20 +130,23 @@ proc `$`(self: Unit): string =
let edits = $self.shared.edits
result =
\"""
{{
"id": "{self.id}",
"start_transform": {{
"basis": [
{elements.indent(6)}
],
"origin": {$self.start_transform.origin}
}},
"start_color": {self.start_color},
"edits": {{
{edits.indent(4)}
}}
}}
"""
{{
"id": "{self.id}",
"start_transform": {{
"basis": [
{elements.indent(6)}
],
"origin": {$self.start_transform.origin}
}},
"start_color": {self.start_color},
"edits": {{
{edits.indent(4)}
}}
}}
"""
proc save*(unit: Unit) =
if not ?unit.clone_of:
@ -184,8 +183,9 @@ proc backup_level*(level_dir: string) =
backup_dir / state.config.level & "_" &
times.now().format("yyyy-MM-dd-HH-mm-ss") & ".zip"
let backups =
walk_files(backup_dir / state.config.level & "_????-??-??-??-??-??.zip").to_seq.sorted
let backups = walk_files(
backup_dir / state.config.level & "_????-??-??-??-??-??.zip"
).to_seq.sorted
if backups.len > 19:
for file in backups[0 ..^ 20]:

View File

@ -13,22 +13,21 @@ proc init*(
billboard = false,
text_only = false,
): Sign =
var self =
Sign(
id: "sign_" & generate_id(),
message_value: ~message,
more_value: ~more,
width_value: ~width,
height_value: ~height,
size_value: ~size,
billboard_value: ~billboard,
frame_created: state.frame_count,
start_color: action_colors[black],
start_transform: transform,
owner_value: ~owner,
text_only: text_only,
parent: owner,
)
var self = Sign(
id: "sign_" & generate_id(),
message_value: ~message,
more_value: ~more,
width_value: ~width,
height_value: ~height,
size_value: ~size,
billboard_value: ~billboard,
frame_created: state.frame_count,
start_color: action_colors[black],
start_transform: transform,
owner_value: ~owner,
text_only: text_only,
parent: owner,
)
self.init_unit
result = self

View File

@ -124,24 +124,23 @@ proc err*(self: GameState, args: varargs[string, `$`]) =
proc init*(_: type GameState): GameState =
let flags = {SyncLocal}
let self =
GameState(
player_value: ~(Player, flags),
local_flags: ~(set[LocalStateFlags], flags),
global_flags: ~(set[GlobalStateFlags], id = "state_global_flags"),
units: ~(seq[Unit], id = "root_units"),
open_unit_value: ~(Unit, flags),
config_value: ~(Config, flags, id = "config"),
tool_value: ~(BlueBlock, flags),
gravity: -80.0,
console: ConsoleModel(log: ~(seq[string], flags)),
open_sign_value: ~(Sign, flags),
wants: ~(seq[LocalStateFlags], flags),
level_name_value: ~("", id = "level_name"),
queued_action_value: ~("", flags),
status_message_value: ~("", flags),
voxel_tasks_value: ~(0, flags),
)
let self = GameState(
player_value: ~(Player, flags),
local_flags: ~(set[LocalStateFlags], flags),
global_flags: ~(set[GlobalStateFlags], id = "state_global_flags"),
units: ~(seq[Unit], id = "root_units"),
open_unit_value: ~(Unit, flags),
config_value: ~(Config, flags, id = "config"),
tool_value: ~(BlueBlock, flags),
gravity: -80.0,
console: ConsoleModel(log: ~(seq[string], flags)),
open_sign_value: ~(Sign, flags),
wants: ~(seq[LocalStateFlags], flags),
level_name_value: ~("", id = "level_name"),
queued_action_value: ~("", flags),
status_message_value: ~("", flags),
voxel_tasks_value: ~(0, flags),
)
result = self
self.open_unit_value.changes:
if added and change.item != nil:

View File

@ -56,9 +56,8 @@ proc find_root*(self: Unit, all_clones = false): Unit =
while parent != nil:
result = parent
if (all_clones and not ?parent.clone_of) or (
not all_clones and Global in parent.global_flags
):
if (all_clones and not ?parent.clone_of) or
(not all_clones and Global in parent.global_flags):
parent = nil
else:
parent = parent.parent

View File

@ -88,8 +88,9 @@ gdobj AimTarget of Sprite3D:
self.look_at(align_normal, self.transform.basis.x)
if ?unit:
if (unit.target_point, unit.target_normal) !=
(local_point, local_normal):
if (unit.target_point, unit.target_normal) != (
local_point, local_normal
):
unit.target_point = local_point
unit.target_normal = local_normal
unit.local_flags.touch TargetMoved

View File

@ -112,11 +112,10 @@ gdobj BotNode of KinematicBody:
var velocity_zid: ZID
if self.model of Bot:
let bot = Bot(self.model)
velocity_zid =
bot.velocity_value.watch:
if touched:
if bot.animation == "auto":
self.set_walk_animation(change.item.length, false)
velocity_zid = bot.velocity_value.watch:
if touched:
if bot.animation == "auto":
self.set_walk_animation(change.item.length, false)
bot.animation_value.watch:
if added or touched and change.item in ["", "auto"]:
self.animation_player.play("idle")
@ -152,10 +151,9 @@ gdobj BotNode of KinematicBody:
if added:
self.set_color(change.item)
self.transform_zid =
self.model.transform_value.watch:
if added:
self.transform = change.item
self.transform_zid = self.model.transform_value.watch:
if added:
self.transform = change.item
self.model.sight_query_value.watch:
if added:

View File

@ -78,9 +78,11 @@ gdobj BuildNode of VoxelTerrain:
"emission", self.model.shared.emission_colors[i].to_variant
)
if Highlight in self.model.local_flags or (
HighlightError in self.model.local_flags and self.error_highlight_on
):
if Highlight in self.model.local_flags or
(
HighlightError in self.model.local_flags and
self.error_highlight_on
):
m.set_shader_param("emission_energy", highlight_glow.to_variant)
else:
m.set_shader_param("emission_energy", self.model.glow.to_variant)
@ -88,14 +90,13 @@ gdobj BuildNode of VoxelTerrain:
proc track_chunk(chunk_id: Vector3) =
if chunk_id in self.model.chunks:
self.draw_block(self.model.chunks[chunk_id])
self.active_chunks[chunk_id] =
self.model.chunks[chunk_id].watch:
# `and not modified` isn't required, but the block will be
# replaced on the next iteration anyway.
if removed and not modified:
self.draw(change.item.key, action_colors[eraser])
elif added:
self.draw(change.item.key, change.item.value.color)
self.active_chunks[chunk_id] = self.model.chunks[chunk_id].watch:
# `and not modified` isn't required, but the block will be
# replaced on the next iteration anyway.
if removed and not modified:
self.draw(change.item.key, action_colors[eraser])
elif added:
self.draw(change.item.key, change.item.value.color)
self.draw_block(self.model.chunks[chunk_id])
else:
self.active_chunks[chunk_id] = empty_zid
@ -126,14 +127,13 @@ gdobj BuildNode of VoxelTerrain:
self.visible = false
proc track_chunks() =
self.chunks_zid =
self.model.chunks.watch:
let id = change.item.key
if id in self.active_chunks:
if added:
self.track_chunk(change.item.key)
elif removed:
self.active_chunks[id] = empty_zid
self.chunks_zid = self.model.chunks.watch:
let id = change.item.key
if id in self.active_chunks:
if added:
self.track_chunk(change.item.key)
elif removed:
self.active_chunks[id] = empty_zid
proc untrack_chunks() =
Zen.thread_ctx.untrack(self.chunks_zid)
@ -191,10 +191,9 @@ gdobj BuildNode of VoxelTerrain:
self.model.transform = self.transform
self.max_view_distance = int(self.default_view_distance.float / scale)
self.transform_zid =
self.model.transform_value.watch:
if added:
self.transform = change.item
self.transform_zid = self.model.transform_value.watch:
if added:
self.transform = change.item
self.model.sight_query_value.watch:
if added:

View File

@ -160,15 +160,13 @@ gdobj PlayerNode of KinematicBody:
self.transform = change.item
self.camera_rig.rotation = vec3(0, deg_to_rad self.model.rotation, 0)
self.rotation_zid =
self.model.rotation_value.watch:
if added or touched:
self.camera_rig.rotation = vec3(0, deg_to_rad change.item, 0)
self.rotation_zid = self.model.rotation_value.watch:
if added or touched:
self.camera_rig.rotation = vec3(0, deg_to_rad change.item, 0)
self.velocity_zid =
self.model.velocity_value.watch:
if added:
self.velocity = change.item
self.velocity_zid = self.model.velocity_value.watch:
if added:
self.velocity = change.item
proc current_raycast*(): RayCast =
if MouseCaptured in state.local_flags: self.aim_ray else: self.world_ray
@ -176,8 +174,7 @@ gdobj PlayerNode of KinematicBody:
method process*(delta: float) =
self.model.velocity_value.pause self.velocity_zid:
self.model.velocity = self.velocity
if EditorVisible notin state.local_flags or
CommandMode in state.local_flags:
if EditorVisible notin state.local_flags or CommandMode in state.local_flags:
var transform = self.camera_rig.global_transform
transform.origin = self.global_transform.origin + self.position_start
@ -243,20 +240,18 @@ gdobj PlayerNode of KinematicBody:
move_direction.y = 0
move_direction += up
var velocity =
self.calculate_velocity(
self.velocity, move_direction, delta, self.flying, self.alt_speed
)
var velocity = self.calculate_velocity(
self.velocity, move_direction, delta, self.flying, self.alt_speed
)
self.model.input_direction = input_direction
self.velocity = self.move_and_slide(velocity, UP)
self.model.transform = self.transform
let collisions =
collect:
for i in 0 .. (self.get_slide_count - 1):
self.get_slide_collision(i)
let collisions = collect:
for i in 0 .. (self.get_slide_count - 1):
self.get_slide_collision(i)
handle_collisions(self.model, collisions)
@ -304,9 +299,8 @@ gdobj PlayerNode of KinematicBody:
self.input_relative += event.as(InputEventMouseMotion).relative()
else:
self.skip_next_mouse_move = false
if EditorVisible in state.local_flags and not self.skip_release and (
event of InputEventJoypadButton or event of InputEventJoypadMotion
):
if EditorVisible in state.local_flags and not self.skip_release and
(event of InputEventJoypadButton or event of InputEventJoypadMotion):
let active_input = self.has_active_input(event.device.int)
if CommandMode in state.local_flags and not active_input:
self.command_timer = input_command_timeout

View File

@ -82,8 +82,9 @@ gdobj Editor of TextEdit:
state.player.open_code = self.text
method on_cursor_changed*() =
state.player.cursor_position =
(int self.cursor_get_line, int self.cursor_get_column)
state.player.cursor_position = (
int self.cursor_get_line, int self.cursor_get_column
)
method ready*() =
self.bind_signals(self, "text_changed", "cursor_changed")
@ -107,14 +108,13 @@ gdobj Editor of TextEdit:
self.visible = false
state.player.open_code = ""
else:
line_zid =
unit.current_line_value.changes:
if added:
# only update the executing line if the code hasn't been changed.
if self.text == state.open_unit.code.nim:
self.executing_line = change.item - 1
else:
self.clear_executing_line()
line_zid = unit.current_line_value.changes:
if added:
# only update the executing line if the code hasn't been changed.
if self.text == state.open_unit.code.nim:
self.executing_line = change.item - 1
else:
self.clear_executing_line()
self.visible = true
self.text = state.open_unit.code.nim
state.player.open_code = self.text

View File

@ -114,10 +114,9 @@ gdobj MarkdownLabel of ScrollContainer:
self.og_text_edit.add_font_override("font", self.local_mono_font)
self.og_label.add_font_override("normal_font", self.local_default_font)
self.zid =
state.config_value.changes:
if added:
self.set_font_sizes()
self.zid = state.config_value.changes:
if added:
self.set_font_sizes()
self.update

View File

@ -40,11 +40,10 @@ gdobj RightPanel of MarginContainer:
var sign = change.item
self.label.markdown = md(sign, sign.more)
self.label.update
self.zid =
sign.more_value.changes:
if added:
self.label.markdown = md(sign, change.item)
self.label.update
self.zid = sign.more_value.changes:
if added:
self.label.markdown = md(sign, change.item)
self.label.update
if removed and change.item != nil:
if change.item.more_value.valid:
change.item.more_value.untrack(self.zid)
@ -63,8 +62,7 @@ gdobj RightPanel of MarginContainer:
self.modulate = solid_alpha
method unhandled_input*(event: InputEvent) =
if DocsFocused in state.local_flags and
event.is_action_pressed("ui_cancel"):
if DocsFocused in state.local_flags and event.is_action_pressed("ui_cancel"):
if not (event of InputEventJoypadButton) or
CommandMode notin state.local_flags:
state.open_sign = nil

View File

@ -27,12 +27,11 @@ gdobj Toolbar of HBoxContainer:
self.visible = true
state.tool = BlueBlock
self.zid =
state.tool_value.changes:
if added:
let b = self.get_child(int(change.item)) as Button
if ?b:
b.set_pressed true
self.zid = state.tool_value.changes:
if added:
let b = self.get_child(int(change.item)) as Button
if ?b:
b.set_pressed true
method process*(delta: float) =
if self.preview_result.is_some:

View File

@ -24,7 +24,7 @@ block:
echo "count1: " & $count1 & " count2: " & $count2
nil -> task1
if count1 > 5:
task1 -> task2:
task1 -> task2 do:
echo "finished in main"
do:
inc count2
@ -102,7 +102,7 @@ block:
nil -> action_b(name)
inc counter
if counter == 2:
action_b -> loop_b as loop_b2:
action_b -> loop_b as loop_b2 do:
counter = -20
loop_b2 -> action_c(name)
if counter == 6:
@ -119,19 +119,19 @@ block:
counter = 0
var name = "loop_main"
loop nil:
nil -> loop_a as initial_loop:
nil -> loop_a as initial_loop do:
echo "initial loop "
inc counter
if done:
initial_loop -> action_a(name):
initial_loop -> action_a(name) do:
counter = 0
if counter == 3:
action_a -> action_b(name):
action_a -> action_b(name) do:
counter = 0
action_b -> action_c(name) as ac:
action_b -> action_c(name) as ac do:
counter = 0
ac -> loop_a:
ac -> loop_a do:
counter = 0
if counter == 70:
loop_a -> nil:
loop_a -> nil do:
echo "loop_main done ", counter

View File

@ -7,14 +7,12 @@ let
vmlib = "vmlib"
e = Engine()
script_dir = nim_filename().parent_dir & "/scripts/instancing"
script_1 =
"""
script_1 = """
var place = "script 1 header"
proc in_script_1_header*() = echo "in script 1 header"
include "script_1.nim"
""".dedent
script_2 =
"""
""".dedent
script_2 = """
import script_1
var place = "script 2 header"
proc in_script_2_header*() = echo "in script 2 header"
@ -23,7 +21,7 @@ let
in_script_1_body()
in_script_2_header()
in_script_2_body()
""".dedent
""".dedent
e.load(script_dir, script_dir & "/script_1.nim", script_1, vmlib)
assert not e.run()

View File

@ -5,9 +5,8 @@ import libs/[interpreters, eval]
var state = GameState.active
state.logger =
proc(level, msg: string) =
echo level, ": ", msg
state.logger = proc(level, msg: string) =
echo level, ": ", msg
state.config.script_dir =
current_source_path().parent_dir / "scripts" / "instancing"

View File

@ -9,8 +9,7 @@ let f: float = 1 .. 100
echo "float: ", f
var position = vec3(1.0, 1.0, 1.0)
position =
vec3(
position.x + -2.0 .. 2.0, position.y + -2.0 .. 2.0, position.z + -2.0 .. 2.0
)
position = vec3(
position.x + -2.0 .. 2.0, position.y + -2.0 .. 2.0, position.z + -2.0 .. 2.0
)
echo position

View File

@ -8,28 +8,26 @@ let
e = Engine()
script_dir = nim_filename().parent_dir & "/scripts"
prefix = "proc log*(s:string) = discard\n"
user_classes1 =
"""
user_classes1 = """
type
Type1* = object
name*: string
""".dedent
user_classes2 =
"""
""".dedent
user_classes2 = """
type
Type1* = object
name*: string
size*: int
Type2* = object
name*: string
""".dedent
""".dedent
script1 =
prefix &
"""
import user_classes
let a = Type1(name: "type1")
log "a=" & a.repr
""".dedent
""".dedent
script2 =
prefix &
"""
@ -38,7 +36,7 @@ let
let b = Type2(name: "type2")
log "a=" & a.repr
log "b=" & b.repr
""".dedent
""".dedent
e.load(script_dir, script_dir & "/user_classes.nim", user_classes1, vmlib)
assert not e.run()
@ -62,4 +60,4 @@ assert output ==
a=(name: "type1")
a=(name: "type1", size: 5)
b=(name: "type2")
""".dedent
""".dedent

View File

@ -3,17 +3,14 @@ import globals, os
proc echo(args: varargs[string, `$`]) =
stdout.write "Enu: " & args.join() & "\n"
echo_console =
proc(msg: string) =
echo msg
echo_console = proc(msg: string) =
echo msg
logger =
proc(level, msg: string) =
echo level, ": ", msg
logger = proc(level, msg: string) =
echo level, ": ", msg
game_node = gdnew[Node]()
config =
Config(
lib_dir: get_current_dir() & "/../vmlib",
script_dir: nim_filename().parent_dir & "/scripts",
)
config = Config(
lib_dir: get_current_dir() & "/../vmlib",
script_dir: nim_filename().parent_dir & "/scripts",
)

View File

@ -24,7 +24,7 @@ block:
echo "count1: " & $count1 & " count2: " & $count2
nil -> task1
if count1 > 5:
task1 -> task2:
task1 -> task2 do:
echo "finished in main"
do:
inc count2
@ -102,7 +102,7 @@ block:
nil -> action_b(name)
inc counter
if counter == 2:
action_b -> loop_b as loop_b2:
action_b -> loop_b as loop_b2 do:
counter = -20
loop_b2 -> action_c(name)
if counter == 6:
@ -119,19 +119,19 @@ block:
counter = 0
var name = "loop_main"
loop nil:
nil -> loop_a as initial_loop:
nil -> loop_a as initial_loop do:
echo "initial loop "
inc counter
if done:
initial_loop -> action_a(name):
initial_loop -> action_a(name) do:
counter = 0
if counter == 3:
action_a -> action_b(name):
action_a -> action_b(name) do:
counter = 0
action_b -> action_c(name) as ac:
action_b -> action_c(name) as ac do:
counter = 0
ac -> loop_a:
ac -> loop_a do:
counter = 0
if counter == 70:
loop_a -> nil:
loop_a -> nil do:
echo "loop_main done ", counter

View File

@ -457,9 +457,8 @@ template x*(count: int, body: untyped): untyped =
macro dump*(x: typed): untyped =
let s = x.toStrLit
let r =
quote:
echo(`s` & " = " & $`x`)
let r = quote:
echo(`s` & " = " & $`x`)
return r
template cycle*[T](args: varargs[T]): T =

View File

@ -81,13 +81,12 @@ proc params_to_accessors(type_name: NimNode, nodes: seq[NimNode]): NimNode =
proc build_ctors(
name_str: string, type_name: NimNode, params: seq[NimNode]
): NimNode =
var ctor_body =
quote:
assert not instance.is_nil
link_dependency(instance)
result = `type_name`()
result.seed = active_unit().seed
new_instance(instance, result)
var ctor_body = quote:
assert not instance.is_nil
link_dependency(instance)
result = `type_name`()
result.seed = active_unit().seed
new_instance(instance, result)
for param in params:
let prop = param[0]
@ -129,13 +128,12 @@ proc build_ctors(
# add baked in constructor params for speed, color, etc.
# probably shouldn't be here.
result =
new_proc(
name = "new".ident.postfix("*"),
params = params,
pragmas = nnkPragma.new_tree("discardable".ident),
body = ctor_body,
)
result = new_proc(
name = "new".ident.postfix("*"),
params = params,
pragmas = nnkPragma.new_tree("discardable".ident),
body = ctor_body,
)
proc extract_class_info(
name_node: NimNode
@ -166,9 +164,8 @@ proc build_class(name_node: NimNode, base_type: NimNode): NimNode =
result = new_stmt_list()
let name_str = name
var type_def =
quote:
type `type_name`* = ref object of `base_type`
var type_def = quote:
type `type_name`* = ref object of `base_type`
type_def[0][2][0][2] = params_to_properties(params)
let accessors = params_to_accessors(type_name, params)
@ -240,13 +237,12 @@ proc auto_insert_receiver(
proc build_proc(sig, body: NimNode, return_type = new_empty_node()): NimNode =
let (name, params, vars) = sig.parse_sig(return_type)
let new_body = new_stmt_list(vars, body)
result =
new_proc(
name = ident(name),
params = params,
body = new_body,
pragmas = new_nim_node(nnkPragma).add(ident"discardable"),
)
result = new_proc(
name = ident(name),
params = params,
body = new_body,
pragmas = new_nim_node(nnkPragma).add(ident"discardable"),
)
proc transform_commands(parent: NimNode): NimNode =
for i, node in parent:

View File

@ -1,12 +1,11 @@
var context: Context
me.advance_state_machine =
proc(): bool =
result =
if not context.is_nil:
context.advance()
else:
true
me.advance_state_machine = proc(): bool =
result =
if not context.is_nil:
context.advance()
else:
true
proc loop_started(ctx: Context, main_loop: bool) =
if main_loop:

View File

@ -114,12 +114,11 @@ macro loop*(body: untyped) =
macro loop*(sig: untyped, body: untyped): untyped =
var (name, params, vars) = sig.parse_sig
let proc_body =
quote:
const this_state {.inject.} = `name . ast_to_str`
`vars`
loop:
`body`
let proc_body = quote:
const this_state {.inject.} = `name . ast_to_str`
`vars`
loop:
`body`
result = new_stmt_list()
params.add new_ident_defs(ident"ctx", ident"Context", new_nil_lit())
@ -129,9 +128,8 @@ macro loop*(sig: untyped, body: untyped): untyped =
macro smart_call*(call: untyped) =
var call_without_ctx = call.copy_nim_tree
call_without_ctx.del call_without_ctx.len - 1
result =
quote:
when compiles(`call`): `call` else: `call_without_ctx`
result = quote:
when compiles(`call`): `call` else: `call_without_ctx`
proc transition(from_state, to_state, body, immediate: NimNode): NimNode =
var
@ -208,35 +206,34 @@ proc transition(from_state, to_state, body, immediate: NimNode): NimNode =
includes_str = includes.join(",")
excludes_str = excludes.join(",")
result =
quote:
when not declared(current_state) or not declared(ctx):
{.error: "`->` or `==>` must be inside a `loop` ".}
if `immediate` or (active and done):
let
from_includes: seq[string] = `includes_str`.split(",")
from_excludes: seq[string] = `excludes_str`.split(",")
if (
current_state in from_includes or (
current_state != "nil" and (
("others" in from_includes and `to_state_name` != current_state) or
"any" in from_includes
)
result = quote:
when not declared(current_state) or not declared(ctx):
{.error: "`->` or `==>` must be inside a `loop` ".}
if `immediate` or (active and done):
let
from_includes: seq[string] = `includes_str`.split(",")
from_excludes: seq[string] = `excludes_str`.split(",")
if (
current_state in from_includes or (
current_state != "nil" and (
("others" in from_includes and `to_state_name` != current_state) or
"any" in from_includes
)
) and current_state notin from_excludes:
current_state = `to_state_name`
first_iteration = true
proc action() =
if first_iteration:
`body`
smart_call(`to_state`)
first_iteration = false
)
) and current_state notin from_excludes:
current_state = `to_state_name`
first_iteration = true
proc action() =
if first_iteration:
`body`
smart_call(`to_state`)
first_iteration = false
if `to_state_name` != "nil":
frame.action = action
else:
frame.action = nil
raise (ref Halt)()
if `to_state_name` != "nil":
frame.action = action
else:
frame.action = nil
raise (ref Halt)()
macro `==>`*(from_state: untyped, to_state: untyped, body: untyped = nil) =
result = transition(from_state, to_state, body, ident"true")
@ -257,7 +254,7 @@ when is_main_module:
echo "count: " & $count
nil -> task1
if count > 5:
task1 -> task2:
task1 -> task2 do:
echo "finished in main"
if count > 10:
echo "true true"
@ -288,7 +285,7 @@ when is_main_module:
loop:
if counter > 100:
(little_square, big_square) -> nil
(nil, big_square) -> square(counter) as little_square:
(nil, big_square) -> square(counter) as little_square do:
inc counter
little_square -> square(2) as big_square
@ -321,7 +318,7 @@ when is_main_module:
nil -> action_b(name)
inc counter
if counter == 2:
action_b ==> loop_b:
action_b ==> loop_b do:
counter = -20
loop_b ==> action_c(name)
if counter == 6:
@ -337,21 +334,21 @@ when is_main_module:
counter = 0
var name = "loop_main"
loop:
nil ==> loop_a as initial_loop:
nil ==> loop_a as initial_loop do:
echo "initial loop "
inc counter
if done:
initial_loop ==> action_a(name):
initial_loop ==> action_a(name) do:
counter = 0
if counter == 3:
action_a ==> action_b(name):
action_a ==> action_b(name) do:
counter = 0
action_b ==> action_c(name) as ac:
action_b ==> action_c(name) as ac do:
counter = 0
ac ==> loop_a:
ac ==> loop_a do:
counter = 0
if counter == 70:
loop_a ==> nil:
loop_a ==> nil do:
echo "loop_main done ", counter
loop:

View File

@ -31,9 +31,8 @@ macro bridged_to_host*(body: untyped): untyped =
let params = node.params
var call = new_call impl_proc
var def = copy_nim_tree(node)
def.body =
quote:
raise_assert `impl_proc` & " must be implemented by host"
def.body = quote:
raise_assert `impl_proc` & " must be implemented by host"
def[0] = ident(impl_proc)
if params.len > 1:

View File

@ -5,29 +5,33 @@ lock = true
let overview =
\"""
World `{world_name()}`
Level `{level_name()}`
"""
World `{world_name()}`
Level `{level_name()}`
"""
let details =
\"""
# Menu
Welcome to level `{level_name()}` of world `{world_name()}`.
# Menu
- [Next Level](<nim://press_action("next_level")>)
Welcome to level `{level_name()}` of world `{world_name()}`.
- [Previous Level](<nim://press_action("prev_level")>)
- [Next Level](<nim://press_action("next_level")>)
- [Help](https://ē.nu/docs/intro.html)
- [Previous Level](<nim://press_action("prev_level")>)
- [Load Tutorial](<nim://load_level("tutorial-1", "tutorial")>)
- [Help](https://ē.nu/docs/intro.html)
- [Load Examples](<nim://load_level("tutorial-2", "tutorial")>)
- [Load Tutorial](<nim://load_level("tutorial-1", "tutorial")>)
- [Reset Level](<nim://reset_level()>)
"""
- [Load Examples](<nim://load_level("tutorial-2", "tutorial")>)
- [Reset Level](<nim://reset_level()>)
"""
turn 180
up 5

View File

@ -39,30 +39,32 @@ proc get_bot(): Bot =
-movement_info:
say "# . . .",
"""
# Welcome to Enu!
Enu is a 3D world where you can code and create almost anything using a
programming language called [Nim](https://nim-lang.org). It's a place to
make games, experiment, and learn to code. You're using an early prototype
that's missing important features and is full of bugs, but it's getting
better quickly. Please contact me at [dsrw@me.com](mailto:dsrw@me.com) if
you have any questions or suggestions.
# Welcome to Enu!
In this tutorial you'll learn to move and do some simple coding. In later
tutorials we'll make towers, tunnels, characters, and will finish with a 3D
survival game called `Inky: Isolation`.
Enu is a 3D world where you can code and create almost anything using a
programming language called [Nim](https://nim-lang.org). It's a place to make
games, experiment, and learn to code. You're using an early prototype that's
missing important features and is full of bugs, but it's getting better quickly.
Please contact me at [dsrw@me.com](mailto:dsrw@me.com) if you have any questions
or suggestions.
Move with the `W`, `A`, `S`, and `D` keys, or with the left stick of your
game controller. Look around with the mouse, trackpad, or right stick of
your game controller.
In this tutorial you'll learn to move and do some simple coding. In later
tutorials we'll make towers, tunnels, characters, and will finish with a 3D
survival game called `Inky: Isolation`.
You can close this window by pressing `ESC` on your keyboard or `B` on your
controller, and can come back at any time by clicking on the text block next
to the `Bot`. You can also move without closing the window by using your
controller, or by holding down left `alt/option (⌥)` on your keyboard.
Move with the `W`, `A`, `S`, and `D` keys, or with the left stick of your game
controller. Look around with the mouse, trackpad, or right stick of your game
controller.
Sneak behind the `Bot` to continue the tutorial.
""",
You can close this window by pressing `ESC` on your keyboard or `B` on your
controller, and can come back at any time by clicking on the text block next to
the `Bot`. You can also move without closing the window by using your
controller, or by holding down left `alt/option (⌥)` on your keyboard.
Sneak behind the `Bot` to continue the tutorial.
""",
width = 0.6
menu.show = true
@ -72,20 +74,22 @@ proc get_bot(): Bot =
-flying_info:
say "- Good!",
"""
# Jumping and Flying
Jump with the `space` bar, or the `A` button on your game controller.
# Jumping and Flying
Fly by jumping twice quickly. Fly upwards by holding the jump button, and
downwards by holding crouch (`C` on the keyboard or `B` on a game
controller). Return to the ground by pressing the `jump` button twice.
Jump with the `space` bar, or the `A` button on your game controller.
Flying is a great way to see things from a better angle and to zip around
your world at high speed. It also lets you move through objects and blocks,
so if you ever get yourself stuck you can probably fly your way out.
Fly by jumping twice quickly. Fly upwards by holding the jump button, and
downwards by holding crouch (`C` on the keyboard or `B` on a game controller).
Return to the ground by pressing the `jump` button twice.
Jump, fly, then returning to the ground to continue the tutorial.
""",
Flying is a great way to see things from a better angle and to zip around your
world at high speed. It also lets you move through objects and blocks, so if you
ever get yourself stuck you can probably fly your way out.
Jump, fly, then returning to the ground to continue the tutorial.
""",
width = 1.4
speed = 1
@ -107,22 +111,24 @@ proc get_bot(): Bot =
-tool_info:
say "- Switch to `Place Bot`",
"""
# Changing tools
Tools are selected from the `Tool Bar` on the bottom of the screen.
# Changing tools
Change tools with the keyboard keys `1` - `8`, the `L1` and `R1` controller
buttons, or by releasing the mouse with `ESC` and selecting a tool with the
mouse pointer.
Tools are selected from the `Tool Bar` on the bottom of the screen.
Enu currently has 8 tools. Tool `1` on the left is the `Code` tool. It can
program almost anything in Enu, and will be explained in more detail later.
Tool `8` on the right is the `Place Bot` tool. Use it to fill your world
with friendly robots. Tools `2` - `7` are the `blue`, `red`, `green`,
`black`, `white`, and `brown` colored blocks.
Change tools with the keyboard keys `1` - `8`, the `L1` and `R1` controller
buttons, or by releasing the mouse with `ESC` and selecting a tool with the
mouse pointer.
Select the `Place Bot` tool to continue the tutorial.
""",
Enu currently has 8 tools. Tool `1` on the left is the `Code` tool. It can
program almost anything in Enu, and will be explained in more detail later.
Tool `8` on the right is the `Place Bot` tool. Use it to fill your world with
friendly robots. Tools `2` - `7` are the `blue`, `red`, `green`, `black`,
`white`, and `brown` colored blocks.
Select the `Place Bot` tool to continue the tutorial.
""",
width = 2.5
sign.open = true
@ -133,15 +139,17 @@ proc get_bot(): Bot =
-bot_info:
say "- Place a `Bot`",
"""
# Bots
`Bots` are NPCs in Enu, and can be programmed to explore, change their
appearance, or offer information.
# Bots
They can be placed with the `left` mouse button or the `R1` gamepad trigger.
`Bots` are NPCs in Enu, and can be programmed to explore, change their
appearance, or offer information.
Place a `Bot` on the ground to continue the tutorial.
""",
They can be placed with the `left` mouse button or the `R1` gamepad trigger.
Place a `Bot` on the ground to continue the tutorial.
""",
width = 2.0
sign.open = true
@ -170,15 +178,17 @@ proc get_bot(): Bot =
-code_info:
say "- Coding",
"""
# Coding Enu
We control Enu using [Nim](https://nim-lang.org), a powerful programming
language that's useful for a wide variety of tasks. Almost everything in Enu
can be coded with Nim. Let's start by teaching our `Bot` to walk to the end
of the course.
# Coding Enu
Switch to the `Code` tool to continue the tutorial.
"""
We control Enu using [Nim](https://nim-lang.org), a powerful programming
language that's useful for a wide variety of tasks. Almost everything in Enu
can be coded with Nim. Let's start by teaching our `Bot` to walk to the end of
the course.
Switch to the `Code` tool to continue the tutorial.
"""
sign.open = true
while player.tool != CodeMode:
@ -188,13 +198,15 @@ proc get_bot(): Bot =
-open_code:
sign.more =
"""
# Coding Enu
With the `Code` tool selected, you can code anything you've created by
clicking on it with the `left` mouse button, or `R1` on the controller
# Coding Enu
Open the code for your `Bot` to continue the tutorial.
"""
With the `Code` tool selected, you can code anything you've created by clicking
on it with the `left` mouse button, or `R1` on the controller
Open the code for your `Bot` to continue the tutorial.
"""
sign.open = true
var bot = get_bot()
@ -206,40 +218,42 @@ proc get_bot(): Bot =
-bot_navigation:
sign.more =
"""
# Coding Bots
We control the bot with simple commands like `forward` and `turn`. Here's
some code to get our `Bot` partway through the course:
# Coding Bots
```nim
forward 17
turn right
forward 10
turn left
```
We control the bot with simple commands like `forward` and `turn`. Here's some
code to get our `Bot` partway through the course:
Please finish the code to get the `Bot` get to the end of the course.
```nim
forward 17
turn right
forward 10
turn left
```
Run your code by pressing left `alt/option (⌥)`. Keep left `alt/option (⌥)`
held down to move around while you're coding. Flying is a great way to get
yourself into position to see what's happening, so try double jumping to get
a better view.
Please finish the code to get the `Bot` get to the end of the course.
If your `Bot` is moving too slowly for your tastes, make her run by setting
the `speed` value.
Run your code by pressing left `alt/option (⌥)`. Keep left `alt/option (⌥)` held
down to move around while you're coding. Flying is a great way to get yourself
into position to see what's happening, so try double jumping to get a better
view.
```nim
speed = 10
If your `Bot` is moving too slowly for your tastes, make her run by setting the
`speed` value.
forward 17
turn right
forward 10
turn left
```
```nim
speed = 10
Good luck! We'll conclude this tutorial when your `Bot` goes past the
`FINISH HERE!` sign at the end of the course.
"""
forward 17
turn right
forward 10
turn left
```
Good luck! We'll conclude this tutorial when your `Bot` goes past the
`FINISH HERE!` sign at the end of the course.
"""
sign.open = true
var bot = get_bot()
@ -253,27 +267,28 @@ proc get_bot(): Bot =
sleep 15
say "- All Done!",
"""
# Great Job!
Good work! You learned the basics of Enu, and you coded your first robot.
There's a lot more to come. Everything here - the signs, the course, the
giant spinning confetti monster, were created with `Nim` code inside Enu,
and we'll cover how to do all of it in future tutorials.
# Great Job!
This is supposed to be where you move to the next tutorial, but it hasn't
been written yet. For now, you can try some
[Enu examples](<nim://load_level("tutorial-2")>) or load
[Inky: Isolation](<nim://load_level("tutorial-3")>), a simple
game written in Enu that's loosley inspired by **Alien: Isolation**, but
features Inky, the blue ghost from Pac-Man. If you're curious how this
tutorial was written, click
[HERE](<nim://player.god = true;sign.open = false>) to turn on `God Mode`,
which will let you see and modify its code. It's a bit of a mess, but some
folks might find it interesting.
Good work! You learned the basics of Enu, and you coded your first robot.
There's a lot more to come. Everything here - the signs, the course, the giant
spinning confetti monster, were created with `Nim` code inside Enu, and we'll
cover how to do all of it in future tutorials.
Contact me at [dsrw@me.com](mailto:dsrw@me.com) if you have any questions or
suggestions. Thanks for trying Enu!
"""
This is supposed to be where you move to the next tutorial, but it hasn't been
written yet. For now, you can try some
[Enu examples](<nim://load_level("tutorial-2")>) or load
[Inky: Isolation](<nim://load_level("tutorial-3")>), a simple
game written in Enu that's loosley inspired by **Alien: Isolation**, but
features Inky, the blue ghost from Pac-Man. If you're curious how this tutorial
was written, click [HERE](<nim://player.god = true;sign.open = false>) to turn
on `God Mode`, which will let you see and modify its code. It's a bit of a mess,
but some folks might find it interesting.
Contact me at [dsrw@me.com](mailto:dsrw@me.com) if you have any questions or
suggestions. Thanks for trying Enu!
"""
sign.open = true

View File

@ -94,15 +94,13 @@ proc scroller(sign: Sign, msg: string, pause = 0, len = msg.len): proc() =
var current = msg
var counter = 0
result =
proc() =
if current == msg and counter < pause:
inc counter
else:
counter = 0
current = current[1 ..^ 1] & current[0]
sign.message =
"`" & current[0 .. (len - 1)].strip(leading = false) & "`"
result = proc() =
if current == msg and counter < pause:
inc counter
else:
counter = 0
current = current[1 ..^ 1] & current[0]
sign.message = "`" & current[0 .. (len - 1)].strip(leading = false) & "`"
var start_scroller1 = start_sign1.scroller(" START HERE! ", 0, 7)

View File

@ -6,36 +6,38 @@ lock = true
let overview =
"""
# ` ` Menu
# ` ` Menu
- `Reset Tutorial`
- `Help`
- `...`
"""
- `Reset Tutorial`
- `Help`
- `...`
"""
let details =
"""
# Menu
Welcome! This is Tutorial 1, which covers the basics of playing and coding in
Enu. If you're new, you should try it. It won't take long.
# Menu
If you're not new, here are some other things to do:
Welcome! This is Tutorial 1, which covers the basics of playing and coding in
Enu. If you're new, you should try it. It won't take long.
- [Clear Changes and Reset Tutorial](<nim://reset_level()>). This resets and
restarts this tutorial. Any changes you've made to the world will be lost.
If you're not new, here are some other things to do:
- [Help](https://ē.nu/docs/intro.html). Help!
- [Clear Changes and Reset Tutorial](<nim://reset_level()>). This resets and
restarts this tutorial. Any changes you've made to the world will be lost.
- [Load Examples](<nim://load_level("tutorial-2")>). See some other things you
can build with Enu.
- [Help](https://ē.nu/docs/intro.html). Help!
- [Load Inky: Isolation](<nim://load_level("tutorial-3")>). `Inky: Isolation`
is a simple game made with Enu. You can see how it was built in
[this video](https://youtu.be/9e9sLsmsu_o).
- [Load Examples](<nim://load_level("tutorial-2")>). See some other things you can build with Enu.
- [Load default world](<nim://load_level("default-1", "default")>).
"""
- [Load Inky: Isolation](<nim://load_level("tutorial-3")>). `Inky: Isolation` is
a simple game made with Enu. You can see how it was built in
[this video](https://youtu.be/9e9sLsmsu_o).
- [Load default world](<nim://load_level("default-1", "default")>).
"""
say overview, details, height = 3, width = 3, size = 320

View File

@ -44,7 +44,7 @@ loop:
if player.near(10) and start_position.near(30):
# `chase` if the player is near and we're not too far
# from home base
(wander, wander_home) ==> chase:
(wander, wander_home) ==> chase do:
# this will be called when the command switches
say cycle(hellos)

View File

@ -2,11 +2,13 @@ lock = true
let text =
"""
# Towers. Too Many Towers.
Code a building and name it `tower`. Give it some friends with `tower.new`.
# Towers. Too Many Towers.
This is an example of a [Prototype](https://ē.nu/docs/coding/concepts.html).
"""
Code a building and name it `tower`. Give it some friends with `tower.new`.
This is an example of a [Prototype](https://ē.nu/docs/coding/concepts.html).
"""
say text, text, width = 3, height = 3

View File

@ -3,11 +3,13 @@ turn left
let text =
"""
# Annoying Robot
Annoying Robot offers words of wisdom while trying to chase you down.
# Annoying Robot
This is an example of a [Command Loop](https://ē.nu/docs/command_loops.html).
"""
Annoying Robot offers words of wisdom while trying to chase you down.
This is an example of a [Command Loop](https://ē.nu/docs/command_loops.html).
"""
say text, text, height = 4

View File

@ -3,11 +3,13 @@ lock = true
turn right
let text =
"""
# ???
I'm not sure what this is supposed to be.
# ???
It uses Nim's [math](https://nim-lang.org/docs/math.html) module to build with sine waves.
"""
I'm not sure what this is supposed to be.
It uses Nim's [math](https://nim-lang.org/docs/math.html) module to build with sine waves.
"""
say text, text, height = 3

View File

@ -6,28 +6,32 @@ let blurb =
let text =
\"""
# Examples
{blurb}
# Examples
- Load Tutorial
- Load Default World
- Clear Changes and Reset Examples
"""
{blurb}
- Load Tutorial
- Load Default World
- Clear Changes and Reset Examples
"""
let more =
\"""
# Menu
{blurb}
# Menu
- [Load Tutorial](<nim://load_level("tutorial-1")>) - Leave this level and load the Enu tutorial.
{blurb}
- [Load Default World](<nim://load_level("default-1", "default")>).
- [Load Tutorial](<nim://load_level("tutorial-1")>) - Leave this level and load the Enu tutorial.
- [Clear Changes and Reset Examples](<nim://reset_level()>) - Reset and reload this level. Be careful, all of your changes will be lost.
- [Load Default World](<nim://load_level("default-1", "default")>).
- [Load Inky: Isolation](<nim://load_level("tutorial-3")>) - Load `Inky: Isolation`, a simple game made with Enu.
"""
- [Clear Changes and Reset Examples](<nim://reset_level()>) - Reset and reload this level. Be careful, all of your changes will be lost.
- [Load Inky: Isolation](<nim://load_level("tutorial-3")>) - Load `Inky: Isolation`, a simple game made with Enu.
"""
say text, more, height = 4, width = 3

View File

@ -4,11 +4,11 @@ move me
loop:
nil -> up(home + 0.2)
if player.hit:
up -> down(home + 0.2):
up -> down(home + 0.2) do:
door.open = true
ghost.charge_player = true
else:
sleep -> up(home + 0.2):
sleep -> up(home + 0.2) do:
door.open = false
down -> sleep(pause)
glow = cycle(0.0, 0.05)

View File

@ -10,20 +10,18 @@ proc stop_playing*() =
proc level_menu*(me: Build, show_restart = true) =
let blurb =
"""
`Inky: Isolation` is a simple survivial game created in Enu. Find buttons to
open new areas of the space station. Hit the top of Inky's head to defeat
them.
"""
"`Inky: Isolation` is a simple survivial game created in Enu. Find buttons to open new areas of the space station. Hit the top of Inky's head to defeat them."
let copy =
\"""
# Inky: Isolation
{blurb}
# Inky: Isolation
- CLICK TO OPEN MENU
"""
{blurb}
- CLICK TO OPEN MENU
"""
me.right 3
@ -48,23 +46,24 @@ proc level_menu*(me: Build, show_restart = true) =
let more =
\"""
# Menu
{blurb}
# Menu
See how `Inky: Isolation` was built in the video [Inky: Isolation. A 90
minute game built with Enu and Nim](https://youtu.be/9e9sLsmsu_o)
{blurb}
{toolbar_action}
See how `Inky: Isolation` was built in the video [Inky: Isolation. A 90 minute game built with Enu and Nim](https://youtu.be/9e9sLsmsu_o)
{return_action}
{toolbar_action}
- [LOAD TUTORIAL](<nim://load_level("tutorial-1")>)
{return_action}
- [LOAD EXAMPLES](<nim://load_level("tutorial-2")>)
- [LOAD TUTORIAL](<nim://load_level("tutorial-1")>)
- [CLEAR CHANGES AND RESET LEVEL](<nim://reset_level()>)
"""
- [LOAD EXAMPLES](<nim://load_level("tutorial-2")>)
- [CLEAR CHANGES AND RESET LEVEL](<nim://reset_level()>)
"""
sign1.more = more
sign2.more = more
sleep 1