mirror of https://github.com/dsrw/enu.git
Instancing
This commit is contained in:
parent
3a8a7b21aa
commit
5f24673be2
32
enu.nimble
32
enu.nimble
|
@ -1,17 +1,19 @@
|
|||
import strformat, strutils, os, json
|
||||
|
||||
let
|
||||
var
|
||||
(target, lib_ext, exe_ext) = case host_os
|
||||
of "windows": ("windows", ".dll", ".exe")
|
||||
of "macosx" : ("osx", ".dylib", "")
|
||||
else : ("x11", ".so", "")
|
||||
generated_dir = "godotapi"
|
||||
generated_dir = "generated/godotapi"
|
||||
api_json = "api.json"
|
||||
generator = "tools/build_helpers"
|
||||
godot_bin = this_dir() & &"/vendor/godot/bin/godot.{target}.opt.tools.64{exe_ext}"
|
||||
godot_build_url = "https://docs.godotengine.org/en/stable/development/compiling/index.html"
|
||||
gcc_dlls = ["libgcc_s_seh-1.dll", "libwinpthread-1.dll"]
|
||||
nim_dlls = ["pcre64.dll"]
|
||||
godot_opts = "target=release_debug"
|
||||
generator_path = ""
|
||||
|
||||
version = "0.1.99"
|
||||
author = "Scott Wadden"
|
||||
|
@ -27,19 +29,20 @@ requires "nim >= 1.4.0",
|
|||
"https://github.com/dsrw/Nim#1d5093d",
|
||||
"cligen 1.2.2",
|
||||
"json_serialization",
|
||||
"print"
|
||||
"print >= 1.0"
|
||||
|
||||
var
|
||||
godot_opts = "target=release_debug"
|
||||
proc gen: string =
|
||||
if generator_path == "":
|
||||
exec &"nimble c {generator}"
|
||||
generator_path = find_exe generator
|
||||
generator_path
|
||||
|
||||
task build_godot, "Build godot":
|
||||
mk_dir generated_dir
|
||||
exec "git submodule update --init"
|
||||
exec &"nimble c {generator}"
|
||||
let
|
||||
gen = find_exe generator
|
||||
scons = find_exe "scons"
|
||||
cores = gorge gen & " core_count"
|
||||
cores = gorge(gen() & " core_count")
|
||||
if scons == "":
|
||||
quit &"*** scons not found on path, and is required to build Godot. See {godot_build_url} ***"
|
||||
with_dir "vendor/godot":
|
||||
|
@ -51,10 +54,9 @@ proc find_and_copy_dlls(dep_path, dest: string, dlls: varargs[string]) =
|
|||
|
||||
task prereqs, "Generate Godot API binding":
|
||||
build_godot_task()
|
||||
let gen = find_exe generator
|
||||
exec &"{godot_bin} --gdnative-generate-json-api {join_path generated_dir, api_json}"
|
||||
exec &"{gen} generate_api -d={generated_dir} -j={api_json}"
|
||||
exec &"{gen} copy_stdlib -d=vmlib/stdlib"
|
||||
exec &"{gen()} generate_api -d={generated_dir} -j={api_json}"
|
||||
exec &"{gen()} copy_stdlib -d=vmlib/stdlib"
|
||||
if host_os == "windows":
|
||||
# Assumes mingw
|
||||
find_and_copy_dlls find_exe("gcc").parent_dir, join_path("app", "_dlls"), gcc_dlls
|
||||
|
@ -74,14 +76,16 @@ task start, "Run Enu":
|
|||
cd "app"
|
||||
exec godot_bin & " --verbose scenes/game.tscn"
|
||||
|
||||
task gen, "Generate build_helpers":
|
||||
discard gen()
|
||||
|
||||
proc code_sign(id, path: string) =
|
||||
exec &"codesign -s '{id}' -v --timestamp --options runtime {path}"
|
||||
|
||||
task dist, "Build distribution":
|
||||
let release_bin = &"vendor/godot/bin/godot.{target}.opt.64{exe_ext}"
|
||||
prereqs_task()
|
||||
let gen = find_exe generator
|
||||
exec &"{gen} write_export_presets --enu_version {version}"
|
||||
exec &"{gen()} write_export_presets --enu_version {version}"
|
||||
godot_opts = "target=release tools=no"
|
||||
build_godot_task()
|
||||
rm_dir "dist"
|
||||
|
@ -90,7 +94,7 @@ task dist, "Build distribution":
|
|||
let config = read_file("dist_config.json").parse_json
|
||||
mkdir "dist"
|
||||
exec "cp -r installer/Enu.app dist/Enu.app"
|
||||
exec &"{gen} write_info_plist --enu_version {version}"
|
||||
exec &"{gen()} write_info_plist --enu_version {version}"
|
||||
exec "mkdir -p dist/Enu.app/Contents/MacOS"
|
||||
exec "mkdir -p dist/Enu.app/Contents/Frameworks"
|
||||
exec &"cp {release_bin} dist/Enu.app/Contents/MacOS/Enu"
|
||||
|
|
|
@ -5,5 +5,6 @@
|
|||
--threads:on
|
||||
--tlsEmulation:off
|
||||
--threadAnalysis:off
|
||||
#--define:gcDestructors
|
||||
#--gc:orc
|
||||
|
||||
switch("path", this_dir())
|
||||
switch("path", this_dir() & "/../generated")
|
||||
|
|
|
@ -14,6 +14,8 @@ var
|
|||
section_start_time: MonoTime
|
||||
current_proc: string
|
||||
|
||||
template nim_filename*: string = instantiation_info(full_paths = true).filename
|
||||
|
||||
template trace*(body: untyped): untyped =
|
||||
# https://github.com/nim-lang/Nim/issues/8212#issuecomment-657202258
|
||||
when not declaredInScope(internalCProcName):
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
import std / [monotimes, os, hashes, sets, strutils]
|
||||
import core, globals, engine/engine, types
|
||||
import godot, compiler/idgen
|
||||
import godotapi / [node]
|
||||
|
||||
export engine
|
||||
|
||||
type
|
||||
Callback* = proc(delta: float): bool
|
||||
ScriptCtx* = ref object
|
||||
speed: float
|
||||
script*: string
|
||||
engine*: Engine
|
||||
timer: MonoTime
|
||||
prefix: string
|
||||
paused: bool
|
||||
id: int
|
||||
load_vars*: proc()
|
||||
reload_script: proc()
|
||||
|
||||
using self: auto
|
||||
|
||||
const ADVANCE_STEP = 0.5.seconds
|
||||
|
||||
var
|
||||
module_names: HashSet[string]
|
||||
active_node: Node
|
||||
ctxs: Table[Engine, ScriptCtx]
|
||||
failed: seq[tuple[ctx: ScriptCtx, ex: ref VMQuit]]
|
||||
starting = true
|
||||
modules_to_load: HashSet[ScriptCtx]
|
||||
|
||||
template ctx: untyped = self.script_ctx
|
||||
|
||||
proc hash*(s: ScriptCtx): Hash = s.id.hash
|
||||
proc init*(t: typedesc[ScriptCtx], prefix: string): ScriptCtx =
|
||||
let e = Engine.init()
|
||||
result = ScriptCtx(id: get_id(), engine: e, timer: MonoTime.high, prefix: prefix)
|
||||
ctxs[e] = result
|
||||
modules_to_load.incl result
|
||||
|
||||
proc current_ctx*(): ScriptCtx = ctxs[current_engine()]
|
||||
|
||||
proc is_script_loadable*(self): bool =
|
||||
ctx.script != "none" and ctx.script.file_exists
|
||||
|
||||
proc engine*(self): Engine = ctx.engine
|
||||
proc script*(self): string = ctx.script
|
||||
proc paused*(self): bool = ctx.paused
|
||||
proc `paused=`*(self; paused: bool) = ctx.paused = paused
|
||||
proc speed*(self): float = ctx.speed
|
||||
proc `speed=`*(self; speed: float) = ctx.speed = speed
|
||||
|
||||
proc set_script*(self) =
|
||||
let path = ctx.prefix & &"_{self.script_index}.nim"
|
||||
ctx.script = join_path(config.script_dir, path)
|
||||
|
||||
proc start_advance_timer*(ctx: ScriptCtx) =
|
||||
ctx.timer = get_mono_time() + ADVANCE_STEP
|
||||
|
||||
proc advance*(self; delta: float64) =
|
||||
let now = get_mono_time()
|
||||
active_node = self
|
||||
let e = ctx.engine
|
||||
if e.callback == nil or (not e.callback(delta)):
|
||||
ctx.timer = MonoTime.high
|
||||
discard e.call_proc("set_action_running", e.module_name, false)
|
||||
self.update_running_state ctx.engine.resume()
|
||||
elif now >= ctx.timer:
|
||||
ctx.timer = now + ADVANCE_STEP
|
||||
e.saved_callback = e.callback
|
||||
e.callback = nil
|
||||
discard e.resume()
|
||||
|
||||
proc load_vars*(self) =
|
||||
let self_name = if self.is_nil: "nil" else: self.name
|
||||
let active_name = if active_node.is_nil: "nil" else: active_node.name
|
||||
let e = ctx.engine
|
||||
when compiles(self.on_load_vars):
|
||||
self.on_load_vars()
|
||||
|
||||
proc begin_move(self; direction: Vector3, steps: float): bool =
|
||||
self.load_vars()
|
||||
current_engine().callback = self.on_begin_move(direction, steps)
|
||||
result = not current_engine().callback.is_nil
|
||||
|
||||
proc begin_turn(self; direction: Vector3, degrees: float): bool =
|
||||
self.load_vars()
|
||||
current_engine().callback = self.on_begin_turn(direction, degrees)
|
||||
result = not current_engine().callback.is_nil
|
||||
|
||||
proc error*(e: Engine, ex: ref VMQuit) =
|
||||
e.running = false
|
||||
when defined(enu_simulate):
|
||||
raise ex
|
||||
else:
|
||||
err ex.msg
|
||||
trigger("script_error")
|
||||
|
||||
proc retry_failed_scripts =
|
||||
# We don't know what order scripts will load in.
|
||||
# Once all scripts have been loaded, retry the
|
||||
# failures. Keep retrying until the list of failures
|
||||
# stops changing, then print any remaining errors.
|
||||
starting = false
|
||||
var cycling = true
|
||||
while cycling:
|
||||
let prev_failed = failed
|
||||
failed = @[]
|
||||
for f in prev_failed:
|
||||
f.ctx.reload_script()
|
||||
if prev_failed == failed:
|
||||
cycling = false
|
||||
|
||||
for f in failed:
|
||||
f.ctx.engine.error(f.ex)
|
||||
|
||||
proc load_script*(self; script = "", retry_failed = true) =
|
||||
modules_to_load.excl ctx
|
||||
defer:
|
||||
if modules_to_load.card == 0 and retry_failed:
|
||||
retry_failed_scripts()
|
||||
|
||||
if script != "":
|
||||
ctx.script = script
|
||||
ctx.reload_script = proc() =
|
||||
self.load_script(retry_failed = false)
|
||||
ctx.engine.callback = nil
|
||||
try:
|
||||
if not self.is_script_loadable:
|
||||
return
|
||||
if not ctx.paused:
|
||||
let module_name = ctx.script.split_file.name
|
||||
module_names.incl module_name
|
||||
var others = module_names
|
||||
others.excl module_name
|
||||
let imports = if others.card > 0:
|
||||
"import " & others.to_seq.join(", ")
|
||||
else:
|
||||
""
|
||||
let code = self.code_template(module_name & ".nim", imports)
|
||||
|
||||
let initialized = ctx.engine.initialized
|
||||
ctx.load_vars = proc() = self.load_vars()
|
||||
ctx.engine.load(config.script_dir, ctx.script, code, config.lib_dir)
|
||||
if not initialized:
|
||||
with ctx.engine:
|
||||
expose "yield_script", proc(a: VmArgs):bool =
|
||||
current_engine().callback = current_engine().saved_callback
|
||||
current_engine().saved_callback = nil
|
||||
true
|
||||
|
||||
expose("begin_move", a => self.begin_move(get_vec3(a, 0), get_float(a, 1)))
|
||||
expose("begin_turn", a => self.begin_turn(get_vec3(a, 0), get_float(a, 1)))
|
||||
expose("echo_console", a => echo_console(get_string(a, 0)))
|
||||
expose "sleep_impl", proc(a: VmArgs): bool =
|
||||
let seconds = get_float(a, 0)
|
||||
var duration = 0.0
|
||||
when compiles(self.on_sleep):
|
||||
self.on_sleep(seconds)
|
||||
current_engine().callback = proc(delta: float): bool =
|
||||
duration += delta
|
||||
return duration < seconds
|
||||
true
|
||||
expose "quit", proc(a: VmArgs): bool =
|
||||
let e = current_engine()
|
||||
e.exit_code = some(a.get_int(0).int)
|
||||
e.pause()
|
||||
e.running = false
|
||||
result = false
|
||||
|
||||
self.on_script_loaded(ctx.engine)
|
||||
if not ctx.paused:
|
||||
self.update_running_state ctx.engine.run()
|
||||
except VMQuit as e:
|
||||
if starting:
|
||||
failed.add (ctx, e)
|
||||
else:
|
||||
self.engine.error(e)
|
|
@ -1,7 +1,8 @@
|
|||
import compiler / [vm, vmdef, options, lineinfos, ast]
|
||||
import eval
|
||||
import os, strformat, std/with, parseutils
|
||||
import ../core
|
||||
import compiler / [vm, vmdef, options, lineinfos, ast, idgen]
|
||||
import types, eval
|
||||
import std / [os, strformat, with, parseutils, hashes]
|
||||
import core
|
||||
import godot
|
||||
export Interpreter
|
||||
|
||||
export VmArgs, get_float, get_int, get_string, get_bool
|
||||
|
@ -23,8 +24,13 @@ type
|
|||
initialized*: bool
|
||||
code: string
|
||||
module_name*: string
|
||||
file_name: string
|
||||
exit_code*: Option[int]
|
||||
errors*: seq[tuple[msg: string, info: TLineInfo]]
|
||||
callback*: Callback
|
||||
saved_callback*: Callback
|
||||
id: int
|
||||
running*: bool
|
||||
|
||||
const
|
||||
STDLIB_PATHS = (w". core pure pure/collections pure/concurrency" &
|
||||
|
@ -33,11 +39,16 @@ const
|
|||
|
||||
var
|
||||
interpreter: Interpreter
|
||||
current_engine: Engine
|
||||
current: Engine
|
||||
|
||||
proc current(): Engine = current_engine
|
||||
proc hash*(e: Engine): Hash = e.id.hash
|
||||
proc current_engine*(): Engine = current
|
||||
proc set_current(e: Engine) =
|
||||
current_engine = e
|
||||
current = e
|
||||
|
||||
proc init*(t: typedesc[Engine]): Engine =
|
||||
result = Engine()
|
||||
result.id = get_id()
|
||||
|
||||
proc init_interpreter(script_dir, vmlib: string) =
|
||||
trace:
|
||||
|
@ -47,8 +58,8 @@ proc init_interpreter(script_dir, vmlib: string) =
|
|||
log_trace("create_interpreter")
|
||||
with interpreter:
|
||||
register_error_hook proc(config, info, msg, severity: auto) {.gcsafe.} =
|
||||
let e = current_engine
|
||||
if severity == Error and config.error_counter >= config.error_max:
|
||||
let e = current_engine()
|
||||
if severity == Severity.Error and config.error_counter >= config.error_max:
|
||||
let file_name = if info.file_index.int >= 0:
|
||||
config.m.file_infos[info.file_index.int].full_path.string
|
||||
else:
|
||||
|
@ -60,7 +71,7 @@ proc init_interpreter(script_dir, vmlib: string) =
|
|||
raise (ref VMQuit)(info: info, msg: msg)
|
||||
|
||||
register_enter_hook proc(c, pc, tos, instr: auto) =
|
||||
let e = current()
|
||||
let e = current_engine()
|
||||
let info = c.debug[pc]
|
||||
if info.file_index.int == 0 and e.previous_line != info:
|
||||
if e.line_changed != nil:
|
||||
|
@ -77,19 +88,16 @@ proc init_interpreter(script_dir, vmlib: string) =
|
|||
log_trace("hooks")
|
||||
|
||||
proc pause*(e: Engine) =
|
||||
set_current e
|
||||
e.pause_requested = true
|
||||
|
||||
proc load*(e: Engine, script_dir, module_name, code, vmlib: string) =
|
||||
proc load*(e: Engine, script_dir, file_name, code, vmlib: string) =
|
||||
e.code = code
|
||||
e.module_name = module_name
|
||||
e.file_name = file_name
|
||||
e.module_name = file_name.split_file.name
|
||||
set_current e
|
||||
if interpreter.is_nil:
|
||||
init_interpreter(script_dir, vmlib)
|
||||
e.is_main_module = true
|
||||
interpreter.implement_routine "*", e.module_name, "quit", proc(a: VmArgs) {.gcsafe.} =
|
||||
e.exit_code = some(a.get_int(0).int)
|
||||
e.pause()
|
||||
e.initialized = true
|
||||
|
||||
proc run*(e: Engine): bool =
|
||||
|
@ -97,26 +105,48 @@ proc run*(e: Engine): bool =
|
|||
e.exit_code = none(int)
|
||||
e.errors = @[]
|
||||
try:
|
||||
interpreter.load_module(e.module_name, e.code)
|
||||
interpreter.load_module(e.file_name, e.code)
|
||||
false
|
||||
except VMPause:
|
||||
e.exit_code.is_none
|
||||
|
||||
proc to_node*(val: int): PNode =
|
||||
new_int_node(nk_int_lit, val)
|
||||
proc get_vec3*(a: VmArgs, pos: int): Vector3 =
|
||||
proc float_val(node: PNode, name: string): float =
|
||||
assert node.kind == nkExprColonExpr
|
||||
assert node.sons[0].sym.kind == skField
|
||||
assert node.sons[0].sym.name.s == name
|
||||
result = node.sons[1].float_val
|
||||
let
|
||||
fields = a.get_node(0).sons
|
||||
x = float_val(fields[1], "x")
|
||||
y = float_val(fields[2], "y")
|
||||
z = float_val(fields[3], "z")
|
||||
result = vec3(x, y, z)
|
||||
|
||||
proc to_node*(val: float): PNode =
|
||||
new_float_node(nk_float_lit, val)
|
||||
# adapted from https://github.com/h0lley/embeddedNimScript/blob/6101fb37d4bd3f947db86bac96f53b35d507736a/embeddedNims/enims.nim#L31
|
||||
proc to_node*(val: int): PNode = new_int_node(nkIntLit, val)
|
||||
proc to_node*(val: float): PNode = new_float_node(nkFloatLit, val)
|
||||
proc to_node*(val: string): PNode = new_str_node(nkStrLit, val)
|
||||
proc to_node*(val: bool): PNode = val.ord.to_node
|
||||
proc to_node*(val: enum): PNode = val.ord.to_node
|
||||
|
||||
proc to_node*(val: string): PNode =
|
||||
new_str_node(nk_str_lit, val)
|
||||
proc to_node*(list: open_array[int|float|string|bool|enum]): PNode =
|
||||
result = nkBracket.new_node
|
||||
result.sons.initialize(list.len)
|
||||
for i in 0..list.high: result.sons[i] = list[i].to_node()
|
||||
|
||||
proc to_node*(val: bool): PNode =
|
||||
let v = if val: 1 else: 0
|
||||
new_int_node(nk_int_lit, v)
|
||||
proc to_node*(tree: tuple|object): PNode =
|
||||
result = nkPar.new_tree
|
||||
for field in tree.fields:
|
||||
result.sons.add(field.to_node)
|
||||
|
||||
proc to_node*(tree: ref tuple|ref object): PNode =
|
||||
result = nkPar.new_tree
|
||||
if tree.is_nil: return result
|
||||
for field in tree.fields:
|
||||
result.sons.add(field.to_node)
|
||||
|
||||
proc call_proc*(e: Engine, proc_name: string, module_name = "", args: varargs[PNode, to_node]): PNode {.discardable.}=
|
||||
set_current e
|
||||
let foreign_proc = interpreter.select_routine(proc_name, module_name = module_name)
|
||||
if foreign_proc == nil:
|
||||
raise new_exception(VMError, &"script does not export a proc of the name: '{proc_name}'")
|
||||
|
@ -134,7 +164,7 @@ proc expose*(e: Engine, proc_name: string,
|
|||
routine: proc(a: VmArgs): bool) {.gcsafe.} =
|
||||
interpreter.implement_routine "*", e.module_name, proc_name, proc(a: VmArgs) {.gcsafe.} =
|
||||
if routine(a):
|
||||
e.pause()
|
||||
current_engine().pause()
|
||||
|
||||
proc call_float*(e: Engine, proc_name: string): float =
|
||||
e.call_proc(proc_name).get_float()
|
||||
|
@ -153,6 +183,9 @@ proc get_bool*(e: Engine, var_name: string, module_name = ""): bool =
|
|||
let b = e.get_var(var_name, module_name).get_int
|
||||
return b == 1
|
||||
|
||||
proc get_string*(e: Engine, var_name: string, module_name = ""): string =
|
||||
result = e.get_var(var_name, module_name).get_str
|
||||
|
||||
proc call_int*(e: Engine, proc_name: string): int =
|
||||
e.call_proc(proc_name).get_int.to_int
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
import std/monotimes
|
||||
import ".." / [core, engine/engine]
|
||||
|
||||
const ADVANCE_STEP = 0.5.seconds
|
||||
|
||||
proc start_advance_timer*(self: auto) =
|
||||
self.advance_timer = get_mono_time() + ADVANCE_STEP
|
||||
|
||||
proc advance*(self: auto, delta: float64) =
|
||||
let now = get_mono_time()
|
||||
if self.callback == nil or not self.callback(delta):
|
||||
self.advance_timer = MonoTime.high
|
||||
discard self.engine.call_proc("set_action_running", self.engine.module_name, false)
|
||||
self.update_running_state self.engine.resume()
|
||||
elif now >= self.advance_timer:
|
||||
self.advance_timer = now + ADVANCE_STEP
|
||||
self.saved_callback = self.callback
|
||||
self.callback = nil
|
||||
discard self.engine.resume()
|
|
@ -0,0 +1,2 @@
|
|||
type
|
||||
Callback* = proc(delta: float): bool
|
96
src/game.nim
96
src/game.nim
|
@ -1,7 +1,7 @@
|
|||
import ../godotapi / [input, input_event, gd_os, node, scene_tree, viewport_container,
|
||||
packed_scene, resource_saver, sprite, control, viewport,
|
||||
performance, label, theme, dynamic_font, resource_loader, main_loop,
|
||||
gd_os, project_settings, input_map, input_event, input_event_action]
|
||||
import godotapi / [input, input_event, gd_os, node, scene_tree, viewport_container,
|
||||
packed_scene, resource_saver, sprite, control, viewport,
|
||||
performance, label, theme, dynamic_font, resource_loader, main_loop,
|
||||
gd_os, project_settings, input_map, input_event, input_event_action]
|
||||
import godot, threadpool, times, os, json_serialization
|
||||
import core, globals
|
||||
|
||||
|
@ -152,58 +152,58 @@ gdobj Game of Node:
|
|||
echo &"loaded {config.scene}"
|
||||
|
||||
method ready* =
|
||||
trace:
|
||||
self.viewport_container = self.get_node("ViewportContainer").as(ViewportContainer)
|
||||
self.scene_packer = gdnew[PackedScene]()
|
||||
self.load_world()
|
||||
self.get_tree().set_auto_accept_quit(false)
|
||||
assert not self.viewport_container.is_nil
|
||||
state.game = self
|
||||
let (theme_holder, theme) = if hostOS == "macosx":
|
||||
( self.find_node("ThemeHolder").as(Container),
|
||||
load("res://themes/AppleTheme.tres").as(Theme))
|
||||
else:
|
||||
let node = self.find_node("Panels").as(Container)
|
||||
(node, node.theme)
|
||||
let
|
||||
font = theme.default_font.as(DynamicFont)
|
||||
bold_font = theme.get_font("bold_font", "RichTextLabel")
|
||||
.as(DynamicFont)
|
||||
self.viewport_container = self.get_node("ViewportContainer").as(ViewportContainer)
|
||||
self.scene_packer = gdnew[PackedScene]()
|
||||
self.load_world()
|
||||
self.get_tree().set_auto_accept_quit(false)
|
||||
assert not self.viewport_container.is_nil
|
||||
state.game = self
|
||||
let (theme_holder, theme) = if hostOS == "macosx":
|
||||
( self.find_node("ThemeHolder").as(Container),
|
||||
load("res://themes/AppleTheme.tres").as(Theme))
|
||||
else:
|
||||
let node = self.find_node("Panels").as(Container)
|
||||
(node, node.theme)
|
||||
let
|
||||
font = theme.default_font.as(DynamicFont)
|
||||
bold_font = theme.get_font("bold_font", "RichTextLabel")
|
||||
.as(DynamicFont)
|
||||
|
||||
font.size = config.font_size
|
||||
bold_font.size = config.font_size
|
||||
theme_holder.theme = theme
|
||||
self.shrink = config.downscale
|
||||
font.size = config.font_size
|
||||
bold_font.size = config.font_size
|
||||
theme_holder.theme = theme
|
||||
self.shrink = config.downscale
|
||||
|
||||
self.mouse_captured = true
|
||||
self.reticle = self.find_node("Reticle").as(Control)
|
||||
self.stats = self.find_node("stats").as(Label)
|
||||
self.stats.visible = config.show_stats
|
||||
|
||||
globals.capture_mouse = proc() =
|
||||
self.mouse_captured = true
|
||||
self.reticle = self.find_node("Reticle").as(Control)
|
||||
self.stats = self.find_node("stats").as(Label)
|
||||
self.stats.visible = config.show_stats
|
||||
|
||||
globals.capture_mouse = proc() =
|
||||
self.mouse_captured = true
|
||||
globals.release_mouse = proc() =
|
||||
self.mouse_captured = false
|
||||
|
||||
globals.release_mouse = proc() =
|
||||
self.mouse_captured = false
|
||||
globals.reload_scripts = proc() =
|
||||
trigger("save")
|
||||
trigger("reload")
|
||||
|
||||
globals.reload_scripts = proc() =
|
||||
trigger("save")
|
||||
trigger("reload")
|
||||
globals.save_and_reload = proc() =
|
||||
trigger("save")
|
||||
trigger("reload_all")
|
||||
globals.save_scene()
|
||||
|
||||
globals.save_and_reload = proc() =
|
||||
trigger("save")
|
||||
trigger("reload_all")
|
||||
globals.save_scene()
|
||||
globals.save_scene = proc(immediate: bool) =
|
||||
if immediate:
|
||||
self.save_requested = some(now())
|
||||
elif not self.save_requested:
|
||||
self.save_requested = some(now() + 5.seconds)
|
||||
|
||||
globals.save_scene = proc(immediate: bool) =
|
||||
if immediate:
|
||||
self.save_requested = some(now())
|
||||
elif not self.save_requested:
|
||||
self.save_requested = some(now() + 5.seconds)
|
||||
|
||||
globals.pause = proc() =
|
||||
trigger("pause")
|
||||
self.ready = true
|
||||
globals.pause = proc() =
|
||||
trigger("pause")
|
||||
self.ready = true
|
||||
trigger("game_ready")
|
||||
|
||||
proc update_action_index*(change: int) =
|
||||
action_index += change
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../godotapi / [node, scene_tree, voxel_buffer],
|
||||
import godotapi / [node, scene_tree, voxel_buffer],
|
||||
godot, hashes,
|
||||
engine/engine, core,
|
||||
strformat, math, strutils, sequtils, compiler/lineinfos, tables, sets, json_serialization
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../../godotapi / [sprite_3d, ray_cast, spatial]
|
||||
import godotapi / [sprite_3d, ray_cast, spatial]
|
||||
import godot, strutils, math
|
||||
import ".." / [globals, core]
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../../godotapi / [kinematic_body, spatial, input, input_event,
|
||||
import godotapi / [kinematic_body, spatial, input, input_event,
|
||||
input_event_mouse_motion, input_event_joypad_motion,
|
||||
ray_cast, scene_tree, input_event_pan_gesture, viewport, camera, global_constants,
|
||||
collision_shape]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../../godotapi / [area, control]
|
||||
import godotapi / [area, control]
|
||||
import godot
|
||||
import ".." / [core, globals, game, world/bot]
|
||||
import player
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../../godotapi / [button, style_box_flat]
|
||||
import godotapi / [button, style_box_flat]
|
||||
import godot
|
||||
import ".." / [core, globals]
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../../godotapi / [text_edit, scene_tree, node, input_event, input_event_key,
|
||||
import godotapi / [text_edit, scene_tree, node, input_event, input_event_key,
|
||||
rich_text_label, global_constants]
|
||||
import godot, strutils
|
||||
import ".." / [globals, core]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../../godotapi / [text_edit, scene_tree, node, input_event, global_constants,
|
||||
import godotapi / [text_edit, scene_tree, node, input_event, global_constants,
|
||||
input_event_key, style_box_flat]
|
||||
import godot, strutils, tables, compiler/lineinfos
|
||||
import ".." / [core, globals, game, engine/engine]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../../godotapi / [viewport, camera, mesh_instance, material, camera,
|
||||
import godotapi / [viewport, camera, mesh_instance, material, camera,
|
||||
viewport_texture, image, resource_loader]
|
||||
import godot
|
||||
import ".." / [core, globals]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../../godotapi / [h_box_container, scene_tree, button, image_texture]
|
||||
import godotapi / [h_box_container, scene_tree, button, image_texture]
|
||||
import godot
|
||||
import ".." / [core, globals, game, ui/preview_maker]
|
||||
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
import ../../godotapi / [scene_tree, kinematic_body, material, mesh_instance, spatial,
|
||||
input_event, animation_player, resource_loader, packed_scene]
|
||||
import godotapi / [scene_tree, kinematic_body, material, mesh_instance, spatial,
|
||||
input_event, animation_player, resource_loader, packed_scene]
|
||||
import godot, std / [math, tables, with, times, sugar, os, monotimes]
|
||||
import ".." / [globals, core]
|
||||
import ../engine / [engine, script_helpers]
|
||||
import globals, core
|
||||
import engine/contexts
|
||||
export contexts
|
||||
|
||||
include "default_robot.nim.nimf"
|
||||
|
||||
var max_bot_index = 0
|
||||
gdobj NimBot of KinematicBody:
|
||||
var
|
||||
speed = 1.0
|
||||
enu_script*: string
|
||||
script_ctx*: ScriptCtx
|
||||
material* {.gdExport.}, highlight_material* {.gdExport.},
|
||||
selected_material* {.gdExport.}: Material
|
||||
script_index* {.gdExport.} = 0
|
||||
disabled* {.gdExport.} = false
|
||||
callback: proc(delta: float): bool
|
||||
saved_callback: proc(delta: float): bool
|
||||
engine: Engine
|
||||
last_error: string
|
||||
orig_rotation: Vector3
|
||||
orig_translation: Vector3
|
||||
skin: Spatial
|
||||
mesh: MeshInstance
|
||||
paused* = true
|
||||
running = false
|
||||
animation_player: AnimationPlayer
|
||||
advance_timer = MonoTime.high
|
||||
|
||||
proc init*() =
|
||||
self.script_ctx = ScriptCtx.init("bot")
|
||||
self.speed = 1.0
|
||||
|
||||
proc code_template(file: string, imports: string): string =
|
||||
result = default_robot(config.script_dir & "/" & file, imports)
|
||||
|
||||
proc update_material*(value: Material) =
|
||||
self.mesh.set_surface_material(0, value)
|
||||
|
@ -41,154 +41,90 @@ gdobj NimBot of KinematicBody:
|
|||
self.set_default_material()
|
||||
|
||||
proc select*() =
|
||||
show_editor self.enu_script, self.engine
|
||||
show_editor self.script_ctx.script, self.script_ctx.engine
|
||||
|
||||
proc destroy*() =
|
||||
self.get_parent.remove_child(self)
|
||||
|
||||
proc print_error(msg: string) =
|
||||
if msg != self.last_error:
|
||||
self.last_error = msg
|
||||
print(msg)
|
||||
proc on_load_vars() =
|
||||
let e = self.script_ctx.engine
|
||||
self.speed = e.get_float("speed", e.module_name)
|
||||
|
||||
proc load_vars() =
|
||||
self.speed = self.engine.get_float("speed", self.engine.module_name)
|
||||
|
||||
proc move(direction: Vector3, steps: float): bool =
|
||||
self.load_vars()
|
||||
proc on_begin_move(direction: Vector3, steps: float): Callback =
|
||||
var duration = 0.0
|
||||
let
|
||||
moving = direction.rotated(UP, self.rotation.y)
|
||||
finish = self.translation + moving * steps
|
||||
finish_time = 1.0 / self.speed * steps
|
||||
|
||||
self.callback = proc(delta: float): bool =
|
||||
duration += delta
|
||||
if duration >= finish_time:
|
||||
self.translation = finish
|
||||
return false
|
||||
else:
|
||||
discard self.move_and_slide(moving * self.speed, UP)
|
||||
return true
|
||||
self.start_advance_timer()
|
||||
true
|
||||
when not defined(enu_simulate):
|
||||
result = proc(delta: float): bool =
|
||||
duration += delta
|
||||
if duration >= finish_time:
|
||||
self.translation = finish
|
||||
return false
|
||||
else:
|
||||
discard self.move_and_slide(moving * self.speed, UP)
|
||||
return true
|
||||
current_ctx().start_advance_timer()
|
||||
else:
|
||||
self.translation = finish
|
||||
|
||||
proc turn(degrees: float): bool =
|
||||
self.load_vars()
|
||||
proc on_begin_turn(axis: Vector3, degrees: float): Callback =
|
||||
assert axis in [LEFT, RIGHT]
|
||||
let degrees = degrees * -axis.x
|
||||
var duration = 0.0
|
||||
# TODO: Why can't this be a one liner?
|
||||
var final_transform = self.transform
|
||||
final_transform.basis.rotate(UP, deg_to_rad(degrees))
|
||||
self.callback = proc(delta: float): bool =
|
||||
duration += delta
|
||||
self.rotate(UP, deg_to_rad(degrees * delta * self.speed))
|
||||
if duration <= 1.0 / self.speed:
|
||||
true
|
||||
else:
|
||||
self.transform = final_transform
|
||||
false
|
||||
self.start_advance_timer()
|
||||
true
|
||||
|
||||
proc forward(steps: float): bool = self.move(FORWARD, steps)
|
||||
proc back(steps: float): bool = self.move(BACK, steps)
|
||||
proc left(steps: float): bool = self.move(LEFT, steps)
|
||||
proc right(steps: float): bool = self.move(RIGHT, steps)
|
||||
proc turn_left(degrees: float): bool = self.turn(degrees)
|
||||
proc turn_right(degrees: float): bool = self.turn(-degrees)
|
||||
|
||||
proc error(e: ref VMQuit) =
|
||||
self.running = false
|
||||
err e.msg
|
||||
trigger("script_error")
|
||||
|
||||
proc is_script_loadable(): bool =
|
||||
if self.enu_script != "none" and file_exists(self.enu_script):
|
||||
let current_code = read_file(self.enu_script).strip
|
||||
result = current_code != ""
|
||||
|
||||
proc load_script() =
|
||||
trace:
|
||||
self.callback = nil
|
||||
|
||||
try:
|
||||
if self.engine.is_nil:
|
||||
self.engine = Engine()
|
||||
if not self.is_script_loadable:
|
||||
return
|
||||
if not self.paused and not self.engine.initialized:
|
||||
debug &"Loading {self.enu_script}"
|
||||
let code = default_robot(self.enu_script.extract_filename)
|
||||
self.engine.load(config.script_dir, self.enu_script.split_file.name, code, config.lib_dir)
|
||||
log_trace("loaded")
|
||||
with self.engine:
|
||||
expose "yield_script", proc(a: VmArgs): bool =
|
||||
self.callback = self.saved_callback
|
||||
self.saved_callback = nil
|
||||
true
|
||||
expose("forward_impl", a => self.forward(get_float(a, 0)))
|
||||
expose("back_impl", a => self.back(get_float(a, 0)))
|
||||
expose("left_impl", a => self.left(get_float(a, 0)))
|
||||
expose("right_impl", a => self.right(get_float(a, 0)))
|
||||
expose("turn_left_impl", a => self.turn_left(get_float(a, 0)))
|
||||
expose("turn_right_impl", a => self.turn_right(get_float(a, 0)))
|
||||
expose("echo", a => echo_console(get_string(a, 0)))
|
||||
expose("play", proc(a: VmArgs): bool =
|
||||
let animation_name = get_string(a, 0)
|
||||
if animation_name == "":
|
||||
self.animation_player.stop(true)
|
||||
else:
|
||||
self.animation_player.play(animation_name)
|
||||
return false
|
||||
)
|
||||
log_trace("exposed")
|
||||
if not self.paused:
|
||||
self.running = self.engine.run()
|
||||
log_trace("running")
|
||||
except VMQuit as e:
|
||||
self.error(e)
|
||||
|
||||
proc set_script() =
|
||||
self.enu_script = join_path(config.script_dir, &"bot_{self.script_index}.nim")
|
||||
when not defined(enu_simulate):
|
||||
result = proc(delta: float): bool =
|
||||
duration += delta
|
||||
self.rotate(UP, deg_to_rad(degrees * delta * self.speed))
|
||||
if duration <= 1.0 / self.speed:
|
||||
true
|
||||
else:
|
||||
self.transform = final_transform
|
||||
false
|
||||
current_ctx().start_advance_timer()
|
||||
else:
|
||||
self.transform = final_transform
|
||||
|
||||
proc setup*() =
|
||||
trace:
|
||||
self.script_index = max_bot_index
|
||||
inc max_bot_index
|
||||
self.name = "Bot_" & $self.script_index
|
||||
self.set_script()
|
||||
write_file self.enu_script, ""
|
||||
self.script_index = max_bot_index
|
||||
inc max_bot_index
|
||||
self.name = "Bot_" & $self.script_index
|
||||
self.set_script()
|
||||
write_file self.script_ctx.script, ""
|
||||
|
||||
method ready*() =
|
||||
trace:
|
||||
if max_bot_index <= self.script_index:
|
||||
max_bot_index = self.script_index + 1
|
||||
self.set_script()
|
||||
with self:
|
||||
skin = self.get_node("Mannequiny").as(Spatial)
|
||||
mesh = self.skin.get_node("root/Skeleton/body001").as(MeshInstance)
|
||||
animation_player = self.skin.get_node("AnimationPlayer").as(AnimationPlayer)
|
||||
orig_rotation = self.rotation
|
||||
orig_translation = self.translation
|
||||
set_default_material()
|
||||
self.bind_signals("game_ready")
|
||||
if max_bot_index <= self.script_index:
|
||||
max_bot_index = self.script_index + 1
|
||||
self.set_script()
|
||||
with self:
|
||||
skin = self.get_node("Mannequiny").as(Spatial)
|
||||
mesh = self.skin.get_node("root/Skeleton/body001").as(MeshInstance)
|
||||
animation_player = self.skin.get_node("AnimationPlayer").as(AnimationPlayer)
|
||||
orig_rotation = self.rotation
|
||||
orig_translation = self.translation
|
||||
set_default_material()
|
||||
|
||||
if not self.disabled:
|
||||
self.bind_signals(w"reload pause reload_all")
|
||||
log_trace()
|
||||
self.load_script()
|
||||
log_trace("load_script")
|
||||
method on_game_ready() =
|
||||
if not self.disabled:
|
||||
self.bind_signals(w"reload pause reload_all")
|
||||
self.load_script()
|
||||
|
||||
proc update_running_state(running: bool) =
|
||||
self.running = running
|
||||
self.engine.running = running
|
||||
|
||||
method physics_process*(delta: float64) =
|
||||
trace:
|
||||
if not self.paused:
|
||||
try:
|
||||
if self.running:
|
||||
self.advance(delta)
|
||||
except VMQuit as e:
|
||||
self.error(e)
|
||||
if not self.paused:
|
||||
try:
|
||||
if self.engine.running:
|
||||
self.advance(delta)
|
||||
except VMQuit as e:
|
||||
self.engine.error(e)
|
||||
|
||||
method reload() =
|
||||
self.animation_player.stop(true)
|
||||
|
@ -197,8 +133,17 @@ gdobj NimBot of KinematicBody:
|
|||
rotation = self.orig_rotation
|
||||
self.load_script()
|
||||
|
||||
proc on_script_loaded*(e: Engine) =
|
||||
e.expose "play", proc(a: VmArgs): bool =
|
||||
let animation_name = get_string(a, 0)
|
||||
if animation_name == "":
|
||||
self.animation_player.stop(true)
|
||||
else:
|
||||
self.animation_player.play(animation_name)
|
||||
return false
|
||||
|
||||
method on_reload*() =
|
||||
if not editing() or open_file == self.enu_script:
|
||||
if not editing() or open_file == self.script:
|
||||
self.paused = false
|
||||
self.reload()
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import ../../godotapi / [spatial, grid_map]
|
||||
import godotapi / [spatial, grid_map]
|
||||
import godot
|
||||
import std / [tables, math, sets, sugar, sequtils, hashes, os, monotimes]
|
||||
import ".." / [core, globals, world/terrain, world/grid]
|
||||
import ../engine / [engine, script_helpers]
|
||||
import core, globals, world / [terrain, grid]
|
||||
import engine/contexts
|
||||
export contexts
|
||||
|
||||
include "default_builder.nim.nimf"
|
||||
|
||||
|
@ -13,9 +14,9 @@ type
|
|||
|
||||
gdobj Builder of Spatial:
|
||||
var
|
||||
script_ctx: ScriptCtx
|
||||
draw_mode* {.gdExport.}: DrawMode
|
||||
script_index* {.gdExport.} = 0
|
||||
enu_script*: string
|
||||
initial_index* {.gdExport.} = 1
|
||||
# FIXME `saved_blocks` and `saved_block_colors` should be
|
||||
# a single `seq[(Vector, int)]`. Will need `to_variant`
|
||||
|
@ -24,10 +25,6 @@ gdobj Builder of Spatial:
|
|||
saved_block_colors {.gdExport.}: seq[int]
|
||||
saved_holes* {.gdExport}: seq[Vector3]
|
||||
original_translation* {.gdExport.}: Vector3
|
||||
paused* = false
|
||||
engine: Engine
|
||||
callback: proc(delta: float): bool
|
||||
saved_callback: proc(delta: float): bool
|
||||
index: int = 1
|
||||
blocks_per_frame = 0.0
|
||||
blocks_remaining_this_frame = 0.0
|
||||
|
@ -42,34 +39,35 @@ gdobj Builder of Spatial:
|
|||
built = false
|
||||
move_mode = false
|
||||
scale_factor = 1.0
|
||||
running = false
|
||||
# If we delete a voxel while it's queued up to be drawn by godot voxel,
|
||||
# the voxel gets drawn anyway, and the polys hang around until they
|
||||
# move out of draw distance. Wait a bit before clearing. FIXME.
|
||||
timers: seq[Timer]
|
||||
advance_timer = MonoTime.high
|
||||
|
||||
proc init*() =
|
||||
self.script_ctx = ScriptCtx.init("grid")
|
||||
|
||||
proc code_template(file: string, imports: string): string =
|
||||
result = default_builder(file, imports)
|
||||
echo "template: ", result
|
||||
|
||||
method ready() =
|
||||
trace:
|
||||
if max_grid_index <= self.script_index:
|
||||
max_grid_index = self.script_index + 1
|
||||
self.set_script()
|
||||
self.translation = self.original_translation
|
||||
self.grid = self.get_node("Grid") as Grid
|
||||
self.terrain = game_node.find_node("Terrain") as Terrain
|
||||
assert self.grid != nil
|
||||
assert self.terrain != nil
|
||||
if max_grid_index <= self.script_index:
|
||||
max_grid_index = self.script_index + 1
|
||||
self.set_script()
|
||||
self.translation = self.original_translation
|
||||
self.grid = self.get_node("Grid") as Grid
|
||||
self.terrain = game_node.find_node("Terrain") as Terrain
|
||||
assert self.grid != nil
|
||||
assert self.terrain != nil
|
||||
|
||||
self.bind_signals self.terrain,
|
||||
w"block_selected last_block_deleted terrain_block_added terrain_block_removed"
|
||||
self.bind_signals self.grid, w"selected deleted grid_block_added grid_block_removed"
|
||||
self.bind_signals w"reload pause reload_all"
|
||||
self.bind_signals self.terrain,
|
||||
w"block_selected last_block_deleted terrain_block_added terrain_block_removed"
|
||||
self.bind_signals self.grid, w"selected deleted grid_block_added grid_block_removed"
|
||||
self.bind_signals w"reload pause reload_all"
|
||||
|
||||
if self.saved_blocks.len > 0 or self.saved_holes.len > 0:
|
||||
self.restore_blocks()
|
||||
|
||||
proc set_script() =
|
||||
self.enu_script = join_path(config.script_dir, &"grid_{self.script_index}.nim")
|
||||
if self.saved_blocks.len > 0 or self.saved_holes.len > 0:
|
||||
self.restore_blocks()
|
||||
|
||||
proc setup*(translation: Vector3) =
|
||||
self.translation = translation
|
||||
|
@ -78,11 +76,7 @@ gdobj Builder of Spatial:
|
|||
inc max_grid_index
|
||||
self.name = "Builder_" & $self.script_index
|
||||
self.set_script()
|
||||
write_file self.enu_script, ""
|
||||
|
||||
proc is_script_loadable(): bool =
|
||||
if self.enu_script != "none" and file_exists(self.enu_script):
|
||||
result = read_file(self.enu_script).strip != ""
|
||||
write_file self.script, ""
|
||||
|
||||
proc save_blocks*() =
|
||||
let data = if self.draw_mode == VoxelMode:
|
||||
|
@ -118,13 +112,13 @@ gdobj Builder of Spatial:
|
|||
self.grid.draw(x, y, z, index, keep, trigger)
|
||||
|
||||
proc update_running_state(running: bool) =
|
||||
self.running = running
|
||||
if not self.running:
|
||||
self.engine.running = running
|
||||
if not running:
|
||||
self.holes = self.kept_holes
|
||||
self.kept_holes.clear()
|
||||
self.save_blocks()
|
||||
self.load_vars()
|
||||
debug(self.enu_script & " done.")
|
||||
debug(self.script & " done.")
|
||||
|
||||
proc includes_any_location*(locations: seq[Vector3]): bool =
|
||||
if self.draw_mode == GridMode:
|
||||
|
@ -147,7 +141,7 @@ gdobj Builder of Spatial:
|
|||
if self.draw_mode == VoxelMode:
|
||||
self.position.origin = self.translation
|
||||
|
||||
self.speed = 30.0
|
||||
self.speed = 1.0
|
||||
self.scale_factor = 1.0
|
||||
self.grid.scale = vec3(1, 1, 1)
|
||||
self.index = 1
|
||||
|
@ -186,7 +180,7 @@ gdobj Builder of Spatial:
|
|||
self.draw_mode = mode
|
||||
self.save_blocks()
|
||||
|
||||
proc load_vars() =
|
||||
proc on_load_vars() =
|
||||
var old_speed = self.speed
|
||||
let
|
||||
e = self.engine
|
||||
|
@ -200,7 +194,7 @@ gdobj Builder of Spatial:
|
|||
self.blocks_per_frame = if self.speed == 0:
|
||||
float.high
|
||||
else:
|
||||
self.speed / 30.0
|
||||
self.speed
|
||||
if self.speed != old_speed:
|
||||
self.blocks_remaining_this_frame = 0
|
||||
if scale_factor != self.scale_factor:
|
||||
|
@ -211,36 +205,25 @@ gdobj Builder of Spatial:
|
|||
self.set_vars()
|
||||
|
||||
method physics_process(delta: float64) =
|
||||
trace:
|
||||
if self.timers.len > 0:
|
||||
var timers = self.timers
|
||||
self.timers = @[]
|
||||
for timer in timers:
|
||||
if timer.until < now():
|
||||
timer.callback()
|
||||
else:
|
||||
self.timers.add timer
|
||||
return
|
||||
if self.timers.len > 0:
|
||||
var timers = self.timers
|
||||
self.timers = @[]
|
||||
for timer in timers:
|
||||
if timer.until < now():
|
||||
timer.callback()
|
||||
else:
|
||||
self.timers.add timer
|
||||
return
|
||||
|
||||
if not self.built:
|
||||
self.build()
|
||||
self.built = true
|
||||
if not self.paused:
|
||||
self.blocks_remaining_this_frame += self.blocks_per_frame
|
||||
try:
|
||||
if self.move_mode:
|
||||
if self.running:
|
||||
self.advance(delta)
|
||||
else:
|
||||
if self.blocks_per_frame > 0:
|
||||
while self.running and self.blocks_remaining_this_frame >= 1:
|
||||
self.advance(delta)
|
||||
else:
|
||||
if self.running:
|
||||
self.advance(delta)
|
||||
|
||||
except VMQuit as e:
|
||||
self.error(e)
|
||||
if not self.built:
|
||||
self.build()
|
||||
self.built = true
|
||||
if not self.paused:
|
||||
try:
|
||||
if self.engine.running:
|
||||
self.advance(delta)
|
||||
except VMQuit as e:
|
||||
self.engine.error(e)
|
||||
|
||||
proc set_vars() =
|
||||
let module_name = self.engine.module_name
|
||||
|
@ -248,8 +231,7 @@ gdobj Builder of Spatial:
|
|||
self.speed, self.scale_factor, int self.draw_mode,
|
||||
self.overwrite, self.move_mode)
|
||||
|
||||
proc move(direction: Vector3, steps: float): bool =
|
||||
self.load_vars()
|
||||
proc on_begin_move(direction: Vector3, steps: float): Callback =
|
||||
if self.move_mode:
|
||||
let steps = steps.float
|
||||
var duration = 0.0
|
||||
|
@ -258,7 +240,7 @@ gdobj Builder of Spatial:
|
|||
finish = self.translation + moving * steps
|
||||
finish_time = 1.0 / self.speed * steps
|
||||
|
||||
self.callback = proc(delta: float): bool =
|
||||
result = proc(delta: float): bool =
|
||||
duration += delta
|
||||
if duration >= finish_time:
|
||||
self.translation = finish
|
||||
|
@ -266,29 +248,32 @@ gdobj Builder of Spatial:
|
|||
else:
|
||||
self.translation = self.translation + (moving * self.speed * delta)
|
||||
return true
|
||||
self.start_advance_timer()
|
||||
return true
|
||||
else:
|
||||
var count = 0
|
||||
self.callback = proc(delta: float): bool =
|
||||
result = proc(delta: float): bool =
|
||||
self.blocks_remaining_this_frame += self.blocks_per_frame
|
||||
var remaining = self.blocks_remaining_this_frame
|
||||
var per_frame = self.blocks_per_frame
|
||||
while count.float < steps and self.blocks_remaining_this_frame >= 1:
|
||||
remaining = self.blocks_remaining_this_frame
|
||||
per_frame = self.blocks_per_frame
|
||||
self.position = self.position.translated(direction)
|
||||
inc count
|
||||
self.blocks_remaining_this_frame -= 1
|
||||
self.drop_block()
|
||||
return count.float < steps
|
||||
self.start_advance_timer()
|
||||
return true
|
||||
current_ctx().start_advance_timer()
|
||||
|
||||
proc turn(degrees: float, axis = UP): bool =
|
||||
self.load_vars()
|
||||
proc on_begin_turn(axis = UP, degrees: float): Callback =
|
||||
let map = {LEFT: UP, RIGHT: DOWN, UP: RIGHT, DOWN: LEFT}.to_table
|
||||
let axis = map[axis]
|
||||
if self.move_mode:
|
||||
var duration = 0.0
|
||||
let axis = self.transform.basis.xform(axis)
|
||||
var final_transform = self.transform
|
||||
final_transform.basis = final_transform.basis.rotated(axis, deg_to_rad(degrees))
|
||||
.orthonormalized()
|
||||
self.callback = proc(delta: float): bool =
|
||||
result = proc(delta: float): bool =
|
||||
duration += delta
|
||||
self.rotate(axis, deg_to_rad(degrees * delta * self.speed))
|
||||
if duration <= 1.0 / self.speed:
|
||||
|
@ -296,23 +281,15 @@ gdobj Builder of Spatial:
|
|||
else:
|
||||
self.transform = final_transform
|
||||
false
|
||||
self.start_advance_timer()
|
||||
return true
|
||||
current_ctx().start_advance_timer()
|
||||
else:
|
||||
let axis = self.position.basis.xform(axis)
|
||||
self.position.basis = self.position.basis.rotated(axis, deg_to_rad(degrees))
|
||||
self.position = self.position.orthonormalized()
|
||||
return false
|
||||
|
||||
proc sleep(seconds: float): bool =
|
||||
var duration = 0.0
|
||||
proc on_sleep(seconds: float) =
|
||||
self.blocks_per_frame = 0.0
|
||||
self.blocks_remaining_this_frame = 0.0
|
||||
self.callback = proc(delta: float): bool =
|
||||
duration += delta
|
||||
return duration < seconds
|
||||
self.start_advance_timer()
|
||||
true
|
||||
|
||||
proc save(name: string): bool =
|
||||
self.load_vars()
|
||||
|
@ -324,21 +301,28 @@ gdobj Builder of Spatial:
|
|||
if self.engine.initialized: self.set_vars()
|
||||
false
|
||||
|
||||
proc forward(steps: float): bool = self.move(FORWARD, steps)
|
||||
proc back(steps: float): bool = self.move(BACK, steps)
|
||||
proc up(steps: float): bool = self.move(UP, steps)
|
||||
proc down(steps: float): bool = self.move(DOWN, steps)
|
||||
proc left(steps: float): bool = self.move(LEFT, steps)
|
||||
proc right(steps: float): bool = self.move(RIGHT, steps)
|
||||
proc turn_left(degrees: float): bool = self.turn(degrees, UP)
|
||||
proc turn_right(degrees: float): bool = self.turn(degrees, DOWN)
|
||||
proc turn_up(degrees: float): bool = self.turn(degrees, RIGHT)
|
||||
proc turn_down(degrees: float): bool = self.turn(degrees, LEFT)
|
||||
|
||||
proc error(e: ref VMQuit) =
|
||||
self.running = false
|
||||
err e.msg
|
||||
trigger("script_error")
|
||||
proc on_script_loaded(e: Engine) =
|
||||
self.blocks_remaining_this_frame = 0
|
||||
e.expose "set_energy", proc(a: VmArgs): bool =
|
||||
let
|
||||
color = get_int(a, 0).int
|
||||
energy = get_float(a, 1)
|
||||
if self.draw_mode == GridMode:
|
||||
self.grid.set_energy(color, energy)
|
||||
else:
|
||||
self.terrain.set_energy(color, energy, self.script_index)
|
||||
false
|
||||
e.expose("save", a => self.save(get_string(a, 0)))
|
||||
e.expose("restore", a => self.restore(get_string(a, 0)))
|
||||
e.expose "reset", proc(a: VmArgs): bool =
|
||||
self.reset(get_bool(a, 0))
|
||||
false
|
||||
e.expose "pause", proc(a: VmArgs): bool =
|
||||
self.paused = true
|
||||
true
|
||||
e.expose "load_defaults", proc(a: VmArgs): bool =
|
||||
self.set_vars()
|
||||
false
|
||||
|
||||
proc clear() =
|
||||
if self.draw_mode == VoxelMode:
|
||||
|
@ -372,77 +356,25 @@ gdobj Builder of Spatial:
|
|||
self.draw(p.x, p.y, p.z, action_index)
|
||||
self.load_script()
|
||||
|
||||
proc load_script() =
|
||||
self.callback = nil
|
||||
self.blocks_remaining_this_frame = 0
|
||||
try:
|
||||
if self.engine.is_nil: self.engine = Engine()
|
||||
if not self.is_script_loadable:
|
||||
return
|
||||
if not (self.paused or self.engine.initialized):
|
||||
let code = default_builder(self.enu_script.extract_filename)
|
||||
with self.engine:
|
||||
load(config.script_dir, self.enu_script.split_file.name, code, config.lib_dir)
|
||||
expose "yield_script", proc(a: VmArgs):bool =
|
||||
self.callback = self.saved_callback
|
||||
self.saved_callback = nil
|
||||
true
|
||||
expose("up_impl", a => self.up(get_float(a, 0)))
|
||||
expose("down_impl", a => self.down(get_float(a, 0)))
|
||||
expose("left_impl", a => self.left(get_float(a, 0)))
|
||||
expose("right_impl", a => self.right(get_float(a, 0)))
|
||||
expose("forward_impl", a => self.forward(get_float(a, 0)))
|
||||
expose("back_impl", a => self.back(get_float(a, 0)))
|
||||
expose("turn_left_impl", a => self.turn_left(get_float(a, 0)))
|
||||
expose("turn_right_impl", a => self.turn_right(get_float(a, 0)))
|
||||
expose("turn_up_impl", a => self.turn_up(get_float(a, 0)))
|
||||
expose("turn_down_impl", a => self.turn_down(get_float(a, 0)))
|
||||
expose("echo_console", a => echo_console(get_string(a, 0)))
|
||||
expose("sleep_impl", a => self.sleep(get_float(a, 0)))
|
||||
expose("save", a => self.save(get_string(a, 0)))
|
||||
expose("restore", a => self.restore(get_string(a, 0)))
|
||||
expose "set_energy", proc(a: VmArgs): bool =
|
||||
let
|
||||
color = get_int(a, 0).int
|
||||
energy = get_float(a, 1)
|
||||
if self.draw_mode == GridMode:
|
||||
self.grid.set_energy(color, energy)
|
||||
else:
|
||||
self.terrain.set_energy(color, energy, self.script_index)
|
||||
false
|
||||
expose "reset", proc(a: VmArgs): bool =
|
||||
self.reset(get_bool(a, 0))
|
||||
false
|
||||
expose "pause", proc(a: VmArgs): bool =
|
||||
self.paused = true
|
||||
true
|
||||
expose "load_defaults", proc(a: VmArgs): bool =
|
||||
self.set_vars()
|
||||
false
|
||||
if not self.paused:
|
||||
self.update_running_state self.engine.run()
|
||||
except VMQuit as e:
|
||||
self.error(e)
|
||||
|
||||
method on_block_selected(offset: int) =
|
||||
if offset == self.script_index:
|
||||
show_editor self.enu_script, self.engine
|
||||
show_editor self.script, self.engine
|
||||
|
||||
method on_selected() =
|
||||
show_editor self.enu_script, self.engine
|
||||
show_editor self.script, self.engine
|
||||
|
||||
proc set_timer(duration: TimeInterval, callback: proc()) =
|
||||
self.timers.add (now() + duration, callback)
|
||||
|
||||
method reload() =
|
||||
let duration = if self.running: 0.5.seconds else: 0.seconds
|
||||
let duration = if self.engine.running: 0.5.seconds else: 0.seconds
|
||||
self.set_timer duration, proc() =
|
||||
self.reset(clear = true, set_vars = false)
|
||||
self.paused = false
|
||||
self.load_script()
|
||||
|
||||
method on_reload() =
|
||||
if not editing() or open_file == self.enu_script:
|
||||
if not editing() or open_file == self.script:
|
||||
self.reload()
|
||||
|
||||
method on_reload_all() =
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#? stdtmpl
|
||||
#proc default_builder(file_name: string): string =
|
||||
proc quit*(exit_code = 0) = discard
|
||||
#proc default_builder(file_name, imports: string): string =
|
||||
import types
|
||||
${imports}
|
||||
template name(n: untyped) = class_name(n, ScriptNode3D)
|
||||
preprocess "name", "${file_name}", "ScriptNode3D"
|
||||
|
||||
include builder
|
||||
include "${file_name}"
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
#? stdtmpl
|
||||
#proc default_robot(file_name: string): string =
|
||||
proc quit*(exit_code = 0) = discard
|
||||
#proc default_robot(file_name, imports: string): string =
|
||||
import types
|
||||
${imports}
|
||||
template name(n: untyped) = class_name(n, ScriptNode)
|
||||
preprocess "name", "${file_name}", "ScriptNode"
|
||||
|
||||
include robot
|
||||
include "${file_name}"
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../../godotapi/[grid_map, mesh_library, mesh, spatial, spatial_material]
|
||||
import godotapi/[grid_map, mesh_library, mesh, spatial, spatial_material]
|
||||
import godot, sets
|
||||
import ".." / [core, globals, world/builder]
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import ../../godotapi / [mesh_instance, node, spatial, resource_loader, packed_scene]
|
||||
import godotapi / [mesh_instance, node, spatial, resource_loader, packed_scene]
|
||||
import godot, sugar
|
||||
import ".." / [core, globals]
|
||||
import ../world / [builder, bot, terrain]
|
||||
import core, globals, world / [builder, bot, terrain]
|
||||
|
||||
gdobj Ground of MeshInstance:
|
||||
var
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import ../../godotapi / [mesh, voxel_terrain, voxel_tool, voxel, voxel_library, shader_material]
|
||||
import godotapi / [mesh, voxel_terrain, voxel_tool, voxel, voxel_library, shader_material]
|
||||
import godot, sets, tables, hashes
|
||||
import ".." / [globals, core]
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import random
|
||||
|
||||
# API
|
||||
proc quit*(exit_code = 0) = discard
|
||||
|
||||
proc echo_console(msg: string) = discard
|
||||
proc echo(msg: varargs[string, `$`]) = echo_console msg.join
|
||||
|
||||
proc begin_move*(direction: Vector3, steps: float) = discard
|
||||
proc begin_turn*(axis: Vector3, steps: float) = discard
|
||||
|
||||
proc look_at_impl(node: Node) = discard
|
||||
proc near(node: Node, distance = 5):bool = discard
|
||||
|
||||
proc forward*(steps = 1.0) = self.wait begin_move(FORWARD, steps)
|
||||
proc back*(steps = 1.0) = self.wait begin_move(BACK, steps)
|
||||
proc left*(steps = 1.0) = self.wait begin_move(LEFT, steps)
|
||||
proc right*(steps = 1.0) = self.wait begin_move(RIGHT, steps)
|
||||
proc turn_left*(degrees = 90.0) = self.wait begin_turn(LEFT, degrees)
|
||||
proc turn_right*(degrees = 90.0) = self.wait begin_turn(RIGHT, degrees)
|
||||
proc look_at*(node: Node) = self.wait look_at_impl(node)
|
||||
|
||||
proc fd*(steps = 1.0) = forward(steps)
|
||||
proc bk*(steps = 1.0) = back(steps)
|
||||
proc lt*(steps = 1.0) = left(steps)
|
||||
proc rt*(steps = 1.0) = right(steps)
|
||||
proc tl*(degrees = 90.0) = turn_left(degrees)
|
||||
proc tr*(degrees = 90.0) = turn_right(degrees)
|
||||
|
||||
when not declared(skip_3d):
|
||||
proc up*(steps = 1.0) = self.wait begin_move(UP, steps)
|
||||
proc down*(steps = 1.0) = self.wait begin_move(DOWN, steps)
|
||||
proc turn_up*(degrees = 0.0) = self.wait begin_turn(UP, degrees)
|
||||
proc turn_down*(degrees = 90.0) = self.wait begin_turn(DOWN, degrees)
|
||||
proc tu*(degrees = 90.0) = turn_up(degrees)
|
||||
proc td*(degrees = 90.0) = turn_down(degrees)
|
|
@ -1,24 +1,10 @@
|
|||
import strformat, strutils, helpers
|
||||
import strformat, strutils, helpers, types
|
||||
export helpers
|
||||
|
||||
include loops
|
||||
|
||||
type
|
||||
ColorIndex* = enum
|
||||
eraser = 0,
|
||||
blue = 1,
|
||||
red = 2,
|
||||
green = 3,
|
||||
black = 4,
|
||||
white = 5
|
||||
|
||||
DrawMode* = enum
|
||||
GridMode, VoxelMode
|
||||
|
||||
Energy = range[0.0..100.0]
|
||||
include loops, core
|
||||
|
||||
var
|
||||
speed*: range[0.0..250.0] = 30.0
|
||||
speed*: range[0.0..250.0] = 1.0
|
||||
move_speed = 1.0
|
||||
drawing* = true
|
||||
color*: ColorIndex
|
||||
|
@ -28,6 +14,21 @@ var
|
|||
scale* = 1.0
|
||||
energies: Table[ColorIndex, float]
|
||||
|
||||
include base_api
|
||||
|
||||
self.ctrl.begin_move = proc(direction: Vector3, steps: float, self: ScriptNode) =
|
||||
self.wait begin_move(direction, steps)
|
||||
|
||||
self.ctrl.begin_turn = proc(axis: Vector3, degrees: float, self: ScriptNode) =
|
||||
self.wait begin_turn(axis, degrees)
|
||||
|
||||
self.ctrl.advance_state_machine = proc(): bool = advance_state_machine()
|
||||
self.ctrl.yield_script = proc() = yield_script()
|
||||
|
||||
self.ctrl.set = proc(name: string, new_speed:float) =
|
||||
speed = new_speed
|
||||
self.ctrl.get = proc(name: string): float = speed
|
||||
|
||||
proc change_color(amount: int) =
|
||||
var color_index = int color
|
||||
color_index += amount
|
||||
|
@ -37,9 +38,6 @@ proc change_color(amount: int) =
|
|||
color_index = int ColorIndex.high
|
||||
color = ColorIndex color_index
|
||||
|
||||
include logo
|
||||
|
||||
proc echo_console*(msg: string) = discard
|
||||
proc sleep*(seconds: float) = discard
|
||||
proc reset*(clear = false) = discard
|
||||
proc save*(name = "default") = discard
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
type
|
||||
ColorIndex* = enum
|
||||
eraser = 0,
|
||||
blue = 1,
|
||||
red = 2,
|
||||
green = 3,
|
||||
black = 4,
|
||||
white = 5
|
||||
|
||||
DrawMode* = enum
|
||||
GridMode, VoxelMode
|
||||
|
||||
Energy* = range[0.0..100.0]
|
|
@ -1,39 +0,0 @@
|
|||
import random, types
|
||||
|
||||
# API
|
||||
proc forward_impl*(steps: float) = discard
|
||||
proc back_impl(steps: float) = discard
|
||||
proc left_impl(steps: float) = discard
|
||||
proc right_impl(steps: float) = discard
|
||||
proc turn_left_impl(degrees: float) = discard
|
||||
proc turn_right_impl(degrees: float) = discard
|
||||
proc look_at_impl(node: Node) = discard
|
||||
proc near*(node: Node, distance = 5):bool = discard
|
||||
|
||||
proc forward*(steps = 1.0) = wait forward_impl(steps)
|
||||
proc back*(steps = 1.0) = wait back_impl(steps)
|
||||
proc left*(steps = 1.0) = wait left_impl(steps)
|
||||
proc right*(steps = 1.0) = wait right_impl(steps)
|
||||
proc turn_left*(degrees = 90.0) = wait turn_left_impl(degrees)
|
||||
proc turn_right*(degrees = 90.0) = wait turn_right_impl(degrees)
|
||||
proc look_at*(node: Node) = wait look_at_impl(node)
|
||||
|
||||
|
||||
proc fd*(steps = 1.0) = forward(steps)
|
||||
proc bk*(steps = 1.0) = back(steps)
|
||||
proc lt*(steps = 1.0) = left(steps)
|
||||
proc rt*(steps = 1.0) = right(steps)
|
||||
proc tl*(degrees = 90.0) = turn_left(degrees)
|
||||
proc tr*(degrees = 90.0) = turn_right(degrees)
|
||||
|
||||
when not declared(skip_3d):
|
||||
proc up_impl(steps: float) = discard
|
||||
proc down_impl(steps: float) = discard
|
||||
proc turn_up_impl(degrees: float) = discard
|
||||
proc turn_down_impl(degrees: float) = discard
|
||||
proc up*(steps = 1.0) = wait up_impl(steps)
|
||||
proc down*(steps = 1.0) = wait down_impl(steps)
|
||||
proc turn_up*(degrees = 0.0) = wait turn_up_impl(degrees)
|
||||
proc turn_down*(degrees = 90.0) = wait turn_down_impl(degrees)
|
||||
proc tu*(degrees = 90.0) = turn_up(degrees)
|
||||
proc td*(degrees = 90.0) = turn_down(degrees)
|
|
@ -1,24 +1,25 @@
|
|||
import state_machine
|
||||
import state_machine, types
|
||||
|
||||
var
|
||||
context: Context
|
||||
action_running = true
|
||||
|
||||
proc yield_script = discard
|
||||
|
||||
proc advance_state_machine(): bool =
|
||||
if not context.is_nil:
|
||||
result = context.advance()
|
||||
result = if not context.is_nil:
|
||||
context.advance()
|
||||
else:
|
||||
true
|
||||
|
||||
proc set_action_running*(running: bool) =
|
||||
action_running = running
|
||||
self.ctrl.action_running = running
|
||||
|
||||
template wait(body: untyped) =
|
||||
action_running = true
|
||||
template wait(node: ScriptNode, body: untyped) =
|
||||
node.ctrl.action_running = true
|
||||
when defined(nimscript):
|
||||
body
|
||||
while action_running and advance_state_machine():
|
||||
yield_script()
|
||||
while node.ctrl.action_running and node.ctrl.advance_state_machine():
|
||||
node.ctrl.yield_script()
|
||||
else:
|
||||
# only for tests
|
||||
var counter = 0
|
||||
|
|
|
@ -1,16 +1,29 @@
|
|||
import helpers, strutils
|
||||
import helpers, strutils, types
|
||||
export helpers
|
||||
|
||||
include loops
|
||||
|
||||
let skip_3d = true
|
||||
include logo
|
||||
|
||||
var
|
||||
speed* = 1.0
|
||||
player* = Node()
|
||||
|
||||
#proc echo*(msg: string) = discard
|
||||
include base_api
|
||||
|
||||
self.ctrl.begin_move = proc(direction: Vector3, steps: float, self: ScriptNode) =
|
||||
self.wait begin_move(direction, steps)
|
||||
|
||||
self.ctrl.begin_turn = proc(axis: Vector3, degrees: float, self: ScriptNode) =
|
||||
self.wait begin_turn(axis, degrees)
|
||||
|
||||
self.ctrl.advance_state_machine = proc(): bool = advance_state_machine()
|
||||
self.ctrl.yield_script = proc() = yield_script()
|
||||
|
||||
self.ctrl.set = proc(name: string, new_speed:float) =
|
||||
speed = new_speed
|
||||
self.ctrl.get = proc(name: string): float = speed
|
||||
|
||||
proc play*(animation_name: string) = discard
|
||||
proc set_speed*(spd: float) = speed = spd
|
||||
|
||||
|
|
|
@ -1,3 +1,93 @@
|
|||
import helpers, strutils, strformat
|
||||
import macros
|
||||
|
||||
type
|
||||
Node* = ref object
|
||||
Vector3* = object
|
||||
x*, y*, z*: float
|
||||
|
||||
Node* = ref object of RootObj
|
||||
id: int
|
||||
name*: string
|
||||
|
||||
Controller* = object of RootObj
|
||||
action_running*: bool
|
||||
advance_state_machine*: proc(): bool
|
||||
yield_script*: proc()
|
||||
begin_move*: proc(direction: Vector3, steps: float, self: ScriptNode)
|
||||
begin_turn*: proc(axis: Vector3, degrees: float, self: ScriptNode)
|
||||
set*: proc(var_name: string, value: float)
|
||||
get*: proc(var_name: string): float
|
||||
|
||||
ScriptNode* = ref object of Node
|
||||
ctrl*: Controller
|
||||
|
||||
ScriptNode3D* = ref object of ScriptNode
|
||||
|
||||
proc vec3*(x, y, z: float): Vector3 {.inline.} =
|
||||
Vector3(x:x, y:y, z:z)
|
||||
|
||||
const
|
||||
UP* = vec3(0, 1, 0)
|
||||
DOWN* = vec3(0, -1, 0)
|
||||
BACK* = vec3(0, 0, 1)
|
||||
FORWARD* = vec3(0, 0, -1)
|
||||
RIGHT* = vec3(1, 0, 0)
|
||||
LEFT* = vec3(-1, 0, 0)
|
||||
|
||||
macro preprocess*(ident_name, file, class_name: static string): untyped =
|
||||
let ast = parse_stmt file.static_read
|
||||
# only checking top level for now. Make this more robust.
|
||||
for node in ast:
|
||||
if node.kind in [nnkCommand, nnkCall]:
|
||||
if node.len == 2 and node[1].kind == nnkIdent and node[0].eq_ident(ident_name):
|
||||
return node
|
||||
return parse_stmt(&"let self = {class_name}(ctrl: Controller())")
|
||||
|
||||
macro class_name*(name, base_class: untyped): untyped =
|
||||
name.expect_kind nnkIdent
|
||||
let name_str = name.str_val
|
||||
let type_name = (name_str & "Type").to_upper_ascii.nim_ident_normalize.ident
|
||||
let var_name = name_str.ident
|
||||
|
||||
result = quote do:
|
||||
when not declared(self) and not declared(`var_name`) and not declared(`type_name`):
|
||||
type
|
||||
`type_name`* = ref object of `base_class`
|
||||
let `var_name`* {.inject.} = `type_name`(name: `name_str`, ctrl: Controller())
|
||||
let self {.inject.} = `var_name`
|
||||
|
||||
template forward*(target: ScriptNode, steps = 1.0) =
|
||||
target.ctrl.begin_move(FORWARD, steps, self)
|
||||
|
||||
template back*(target: ScriptNode, steps = 1.0) =
|
||||
target.ctrl.begin_move(BACK, steps, self)
|
||||
|
||||
template left*(target: ScriptNode, steps = 1.0) =
|
||||
target.ctrl.begin_move(LEFT, steps, self)
|
||||
|
||||
template right*(target: ScriptNode, steps = 1.0) =
|
||||
target.ctrl.begin_move(RIGHT, steps, self)
|
||||
|
||||
template turn_left*(target: ScriptNode, degrees = 90.0) =
|
||||
target.ctrl.begin_turn(LEFT, degrees, self)
|
||||
|
||||
template turn_right*(target: ScriptNode, degrees = 90.0) =
|
||||
target.ctrl.begin_turn(RIGHT, degrees, self)
|
||||
|
||||
proc `speed=`*(self: ScriptNode, speed: float) =
|
||||
self.ctrl.set("speed", speed)
|
||||
|
||||
proc speed*(self: ScriptNode): float =
|
||||
self.ctrl.get("speed")
|
||||
|
||||
template up*(target: ScriptNode3D, steps = 1.0) =
|
||||
target.ctrl.begin_move(UP, steps, self)
|
||||
|
||||
template down*(target: ScriptNode3D, steps = 1.0) =
|
||||
target.ctrl.begin_move(DOWN, steps, self)
|
||||
|
||||
template turn_up*(target: ScriptNode3D, degrees = 90.0) =
|
||||
target.ctrl.begin_turn(UP, degrees, self)
|
||||
|
||||
template turn_down*(target: ScriptNode3D, degrees = 90.0) =
|
||||
target.ctrl.begin_turn(DOWN, degrees, self)
|
||||
|
|
Loading…
Reference in New Issue