mirror of https://github.com/dsrw/enu.git
Formatted with nph v0.3-11-gaeceee1
This commit is contained in:
parent
5b304e9697
commit
48639ef4af
|
@ -1,29 +1,30 @@
|
|||
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)
|
||||
|
|
|
@ -2,7 +2,8 @@ import nimib, nimibook
|
|||
import enuib
|
||||
|
||||
nb_init(theme = use_enu)
|
||||
nb_text: """
|
||||
nb_text:
|
||||
"""
|
||||
|
||||
# Introduction
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import std / [sugar, strutils, os, enumerate, pathnorm, macros]
|
||||
import pkg / [pretty, nimibook, nimib, nimib / themes]
|
||||
import pkg / nimibook / [types, commands, entries, toc_render]
|
||||
import std/[sugar, strutils, os, enumerate, pathnorm, macros]
|
||||
import pkg/[pretty, nimibook, nimib, nimib/themes]
|
||||
import pkg/nimibook/[types, commands, entries, toc_render]
|
||||
|
||||
export pathutils, pretty
|
||||
# adapted from https://raw.githubusercontent.com/pietroppeter/nimibook/ef700f646db8ec0bbe8a3319cbb3561aaac89a34/src/nimibook/themes.nim
|
||||
|
@ -8,7 +8,8 @@ export pathutils, pretty
|
|||
const document* = hl_html static_read("./book/template.html.mustache")
|
||||
|
||||
proc use_enu*(doc: var NbDoc) =
|
||||
doc.context["path_to_root"] = doc.src_dir_rel.string & "/" # I probably should make sure to have / at the end
|
||||
doc.context["path_to_root"] = doc.src_dir_rel.string & "/"
|
||||
# I probably should make sure to have / at the end
|
||||
|
||||
# templates are in memory
|
||||
doc.partials["document"] = document
|
||||
|
@ -36,7 +37,9 @@ proc use_enu*(doc: var NbDoc) =
|
|||
var this_entry: Entry
|
||||
# process toc
|
||||
for i, entry in enumerate(book.toc.entries.mitems):
|
||||
if normalize_path(entry.url) == normalize_path(doc.filename.replace('\\', '/')): # replace needed for windows
|
||||
if normalize_path(entry.url) ==
|
||||
normalize_path(doc.filename.replace('\\', '/')):
|
||||
# replace needed for windows
|
||||
this_entry = entry
|
||||
entry.is_active = true
|
||||
let
|
||||
|
@ -57,10 +60,10 @@ template load_md*(file) =
|
|||
nb_init(theme = use_enu)
|
||||
nb_text(text)
|
||||
|
||||
template caller_path: string =
|
||||
template caller_path(): string =
|
||||
instantiation_info(0, true).filename
|
||||
|
||||
proc root_dir: string =
|
||||
proc root_dir(): string =
|
||||
caller_path().splitfile.dir
|
||||
|
||||
template md*(file: string) =
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
import controllers / [node_controllers, script_controllers]
|
||||
import controllers/[node_controllers, script_controllers]
|
||||
export node_controllers, script_controllers
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import std / [tables, typetraits]
|
||||
import std/[tables, typetraits]
|
||||
import pkg/godot except print
|
||||
import godotapi / [node, spatial]
|
||||
import core, models, nodes / [bot_node, build_node, sign_node, player_node]
|
||||
import godotapi/[node, spatial]
|
||||
import core, models, nodes/[bot_node, build_node, sign_node, player_node]
|
||||
|
||||
proc remove_from_scene(unit: Unit) =
|
||||
debug "removing unit", unit = unit.id
|
||||
assert ?unit.node
|
||||
if unit == previous_build: previous_build = nil
|
||||
if unit == current_build: current_build = nil
|
||||
if unit == previous_build:
|
||||
previous_build = nil
|
||||
if unit == current_build:
|
||||
current_build = nil
|
||||
|
||||
for zid in unit.zids:
|
||||
Zen.thread_ctx.untrack zid
|
||||
|
@ -45,8 +47,8 @@ proc add_to_scene(unit: Unit) =
|
|||
node.transform = unit.transform
|
||||
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
|
||||
unit.node.visible =
|
||||
Visible in unit.global_flags and ScriptInitializing notin unit.global_flags
|
||||
|
||||
parent_node.add_child(unit.node)
|
||||
unit.node.owner = parent_node
|
||||
|
@ -55,14 +57,15 @@ proc add_to_scene(unit: Unit) =
|
|||
unit.main_thread_joined
|
||||
unit.global_flags += Ready
|
||||
|
||||
let parent_node = if Global in unit.global_flags:
|
||||
state.nodes.data
|
||||
else:
|
||||
unit.parent.node
|
||||
let parent_node =
|
||||
if Global in unit.global_flags: state.nodes.data else: unit.parent.node
|
||||
|
||||
if unit of Bot: Bot(unit).add(BotNode, parent_node)
|
||||
elif unit of Build: Build(unit).add(BuildNode, parent_node)
|
||||
elif unit of Sign: Sign(unit).add(SignNode, parent_node)
|
||||
if unit of Bot:
|
||||
Bot(unit).add(BotNode, parent_node)
|
||||
elif unit of Build:
|
||||
Build(unit).add(BuildNode, parent_node)
|
||||
elif unit of Sign:
|
||||
Sign(unit).add(SignNode, parent_node)
|
||||
elif unit of Player:
|
||||
let player = Player(unit)
|
||||
# TODO: PlayerNode should work for connected players as well
|
||||
|
@ -84,11 +87,13 @@ proc set_global(unit: Unit, global: bool) =
|
|||
if global:
|
||||
state.nodes.data.add_child(unit.node)
|
||||
unit.node.owner = state.nodes.data
|
||||
unit.transform_value.origin = unit.transform.origin + unit.start_transform.origin
|
||||
unit.transform_value.origin =
|
||||
unit.transform.origin + unit.start_transform.origin
|
||||
else:
|
||||
unit.parent.node.add_child(unit.node)
|
||||
unit.node.owner = unit.parent.node
|
||||
unit.transform_value.origin = unit.transform.origin - unit.start_transform.origin
|
||||
unit.transform_value.origin =
|
||||
unit.transform.origin - unit.start_transform.origin
|
||||
|
||||
proc reset_nodes() =
|
||||
current_build = nil
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import core
|
||||
import ./ script_controllers / worker
|
||||
import ./script_controllers/worker
|
||||
|
||||
proc init*(T: type ScriptController): ScriptController =
|
||||
result = ScriptController()
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import std / [os, macros, math, asyncfutures, hashes]
|
||||
import std/[os, macros, math, asyncfutures, hashes]
|
||||
import locks except Lock
|
||||
import pkg / godot except print
|
||||
import pkg / compiler / vm except get_int
|
||||
from pkg / compiler / vm {.all.} import stack_trace_aux
|
||||
import pkg / compiler / ast except new_node
|
||||
import pkg / compiler / [vmdef, renderer, msgs]
|
||||
import pkg/godot except print
|
||||
import pkg/compiler/vm except get_int
|
||||
from pkg/compiler/vm {.all.} import stack_trace_aux
|
||||
import pkg/compiler/ast except new_node
|
||||
import pkg/compiler/[vmdef, renderer, msgs]
|
||||
|
||||
import godotapi / [spatial, ray_cast]
|
||||
import core, models / [states, bots, builds, units, colors, signs, serializers]
|
||||
import libs / [interpreters, eval]
|
||||
import shared / errors
|
||||
import godotapi/[spatial, ray_cast]
|
||||
import core, models/[states, bots, builds, units, colors, signs, serializers]
|
||||
import libs/[interpreters, eval]
|
||||
import shared/errors
|
||||
|
||||
import ./ [vars, scripting]
|
||||
include ./ host_bridge_utils
|
||||
import ./[vars, scripting]
|
||||
include ./host_bridge_utils
|
||||
|
||||
proc get_last_error(self: Worker): ErrorData =
|
||||
result = self.last_exception.from_exception
|
||||
|
@ -32,7 +32,8 @@ proc unmap_unit*(self: Worker, unit: Unit) =
|
|||
proc link_dependency(self: Worker, dep: Unit) =
|
||||
let dep = dep.find_root
|
||||
let active = self.active_unit.find_root
|
||||
debug "linking dependency", dependency = dep.script_ctx.module_name,
|
||||
debug "linking dependency",
|
||||
dependency = dep.script_ctx.module_name,
|
||||
will_reload = active.script_ctx.module_name
|
||||
dep.script_ctx.dependents.incl active.script_ctx.module_name
|
||||
|
||||
|
@ -41,8 +42,9 @@ proc write_stack_trace(self: Worker) =
|
|||
|
||||
let ctx = self.active_unit.script_ctx
|
||||
{.gcsafe.}:
|
||||
msg_writeln(ctx.ctx.config, "stack trace: (most recent call last)",
|
||||
{msg_no_unit_sep})
|
||||
msg_writeln(
|
||||
ctx.ctx.config, "stack trace: (most recent call last)", {msg_no_unit_sep}
|
||||
)
|
||||
|
||||
stack_trace_aux(ctx.ctx, ctx.tos, ctx.pc)
|
||||
|
||||
|
@ -87,18 +89,21 @@ proc register_active(self: Worker, pnode: PNode) =
|
|||
self.map_unit(self.active_unit, pnode)
|
||||
|
||||
proc new_instance(self: Worker, src: Unit, dest: PNode) =
|
||||
let id = src.id & "_" & self.active_unit.id & "_instance_" &
|
||||
$(self.active_unit.units.len + 1)
|
||||
let id =
|
||||
src.id & "_" & self.active_unit.id & "_instance_" &
|
||||
$(self.active_unit.units.len + 1)
|
||||
|
||||
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)
|
||||
|
||||
debug "adding to active unit", unit = clone.id,
|
||||
active_unit = self.active_unit.id
|
||||
debug "adding to active unit",
|
||||
unit = clone.id, active_unit = self.active_unit.id
|
||||
|
||||
self.active_unit.units.add(clone)
|
||||
|
||||
|
@ -111,7 +116,8 @@ proc exec_instance(self: Worker, unit: Unit) =
|
|||
ctx.timeout_at = get_mono_time() + script_timeout
|
||||
ctx.running = ctx.call_proc("run_script", self.node_map[unit], true).paused
|
||||
|
||||
proc active_unit(self: Worker): Unit = self.active_unit
|
||||
proc active_unit(self: Worker): Unit =
|
||||
self.active_unit
|
||||
|
||||
proc wake(self: Unit) =
|
||||
self.script_ctx.timer = get_mono_time()
|
||||
|
@ -143,18 +149,26 @@ proc reset_level(self: Worker) =
|
|||
self.exit(self.active_unit.script_ctx, 0)
|
||||
after_boop:
|
||||
let current_level = state.config.level_dir
|
||||
state.config_value.value: level_dir = ""
|
||||
state.config_value.value:
|
||||
level_dir = ""
|
||||
remove_dir current_level
|
||||
state.config_value.value:
|
||||
level_dir = current_level
|
||||
|
||||
proc world_name: string = state.config.world
|
||||
proc world_name(): string =
|
||||
state.config.world
|
||||
|
||||
proc level_name: string = state.config.level
|
||||
|
||||
proc begin_turn(self: Worker, unit: Unit, direction: Vector3, degrees: float,
|
||||
lean: bool, move_mode: int): string =
|
||||
proc level_name(): string =
|
||||
state.config.level
|
||||
|
||||
proc begin_turn(
|
||||
self: Worker,
|
||||
unit: Unit,
|
||||
direction: Vector3,
|
||||
degrees: float,
|
||||
lean: bool,
|
||||
move_mode: int,
|
||||
): string =
|
||||
assert not degrees.is_nan
|
||||
var degrees = floor_mod(degrees, 360)
|
||||
let ctx = self.active_unit.script_ctx
|
||||
|
@ -163,9 +177,9 @@ proc begin_turn(self: Worker, unit: Unit, direction: Vector3, degrees: float,
|
|||
if not ctx.callback.is_nil:
|
||||
self.pause_script()
|
||||
|
||||
proc begin_move(self: Worker, unit: Unit, direction: Vector3, steps: float,
|
||||
move_mode: int) =
|
||||
|
||||
proc begin_move(
|
||||
self: Worker, unit: Unit, direction: Vector3, steps: float, move_mode: int
|
||||
) =
|
||||
var steps = steps
|
||||
var direction = direction
|
||||
let ctx = self.active_unit.script_ctx
|
||||
|
@ -179,14 +193,15 @@ proc begin_move(self: Worker, unit: Unit, direction: Vector3, steps: float,
|
|||
|
||||
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()
|
||||
|
||||
|
@ -209,7 +224,8 @@ proc `action_running=`(self: Unit, value: bool) =
|
|||
self.script_ctx.timer = MonoTime.high
|
||||
self.script_ctx.action_running = value
|
||||
|
||||
proc id(self: Unit): string = self.id
|
||||
proc id(self: Unit): string =
|
||||
self.id
|
||||
|
||||
proc global(self: Unit): bool =
|
||||
Global in self.global_flags
|
||||
|
@ -310,7 +326,9 @@ proc `rotation=`(self: Unit, degrees: float) =
|
|||
t.origin = self.transform.origin
|
||||
self.transform = t
|
||||
|
||||
proc sees(self: Worker, unit: Unit, target: Unit, distance: float): Future[bool] =
|
||||
proc sees(
|
||||
self: Worker, unit: Unit, target: Unit, distance: float
|
||||
): Future[bool] =
|
||||
result = Future.init(bool, "sees")
|
||||
|
||||
if target == state.player and Flying in state.local_flags:
|
||||
|
@ -324,18 +342,20 @@ proc sees(self: Worker, unit: Unit, target: Unit, distance: float): Future[bool]
|
|||
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()
|
||||
|
||||
proc frame_count(): int = state.frame_count
|
||||
proc frame_count(): int =
|
||||
state.frame_count
|
||||
|
||||
proc frame_created(unit: Unit): int =
|
||||
unit.frame_created
|
||||
|
@ -353,11 +373,15 @@ proc drop_transform(unit: Unit): Transform =
|
|||
|
||||
proc reset(self: Unit, clear: bool) =
|
||||
if clear:
|
||||
if self of Build: Build(self).reset()
|
||||
elif self of Bot: Bot(self).reset()
|
||||
if self of Build:
|
||||
Build(self).reset()
|
||||
elif self of Bot:
|
||||
Bot(self).reset()
|
||||
else:
|
||||
if self of Build: Build(self).reset_state()
|
||||
elif self of Bot: Bot(self).reset_state()
|
||||
if self of Build:
|
||||
Build(self).reset_state()
|
||||
elif self of Bot:
|
||||
Bot(self).reset_state()
|
||||
|
||||
# Bot bindings
|
||||
|
||||
|
@ -405,11 +429,11 @@ proc draw_position_set(self: Build, position: Vector3) =
|
|||
|
||||
proc save(self: Build, name: string) =
|
||||
self.save_points[name] =
|
||||
(self.draw_transform, self.color_value.value, self.drawing)
|
||||
(self.draw_transform, self.color_value.value, self.drawing)
|
||||
|
||||
proc restore(self: Build, name: string) =
|
||||
(self.draw_transform, self.color_value.value, self.drawing) =
|
||||
self.save_points[name]
|
||||
self.save_points[name]
|
||||
|
||||
# Player binding
|
||||
|
||||
|
@ -451,22 +475,43 @@ proc `open_sign=`(self: Unit, value: Sign) =
|
|||
|
||||
# Sign bindings
|
||||
|
||||
proc new_markdown_sign(self: Worker,
|
||||
unit: Unit, pnode: PNode, message: string, more: string, width: float,
|
||||
height: float, 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)
|
||||
proc new_markdown_sign(
|
||||
self: Worker,
|
||||
unit: Unit,
|
||||
pnode: PNode,
|
||||
message: string,
|
||||
more: string,
|
||||
width: float,
|
||||
height: float,
|
||||
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,
|
||||
)
|
||||
|
||||
info "creating sign", id = result.id
|
||||
self.map_unit(result, pnode)
|
||||
unit.units.add(result)
|
||||
|
||||
proc update_markdown_sign(self: Worker, unit: Sign, message: string,
|
||||
more: string, width: float, height: float, size: int, billboard: bool) =
|
||||
|
||||
proc update_markdown_sign(
|
||||
self: Worker,
|
||||
unit: Sign,
|
||||
message: string,
|
||||
more: string,
|
||||
width: float,
|
||||
height: float,
|
||||
size: int,
|
||||
billboard: bool,
|
||||
) =
|
||||
unit.width = width
|
||||
unit.height = height
|
||||
unit.size = size
|
||||
|
@ -511,7 +556,6 @@ proc `coding=`(self: Unit, value: Unit) =
|
|||
# End of bindings
|
||||
|
||||
proc bridge_to_vm*(worker: Worker) =
|
||||
|
||||
# host_bridge_utils.nim is expecting a var called `result`. Fix this.
|
||||
var result = worker
|
||||
|
||||
|
@ -530,8 +574,7 @@ proc bridge_to_vm*(worker: Worker) =
|
|||
begin_turn, begin_move, sleep_impl, position_set, new_markdown_sign,
|
||||
update_markdown_sign
|
||||
|
||||
result.bridged_from_vm "bots",
|
||||
play, all_bots
|
||||
result.bridged_from_vm "bots", play, all_bots
|
||||
|
||||
result.bridged_from_vm "builds",
|
||||
drawing, `drawing=`, initial_position, save, restore, all_builds,
|
||||
|
@ -542,5 +585,5 @@ proc bridge_to_vm*(worker: Worker) =
|
|||
size, `size=`, open, `open=`, billboard, `billboard=`
|
||||
|
||||
result.bridged_from_vm "players",
|
||||
playing, `playing=`, god, `god=`, flying, `flying=`, tool, `tool=`,
|
||||
coding, `coding=`, running, `running=`, open_sign, `open_sign=`
|
||||
playing, `playing=`, god, `god=`, flying, `flying=`, tool, `tool=`, coding,
|
||||
`coding=`, running, `running=`, open_sign, `open_sign=`
|
||||
|
|
|
@ -1,50 +1,72 @@
|
|||
proc get_int(a: VmArgs; i: Natural): int = int vm.get_int(a, i)
|
||||
proc get_int(a: VmArgs, i: Natural): int =
|
||||
int vm.get_int(a, i)
|
||||
|
||||
proc get_colors(a: VmArgs; i: Natural): Colors = Colors(vm.get_int(a, 1))
|
||||
proc get_colors(a: VmArgs, i: Natural): Colors =
|
||||
Colors(vm.get_int(a, 1))
|
||||
|
||||
proc get_pnode(a: VmArgs, pos: int): PNode {.inline.} = a.get_node(pos)
|
||||
proc get_pnode(a: VmArgs, pos: int): PNode {.inline.} =
|
||||
a.get_node(pos)
|
||||
|
||||
proc get_vector3(a: VmArgs, pos: int): Vector3 =
|
||||
let fields = a.get_node(pos).sons
|
||||
result = vec3(fields[0].float_val, fields[1].float_val, fields[2].float_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(a: bool): Pnode = new_int_node(nkIntLit, a.BiggestInt)
|
||||
proc to_node(val: enum): PNode = val.ord.to_node
|
||||
proc to_node(val: int): PNode =
|
||||
new_int_node(nkIntLit, val)
|
||||
|
||||
proc to_node(list: open_array[int|float|string|bool|enum]): PNode =
|
||||
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(a: bool): Pnode =
|
||||
new_int_node(nkIntLit, a.BiggestInt)
|
||||
|
||||
proc to_node(val: enum): PNode =
|
||||
val.ord.to_node
|
||||
|
||||
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()
|
||||
for i in 0 .. list.high:
|
||||
result.sons[i] = list[i].to_node()
|
||||
|
||||
proc to_node(tree: tuple|object): PNode =
|
||||
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(a: PNode): PNode = a
|
||||
proc to_node(a: PNode): PNode =
|
||||
a
|
||||
|
||||
proc to_node(tree: ref tuple|ref object): PNode =
|
||||
proc to_node(tree: ref tuple | ref object): PNode =
|
||||
result = nkPar.new_tree
|
||||
if tree.is_nil: return result
|
||||
if tree.is_nil:
|
||||
return result
|
||||
for field in tree.fields:
|
||||
result.sons.add(field.to_node)
|
||||
|
||||
proc to_result(val: float): BiggestFloat = BiggestFloat(val)
|
||||
proc to_result(val: SomeOrdinal or enum or bool): BiggestInt = BiggestInt(val)
|
||||
proc to_result(val: Vector3 or string or tuple): PNode = val.to_node
|
||||
proc to_result(val: PNode): PNode = result = val
|
||||
proc to_result(val: float): BiggestFloat =
|
||||
BiggestFloat(val)
|
||||
|
||||
proc to_result(val: SomeOrdinal or enum or bool): BiggestInt =
|
||||
BiggestInt(val)
|
||||
|
||||
proc to_result(val: Vector3 or string or tuple): PNode =
|
||||
val.to_node
|
||||
|
||||
proc to_result(val: PNode): PNode =
|
||||
result = val
|
||||
|
||||
proc await_future[T](future: Future[T], a: VmArgs) =
|
||||
future.add_callback proc(future: Future[T]) =
|
||||
set_result(a, to_result(future.read))
|
||||
|
||||
macro bridged_from_vm(self: Worker,
|
||||
module_name: string, proc_refs: varargs[untyped]): untyped =
|
||||
|
||||
macro bridged_from_vm(
|
||||
self: Worker, module_name: string, proc_refs: varargs[untyped]
|
||||
): untyped =
|
||||
result = new_stmt_list()
|
||||
result.add quote do:
|
||||
when not declared_in_scope(script_engine):
|
||||
|
@ -53,57 +75,66 @@ macro bridged_from_vm(self: Worker,
|
|||
for proc_ref in proc_refs:
|
||||
let
|
||||
symbol = bind_sym($proc_ref)
|
||||
proc_impl = (if symbol.kind == nnkSym: symbol else: symbol[0]).get_impl
|
||||
proc_impl = (if symbol.kind == nnkSym: symbol
|
||||
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]
|
||||
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 do: 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")
|
||||
|
||||
result.add quote do:
|
||||
mixin implement_routine
|
||||
`self`.interpreter.implement_routine "enu", `module_name`, `proc_impl_name`,
|
||||
proc(a {.inject.}: VmArgs) {.gcsafe.} =
|
||||
|
||||
debug "calling routine", name = `proc_name`
|
||||
try:
|
||||
`call`
|
||||
except Exception as e:
|
||||
error "Exception calling into host", kind = $e.type, msg = e.msg
|
||||
echo e.get_stack_trace()
|
||||
script_engine.last_exception = e
|
||||
`self`.interpreter.implement_routine "enu",
|
||||
`module_name`,
|
||||
`proc_impl_name`,
|
||||
proc(a {.inject.}: VmArgs) {.gcsafe.} =
|
||||
debug "calling routine", name = `proc_name`
|
||||
try:
|
||||
`call`
|
||||
except Exception as e:
|
||||
error "Exception calling into host", kind = $e.type, msg = e.msg
|
||||
echo e.get_stack_trace()
|
||||
script_engine.last_exception = e
|
||||
|
|
|
@ -1,27 +1,36 @@
|
|||
import std / [os, re]
|
||||
import std/[os, re]
|
||||
|
||||
import pkg / godot except print
|
||||
import pkg / compiler / ast except new_node
|
||||
import pkg / compiler / [lineinfos, renderer, msgs, vmdef]
|
||||
import godotapi / [spatial, ray_cast, voxel_terrain]
|
||||
import core, models / [states, bots, builds, units, signs, players]
|
||||
import libs / [interpreters, eval]
|
||||
import ./ vars
|
||||
import pkg/godot except print
|
||||
import pkg/compiler/ast except new_node
|
||||
import pkg/compiler/[lineinfos, renderer, msgs, vmdef]
|
||||
import godotapi/[spatial, ray_cast, voxel_terrain]
|
||||
import core, models/[states, bots, builds, units, signs, players]
|
||||
import libs/[interpreters, eval]
|
||||
import ./vars
|
||||
|
||||
proc init*(_: type ScriptCtx, owner: Unit, 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
|
||||
)
|
||||
proc init*(
|
||||
_: type ScriptCtx,
|
||||
owner: Unit,
|
||||
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,
|
||||
)
|
||||
|
||||
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
|
||||
|
@ -36,33 +45,34 @@ proc script_error*(self: Worker, unit: Unit, e: ref VMQuit) =
|
|||
proc init_interpreter*[T](self: Worker, _: T) {.gcsafe.} =
|
||||
private_access ScriptCtx
|
||||
|
||||
var interpreter = Interpreter.init(state.config.script_dir,
|
||||
state.config.lib_dir)
|
||||
var interpreter =
|
||||
Interpreter.init(state.config.script_dir, state.config.lib_dir)
|
||||
|
||||
let controller = self
|
||||
|
||||
self.interpreter = interpreter
|
||||
interpreter.config.spell_suggest_max = 0
|
||||
|
||||
interpreter.register_error_hook proc(config, info, msg,
|
||||
severity: auto) {.gcsafe.} =
|
||||
|
||||
interpreter.register_error_hook proc(
|
||||
config, info, msg, severity: auto
|
||||
) {.gcsafe.} =
|
||||
var info = info
|
||||
var msg = msg
|
||||
let ctx = controller.active_unit.script_ctx
|
||||
let errors = controller.active_unit.errors
|
||||
if severity == Severity.Error and config.error_counter >= config.error_max:
|
||||
var file_name = if info.file_index.int >= 0:
|
||||
config.m.file_infos[info.file_index.int].full_path.string
|
||||
else:
|
||||
"???"
|
||||
var file_name =
|
||||
if info.file_index.int >= 0:
|
||||
config.m.file_infos[info.file_index.int].full_path.string
|
||||
else:
|
||||
"???"
|
||||
|
||||
if file_exists(file_name) and ?ctx.file_name:
|
||||
if file_name.get_file_info != ctx.file_name.get_file_info:
|
||||
(file_name, info) = extract_file_info msg
|
||||
# msg = msg.replace(re"unhandled exception:.*\) Error\: ", "")
|
||||
# else:
|
||||
# msg = msg.replace(re"(?ms);.*", "")
|
||||
# msg = msg.replace(re"(?ms);.*", "")
|
||||
else:
|
||||
error "File not found handling error", file_name
|
||||
|
||||
|
@ -73,45 +83,48 @@ 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 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 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
|
||||
|
@ -125,10 +138,11 @@ proc load_script*(self: Worker, unit: Unit, timeout = script_timeout) =
|
|||
var others = self.module_names
|
||||
self.module_names.incl module_name
|
||||
others.excl module_name
|
||||
let imports = if others.card > 0:
|
||||
"import " & others.to_seq.join(", ")
|
||||
else:
|
||||
""
|
||||
let imports =
|
||||
if others.card > 0:
|
||||
"import " & others.to_seq.join(", ")
|
||||
else:
|
||||
""
|
||||
let code = unit.code_template(imports)
|
||||
ctx.timeout_at = get_mono_time() + timeout
|
||||
ctx.file_index = -1
|
||||
|
@ -141,7 +155,6 @@ proc load_script*(self: Worker, unit: Unit, timeout = script_timeout) =
|
|||
if not ctx.running and not ?unit.clone_of:
|
||||
unit.collect_garbage
|
||||
unit.ensure_visible
|
||||
|
||||
except VMQuit as e:
|
||||
ctx.running = false
|
||||
self.interpreter.reset_module(unit.script_ctx.module_name)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import std / [locks, os, random, net]
|
||||
import std / times except seconds, minutes
|
||||
from pkg / netty import Reactor
|
||||
import core, models, models / [serializers], libs / [interpreters, eval]
|
||||
import ./ [vars, host_bridge, scripting]
|
||||
import std/[locks, os, random, net]
|
||||
import std/times except seconds, minutes
|
||||
from pkg/netty import Reactor
|
||||
import core, models, models/[serializers], libs/[interpreters, eval]
|
||||
import ./[vars, host_bridge, scripting]
|
||||
|
||||
var
|
||||
worker_lock: locks.Lock
|
||||
|
@ -24,16 +24,17 @@ proc advance_unit(self: Worker, unit: Unit, timeout: MonoTime): bool =
|
|||
|
||||
let now = get_mono_time()
|
||||
|
||||
let delta = if ?ctx.last_ran:
|
||||
(now - ctx.last_ran).in_microseconds.float / 1000000.0
|
||||
else:
|
||||
0.0
|
||||
let delta =
|
||||
if ?ctx.last_ran:
|
||||
(now - ctx.last_ran).in_microseconds.float / 1000000.0
|
||||
else:
|
||||
0.0
|
||||
|
||||
ctx.last_ran = now
|
||||
if ctx.callback == nil or
|
||||
(task_state = ctx.callback(delta, timeout);
|
||||
task_state in {Done, NextTask}):
|
||||
|
||||
if ctx.callback == nil or (;
|
||||
task_state = ctx.callback(delta, timeout)
|
||||
task_state in {Done, NextTask}
|
||||
):
|
||||
ctx.timer = MonoTime.high
|
||||
ctx.action_running = false
|
||||
self.active_unit = unit
|
||||
|
@ -45,7 +46,6 @@ proc advance_unit(self: Worker, unit: Unit, timeout: MonoTime): bool =
|
|||
unit.current_line = 0
|
||||
|
||||
result = ctx.running and task_state == NextTask
|
||||
|
||||
elif now >= ctx.timer:
|
||||
ctx.timer = now + advance_step
|
||||
ctx.saved_callback = ctx.callback
|
||||
|
@ -53,7 +53,6 @@ proc advance_unit(self: Worker, unit: Unit, timeout: MonoTime): bool =
|
|||
self.active_unit = unit
|
||||
ctx.timeout_at = now + script_timeout
|
||||
discard ctx.resume()
|
||||
|
||||
except VMQuit as e:
|
||||
self.interpreter.reset_module(unit.script_ctx.module_name)
|
||||
self.script_error(unit, e)
|
||||
|
@ -116,18 +115,20 @@ proc watch_code(self: Worker, unit: Unit) =
|
|||
self.script_error(unit, e)
|
||||
|
||||
if unit.script_ctx.is_nil:
|
||||
unit.script_ctx = ScriptCtx.init(owner = unit,
|
||||
interpreter = self.interpreter)
|
||||
unit.script_ctx =
|
||||
ScriptCtx.init(owner = unit, interpreter = self.interpreter)
|
||||
|
||||
unit.script_ctx.script = script_file_for unit
|
||||
|
||||
proc watch_units(self: Worker,
|
||||
units: ZenSeq[Unit],
|
||||
parent: Unit,
|
||||
body: proc(unit: Unit, change: Change[Unit], added: bool,
|
||||
removed: bool) {.gcsafe.}
|
||||
proc watch_units(
|
||||
self: Worker,
|
||||
units: ZenSeq[Unit],
|
||||
parent: Unit,
|
||||
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:
|
||||
let unit = change.item
|
||||
|
@ -143,10 +144,11 @@ proc watch_units(self: Worker,
|
|||
self.watch_units(unit.units, unit, body)
|
||||
|
||||
template for_all_units(self: Worker, body: untyped) {.dirty.} =
|
||||
self.watch_units state.units, parent = nil,
|
||||
proc(unit: Unit, change: Change[Unit], added: bool,
|
||||
removed: bool) {.gcsafe.} =
|
||||
|
||||
self.watch_units state.units,
|
||||
parent = nil,
|
||||
proc(
|
||||
unit: Unit, change: Change[Unit], added: bool, removed: bool
|
||||
) {.gcsafe.} =
|
||||
body
|
||||
|
||||
proc worker_thread(params: (ZenContext, GameState)) {.gcsafe.} =
|
||||
|
@ -154,9 +156,14 @@ 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)
|
||||
|
@ -192,8 +199,7 @@ proc worker_thread(params: (ZenContext, GameState)) {.gcsafe.} =
|
|||
unit.script_ctx.running = false
|
||||
unit.script_ctx.callback = nil
|
||||
if not (unit of Player) and LoadingScript notin state.local_flags and
|
||||
not ?unit.clone_of:
|
||||
|
||||
not ?unit.clone_of:
|
||||
remove_file unit.script_ctx.script
|
||||
remove_dir unit.data_dir
|
||||
|
||||
|
@ -240,7 +246,6 @@ proc worker_thread(params: (ZenContext, GameState)) {.gcsafe.} =
|
|||
level_dir = change.item.level_dir
|
||||
if level_dir != "":
|
||||
worker.load_level(level_dir)
|
||||
|
||||
else:
|
||||
var timeout_at = get_mono_time() + 30.seconds
|
||||
var connected = false
|
||||
|
@ -257,9 +262,18 @@ 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
|
||||
|
@ -306,14 +320,14 @@ proc worker_thread(params: (ZenContext, GameState)) {.gcsafe.} =
|
|||
break
|
||||
|
||||
var to_process: seq[Unit]
|
||||
state.units.value.walk_tree proc(unit: Unit) = to_process.add unit
|
||||
state.units.value.walk_tree proc(unit: Unit) =
|
||||
to_process.add unit
|
||||
to_process.shuffle
|
||||
|
||||
var batched: HashSet[Unit]
|
||||
|
||||
while Zen.thread_ctx.pressure < 0.9 and to_process.len > 0 and state.voxel_tasks <= 10 and
|
||||
get_mono_time() < timeout:
|
||||
|
||||
while Zen.thread_ctx.pressure < 0.9 and to_process.len > 0 and
|
||||
state.voxel_tasks <= 10 and get_mono_time() < timeout:
|
||||
let units = to_process
|
||||
to_process = @[]
|
||||
for unit in units:
|
||||
|
@ -338,10 +352,9 @@ proc worker_thread(params: (ZenContext, GameState)) {.gcsafe.} =
|
|||
|
||||
if now < wait_until:
|
||||
sleep int((wait_until - get_mono_time()).in_milliseconds)
|
||||
|
||||
except Exception as e:
|
||||
error "Unhandled worker thread exception", kind = $e.type, msg = e.msg,
|
||||
stacktrace = e.get_stack_trace
|
||||
error "Unhandled worker thread exception",
|
||||
kind = $e.type, msg = e.msg, stacktrace = e.get_stack_trace
|
||||
|
||||
state.push_flag(NeedsRestart)
|
||||
|
||||
|
@ -356,8 +369,9 @@ proc worker_thread(params: (ZenContext, GameState)) {.gcsafe.} =
|
|||
except Exception:
|
||||
discard
|
||||
|
||||
proc launch_worker*(ctx: ZenContext, state: GameState): system.Thread[tuple[
|
||||
ctx: ZenContext, state: GameState]] =
|
||||
proc launch_worker*(
|
||||
ctx: ZenContext, state: GameState
|
||||
): system.Thread[tuple[ctx: ZenContext, state: GameState]] =
|
||||
worker_lock.acquire
|
||||
result.create_thread(worker_thread, (ctx, state))
|
||||
work_done.wait(worker_lock)
|
||||
|
|
64
src/core.nim
64
src/core.nim
|
@ -1,8 +1,8 @@
|
|||
import types
|
||||
export types
|
||||
|
||||
import pkg / model_citizen / utils
|
||||
import std / [sequtils, strutils, sugar, macros, asyncfutures, importutils]
|
||||
import pkg/model_citizen/utils
|
||||
import std/[sequtils, strutils, sugar, macros, asyncfutures, importutils]
|
||||
export utils, sequtils, strutils, sugar, importutils
|
||||
|
||||
### Globals ###
|
||||
|
@ -13,9 +13,9 @@ var state* {.threadvar.}: GameState
|
|||
### Sugar ###
|
||||
|
||||
from sugar import dup, dump, collect
|
||||
import std / [with, sets, monotimes, tables]
|
||||
import std / times except seconds
|
||||
import pkg / [pretty, flatty]
|
||||
import std/[with, sets, monotimes, tables]
|
||||
import std/times except seconds
|
||||
import pkg/[pretty, flatty]
|
||||
|
||||
export with, sets, tables, pretty, flatty
|
||||
|
||||
|
@ -26,10 +26,11 @@ proc minutes*(m: float | int): Duration {.inline.} =
|
|||
|
||||
export dump
|
||||
|
||||
import pkg / chronicles
|
||||
import pkg/chronicles
|
||||
export chronicles
|
||||
|
||||
template nim_filename*: string = instantiation_info(full_paths = true).filename
|
||||
template nim_filename*(): string =
|
||||
instantiation_info(full_paths = true).filename
|
||||
|
||||
### options ###
|
||||
|
||||
|
@ -44,16 +45,10 @@ proc `||=`*[T](opt: var Option[T], val: T): T {.discardable.} =
|
|||
result = opt.get()
|
||||
|
||||
proc `||`*[T](a: Option[T], b: T): T =
|
||||
if ?a:
|
||||
a.get
|
||||
else:
|
||||
b
|
||||
if ?a: a.get else: b
|
||||
|
||||
proc `||`*[T](a, b: T): T =
|
||||
if ?a:
|
||||
a
|
||||
else:
|
||||
b
|
||||
if ?a: a else: b
|
||||
|
||||
converter from_option*[T](val: Option[T]): T =
|
||||
val.get()
|
||||
|
@ -121,9 +116,9 @@ proc `z=`*(self: var Basis, value: Vector3) {.inline.} =
|
|||
|
||||
proc surrounding*(point: Vector3): seq[Vector3] =
|
||||
collect(new_seq):
|
||||
for x in 0..2:
|
||||
for y in 0..2:
|
||||
for z in 0..2:
|
||||
for x in 0 .. 2:
|
||||
for y in 0 .. 2:
|
||||
for z in 0 .. 2:
|
||||
point + vec3(x - 1, y - 1, z - 1)
|
||||
|
||||
# math
|
||||
|
@ -145,10 +140,10 @@ proc wrap*[T](value, min, max: T): float =
|
|||
# output
|
||||
|
||||
when not defined(no_godot):
|
||||
import pkg / godot
|
||||
import pkg/godot
|
||||
|
||||
default_chronicles_stream.output.writer =
|
||||
proc (logLevel: LogLevel, msg: LogOutputStr) {.gcsafe.} =
|
||||
proc(logLevel: LogLevel, msg: LogOutputStr) {.gcsafe.} =
|
||||
when defined(release):
|
||||
godot.print msg
|
||||
else:
|
||||
|
@ -159,12 +154,12 @@ when not defined(no_godot):
|
|||
proc init*(_: type Future, T: type, proc_name = ""): Future[T] =
|
||||
return new_future[T](proc_name)
|
||||
|
||||
import pkg / core / transforms
|
||||
import pkg/core/transforms
|
||||
export transforms
|
||||
|
||||
import pkg / godot
|
||||
import pkg/godot
|
||||
|
||||
import pkg / model_citizen
|
||||
import pkg/model_citizen
|
||||
export model_citizen
|
||||
|
||||
proc global_from*(self: Vector3, unit: Unit): Vector3 =
|
||||
|
@ -200,7 +195,8 @@ proc `basis=`*(self: ZenValue[Transform], value: Basis) =
|
|||
transform.basis = value
|
||||
self.value = transform
|
||||
|
||||
proc init*(_: type Basis): Basis = init_basis()
|
||||
proc init*(_: type Basis): Basis =
|
||||
init_basis()
|
||||
|
||||
proc init*(_: type Transform, origin = vec3()): Transform =
|
||||
result = init_transform()
|
||||
|
@ -221,15 +217,18 @@ 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:
|
||||
{. error:
|
||||
"Watch needs a Unit object to bind its lifetime to. The Unit " &
|
||||
"can be passed explicitly, or found implicitly by evaluating " &
|
||||
"`self.model`, then `self`." .}
|
||||
{.
|
||||
error:
|
||||
"Watch needs a Unit object to bind its lifetime to. The Unit " &
|
||||
"can be passed explicitly, or found implicitly by evaluating " &
|
||||
"`self.model`, then `self`."
|
||||
.}
|
||||
|
||||
template watch*[T, O](zen: Zen[T, O], body: untyped) =
|
||||
when compiles(self.model):
|
||||
|
@ -246,7 +245,8 @@ macro enum_fields*(n: typed): untyped =
|
|||
case f.kind
|
||||
of nnk_sym, nnk_ident:
|
||||
result.add new_lit(f.str_val)
|
||||
else: discard
|
||||
else:
|
||||
discard
|
||||
|
||||
template value*(self: ZenValue, body: untyped) {.dirty.} =
|
||||
block:
|
||||
|
@ -260,7 +260,7 @@ template after_boop*(body: untyped) =
|
|||
deferred.add proc() =
|
||||
body
|
||||
|
||||
proc run_deferred* =
|
||||
proc run_deferred*() =
|
||||
for fn in deferred:
|
||||
fn()
|
||||
deferred.set_len(0)
|
||||
|
|
16
src/enu.nim
16
src/enu.nim
|
@ -1,11 +1,17 @@
|
|||
{.warning[UnusedImport]: off.}
|
||||
|
||||
import libs / segfaults
|
||||
import libs/segfaults
|
||||
import core, game
|
||||
|
||||
import ui / [editor, console, toolbar, action_button, preview_maker,
|
||||
markdown_label, right_panel]
|
||||
import nodes / [player_node, aim_target, selection_area, bot_node, ground_node,
|
||||
build_node, sign_node]
|
||||
import
|
||||
ui/[
|
||||
editor, console, toolbar, action_button, preview_maker, markdown_label,
|
||||
right_panel
|
||||
]
|
||||
import
|
||||
nodes/[
|
||||
player_node, aim_target, selection_area, bot_node, ground_node, build_node,
|
||||
sign_node
|
||||
]
|
||||
|
||||
Zen.bootstrap
|
||||
|
|
128
src/game.nim
128
src/game.nim
|
@ -1,13 +1,15 @@
|
|||
import std / [monotimes, os, json, math, random, net]
|
||||
import pkg / [godot, metrics, metrics / stdlib_httpserver]
|
||||
import std/[monotimes, os, json, math, random, net]
|
||||
import pkg/[godot, metrics, metrics/stdlib_httpserver]
|
||||
from dotenv import nil
|
||||
import godotapi / [input, input_event, gd_os, node, scene_tree, packed_scene,
|
||||
sprite, control, viewport, viewport_texture, performance, label, theme,
|
||||
dynamic_font, resource_loader, main_loop, project_settings, input_map,
|
||||
input_event_action, input_event_key, global_constants, scroll_container,
|
||||
voxel_server]
|
||||
import
|
||||
godotapi/[
|
||||
input, input_event, gd_os, node, scene_tree, packed_scene, sprite, control,
|
||||
viewport, viewport_texture, performance, label, theme, dynamic_font,
|
||||
resource_loader, main_loop, project_settings, input_map, input_event_action,
|
||||
input_event_key, global_constants, scroll_container, voxel_server
|
||||
]
|
||||
|
||||
import core, types, globals, controllers, models / [serializers, units, colors]
|
||||
import core, types, globals, controllers, models/[serializers, units, colors]
|
||||
|
||||
if file_exists(".env"):
|
||||
dotenv.overload()
|
||||
|
@ -18,8 +20,9 @@ when defined(metrics):
|
|||
ZenContext.init_metrics "main", "worker"
|
||||
|
||||
# saved state when restarting worker thread
|
||||
const savable_flags = {ConsoleVisible, MouseCaptured, Flying, God, AltWalkSpeed,
|
||||
AltFlySpeed}
|
||||
const savable_flags = {
|
||||
ConsoleVisible, MouseCaptured, Flying, God, AltWalkSpeed, AltFlySpeed
|
||||
}
|
||||
|
||||
var saved_transform {.threadvar.}: Transform
|
||||
var saved_rotation {.threadvar.}: float
|
||||
|
@ -57,7 +60,8 @@ gdobj Game of Node:
|
|||
state.units.value.walk_tree proc(unit: Unit) =
|
||||
inc unit_count
|
||||
|
||||
self.stats.text = \"""
|
||||
self.stats.text =
|
||||
\"""
|
||||
|
||||
FPS: {fps}
|
||||
scale_factor: {state.scale_factor}
|
||||
|
@ -68,7 +72,8 @@ level: {state.level_name}
|
|||
{get_stats()}
|
||||
|
||||
"""
|
||||
state.voxel_tasks = parse_int($get_stats()["tasks"].as_dictionary["main_thread"])
|
||||
state.voxel_tasks =
|
||||
parse_int($get_stats()["tasks"].as_dictionary["main_thread"])
|
||||
|
||||
if time > self.rescale_at:
|
||||
self.rescale_at = MonoTime.high
|
||||
|
@ -79,8 +84,8 @@ level: {state.level_name}
|
|||
|
||||
proc rescale*() =
|
||||
let vp = self.get_viewport().size
|
||||
state.scale_factor = sqrt(state.config.mega_pixels *
|
||||
1_000_000.0 / (vp.x * vp.y))
|
||||
state.scale_factor =
|
||||
sqrt(state.config.mega_pixels * 1_000_000.0 / (vp.x * vp.y))
|
||||
|
||||
self.scaled_viewport.size = vp * state.scale_factor
|
||||
|
||||
|
@ -91,7 +96,7 @@ level: {state.level_name}
|
|||
if what == main_loop.NOTIFICATION_WM_ABOUT:
|
||||
alert \"Enu {enu_version}\n\n© 2023 Scott Wadden", "Enu"
|
||||
|
||||
proc add_platform_input_actions =
|
||||
proc add_platform_input_actions() =
|
||||
let suffix = "." & host_os
|
||||
for action in get_actions():
|
||||
let action = action.as_string()
|
||||
|
@ -105,19 +110,24 @@ level: {state.level_name}
|
|||
action_add_event(name, event)
|
||||
erase_action(action)
|
||||
|
||||
proc init* =
|
||||
proc init*() =
|
||||
self.process_priority = -100
|
||||
|
||||
let
|
||||
screen_scale = if host_os == "macos":
|
||||
let screen_scale =
|
||||
if host_os == "macos":
|
||||
get_screen_scale(-1)
|
||||
else:
|
||||
get_screen_dpi(-1).float / 96.0
|
||||
|
||||
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
|
||||
|
@ -140,11 +150,15 @@ level: {state.level_name}
|
|||
listen_address = ""
|
||||
|
||||
if host_os == "macosx" and not restarting:
|
||||
global_menu_add_item("Help", "Documentation", "help".to_variant, "".to_variant)
|
||||
global_menu_add_item(
|
||||
"Help", "Documentation", "help".to_variant, "".to_variant
|
||||
)
|
||||
global_menu_add_item("Help", "Web Site", "site".to_variant, "".to_variant)
|
||||
if connect_address == "":
|
||||
global_menu_add_separator("Help")
|
||||
global_menu_add_item("Help", "Launch Tutorial", "tutorial".to_variant, "".to_variant)
|
||||
global_menu_add_item(
|
||||
"Help", "Launch Tutorial", "tutorial".to_variant, "".to_variant
|
||||
)
|
||||
|
||||
state.config_value.value:
|
||||
work_dir = get_user_data_dir()
|
||||
|
@ -156,8 +170,8 @@ level: {state.level_name}
|
|||
mega_pixels = uc.mega_pixels ||= 2.0
|
||||
start_full_screen = uc.start_full_screen ||= true
|
||||
semicolon_as_colon = uc.semicolon_as_colon ||= false
|
||||
lib_dir = join_path(get_executable_path().parent_dir(), "..", "..", "..",
|
||||
"vmlib")
|
||||
lib_dir =
|
||||
join_path(get_executable_path().parent_dir(), "..", "..", "..", "vmlib")
|
||||
|
||||
connect_address = connect_address
|
||||
listen_address = listen_address
|
||||
|
@ -176,10 +190,11 @@ level: {state.level_name}
|
|||
|
||||
set_window_fullscreen state.config.start_full_screen
|
||||
when defined(metrics):
|
||||
let metrics_port = if ?get_env("ENU_METRICS_PORT"):
|
||||
get_env("ENU_METRICS_PORT").parse_int
|
||||
else:
|
||||
8000
|
||||
let metrics_port =
|
||||
if ?get_env("ENU_METRICS_PORT"):
|
||||
get_env("ENU_METRICS_PORT").parse_int
|
||||
else:
|
||||
8000
|
||||
|
||||
{.cast(gcsafe).}:
|
||||
start_metrics_http_server("0.0.0.0", Port(metrics_port))
|
||||
|
@ -191,11 +206,9 @@ level: {state.level_name}
|
|||
if host_os == "macosx":
|
||||
state.config_value.value:
|
||||
lib_dir = join_path(exe_dir.parent_dir, "Resources", "vmlib")
|
||||
|
||||
elif host_os == "windows":
|
||||
state.config_value.value:
|
||||
lib_dir = join_path(exe_dir, "vmlib")
|
||||
|
||||
elif host_os == "linux":
|
||||
state.config_value.value:
|
||||
lib_dir = join_path(exe_dir.parent_dir, "lib", "vmlib")
|
||||
|
@ -218,18 +231,17 @@ level: {state.level_name}
|
|||
theme_holder = self.find_node("LeftPanel").as(Container)
|
||||
theme = theme_holder.theme
|
||||
font = theme.default_font.as(DynamicFont)
|
||||
bold_font = theme.get_font("bold_font", "RichTextLabel")
|
||||
.as(DynamicFont)
|
||||
bold_font = theme.get_font("bold_font", "RichTextLabel").as(DynamicFont)
|
||||
|
||||
font.size = size
|
||||
bold_font.size = size
|
||||
theme_holder.theme = theme
|
||||
|
||||
method ready* =
|
||||
method ready*() =
|
||||
state.nodes.data = state.nodes.game.find_node("Level").get_node("data")
|
||||
assert not state.nodes.data.is_nil
|
||||
self.scaled_viewport =
|
||||
self.get_node("ViewportContainer/Viewport") as Viewport
|
||||
self.get_node("ViewportContainer/Viewport") as Viewport
|
||||
|
||||
self.bind_signals(self.get_viewport(), "size_changed")
|
||||
self.bind_signals(self.get_tree(), "global_menu_action")
|
||||
|
@ -277,7 +289,8 @@ level: {state.level_name}
|
|||
discard self.get_tree.reload_current_scene()
|
||||
|
||||
if Connecting.added:
|
||||
state.status_message = \"""
|
||||
state.status_message =
|
||||
\"""
|
||||
|
||||
# Connecting...
|
||||
|
||||
|
@ -320,7 +333,8 @@ Trying to connect to {state.config.connect_address}.
|
|||
elif action == "site":
|
||||
discard shell_open("http://getenu.com")
|
||||
elif action == "tutorial":
|
||||
state.config_value.value: level_dir = ""
|
||||
state.config_value.value:
|
||||
level_dir = ""
|
||||
state.player.transform = Transform.init(origin = vec3(0, 2, 0))
|
||||
state.player.rotation = 0
|
||||
change_loaded_level("tutorial-1", "tutorial")
|
||||
|
@ -331,34 +345,35 @@ Trying to connect to {state.config.connect_address}.
|
|||
var level = config.level
|
||||
let prefix = config.world & "-"
|
||||
level.remove_prefix(prefix)
|
||||
var num = try:
|
||||
level.parse_int
|
||||
except ValueError:
|
||||
1
|
||||
var num =
|
||||
try:
|
||||
level.parse_int
|
||||
except ValueError:
|
||||
1
|
||||
num += diff
|
||||
change_loaded_level(prefix & $num, state.config.world)
|
||||
else:
|
||||
# force a reload of the current world
|
||||
let current_level = state.config.level_dir
|
||||
state.config_value.value: level_dir = ""
|
||||
state.config_value.value: level_dir = current_level
|
||||
state.config_value.value:
|
||||
level_dir = ""
|
||||
state.config_value.value:
|
||||
level_dir = current_level
|
||||
|
||||
method unhandled_input*(event: InputEvent) =
|
||||
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:
|
||||
state.pop_flag CommandMode
|
||||
|
||||
if EditorVisible in state.local_flags or DocsVisible in state.local_flags or
|
||||
ConsoleVisible in state.local_flags:
|
||||
|
||||
ConsoleVisible in state.local_flags:
|
||||
if event.is_action_pressed("zoom_in"):
|
||||
self.set_font_size state.config.font_size + 1
|
||||
elif event.is_action_pressed("zoom_out"):
|
||||
|
@ -371,11 +386,11 @@ Trying to connect to {state.config.connect_address}.
|
|||
state.update_action_index(-1)
|
||||
# NOTE: alt+enter isn't being picked up on windows if the editor is
|
||||
# open. Needs investigation.
|
||||
if event.is_action_pressed("toggle_fullscreen") or (host_os == "windows" and
|
||||
CommandMode in state.local_flags and EditorVisible in state.local_flags and
|
||||
event of InputEventKey and
|
||||
event.as(InputEventKey).scancode == KEY_ENTER):
|
||||
|
||||
if event.is_action_pressed("toggle_fullscreen") or (
|
||||
host_os == "windows" and CommandMode in state.local_flags and
|
||||
EditorVisible in state.local_flags and event of InputEventKey and
|
||||
event.as(InputEventKey).scancode == KEY_ENTER
|
||||
):
|
||||
set_window_fullscreen not is_window_fullscreen()
|
||||
var user_config = load_user_config()
|
||||
state.config_value.value:
|
||||
|
@ -393,7 +408,6 @@ Trying to connect to {state.config.connect_address}.
|
|||
self.switch_world(0)
|
||||
state.pop_flag ResettingVM
|
||||
self.get_tree().set_input_as_handled()
|
||||
|
||||
elif event.is_action_pressed("pause"):
|
||||
state.paused = not state.paused
|
||||
elif event.is_action_pressed("clear_console"):
|
||||
|
@ -435,15 +449,13 @@ Trying to connect to {state.config.connect_address}.
|
|||
method on_meta_clicked(url: string) =
|
||||
if url.starts_with("nim://"):
|
||||
assert ?state.open_sign
|
||||
state.open_sign.owner.eval = url[6..^1]
|
||||
|
||||
state.open_sign.owner.eval = url[6 ..^ 1]
|
||||
elif url.starts_with("unit://"):
|
||||
let id = url[7..^1]
|
||||
let id = url[7 ..^ 1]
|
||||
for unit in state.units:
|
||||
if unit.id == id:
|
||||
state.open_unit = unit
|
||||
return
|
||||
logger("err", \"Unable to open unit {id}")
|
||||
|
||||
elif shell_open(url) != godotcoretypes.Error.OK:
|
||||
logger("err", \"Unable to open url {url}")
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
import std / [strformat]
|
||||
import pkg / [godot]
|
||||
import godotapi / [node, scene_tree, voxel_buffer]
|
||||
import core, models / [states]
|
||||
import std/[strformat]
|
||||
import pkg/[godot]
|
||||
import godotapi/[node, scene_tree, voxel_buffer]
|
||||
import core, models/[states]
|
||||
export strformat.`&`, states, types
|
||||
|
||||
proc bind_signals*(receiver, sender: Object, signals: varargs[string]) =
|
||||
let send_node = if sender == nil:
|
||||
state.nodes.game
|
||||
else:
|
||||
sender
|
||||
let send_node = if sender == nil: state.nodes.game else: sender
|
||||
|
||||
for signal in signals:
|
||||
let meth = "_on_" & signal
|
||||
|
@ -19,7 +16,9 @@ proc bind_signals*(receiver, sender: Object, signals: varargs[string]) =
|
|||
proc bind_signals*(receiver: Node, signals: varargs[string]) =
|
||||
bind_signals(receiver, nil, signals)
|
||||
|
||||
proc trigger*(node: Object, signal: string, args: varargs[Variant, `new_variant`]) =
|
||||
proc trigger*(
|
||||
node: Object, signal: string, args: varargs[Variant, `new_variant`]
|
||||
) =
|
||||
if not node.has_user_signal(signal):
|
||||
node.add_user_signal(signal)
|
||||
node.emit_signal(signal, args)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import std / options
|
||||
import compiler / [syntaxes, reorder, vmdef, msgs]
|
||||
import compiler / passes {.all.}
|
||||
import std/options
|
||||
import compiler/[syntaxes, reorder, vmdef, msgs]
|
||||
import compiler/passes {.all.}
|
||||
|
||||
{.warning[UnusedImport]: off.}
|
||||
include compiler / [nimeval, pipelines]
|
||||
include compiler/[nimeval, pipelines]
|
||||
|
||||
export Interpreter, VmArgs, PCtx, PStackFrame, TLineInfo
|
||||
|
||||
|
@ -16,10 +16,15 @@ export Interpreter, VmArgs, PCtx, PStackFrame, TLineInfo
|
|||
# https://github.com/nim-lang/Nim/blob/v2.0.2/compiler/pipelines.nim#L88
|
||||
# Normal module loading procedure, but makes PContext a param so it can be
|
||||
# passed to extend_module
|
||||
proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
||||
stream: PLLStream, ctx: var PContext): bool {.discardable.} =
|
||||
|
||||
if graph.stopCompile(): return true
|
||||
proc processModule*(
|
||||
graph: ModuleGraph,
|
||||
module: PSym,
|
||||
idgen: IdGenerator,
|
||||
stream: PLLStream,
|
||||
ctx: var PContext,
|
||||
): bool {.discardable.} =
|
||||
if graph.stopCompile():
|
||||
return true
|
||||
let bModule = setupEvalGen(graph, module, idgen)
|
||||
|
||||
var
|
||||
|
@ -40,26 +45,35 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
|||
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
|
||||
# for the interactive mode.
|
||||
if module.name.s != "nimscriptapi":
|
||||
processImplicitImports graph, graph.config.implicitImports, nkImportStmt, module, ctx, bModule, idgen
|
||||
processImplicitImports graph, graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule, idgen
|
||||
processImplicitImports graph,
|
||||
graph.config.implicitImports, nkImportStmt, module, ctx, bModule,
|
||||
idgen
|
||||
processImplicitImports graph,
|
||||
graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule,
|
||||
idgen
|
||||
|
||||
checkFirstLineIndentation(p)
|
||||
block processCode:
|
||||
if graph.stopCompile(): break processCode
|
||||
if graph.stopCompile():
|
||||
break processCode
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty: break processCode
|
||||
if n.kind == nkEmpty:
|
||||
break processCode
|
||||
# read everything, no streaming possible
|
||||
var sl = newNodeI(nkStmtList, n.info)
|
||||
sl.add n
|
||||
while true:
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty: break
|
||||
if n.kind == nkEmpty:
|
||||
break
|
||||
sl.add n
|
||||
|
||||
prePass(ctx, sl)
|
||||
|
@ -67,7 +81,8 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
|||
discard processPipeline(graph, semNode, bModule)
|
||||
|
||||
closeParser(p)
|
||||
if s.kind != llsStdIn: break
|
||||
if s.kind != llsStdIn:
|
||||
break
|
||||
|
||||
assert graph.pipelinePass == EvalPass
|
||||
let finalNode = closePContext(graph, ctx, nil)
|
||||
|
@ -81,8 +96,12 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
|||
result = true
|
||||
|
||||
# from nimeval. Added moduleName
|
||||
proc selectUniqueSymbol*(i: Interpreter; name: string;
|
||||
symKinds: set[TSymKind] = {skLet, skVar}; moduleName: string): PSym =
|
||||
proc selectUniqueSymbol*(
|
||||
i: Interpreter,
|
||||
name: string,
|
||||
symKinds: set[TSymKind] = {skLet, skVar},
|
||||
moduleName: string,
|
||||
): PSym =
|
||||
## Can be used to access a unique symbol of ``name`` and
|
||||
## the given ``symKinds`` filter.
|
||||
assert i != nil
|
||||
|
@ -98,18 +117,25 @@ proc selectUniqueSymbol*(i: Interpreter; name: string;
|
|||
result = nil
|
||||
while s != nil:
|
||||
if s.kind in symKinds:
|
||||
if result == nil: result = s
|
||||
else: return nil # ambiguous
|
||||
if result == nil:
|
||||
result = s
|
||||
else:
|
||||
return nil # ambiguous
|
||||
s = nextModuleIter(it, i.graph)
|
||||
|
||||
# from nimeval. Added moduleName
|
||||
proc selectRoutine*(i: Interpreter; name: string, module_name: string): PSym =
|
||||
proc selectRoutine*(i: Interpreter, name: string, module_name: string): PSym =
|
||||
## Selects a declared routine (proc/func/etc) from the main module.
|
||||
## 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:
|
||||
|
@ -118,17 +144,16 @@ proc resetModule*(i: Interpreter, moduleName: string) =
|
|||
iface.module.ast = nil
|
||||
break
|
||||
|
||||
proc loadModule*(i: Interpreter, fileName, code: string, ctx: var PContext
|
||||
) {.gcsafe.} =
|
||||
|
||||
proc loadModule*(
|
||||
i: Interpreter, fileName, code: string, ctx: var PContext
|
||||
) {.gcsafe.} =
|
||||
assert i != nil
|
||||
|
||||
var module: PSym
|
||||
let moduleName = fileName.splitFile.name
|
||||
for iface in i.graph.ifaces:
|
||||
if iface.module != nil and iface.module.name.s == moduleName and
|
||||
fileName == toFullPath(i.graph.config, iface.module.info):
|
||||
|
||||
fileName == toFullPath(i.graph.config, iface.module.info):
|
||||
module = iface.module
|
||||
break
|
||||
|
||||
|
@ -152,10 +177,15 @@ proc loadModule*(i: Interpreter, fileName, code: string, ctx: var PContext
|
|||
|
||||
# adapted from
|
||||
# https://github.com/nim-lang/Nim/blob/v2.0.2/compiler/pipelines.nim#L88
|
||||
proc extendModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
||||
stream: PLLStream, ctx: var PContext): bool {.discardable.} =
|
||||
|
||||
if graph.stopCompile(): return true
|
||||
proc extendModule*(
|
||||
graph: ModuleGraph,
|
||||
module: PSym,
|
||||
idgen: IdGenerator,
|
||||
stream: PLLStream,
|
||||
ctx: var PContext,
|
||||
): bool {.discardable.} =
|
||||
if graph.stopCompile():
|
||||
return true
|
||||
let bModule = setupEvalGen(graph, module, idgen)
|
||||
|
||||
var
|
||||
|
@ -169,15 +199,18 @@ proc extendModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
|||
checkFirstLineIndentation(p)
|
||||
assert graph.pipelinePass == EvalPass
|
||||
block processCode:
|
||||
if graph.stopCompile(): break processCode
|
||||
if graph.stopCompile():
|
||||
break processCode
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty: break processCode
|
||||
if n.kind == nkEmpty:
|
||||
break processCode
|
||||
# read everything, no streaming possible
|
||||
var sl = newNodeI(nkStmtList, n.info)
|
||||
sl.add n
|
||||
while true:
|
||||
var n = parseTopLevelStmt(p)
|
||||
if n.kind == nkEmpty: break
|
||||
if n.kind == nkEmpty:
|
||||
break
|
||||
sl.add n
|
||||
|
||||
prePass(ctx, sl)
|
||||
|
@ -186,7 +219,8 @@ proc extendModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
|
|||
discard processPipeline(graph, semNode, bModule)
|
||||
|
||||
closeParser(p)
|
||||
if s.kind != llsStdIn: break
|
||||
if s.kind != llsStdIn:
|
||||
break
|
||||
|
||||
result = true
|
||||
|
||||
|
@ -204,15 +238,25 @@ proc eval*(i: Interpreter, ctx: var PContext, fileName, code: string) =
|
|||
let s = llStreamOpen(code)
|
||||
extendModule(i.graph, module, i.idgen, s, ctx)
|
||||
|
||||
proc config*(i: Interpreter): ConfigRef = i.graph.config
|
||||
proc config*(i: Interpreter): ConfigRef =
|
||||
i.graph.config
|
||||
|
||||
proc `exit_hook=`*(i: Interpreter, hook: proc (c: PCtx, pc: int, tos: PStackFrame)) =
|
||||
proc `exit_hook=`*(
|
||||
i: Interpreter, hook: proc(c: PCtx, pc: int, tos: PStackFrame)
|
||||
) =
|
||||
(PCtx i.graph.vm).exitHook = hook
|
||||
|
||||
proc `enter_hook=`*(i: Interpreter, hook: proc (c: PCtx, pc: int, tos: PStackFrame, instr: TInstr)) =
|
||||
proc `enter_hook=`*(
|
||||
i: Interpreter,
|
||||
hook: proc(c: PCtx, pc: int, tos: PStackFrame, instr: TInstr),
|
||||
) =
|
||||
(PCtx i.graph.vm).enterHook = hook
|
||||
|
||||
proc `error_hook=`*(i: Interpreter, hook: proc(config: ConfigRef,
|
||||
info: TLineInfo; msg: string, severity: Severity) {.gcsafe.}) =
|
||||
|
||||
proc `error_hook=`*(
|
||||
i: Interpreter,
|
||||
hook:
|
||||
proc(config: ConfigRef, info: TLineInfo, msg: string, severity: Severity) {.
|
||||
gcsafe
|
||||
.},
|
||||
) =
|
||||
i.registerErrorHook(hook)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import std / [os, strformat, importutils]
|
||||
import pkg / compiler / ast except new_node
|
||||
import pkg / compiler / [vm, vmdef]
|
||||
import std/[os, strformat, importutils]
|
||||
import pkg/compiler/ast except new_node
|
||||
import pkg/compiler/[vm, vmdef]
|
||||
import core, eval
|
||||
|
||||
export Interpreter, VmArgs, set_result
|
||||
|
@ -8,9 +8,9 @@ export Interpreter, VmArgs, set_result
|
|||
log_scope:
|
||||
topics = "scripting"
|
||||
|
||||
const
|
||||
STDLIB_PATHS = [".", "core", "pure", "pure/collections", "pure/concurrency",
|
||||
"std", "fusion"]
|
||||
const STDLIB_PATHS = [
|
||||
".", "core", "pure", "pure/collections", "pure/concurrency", "std", "fusion"
|
||||
]
|
||||
|
||||
private_access ScriptCtx
|
||||
|
||||
|
@ -18,8 +18,12 @@ 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) =
|
||||
|
@ -69,22 +73,29 @@ proc eval*(self: ScriptCtx, code: string): bool =
|
|||
self.exit_code = some(99)
|
||||
raise
|
||||
|
||||
proc call_proc*(self: ScriptCtx, proc_name: string, args: varargs[PNode, `to_node`]): tuple[paused: bool, result: PNode] =
|
||||
let foreign_proc = self.interpreter.select_routine(proc_name, module_name = self.module_name)
|
||||
proc call_proc*(
|
||||
self: ScriptCtx, proc_name: string, args: varargs[PNode, `to_node`]
|
||||
): tuple[paused: bool, result: PNode] =
|
||||
let foreign_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}'")
|
||||
result = try:
|
||||
{.gcsafe.}:
|
||||
(false, self.interpreter.call_routine(foreign_proc, args))
|
||||
except VMPause:
|
||||
(self.exit_code.is_none, nil)
|
||||
except CatchableError:
|
||||
self.running = false
|
||||
self.exit_code = some(99)
|
||||
raise
|
||||
raise new_exception(
|
||||
VMError, \"script does not export a proc of the name: '{proc_name}'"
|
||||
)
|
||||
result =
|
||||
try:
|
||||
{.gcsafe.}:
|
||||
(false, self.interpreter.call_routine(foreign_proc, args))
|
||||
except VMPause:
|
||||
(self.exit_code.is_none, nil)
|
||||
except CatchableError:
|
||||
self.running = false
|
||||
self.exit_code = some(99)
|
||||
raise
|
||||
|
||||
proc get_var*(self: ScriptCtx, var_name: string, module_name: string): PNode =
|
||||
let sym = self.interpreter.select_unique_symbol(var_name, module_name = module_name)
|
||||
let sym =
|
||||
self.interpreter.select_unique_symbol(var_name, module_name = module_name)
|
||||
self.interpreter.get_global_value(sym)
|
||||
|
||||
proc resume*(self: ScriptCtx): bool =
|
||||
|
@ -93,13 +104,14 @@ proc resume*(self: ScriptCtx): bool =
|
|||
assert not self.tos.is_nil
|
||||
|
||||
trace "resuming", script = self.file_name, module = self.module_name
|
||||
result = try:
|
||||
{.gcsafe.}:
|
||||
discard exec_from_ctx(self.ctx, self.pc, self.tos)
|
||||
false
|
||||
except VMPause:
|
||||
self.exit_code.is_none
|
||||
except CatchableError:
|
||||
self.running = false
|
||||
self.exit_code = some(99)
|
||||
raise
|
||||
result =
|
||||
try:
|
||||
{.gcsafe.}:
|
||||
discard exec_from_ctx(self.ctx, self.pc, self.tos)
|
||||
false
|
||||
except VMPause:
|
||||
self.exit_code.is_none
|
||||
except CatchableError:
|
||||
self.running = false
|
||||
self.exit_code = some(99)
|
||||
raise
|
||||
|
|
|
@ -36,17 +36,22 @@ when defined(windows):
|
|||
EXCEPTION_CONTINUE_SEARCH = LONG(0)
|
||||
|
||||
type
|
||||
PEXCEPTION_RECORD = ptr object
|
||||
exceptionCode: DWORD # other fields left out
|
||||
PEXCEPTION_RECORD =
|
||||
ptr object
|
||||
exceptionCode: DWORD # other fields left out
|
||||
|
||||
PEXCEPTION_POINTERS = ptr object
|
||||
exceptionRecord: PEXCEPTION_RECORD
|
||||
contextRecord: pointer
|
||||
PEXCEPTION_POINTERS =
|
||||
ptr object
|
||||
exceptionRecord: PEXCEPTION_RECORD
|
||||
contextRecord: pointer
|
||||
|
||||
VectoredHandler = proc (p: PEXCEPTION_POINTERS): LONG {.stdcall.}
|
||||
proc addVectoredExceptionHandler(firstHandler: ULONG,
|
||||
handler: VectoredHandler): pointer {.
|
||||
importc: "AddVectoredExceptionHandler", stdcall, dynlib: "kernel32.dll".}
|
||||
VectoredHandler = proc(p: PEXCEPTION_POINTERS): LONG {.stdcall.}
|
||||
|
||||
proc addVectoredExceptionHandler(
|
||||
firstHandler: ULONG, handler: VectoredHandler
|
||||
): pointer {.
|
||||
importc: "AddVectoredExceptionHandler", stdcall, dynlib: "kernel32.dll"
|
||||
.}
|
||||
|
||||
{.push stackTrace: off.}
|
||||
proc segfaultHandler(p: PEXCEPTION_POINTERS): LONG {.stdcall.} =
|
||||
|
@ -55,6 +60,7 @@ when defined(windows):
|
|||
raise se
|
||||
else:
|
||||
result = EXCEPTION_CONTINUE_SEARCH
|
||||
|
||||
{.pop.}
|
||||
|
||||
discard addVectoredExceptionHandler(0, segfaultHandler)
|
||||
|
@ -64,9 +70,9 @@ when defined(windows):
|
|||
proc segfaultHandler(sig: cint) {.noconv.} =
|
||||
{.gcsafe.}:
|
||||
rawRaise se
|
||||
|
||||
{.pop.}
|
||||
c_signal(SIGSEGV, segfaultHandler)
|
||||
|
||||
else:
|
||||
import posix
|
||||
|
||||
|
@ -82,6 +88,7 @@ else:
|
|||
raise se
|
||||
else:
|
||||
quit(1)
|
||||
|
||||
{.pop.}
|
||||
|
||||
discard sigemptyset(sa.sa_mask)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import models / [states, units, builds, bots, ground, colors, players, signs]
|
||||
import models/[states, units, builds, bots, ground, colors, players, signs]
|
||||
|
||||
export states, units, builds, bots, ground, colors, players, signs
|
||||
|
|
|
@ -1,48 +1,57 @@
|
|||
import std / [math, sugar, monotimes, base64]
|
||||
import godotapi / spatial
|
||||
import core, models / [states, units, colors]
|
||||
import std/[math, sugar, monotimes, base64]
|
||||
import godotapi/spatial
|
||||
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)
|
||||
|
||||
method on_begin_move*(self: Bot, direction: Vector3, steps: float,
|
||||
moving_mode: int): Callback =
|
||||
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
|
||||
): Callback =
|
||||
# move_mode param is ignored
|
||||
var duration = 0.0
|
||||
let
|
||||
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
|
||||
|
||||
method on_begin_turn*(self: Bot, axis: Vector3, degrees: float, lean: bool,
|
||||
move_mode: int): Callback =
|
||||
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
|
||||
): Callback =
|
||||
# move mode param is ignored
|
||||
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:
|
||||
|
@ -64,28 +73,35 @@ method reset*(self: Bot) =
|
|||
method destroy*(self: Bot) =
|
||||
self.destroy_impl
|
||||
|
||||
proc init*(_: type Bot, id = "bot_" & generate_id(), transform = Transform.init,
|
||||
clone_of: Bot = nil, 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
|
||||
)
|
||||
proc init*(
|
||||
_: type Bot,
|
||||
id = "bot_" & generate_id(),
|
||||
transform = Transform.init,
|
||||
clone_of: Bot = nil,
|
||||
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,
|
||||
)
|
||||
|
||||
self.init_unit
|
||||
|
||||
if global: self.global_flags += Global
|
||||
if global:
|
||||
self.global_flags += Global
|
||||
result = self
|
||||
|
||||
method clone*(self: Bot, clone_to: Unit, id: string): Unit =
|
||||
var transform = clone_to.transform
|
||||
result = Bot.init(id = id, transform = transform, clone_of = self,
|
||||
parent = clone_to)
|
||||
result =
|
||||
Bot.init(id = id, transform = transform, clone_of = self, parent = clone_to)
|
||||
|
||||
method on_collision*(self: Unit, partner: Model, normal: Vector3) =
|
||||
self.collisions.add (partner.id, normal)
|
||||
|
@ -97,8 +113,12 @@ method off_collision*(self: Unit, partner: Model) =
|
|||
|
||||
method worker_thread_joined*(self: Bot) =
|
||||
state.local_flags.watch:
|
||||
debug "state flag changed", zid, changes = change.changes,
|
||||
item = change.item, unit = self.id, zen_id = self.local_flags.id
|
||||
debug "state flag changed",
|
||||
zid,
|
||||
changes = change.changes,
|
||||
item = change.item,
|
||||
unit = self.id,
|
||||
zen_id = self.local_flags.id
|
||||
|
||||
if Hover in self.local_flags:
|
||||
if PrimaryDown.added and state.tool == CodeMode:
|
||||
|
@ -118,15 +138,21 @@ method worker_thread_joined*(self: Bot) =
|
|||
self.parent.units -= self
|
||||
|
||||
self.local_flags.watch:
|
||||
debug "self flag changed", zid, changes = change.changes,
|
||||
item = change.item, unit = self.id, zen_id = self.local_flags.id
|
||||
debug "self flag changed",
|
||||
zid,
|
||||
changes = change.changes,
|
||||
item = change.item,
|
||||
unit = self.id,
|
||||
zen_id = self.local_flags.id
|
||||
|
||||
if Hover.added:
|
||||
state.push_flag ReticleVisible
|
||||
if state.tool in {CodeMode, PlaceBot}:
|
||||
let root = self.find_root(true)
|
||||
root.walk_tree proc(unit: Unit) = unit.local_flags += Highlight
|
||||
root.walk_tree proc(unit: Unit) =
|
||||
unit.local_flags += Highlight
|
||||
elif Hover.removed:
|
||||
let root = self.find_root(true)
|
||||
root.walk_tree proc(unit: Unit) = unit.local_flags -= Highlight
|
||||
root.walk_tree proc(unit: Unit) =
|
||||
unit.local_flags -= Highlight
|
||||
state.pop_flag ReticleVisible
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import std / [tables, sets, options, sequtils, math, wrapnils, monotimes, sugar,
|
||||
deques, macros, base64]
|
||||
import godotapi / spatial
|
||||
import core, models / [states, bots, colors, units]
|
||||
import
|
||||
std/[
|
||||
tables, sets, options, sequtils, math, wrapnils, monotimes, sugar, deques,
|
||||
macros, base64
|
||||
]
|
||||
import godotapi/spatial
|
||||
import core, models/[states, bots, colors, units]
|
||||
const ChunkSize = vec3(16, 16, 16)
|
||||
|
||||
include "build_code_template.nim.nimf"
|
||||
|
@ -19,10 +22,15 @@ 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
|
||||
proc buffer(position: Vector3): Vector3 =
|
||||
(position / ChunkSize).floor
|
||||
|
||||
proc contains*(self: Build, position: Vector3): bool =
|
||||
let buf = position.buffer
|
||||
|
@ -33,10 +41,11 @@ proc voxel_info*(self: Build, position: Vector3): VoxelInfo =
|
|||
|
||||
proc find_voxel*(self: Build, position: Vector3): Option[VoxelInfo] =
|
||||
let buf = position.buffer
|
||||
result = if buf in self:
|
||||
some(self.chunks[buf][position])
|
||||
else:
|
||||
none(VoxelInfo)
|
||||
result =
|
||||
if buf in self:
|
||||
some(self.chunks[buf][position])
|
||||
else:
|
||||
none(VoxelInfo)
|
||||
|
||||
proc find_first*(units: ZenSeq[Unit], positions: open_array[Vector3]): Build =
|
||||
for unit in units:
|
||||
|
@ -67,9 +76,9 @@ proc add_build(self, source: Build) =
|
|||
source.parent.units -= source
|
||||
dont_join = false
|
||||
|
||||
proc maybe_join_previous_build(self: Build,
|
||||
position: Vector3, voxel: VoxelInfo) =
|
||||
|
||||
proc maybe_join_previous_build(
|
||||
self: Build, position: Vector3, voxel: VoxelInfo
|
||||
) =
|
||||
if self != current_build:
|
||||
previous_build = current_build
|
||||
current_build = self
|
||||
|
@ -121,7 +130,6 @@ proc add_voxel(self: Build, position: Vector3, voxel: VoxelInfo) =
|
|||
if self.batching:
|
||||
if position notin self.chunks[buffer] or
|
||||
self.chunks[buffer][position] != voxel:
|
||||
|
||||
if buffer notin self.batched_voxels:
|
||||
self.batched_voxels[buffer] = init_table[Vector3, VoxelInfo]()
|
||||
self.batched_voxels[buffer][position] = voxel
|
||||
|
@ -163,15 +171,12 @@ proc draw*(self: Build, position: Vector3, voxel: VoxelInfo) {.gcsafe.} =
|
|||
var locations = self.shared.edits[self.id]
|
||||
locations.del position
|
||||
self.shared.edits[self.id] = locations
|
||||
elif ? self.clone_of and position in
|
||||
self.clone_of.shared.edits[self.clone_of.id] and
|
||||
self.clone_of.shared.edits[self.clone_of.id][position].kind ==
|
||||
Hole:
|
||||
|
||||
elif ?self.clone_of and
|
||||
position in self.clone_of.shared.edits[self.clone_of.id] and
|
||||
self.clone_of.shared.edits[self.clone_of.id][position].kind == Hole:
|
||||
return
|
||||
else:
|
||||
self.add_voxel(position, voxel)
|
||||
|
||||
else:
|
||||
self.global_flags += Dirty
|
||||
# :( Crash fix hack. Why would shared be nil?
|
||||
|
@ -204,16 +209,19 @@ proc remove(self: Build) =
|
|||
if state.tool notin {CodeMode, PlaceBot}:
|
||||
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)
|
||||
let point =
|
||||
self.target_point - self.target_normal - (
|
||||
self.target_normal.inverse_normalized * 0.5
|
||||
)
|
||||
|
||||
skip_point = vec3()
|
||||
last_point = self.target_point
|
||||
self.draw(point, (Hole, action_colors[eraser]))
|
||||
|
||||
if self.units.len == 0 and not self.chunks.any_it(
|
||||
it.value.any_it(it.value.color != action_colors[eraser])):
|
||||
|
||||
if self.units.len == 0 and
|
||||
not self.chunks.any_it(
|
||||
it.value.any_it(it.value.color != action_colors[eraser])
|
||||
):
|
||||
if self.parent.is_nil:
|
||||
state.units -= self
|
||||
else:
|
||||
|
@ -230,7 +238,6 @@ proc fire(self: Build) =
|
|||
self.draw(point, (Manual, state.selected_color))
|
||||
elif state.tool == PlaceBot and BlockTargetVisible in state.local_flags and
|
||||
state.bot_at(global_point).is_nil:
|
||||
|
||||
let transform = Transform.init(origin = global_point)
|
||||
state.units += Bot.init(transform = transform)
|
||||
elif state.tool == CodeMode:
|
||||
|
@ -252,9 +259,9 @@ method apply_changes*(self: Build) =
|
|||
self.batched_voxels.clear
|
||||
self.batching = false
|
||||
|
||||
method on_begin_move*(self: Build,
|
||||
direction: Vector3, steps: float, move_mode: int): Callback =
|
||||
|
||||
method on_begin_move*(
|
||||
self: Build, direction: Vector3, steps: float, move_mode: int
|
||||
): Callback =
|
||||
let move = self.is_moving(move_mode)
|
||||
if move:
|
||||
let steps = steps.float
|
||||
|
@ -264,16 +271,17 @@ method on_begin_move*(self: Build,
|
|||
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
|
||||
|
@ -282,32 +290,29 @@ method on_begin_move*(self: Build,
|
|||
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:
|
||||
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 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): Callback =
|
||||
|
||||
let map = if lean:
|
||||
{LEFT: BACK, RIGHT: FORWARD, BACK: RIGHT, FORWARD: LEFT}.to_table
|
||||
else:
|
||||
{LEFT: UP, RIGHT: DOWN, UP: RIGHT, DOWN: LEFT}.to_table
|
||||
method on_begin_turn*(
|
||||
self: Build, axis: Vector3, degrees: float, lean: bool, move_mode: int
|
||||
): Callback =
|
||||
let map =
|
||||
if lean:
|
||||
{LEFT: BACK, RIGHT: FORWARD, BACK: RIGHT, FORWARD: LEFT}.to_table
|
||||
else:
|
||||
{LEFT: UP, RIGHT: DOWN, UP: RIGHT, DOWN: LEFT}.to_table
|
||||
let axis = map[axis]
|
||||
let move = self.is_moving(move_mode)
|
||||
if move:
|
||||
|
@ -316,23 +321,28 @@ method on_begin_turn*(self: Build,
|
|||
let axis = self.transform.basis.orthonormalized.xform(axis)
|
||||
let scale = self.scale
|
||||
var final_transform = self.transform
|
||||
final_transform.basis = final_transform.basis.rotated(axis,
|
||||
deg_to_rad(degrees)).orthonormalized.scaled(vec3(scale, scale, scale))
|
||||
final_transform.basis =
|
||||
final_transform.basis
|
||||
.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 =
|
||||
self.draw_transform.basis.rotated(axis, deg_to_rad(degrees))
|
||||
self.draw_transform.basis.rotated(axis, deg_to_rad(degrees))
|
||||
|
||||
self.draw_transform = self.draw_transform.orthonormalized()
|
||||
|
||||
|
@ -368,40 +378,49 @@ method ensure_visible*(self: Build) =
|
|||
# the unit will still exist but will have no presence in the world, and is
|
||||
# therefor impossible to select or modify. In that case we want to draw a
|
||||
# single block.
|
||||
if self.units.len == 0 and not self.chunks.any_it(
|
||||
it.value.any_it(it.value.color != action_colors[eraser])):
|
||||
|
||||
let color = if self.start_color == action_colors[eraser]:
|
||||
action_colors[blue]
|
||||
else:
|
||||
self.start_color
|
||||
if self.units.len == 0 and
|
||||
not self.chunks.any_it(
|
||||
it.value.any_it(it.value.color != action_colors[eraser])
|
||||
):
|
||||
let color =
|
||||
if self.start_color == action_colors[eraser]:
|
||||
action_colors[blue]
|
||||
else:
|
||||
self.start_color
|
||||
self.draw(vec3(), (Computed, color))
|
||||
|
||||
method destroy*(self: Build) =
|
||||
self.destroy_impl
|
||||
|
||||
proc init*(_: type Build,
|
||||
id = "build_" & generate_id(), transform = Transform.init,
|
||||
color = default_color, clone_of: Unit = nil, global = true,
|
||||
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
|
||||
)
|
||||
proc init*(
|
||||
_: type Build,
|
||||
id = "build_" & generate_id(),
|
||||
transform = Transform.init,
|
||||
color = default_color,
|
||||
clone_of: Unit = nil,
|
||||
global = true,
|
||||
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,
|
||||
)
|
||||
|
||||
self.init_unit
|
||||
|
||||
if global: self.global_flags += Global
|
||||
if global:
|
||||
self.global_flags += Global
|
||||
self.reset()
|
||||
result = self
|
||||
|
||||
|
@ -412,20 +431,23 @@ method main_thread_joined*(self: Build) =
|
|||
if Hover.added and state.tool == CodeMode:
|
||||
if Playing notin state.local_flags:
|
||||
let root = self.find_root(true)
|
||||
root.walk_tree proc(unit: Unit) = unit.local_flags += Highlight
|
||||
root.walk_tree proc(unit: Unit) =
|
||||
unit.local_flags += Highlight
|
||||
elif Hover.removed:
|
||||
let root = self.find_root(true)
|
||||
root.walk_tree proc(unit: Unit) = unit.local_flags -= Highlight
|
||||
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
|
||||
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:
|
||||
|
@ -473,9 +495,16 @@ 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]:
|
||||
|
|
|
@ -4,20 +4,37 @@ import pkg/chroma
|
|||
export chroma
|
||||
|
||||
converter to_chroma_color*(self: godot.Color): chroma.Color =
|
||||
cast[chroma.Color](self)
|
||||
cast[chroma.Color](self)
|
||||
|
||||
converter to_godot_color*(self: chroma.Color): godot.Color =
|
||||
cast[godot.Color](self)
|
||||
cast[godot.Color](self)
|
||||
|
||||
proc col*(hex: string): chroma.Color = hex.parse_hex
|
||||
proc col*(hex: string): chroma.Color =
|
||||
hex.parse_hex
|
||||
|
||||
type
|
||||
Colors* = enum
|
||||
eraser, blue, red, green, black, white, brown
|
||||
eraser
|
||||
blue
|
||||
red
|
||||
green
|
||||
black
|
||||
white
|
||||
brown
|
||||
|
||||
Theme* = enum
|
||||
normal, comment, entity, keyword, operator, class, storage, constant,
|
||||
text, number, variable, invalid
|
||||
normal
|
||||
comment
|
||||
entity
|
||||
keyword
|
||||
operator
|
||||
class
|
||||
storage
|
||||
constant
|
||||
text
|
||||
number
|
||||
variable
|
||||
invalid
|
||||
|
||||
const ir_black* = [
|
||||
normal: col"F6F3E8",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import godotapi / spatial
|
||||
import godotapi/spatial
|
||||
import core, states, bots, builds
|
||||
|
||||
var add_to {.threadvar.}: Build
|
||||
|
@ -12,12 +12,15 @@ 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
|
||||
|
||||
elif state.tool == PlaceBot and state.bot_at(self.target_point).is_nil:
|
||||
var t = Transform.init(origin = self.target_point)
|
||||
var bot = Bot.init(transform = t)
|
||||
|
@ -25,10 +28,11 @@ 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:
|
||||
|
|
|
@ -1,23 +1,28 @@
|
|||
import std / [math]
|
||||
import godotapi / spatial
|
||||
import core, models / units
|
||||
import std/[math]
|
||||
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
|
||||
|
||||
method on_begin_turn*(self: Player, direction: Vector3, degrees: float,
|
||||
lean: bool, move_mode: int): Callback =
|
||||
|
||||
method on_begin_turn*(
|
||||
self: Player, direction: Vector3, degrees: float, lean: bool, move_mode: int
|
||||
): Callback =
|
||||
let rotation = floor_mod(self.rotation, 360)
|
||||
let degrees = if direction == LEFT: -degrees else: degrees
|
||||
let degrees =
|
||||
if direction == LEFT:
|
||||
-degrees
|
||||
else:
|
||||
degrees
|
||||
self.rotation_value.touch rotation - degrees
|
||||
self.transform = Transform.init(origin = self.transform.origin)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import std / [json, jsonutils, sugar, tables, strutils, os, times, algorithm]
|
||||
import pkg / zippy / ziparchives_v1
|
||||
import std/[json, jsonutils, sugar, tables, strutils, os, times, algorithm]
|
||||
import pkg/zippy/ziparchives_v1
|
||||
import core except to_json
|
||||
import models
|
||||
import controllers / script_controllers / scripting
|
||||
import controllers/script_controllers/scripting
|
||||
|
||||
var load_chunks {.threadvar.}: bool
|
||||
|
||||
|
@ -10,13 +10,14 @@ type LevelInfo = object
|
|||
enu_version, format_version: string
|
||||
|
||||
proc to_json_hook(self: Color): JsonNode =
|
||||
result = if self == action_colors[eraser]:
|
||||
%""
|
||||
else:
|
||||
for i, color in Colors.enum_fields:
|
||||
if self == action_colors[Colors(i)]:
|
||||
return %color
|
||||
%self.to_html_hex
|
||||
result =
|
||||
if self == action_colors[eraser]:
|
||||
%""
|
||||
else:
|
||||
for i, color in Colors.enum_fields:
|
||||
if self == action_colors[Colors(i)]:
|
||||
return %color
|
||||
%self.to_html_hex
|
||||
|
||||
proc from_json_hook(self: var Color, json: JsonNode) =
|
||||
let hex = json.get_str
|
||||
|
@ -41,9 +42,9 @@ proc from_json_hook(self: var Vector3, json: JsonNode) =
|
|||
self.y = json[1].get_float
|
||||
self.z = json[2].get_float
|
||||
|
||||
proc from_json_hook(self: var ZenTable[Vector3, VoxelInfo],
|
||||
json: JsonNode) {.gcsafe.} =
|
||||
|
||||
proc from_json_hook(
|
||||
self: var ZenTable[Vector3, VoxelInfo], json: JsonNode
|
||||
) {.gcsafe.} =
|
||||
assert load_chunks
|
||||
self = ~Table[Vector3, VoxelInfo]
|
||||
for chunks in json:
|
||||
|
@ -52,9 +53,9 @@ proc from_json_hook(self: var ZenTable[Vector3, VoxelInfo],
|
|||
let info = chunk[1].json_to(VoxelInfo)
|
||||
self[location] = info
|
||||
|
||||
proc from_json_hook(self: var ZenTable[string, ZenTable[Vector3, VoxelInfo]],
|
||||
json: JsonNode) =
|
||||
|
||||
proc from_json_hook(
|
||||
self: var ZenTable[string, ZenTable[Vector3, VoxelInfo]], json: JsonNode
|
||||
) =
|
||||
assert not load_chunks
|
||||
for id, edits in json:
|
||||
for edit in edits:
|
||||
|
@ -68,18 +69,23 @@ proc from_json_hook(self: var ZenTable[string, ZenTable[Vector3, VoxelInfo]],
|
|||
|
||||
proc from_json_hook(self: var Transform, json: JsonNode) =
|
||||
self = Transform.init(origin = json["origin"].json_to(Vector3))
|
||||
let elements = if json["basis"].kind == JObject:
|
||||
# old way
|
||||
json["basis"]["elements"]
|
||||
else:
|
||||
# new way
|
||||
json["basis"]
|
||||
let elements =
|
||||
if json["basis"].kind == JObject:
|
||||
# old way
|
||||
json["basis"]["elements"]
|
||||
else:
|
||||
# new way
|
||||
json["basis"]
|
||||
self.basis.elements.from_json(elements)
|
||||
|
||||
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]()
|
||||
|
@ -89,8 +95,11 @@ 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"])
|
||||
|
@ -98,7 +107,7 @@ proc from_json_hook(self: var Bot, json: JsonNode) =
|
|||
proc `$`(self: Color): string =
|
||||
$json_utils.to_json(self)
|
||||
|
||||
proc `$`(self: VoxelInfo): string =
|
||||
proc `$`(self: VoxelInfo): string =
|
||||
\"[{self.kind.ord}, \"{self.color}\"]"
|
||||
|
||||
proc `$`(self: Vector3): string =
|
||||
|
@ -108,20 +117,23 @@ 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 =
|
||||
let elements = self.start_transform.basis.elements.map_it($it).join(",\n")
|
||||
let edits = $self.shared.edits
|
||||
result = \"""
|
||||
result =
|
||||
\"""
|
||||
|
||||
{{
|
||||
"id": "{self.id}",
|
||||
|
@ -157,14 +169,12 @@ proc save_level*(level_dir: string, save_all = false) =
|
|||
if Server in state.local_flags:
|
||||
debug "saving level"
|
||||
let level = LevelInfo(enu_version: enu_version, format_version: "v0.9.2")
|
||||
write_file level_dir / "level.json",
|
||||
jsonutils.to_json(level).pretty
|
||||
write_file level_dir / "level.json", jsonutils.to_json(level).pretty
|
||||
|
||||
for unit in state.units:
|
||||
if save_all or Dirty in unit.global_flags:
|
||||
unit.save
|
||||
unit.global_flags -= Dirty
|
||||
|
||||
else:
|
||||
debug "not server. Skipping save."
|
||||
|
||||
|
@ -173,25 +183,22 @@ proc backup_level*(level_dir: string) =
|
|||
let backup_dir = state.config.world_dir / "backups"
|
||||
create_dir backup_dir
|
||||
|
||||
let backup_file = backup_dir / state.config.level & "_" &
|
||||
let backup_file =
|
||||
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]:
|
||||
for file in backups[0 ..^ 20]:
|
||||
remove_file file
|
||||
|
||||
create_zip_archive(level_dir, backup_file)
|
||||
|
||||
proc load_units(parent: Unit) =
|
||||
let opts = JOptions(allow_missing_keys: true)
|
||||
let path =
|
||||
if ?parent:
|
||||
parent.data_dir
|
||||
else:
|
||||
state.config.data_dir
|
||||
let path = if ?parent: parent.data_dir else: state.config.data_dir
|
||||
for dir in walk_dirs(path / "*"):
|
||||
let unit_id = dir.split_path.tail
|
||||
let file_name = dir / unit_id & ".json"
|
||||
|
|
|
@ -1,25 +1,34 @@
|
|||
import godotapi / spatial
|
||||
import core, states, bots, builds, models / [colors, units]
|
||||
import godotapi/spatial
|
||||
import core, states, bots, builds, models/[colors, units]
|
||||
|
||||
proc init*(_: type Sign, message: string, more = "", owner: Unit,
|
||||
transform = Transform.init, width = 1.0, height = 1.0, size = 32,
|
||||
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
|
||||
)
|
||||
proc init*(
|
||||
_: type Sign,
|
||||
message: string,
|
||||
more = "",
|
||||
owner: Unit,
|
||||
transform = Transform.init,
|
||||
width = 1.0,
|
||||
height = 1.0,
|
||||
size = 32,
|
||||
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,
|
||||
)
|
||||
self.init_unit
|
||||
result = self
|
||||
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
import std / [tables, strutils, sequtils, sets, sugar]
|
||||
import core, models / [colors]
|
||||
import std/[tables, strutils, sequtils, sets, sugar]
|
||||
import core, models/[colors]
|
||||
|
||||
log_scope:
|
||||
topics = "state"
|
||||
ctx = Zen.thread_ctx.id
|
||||
|
||||
# only one flag from the group is active at a time
|
||||
const groups = @[
|
||||
{EditorFocused, ConsoleFocused, DocsFocused},
|
||||
{ReticleVisible, BlockTargetVisible},
|
||||
{Playing, Flying}
|
||||
]
|
||||
const groups =
|
||||
@[
|
||||
{EditorFocused, ConsoleFocused, DocsFocused},
|
||||
{ReticleVisible, BlockTargetVisible},
|
||||
{Playing, Flying}
|
||||
]
|
||||
|
||||
proc resolve_flags(self: GameState) =
|
||||
debug "resolving flags", flags = self.local_flags.value, wants = self.wants.value
|
||||
debug "resolving flags",
|
||||
flags = self.local_flags.value, wants = self.wants.value
|
||||
var result: set[LocalStateFlags]
|
||||
for flag in self.wants:
|
||||
for group in groups:
|
||||
|
@ -92,11 +94,13 @@ proc toggle_flag*(self: GameState, flag: LocalStateFlags) =
|
|||
else:
|
||||
self.pop_flag flag
|
||||
|
||||
proc `+=`*(self: ZenSet[LocalStateFlags], flag: LocalStateFlags) {.error:
|
||||
"Use `push_flag`, `pop_flag` and `replace_flag`".}
|
||||
proc `+=`*(
|
||||
self: ZenSet[LocalStateFlags], flag: LocalStateFlags
|
||||
) {.error: "Use `push_flag`, `pop_flag` and `replace_flag`".}
|
||||
|
||||
proc `-=`*(self: ZenSet[LocalStateFlags], flag: LocalStateFlags) {.error:
|
||||
"Use `push_flag`, `pop_flag` and `replace_flag`".}
|
||||
proc `-=`*(
|
||||
self: ZenSet[LocalStateFlags], flag: LocalStateFlags
|
||||
) {.error: "Use `push_flag`, `pop_flag` and `replace_flag`".}
|
||||
|
||||
proc selected_color*(self: GameState): Color =
|
||||
action_colors[Colors(ord self.tool)]
|
||||
|
@ -120,23 +124,24 @@ 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:
|
||||
|
@ -157,10 +162,10 @@ proc init*(_: type GameState): GameState =
|
|||
result = self
|
||||
|
||||
when is_main_module:
|
||||
import pkg / print
|
||||
import pkg/print
|
||||
on_unhandled_exception = nil
|
||||
|
||||
import std / [unittest, sequtils]
|
||||
import std/[unittest, sequtils]
|
||||
type Node = ref object
|
||||
var state = GameState.init
|
||||
|
||||
|
@ -198,8 +203,10 @@ when is_main_module:
|
|||
added = {}
|
||||
removed = {}
|
||||
for change in changes:
|
||||
if Added in change.changes: added.incl change.item
|
||||
if Removed in change.changes: removed.incl change.item
|
||||
if Added in change.changes:
|
||||
added.incl change.item
|
||||
if Removed in change.changes:
|
||||
removed.incl change.item
|
||||
|
||||
state.push_flag CommandMode
|
||||
check:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import std / [os, with, tables]
|
||||
import godotapi / spatial
|
||||
from pkg / core / godotcoretypes import Basis
|
||||
import core, models / [states, colors], libs / interpreters
|
||||
import std/[os, with, tables]
|
||||
import godotapi/spatial
|
||||
from pkg/core/godotcoretypes import Basis
|
||||
import core, models/[states, colors], libs/interpreters
|
||||
|
||||
proc fix_parents*(self: Unit, parent: Unit) =
|
||||
self.parent = parent
|
||||
|
@ -56,9 +56,9 @@ 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
|
||||
|
@ -80,22 +80,26 @@ proc data_dir*(self: Unit): string =
|
|||
proc data_file*(self: Unit): string =
|
||||
self.data_dir / self.id & ".json"
|
||||
|
||||
method main_thread_joined*(self: Unit) {.base, gcsafe.} = discard
|
||||
method main_thread_joined*(self: Unit) {.base, gcsafe.} =
|
||||
discard
|
||||
|
||||
method worker_thread_joined*(self: Unit) {.base, gcsafe.} = discard
|
||||
method worker_thread_joined*(self: Unit) {.base, gcsafe.} =
|
||||
discard
|
||||
|
||||
method batch_changes*(self: Unit): bool {.base, gcsafe.} = discard
|
||||
method batch_changes*(self: Unit): bool {.base, gcsafe.} =
|
||||
discard
|
||||
|
||||
method apply_changes*(self: Unit) {.base, gcsafe.} = discard
|
||||
|
||||
method on_begin_move*(self: Unit, direction: Vector3, steps: float,
|
||||
move_mode: int): Callback {.base, gcsafe.} =
|
||||
method apply_changes*(self: Unit) {.base, gcsafe.} =
|
||||
discard
|
||||
|
||||
method on_begin_move*(
|
||||
self: Unit, direction: Vector3, steps: float, move_mode: int
|
||||
): Callback {.base, gcsafe.} =
|
||||
fail "override me"
|
||||
|
||||
method on_begin_turn*(self: Unit, direction: Vector3, degrees: float,
|
||||
lean: bool, move_mode: int): Callback {.base, gcsafe.} =
|
||||
|
||||
method on_begin_turn*(
|
||||
self: Unit, direction: Vector3, degrees: float, lean: bool, move_mode: int
|
||||
): Callback {.base, gcsafe.} =
|
||||
fail "override me"
|
||||
|
||||
method clone*(self: Unit, clone_to: Unit, id: string): Unit {.base, gcsafe.} =
|
||||
|
@ -122,7 +126,9 @@ method collect_garbage*(self: Unit) {.base, gcsafe.} =
|
|||
method ensure_visible*(self: Unit) {.base, gcsafe.} =
|
||||
discard
|
||||
|
||||
method on_collision*(self: Model, partner: Model, normal: Vector3) {.base, gcsafe.} =
|
||||
method on_collision*(
|
||||
self: Model, partner: Model, normal: Vector3
|
||||
) {.base, gcsafe.} =
|
||||
discard
|
||||
|
||||
method off_collision*(self: Model, partner: Model) {.base, gcsafe.} =
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import std / [strutils, wrapnils]
|
||||
import pkg / [godot]
|
||||
import godotapi / [sprite_3d, ray_cast, spatial]
|
||||
import std/[strutils, wrapnils]
|
||||
import pkg/[godot]
|
||||
import godotapi/[sprite_3d, ray_cast, spatial]
|
||||
import globals, core, nodes/helpers, models
|
||||
|
||||
gdobj AimTarget of Sprite3D:
|
||||
|
@ -27,21 +27,20 @@ gdobj AimTarget of Sprite3D:
|
|||
|
||||
proc update*(ray: RayCast) =
|
||||
ray.force_raycast_update()
|
||||
let collider = if ray.is_colliding():
|
||||
ray.get_collider() as Spatial
|
||||
else:
|
||||
nil
|
||||
let collider =
|
||||
if ray.is_colliding():
|
||||
ray.get_collider() as Spatial
|
||||
else:
|
||||
nil
|
||||
|
||||
let unit = ?.collider.model
|
||||
if ?self.target_model:
|
||||
# :(
|
||||
if ?self.target_model.global_flags and
|
||||
self.target_model.global_flags.destroyed:
|
||||
|
||||
self.target_model = nil
|
||||
elif ?self.target_model.local_flags and
|
||||
self.target_model.local_flags.destroyed:
|
||||
|
||||
self.target_model = nil
|
||||
|
||||
if unit != self.target_model:
|
||||
|
@ -50,10 +49,12 @@ gdobj AimTarget of Sprite3D:
|
|||
state.pop_flag BlockTargetVisible
|
||||
self.target_model = unit
|
||||
# :(
|
||||
if not (unit == nil or (unit of Sign and Sign(unit).more == "") or
|
||||
(God notin state.local_flags and (unit of Bot or unit of Build) and
|
||||
Lock in Unit(unit).find_root.global_flags)):
|
||||
|
||||
if not (
|
||||
unit == nil or (unit of Sign and Sign(unit).more == "") or (
|
||||
God notin state.local_flags and (unit of Bot or unit of Build) and
|
||||
Lock in Unit(unit).find_root.global_flags
|
||||
)
|
||||
):
|
||||
unit.local_flags += Hover
|
||||
if unit of Build or unit of Ground:
|
||||
state.push_flag BlockTargetVisible
|
||||
|
@ -66,8 +67,8 @@ gdobj AimTarget of Sprite3D:
|
|||
local_collision_point = collider.to_local(ray.get_collision_point())
|
||||
basis = collider.global_transform.basis
|
||||
half = vec3(0.5, 0.5, 0.5)
|
||||
local_normal = (basis.xform_inv(global_normal) / collider.scale)
|
||||
.snapped(half)
|
||||
local_normal =
|
||||
(basis.xform_inv(global_normal) / collider.scale).snapped(half)
|
||||
factor = local_normal.inverse_normalized() * 0.5
|
||||
|
||||
if not local_normal.is_axis_aligned:
|
||||
|
@ -75,17 +76,20 @@ gdobj AimTarget of Sprite3D:
|
|||
# If it isn't, we probably got a corner or something.
|
||||
return
|
||||
|
||||
local_point = (local_collision_point - factor).snapped(vec3(1, 1, 1)) + factor
|
||||
local_point =
|
||||
(local_collision_point - factor).snapped(vec3(1, 1, 1)) + factor
|
||||
global_normal = basis.xform(local_normal) / collider.scale
|
||||
|
||||
self.translation = collider.to_global local_point + (local_normal * 0.01) / collider.scale
|
||||
self.translation =
|
||||
collider.to_global local_point + (local_normal * 0.01) / collider.scale
|
||||
self.scale = collider.scale
|
||||
|
||||
let align_normal = self.transform.origin + global_normal
|
||||
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
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
import std / [tables, math]
|
||||
import std/[tables, math]
|
||||
import pkg/godot except print
|
||||
import pkg / [chroma]
|
||||
import godotapi / [scene_tree, kinematic_body, material, mesh_instance, spatial,
|
||||
input_event, animation_player, resource_loader, packed_scene,
|
||||
spatial_material, text_edit]
|
||||
import globals, core, models / [colors], ui / markdown_label
|
||||
import ./ queries
|
||||
import pkg/[chroma]
|
||||
import
|
||||
godotapi/[
|
||||
scene_tree, kinematic_body, material, mesh_instance, spatial, input_event,
|
||||
animation_player, resource_loader, packed_scene, spatial_material, text_edit
|
||||
]
|
||||
import globals, core, models/[colors], ui/markdown_label
|
||||
import ./queries
|
||||
|
||||
gdobj BotNode of KinematicBody:
|
||||
var
|
||||
model*: Unit
|
||||
material* {.gdExport.}, highlight_material* {.gdExport.},
|
||||
material* {.gdExport.},
|
||||
highlight_material* {.gdExport.},
|
||||
selected_material* {.gdExport.}: Material
|
||||
skin: Spatial
|
||||
mesh: MeshInstance
|
||||
|
@ -31,7 +34,7 @@ gdobj BotNode of KinematicBody:
|
|||
self.mesh = self.skin.get_node("root/Skeleton/body001").as(MeshInstance)
|
||||
self.set_default_material()
|
||||
self.animation_player =
|
||||
self.skin.get_node("AnimationPlayer").as(AnimationPlayer)
|
||||
self.skin.get_node("AnimationPlayer").as(AnimationPlayer)
|
||||
if self.model of Player:
|
||||
# hack so player model doesn't hover
|
||||
self.skin.translate DOWN * 0.8
|
||||
|
@ -52,7 +55,7 @@ gdobj BotNode of KinematicBody:
|
|||
debug "setting bot color", color, adjusted
|
||||
SpatialMaterial(self.material).albedo_color = adjusted
|
||||
|
||||
proc set_visibility =
|
||||
proc set_visibility() =
|
||||
var color = self.model.color
|
||||
if Visible in self.model.global_flags:
|
||||
self.visible = true
|
||||
|
@ -90,9 +93,10 @@ gdobj BotNode of KinematicBody:
|
|||
self.set_default_material()
|
||||
|
||||
self.model.global_flags.watch:
|
||||
if (change.item == Visible and ScriptInitializing notin
|
||||
self.model.global_flags) or ScriptInitializing.removed:
|
||||
|
||||
if (
|
||||
change.item == Visible and
|
||||
ScriptInitializing notin self.model.global_flags
|
||||
) or ScriptInitializing.removed:
|
||||
self.set_visibility
|
||||
|
||||
self.model.local_flags.watch:
|
||||
|
@ -108,16 +112,16 @@ 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")
|
||||
elif added:
|
||||
self.animation_player.play(change.item)
|
||||
|
||||
elif self.model of Player:
|
||||
let player = Player(self.model)
|
||||
player.rotation_value.watch:
|
||||
|
@ -127,8 +131,9 @@ gdobj BotNode of KinematicBody:
|
|||
player.velocity_value.watch:
|
||||
if added:
|
||||
var velocity = change.item.length
|
||||
self.set_walk_animation(change.item.length,
|
||||
player.input_direction.z > 0.0)
|
||||
self.set_walk_animation(
|
||||
change.item.length, player.input_direction.z > 0.0
|
||||
)
|
||||
|
||||
player.cursor_position_value.watch:
|
||||
if added:
|
||||
|
@ -147,9 +152,10 @@ 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:
|
||||
|
@ -157,7 +163,7 @@ gdobj BotNode of KinematicBody:
|
|||
query.run(self.model)
|
||||
self.model.sight_query = query
|
||||
|
||||
proc setup* =
|
||||
proc setup*() =
|
||||
self.set_color(self.model.color)
|
||||
self.track_changes
|
||||
self.model.sight_ray = self.get_node("SightRay") as RayCast
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import std / [tables, bitops]
|
||||
import pkg / godot except print, Color
|
||||
import godotapi / [node, voxel_terrain, voxel_mesher_blocky, voxel_tool,
|
||||
voxel_library, shader_material,resource_loader, packed_scene, ray_cast]
|
||||
import core, models / [units, builds, colors], globals
|
||||
import ./ queries
|
||||
import std/[tables, bitops]
|
||||
import pkg/godot except print, Color
|
||||
import
|
||||
godotapi/[
|
||||
node, voxel_terrain, voxel_mesher_blocky, voxel_tool, voxel_library,
|
||||
shader_material, resource_loader, packed_scene, ray_cast
|
||||
]
|
||||
import core, models/[units, builds, colors], globals
|
||||
import ./queries
|
||||
|
||||
const
|
||||
highlight_glow = 1.0
|
||||
|
@ -29,19 +32,20 @@ gdobj BuildNode of VoxelTerrain:
|
|||
self.bind_signals self, "block_loaded", "block_unloaded"
|
||||
self.default_view_distance = self.max_view_distance.int
|
||||
|
||||
proc prepare_materials =
|
||||
proc prepare_materials() =
|
||||
if self.model.shared.materials.len == 0:
|
||||
# generate our own copy of the library materials, so we can manipulate
|
||||
# them without impacting other builds.
|
||||
for i in 0..int.high:
|
||||
for i in 0 .. int.high:
|
||||
let m = self.get_material(i)
|
||||
if m.is_nil:
|
||||
break
|
||||
else:
|
||||
let m = m.duplicate.as(ShaderMaterial)
|
||||
m.set_shader_param("emission_energy", default_glow.to_variant)
|
||||
self.model.shared.emission_colors.add(m.get_shader_param("emission").
|
||||
as_color)
|
||||
self.model.shared.emission_colors.add(
|
||||
m.get_shader_param("emission").as_color
|
||||
)
|
||||
|
||||
self.model.shared.materials.add(m)
|
||||
|
||||
|
@ -57,25 +61,26 @@ gdobj BuildNode of VoxelTerrain:
|
|||
|
||||
proc set_glow(glow: float) =
|
||||
let library = self.mesher.as(VoxelMesherBlocky).library
|
||||
for i in 0..<library.voxel_count.int:
|
||||
for i in 0 ..< library.voxel_count.int:
|
||||
let m = self.get_material(i).as(ShaderMaterial)
|
||||
if not m.is_nil:
|
||||
m.set_shader_param("emission_energy", glow.to_variant)
|
||||
|
||||
proc set_highlight() =
|
||||
let library = self.mesher.as(VoxelMesherBlocky).library
|
||||
for i in 0..<library.voxel_count.int:
|
||||
for i in 0 ..< library.voxel_count.int:
|
||||
let m = self.get_material(i).as(ShaderMaterial)
|
||||
if not m.is_nil:
|
||||
if self.error_highlight_on:
|
||||
m.set_shader_param("emission", action_colors[red].to_variant)
|
||||
else:
|
||||
m.set_shader_param("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):
|
||||
m.set_shader_param(
|
||||
"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
|
||||
):
|
||||
m.set_shader_param("emission_energy", highlight_glow.to_variant)
|
||||
else:
|
||||
m.set_shader_param("emission_energy", self.model.glow.to_variant)
|
||||
|
@ -83,13 +88,14 @@ 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
|
||||
|
@ -105,7 +111,7 @@ gdobj BuildNode of VoxelTerrain:
|
|||
self.model.chunks[chunk_id].untrack(zid)
|
||||
self.active_chunks.del(chunk_id)
|
||||
|
||||
proc set_visibility =
|
||||
proc set_visibility() =
|
||||
if Visible in self.model.global_flags:
|
||||
self.visible = true
|
||||
|
||||
|
@ -120,13 +126,14 @@ 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)
|
||||
|
@ -148,17 +155,16 @@ gdobj BuildNode of VoxelTerrain:
|
|||
self.track_chunks()
|
||||
|
||||
self.model.global_flags.watch:
|
||||
if (change.item == Visible and ScriptInitializing notin
|
||||
self.model.global_flags) or ScriptInitializing.removed:
|
||||
|
||||
if (
|
||||
change.item == Visible and
|
||||
ScriptInitializing notin self.model.global_flags
|
||||
) or ScriptInitializing.removed:
|
||||
self.set_visibility
|
||||
|
||||
elif Resetting.added:
|
||||
self.untrack_chunks()
|
||||
let model = self.model
|
||||
self.generator = nil
|
||||
self.stream = nil
|
||||
|
||||
elif Resetting.removed:
|
||||
self.generator = gdnew[VoxelGeneratorFlat]()
|
||||
self.track_chunks()
|
||||
|
@ -185,9 +191,10 @@ 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:
|
||||
|
@ -210,7 +217,7 @@ gdobj BuildNode of VoxelTerrain:
|
|||
self.toggle_error_highlight_at = get_mono_time() + error_flash_time
|
||||
self.set_highlight()
|
||||
|
||||
proc setup* =
|
||||
proc setup*() =
|
||||
let was_skipping_join = dont_join
|
||||
dont_join = true
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import godotapi / [mesh_instance]
|
||||
import godotapi/[mesh_instance]
|
||||
import pkg/godot except print
|
||||
import core, globals, models
|
||||
|
||||
|
|
|
@ -3,13 +3,14 @@ import godotapi/node
|
|||
import core, bot_node, build_node, ground_node, selection_area, sign_node
|
||||
|
||||
proc model*(self: Object): Model =
|
||||
result = if self of SelectionArea:
|
||||
SelectionArea(self).bot.model
|
||||
elif self of BuildNode:
|
||||
BuildNode(self).model
|
||||
elif self of GroundNode:
|
||||
GroundNode(self).model
|
||||
elif self of StaticBody and StaticBody(self).name == "SignBody":
|
||||
SignNode(StaticBody(self).get_parent.get_parent).model
|
||||
else:
|
||||
nil
|
||||
result =
|
||||
if self of SelectionArea:
|
||||
SelectionArea(self).bot.model
|
||||
elif self of BuildNode:
|
||||
BuildNode(self).model
|
||||
elif self of GroundNode:
|
||||
GroundNode(self).model
|
||||
elif self of StaticBody and StaticBody(self).name == "SignBody":
|
||||
SignNode(StaticBody(self).get_parent.get_parent).model
|
||||
else:
|
||||
nil
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
import std / [math, sugar]
|
||||
from std / times import `+`
|
||||
import pkg / godot except print
|
||||
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, kinematic_collision, packed_scene, resource_loader]
|
||||
import core, globals, nodes / helpers
|
||||
import std/[math, sugar]
|
||||
from std/times import `+`
|
||||
import pkg/godot except print
|
||||
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, kinematic_collision,
|
||||
packed_scene, resource_loader
|
||||
]
|
||||
import core, globals, nodes/helpers
|
||||
import aim_target, models
|
||||
|
||||
proc handle_collisions(self: Player, collisions:
|
||||
seq[KinematicCollision]) {.inline, gcsafe.} =
|
||||
|
||||
proc handle_collisions(
|
||||
self: Player, collisions: seq[KinematicCollision]
|
||||
) {.inline, gcsafe.} =
|
||||
var colliders: HashSet[Model]
|
||||
for collision in collisions:
|
||||
let collider = collision.collider
|
||||
|
@ -67,8 +70,10 @@ gdobj PlayerNode of KinematicBody:
|
|||
state.set_flag Flying, value
|
||||
|
||||
proc get_look_direction(): Vector2 =
|
||||
vec2(get_action_strength("look_right") - get_action_strength("look_left"),
|
||||
get_action_strength("look_up") - get_action_strength("look_down"))
|
||||
vec2(
|
||||
get_action_strength("look_right") - get_action_strength("look_left"),
|
||||
get_action_strength("look_up") - get_action_strength("look_down"),
|
||||
)
|
||||
|
||||
proc update_rotation(offset: Vector2) =
|
||||
var r = self.camera_rig.rotation
|
||||
|
@ -79,12 +84,18 @@ gdobj PlayerNode of KinematicBody:
|
|||
self.camera_rig.rotation = r
|
||||
|
||||
proc get_input_direction(): Vector3 =
|
||||
vec3(get_action_strength("move_right") - get_action_strength("move_left"),
|
||||
get_action_strength("jump") - get_action_strength("crouch"),
|
||||
get_action_strength("move_back") - get_action_strength("move_front"))
|
||||
vec3(
|
||||
get_action_strength("move_right") - get_action_strength("move_left"),
|
||||
get_action_strength("jump") - get_action_strength("crouch"),
|
||||
get_action_strength("move_back") - get_action_strength("move_front"),
|
||||
)
|
||||
|
||||
proc calculate_velocity(velocity_current: Vector3, move_direction: Vector3,
|
||||
delta: float, flying, alt_speed: bool): Vector3 =
|
||||
proc calculate_velocity(
|
||||
velocity_current: Vector3,
|
||||
move_direction: Vector3,
|
||||
delta: float,
|
||||
flying, alt_speed: bool,
|
||||
): Vector3 =
|
||||
let speed =
|
||||
if not flying and not (alt_speed xor AltWalkSpeed in state.local_flags):
|
||||
vec3(state.config.walk_speed)
|
||||
|
@ -98,17 +109,20 @@ gdobj PlayerNode of KinematicBody:
|
|||
result = move_direction * delta * speed
|
||||
|
||||
if not flying:
|
||||
let float_time = if alt_speed:
|
||||
float_time + float_time
|
||||
else:
|
||||
float_time
|
||||
let floating = self.jump_down and ?self.jump_time and
|
||||
self.jump_time.get + float_time > get_mono_time()
|
||||
let float_time =
|
||||
if alt_speed:
|
||||
float_time + float_time
|
||||
else:
|
||||
float_time
|
||||
let floating =
|
||||
self.jump_down and ?self.jump_time and
|
||||
self.jump_time.get + float_time > get_mono_time()
|
||||
|
||||
let gravity = if floating:
|
||||
state.gravity / 4
|
||||
else:
|
||||
state.gravity
|
||||
let gravity =
|
||||
if floating:
|
||||
state.gravity / 4
|
||||
else:
|
||||
state.gravity
|
||||
result.y = velocity_current.y + gravity * delta
|
||||
|
||||
method ready*() =
|
||||
|
@ -146,24 +160,24 @@ 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
|
||||
proc current_raycast*(): RayCast =
|
||||
if MouseCaptured in state.local_flags: self.aim_ray else: self.world_ray
|
||||
|
||||
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
|
||||
|
||||
|
@ -186,11 +200,12 @@ gdobj PlayerNode of KinematicBody:
|
|||
let ray_length = if state.tool == CodeMode: 200.0 else: 100.0
|
||||
if MouseCaptured notin state.local_flags:
|
||||
let
|
||||
mouse_pos = self.get_viewport().get_mouse_position() *
|
||||
float state.scale_factor
|
||||
mouse_pos =
|
||||
self.get_viewport().get_mouse_position() * float state.scale_factor
|
||||
cast_from = self.camera.project_ray_origin(mouse_pos)
|
||||
cast_to = self.aim_ray.translation +
|
||||
self.camera.project_ray_normal(mouse_pos) * ray_length
|
||||
cast_to =
|
||||
self.aim_ray.translation +
|
||||
self.camera.project_ray_normal(mouse_pos) * ray_length
|
||||
|
||||
self.world_ray.cast_to = cast_to
|
||||
self.world_ray.translation = cast_from
|
||||
|
@ -210,7 +225,10 @@ gdobj PlayerNode of KinematicBody:
|
|||
process_input =
|
||||
EditorVisible notin state.local_flags or CommandMode in state.local_flags
|
||||
input_direction =
|
||||
if process_input: self.get_input_direction() else: vec3()
|
||||
if process_input:
|
||||
self.get_input_direction()
|
||||
else:
|
||||
vec3()
|
||||
|
||||
basis = self.camera_rig.global_transform.basis
|
||||
right = basis.x * input_direction.x
|
||||
|
@ -225,17 +243,20 @@ 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)
|
||||
|
||||
|
@ -247,8 +268,12 @@ gdobj PlayerNode of KinematicBody:
|
|||
self.down_ray.translation = move_direction * 0.3 + vec3(0, 1, 0)
|
||||
if self.down_ray.is_colliding():
|
||||
let length = 1.85
|
||||
let diff = length - (self.down_ray.global_transform.origin -
|
||||
self.down_ray.get_collision_point).y
|
||||
let diff =
|
||||
length -
|
||||
(
|
||||
self.down_ray.global_transform.origin -
|
||||
self.down_ray.get_collision_point
|
||||
).y
|
||||
if diff > 0 and (self.is_on_floor() or not self.boosted):
|
||||
let boost = 16.1 * cbrt(diff)
|
||||
if boost > self.velocity.y:
|
||||
|
@ -260,12 +285,11 @@ gdobj PlayerNode of KinematicBody:
|
|||
self.translation = vec3(0, 100, 0)
|
||||
|
||||
proc has_active_input(device: int): bool =
|
||||
for axis in 0..JOY_AXIS_MAX:
|
||||
for axis in 0 .. JOY_AXIS_MAX:
|
||||
if axis != JOY_ANALOG_L2 and axis != JOY_ANALOG_R2 and
|
||||
get_joy_axis(device, axis).abs >= 0.2:
|
||||
|
||||
return true
|
||||
for button in 0..JOY_BUTTON_MAX:
|
||||
for button in 0 .. JOY_BUTTON_MAX:
|
||||
if is_joy_button_pressed(device, button):
|
||||
return true
|
||||
|
||||
|
@ -280,9 +304,9 @@ 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
|
||||
|
@ -326,9 +350,7 @@ gdobj PlayerNode of KinematicBody:
|
|||
elif event.is_action_released("run"):
|
||||
self.alt_speed = false
|
||||
|
||||
if event of InputEventPanGesture and
|
||||
state.tool notin {CodeMode, PlaceBot}:
|
||||
|
||||
if event of InputEventPanGesture and state.tool notin {CodeMode, PlaceBot}:
|
||||
let pan = event as InputEventPanGesture
|
||||
self.pan_delta += pan.delta.y
|
||||
if self.pan_delta > 2:
|
||||
|
@ -351,7 +373,8 @@ gdobj PlayerNode of KinematicBody:
|
|||
elif event.is_action_released("remove"):
|
||||
state.pop_flag SecondaryDown
|
||||
|
||||
proc get_player*(): PlayerNode = PlayerNode(state.nodes.player)
|
||||
proc get_player*(): PlayerNode =
|
||||
PlayerNode(state.nodes.player)
|
||||
|
||||
var scene {.threadvar.}: PackedScene
|
||||
proc init*(_: type PlayerNode): PlayerNode =
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import pkg / godot
|
||||
import godotapi / [ray_cast]
|
||||
import core, models / [units]
|
||||
import pkg/godot
|
||||
import godotapi/[ray_cast]
|
||||
import core, models/[units]
|
||||
|
||||
proc run*(query: var SightQuery, source: Unit) =
|
||||
assert not ?query.answer
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import pkg / [godot]
|
||||
import godotapi / [area, control]
|
||||
import pkg/[godot]
|
||||
import godotapi/[area, control]
|
||||
import core, globals, nodes/bot_node
|
||||
|
||||
gdobj SelectionArea of Area:
|
||||
var
|
||||
bot*: BotNode
|
||||
var bot*: BotNode
|
||||
|
||||
method ready*() =
|
||||
self.bot = self.get_node("..") as BotNode
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import pkg / [godot]
|
||||
import godotapi / [spatial, resource_loader, packed_scene, collision_shape,
|
||||
mesh_instance, quad_mesh, spatial_material, viewport, style_box_flat,
|
||||
text_edit]
|
||||
import ui / [markdown_label, editor]
|
||||
import pkg/[godot]
|
||||
import
|
||||
godotapi/[
|
||||
spatial, resource_loader, packed_scene, collision_shape, mesh_instance,
|
||||
quad_mesh, spatial_material, viewport, style_box_flat, text_edit
|
||||
]
|
||||
import ui/[markdown_label, editor]
|
||||
import core
|
||||
|
||||
const
|
||||
|
@ -20,7 +22,7 @@ gdobj SignNode of Spatial:
|
|||
var counter: int
|
||||
var expanded: bool
|
||||
|
||||
proc set_visibility =
|
||||
proc set_visibility() =
|
||||
if Hide in self.model.local_flags:
|
||||
self.visible = false
|
||||
elif Visible in self.model.global_flags:
|
||||
|
@ -47,7 +49,7 @@ gdobj SignNode of Spatial:
|
|||
self.quad.size = vec2(self.model.width, self.model.width * ratio)
|
||||
self.shape.scale = vec3(self.model.width, self.model.width * ratio, 1)
|
||||
|
||||
proc setup* =
|
||||
proc setup*() =
|
||||
debug "sign setup", sign = self.model.id
|
||||
|
||||
var mesh = self.get_node("MeshInstance") as MeshInstance
|
||||
|
@ -67,7 +69,7 @@ gdobj SignNode of Spatial:
|
|||
# hide scrollbars
|
||||
ScrollBar(child).rect_scale = vec2()
|
||||
|
||||
proc resize =
|
||||
proc resize() =
|
||||
debug "sign resize", sign = self.model.id
|
||||
|
||||
var
|
||||
|
@ -100,10 +102,7 @@ gdobj SignNode of Spatial:
|
|||
|
||||
resize()
|
||||
self.material.params_billboard_mode =
|
||||
if self.model.billboard:
|
||||
BILLBOARD_ENABLED
|
||||
else:
|
||||
BILLBOARD_DISABLED
|
||||
if self.model.billboard: BILLBOARD_ENABLED else: BILLBOARD_DISABLED
|
||||
|
||||
if self.model.text_only:
|
||||
text_edit.text = self.model.message
|
||||
|
@ -130,9 +129,10 @@ gdobj SignNode of Spatial:
|
|||
self.transform = change.item
|
||||
|
||||
self.model.global_flags.watch:
|
||||
if (change.item == Visible and ScriptInitializing notin
|
||||
self.model.global_flags) or ScriptInitializing.removed:
|
||||
|
||||
if (
|
||||
change.item == Visible and
|
||||
ScriptInitializing notin self.model.global_flags
|
||||
) or ScriptInitializing.removed:
|
||||
self.set_visibility
|
||||
|
||||
state.local_flags.watch:
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import std / [tables, monotimes, sets, options, macros]
|
||||
import godotapi / [spatial, ray_cast]
|
||||
import std/[tables, monotimes, sets, options, macros]
|
||||
import godotapi/[spatial, ray_cast]
|
||||
import pkg/core/godotcoretypes except Color
|
||||
import pkg / core / [vector3, basis, aabb, godotbase]
|
||||
import pkg / compiler / [ast, lineinfos, semdata]
|
||||
import pkg / [model_citizen]
|
||||
import models / colors, libs / [eval]
|
||||
import pkg/core/[vector3, basis, aabb, godotbase]
|
||||
import pkg/compiler/[ast, lineinfos, semdata]
|
||||
import pkg/[model_citizen]
|
||||
import models/colors, libs/[eval]
|
||||
|
||||
from pkg / godot import NimGodotObject
|
||||
from pkg/godot import NimGodotObject
|
||||
|
||||
export Vector3, Transform, vector3, basis, AABB, aabb
|
||||
export godotbase except print
|
||||
|
@ -15,27 +15,64 @@ export Interpreter
|
|||
type
|
||||
EnuError* = object of CatchableError
|
||||
LocalStateFlags* = enum
|
||||
CommandMode, EditorVisible, ConsoleVisible,
|
||||
BlockTargetVisible, ReticleVisible, DocsVisible, MouseCaptured,
|
||||
PrimaryDown, SecondaryDown, EditorFocused, ConsoleFocused, DocsFocused,
|
||||
Playing, Flying, God, AltWalkSpeed, AltFlySpeed,
|
||||
LoadingScript, Server, Quitting, ResettingVM, NeedsRestart, Connecting
|
||||
CommandMode
|
||||
EditorVisible
|
||||
ConsoleVisible
|
||||
BlockTargetVisible
|
||||
ReticleVisible
|
||||
DocsVisible
|
||||
MouseCaptured
|
||||
PrimaryDown
|
||||
SecondaryDown
|
||||
EditorFocused
|
||||
ConsoleFocused
|
||||
DocsFocused
|
||||
Playing
|
||||
Flying
|
||||
God
|
||||
AltWalkSpeed
|
||||
AltFlySpeed
|
||||
LoadingScript
|
||||
Server
|
||||
Quitting
|
||||
ResettingVM
|
||||
NeedsRestart
|
||||
Connecting
|
||||
|
||||
GlobalStateFlags* = enum
|
||||
LoadingLevel
|
||||
|
||||
LocalModelFlags* = enum
|
||||
Hover, TargetMoved, Highlight, HighlightError, Hide
|
||||
Hover
|
||||
TargetMoved
|
||||
Highlight
|
||||
HighlightError
|
||||
Hide
|
||||
|
||||
GlobalModelFlags* = enum
|
||||
Global, Visible, Lock, Ready, ScriptInitializing, Dirty, Resetting
|
||||
Global
|
||||
Visible
|
||||
Lock
|
||||
Ready
|
||||
ScriptInitializing
|
||||
Dirty
|
||||
Resetting
|
||||
|
||||
Tools* = enum
|
||||
CodeMode, BlueBlock, RedBlock, GreenBlock, BlackBlock, WhiteBlock,
|
||||
BrownBlock, PlaceBot, Disabled
|
||||
CodeMode
|
||||
BlueBlock
|
||||
RedBlock
|
||||
GreenBlock
|
||||
BlackBlock
|
||||
WhiteBlock
|
||||
BrownBlock
|
||||
PlaceBot
|
||||
Disabled
|
||||
|
||||
TaskStates* = enum
|
||||
Running, Done, NextTask
|
||||
Running
|
||||
Done
|
||||
NextTask
|
||||
|
||||
ConsoleModel* = ref object
|
||||
log*: ZenSeq[string]
|
||||
|
@ -48,11 +85,7 @@ type
|
|||
open_unit_value*: ZenValue[Unit]
|
||||
tool_value*: ZenValue[Tools]
|
||||
gravity*: float
|
||||
nodes*: tuple[
|
||||
game: Node,
|
||||
data: Node,
|
||||
player: Node
|
||||
]
|
||||
nodes*: tuple[game: Node, data: Node, player: Node]
|
||||
player_value*: ZenValue[Player]
|
||||
units*: ZenSeq[Unit]
|
||||
ground*: Ground
|
||||
|
@ -135,11 +168,11 @@ type
|
|||
text_only*: bool
|
||||
|
||||
VoxelKind* = enum
|
||||
Hole, Manual, Computed
|
||||
Hole
|
||||
Manual
|
||||
Computed
|
||||
|
||||
VoxelInfo* = tuple
|
||||
kind: VoxelKind
|
||||
color: Color
|
||||
VoxelInfo* = tuple[kind: VoxelKind, color: Color]
|
||||
|
||||
Chunk* = ZenTable[Vector3, VoxelInfo]
|
||||
|
||||
|
@ -234,7 +267,8 @@ type
|
|||
|
||||
VMError* = object of CatchableError
|
||||
QuitKind* = enum
|
||||
Unknown, Timeout
|
||||
Unknown
|
||||
Timeout
|
||||
|
||||
VMQuit* = object of VMError
|
||||
info*: TLineInfo
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import godotapi / [button, style_box_flat]
|
||||
import godotapi/[button, style_box_flat]
|
||||
import godot
|
||||
import ".." / [core, globals]
|
||||
import ".."/[core, globals]
|
||||
|
||||
gdobj ActionButton of Button:
|
||||
method ready*() =
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import godotapi / [text_edit, scene_tree, node, input_event, input_event_key,
|
||||
rich_text_label, global_constants]
|
||||
import
|
||||
godotapi/[
|
||||
text_edit, scene_tree, node, input_event, input_event_key, rich_text_label,
|
||||
global_constants
|
||||
]
|
||||
import godot
|
||||
import std / strutils
|
||||
import std/strutils
|
||||
import core, globals
|
||||
|
||||
gdobj Console of RichTextLabel:
|
||||
var
|
||||
default_mouse_filter: int64
|
||||
var default_mouse_filter: int64
|
||||
|
||||
proc init*() =
|
||||
state.local_flags.changes:
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import std / [strutils, tables]
|
||||
import pkg / [godot]
|
||||
import pkg / compiler / [lineinfos]
|
||||
import godotapi / [text_edit, scene_tree, node, input_event, global_constants,
|
||||
input_event_key, style_box_flat, gd_os]
|
||||
import std/[strutils, tables]
|
||||
import pkg/[godot]
|
||||
import pkg/compiler/[lineinfos]
|
||||
import
|
||||
godotapi/[
|
||||
text_edit, scene_tree, node, input_event, global_constants, input_event_key,
|
||||
style_box_flat, gd_os
|
||||
]
|
||||
import core, globals
|
||||
import models except Color
|
||||
|
||||
|
@ -24,7 +27,7 @@ gdobj Editor of TextEdit:
|
|||
let column = int self.cursor_get_column - 1
|
||||
if column > 0:
|
||||
let
|
||||
line = self.get_line(self.cursor_get_line)[0..column]
|
||||
line = self.get_line(self.cursor_get_line)[0 .. column]
|
||||
stripped = line.strip()
|
||||
|
||||
if stripped.high > 0:
|
||||
|
@ -40,9 +43,7 @@ gdobj Editor of TextEdit:
|
|||
if not event.is_nil and event.pressed:
|
||||
if event.scancode == KEY_ENTER:
|
||||
self.indent_new_line()
|
||||
if event.scancode == KEY_SEMICOLON and
|
||||
state.config.semicolon_as_colon:
|
||||
|
||||
if event.scancode == KEY_SEMICOLON and state.config.semicolon_as_colon:
|
||||
self.insert_text_at_cursor(":")
|
||||
self.get_tree.set_input_as_handled()
|
||||
elif event.scancode == KEY_HOME:
|
||||
|
@ -53,17 +54,19 @@ gdobj Editor of TextEdit:
|
|||
self.get_tree.set_input_as_handled()
|
||||
|
||||
method unhandled_input*(event: InputEvent) =
|
||||
if EditorFocused in state.local_flags and event.is_action_pressed("ui_cancel"):
|
||||
if not (event of InputEventJoypadButton) or CommandMode notin state.local_flags:
|
||||
if EditorFocused in state.local_flags and
|
||||
event.is_action_pressed("ui_cancel"):
|
||||
if not (event of InputEventJoypadButton) or
|
||||
CommandMode notin state.local_flags:
|
||||
state.open_unit.code = Code.init(self.text)
|
||||
state.open_unit = nil
|
||||
self.get_tree().set_input_as_handled()
|
||||
|
||||
proc clear_errors =
|
||||
for i in 0..<self.get_line_count():
|
||||
proc clear_errors() =
|
||||
for i in 0 ..< self.get_line_count():
|
||||
self.set_line_as_marked(i, false)
|
||||
|
||||
proc highlight_errors =
|
||||
proc highlight_errors() =
|
||||
self.clear_executing_line()
|
||||
if ?state.open_unit:
|
||||
for err in state.open_unit.errors:
|
||||
|
@ -82,7 +85,7 @@ gdobj Editor of TextEdit:
|
|||
state.player.cursor_position =
|
||||
(int self.cursor_get_line, int self.cursor_get_column)
|
||||
|
||||
method ready* =
|
||||
method ready*() =
|
||||
self.bind_signals(self, "text_changed", "cursor_changed")
|
||||
var stylebox = self.get_stylebox("normal").as(StyleBoxFlat)
|
||||
self.og_bg_color = stylebox.bg_color
|
||||
|
@ -104,13 +107,14 @@ 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
|
||||
|
@ -137,7 +141,6 @@ gdobj Editor of TextEdit:
|
|||
|
||||
self.modulate = dimmed_alpha
|
||||
self.release_focus
|
||||
|
||||
elif CommandMode.removed:
|
||||
if EditorVisible in state.local_flags:
|
||||
self.modulate = solid_alpha
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import std / [lists, algorithm, tables]
|
||||
import pkg / [godot, markdown]
|
||||
import godotapi / [rich_text_label, scroll_container, text_edit, theme,
|
||||
dynamic_font, dynamic_font_data, style_box_flat, main_loop]
|
||||
import core, globals, ui / editor
|
||||
import models / colors except Color
|
||||
import std/[lists, algorithm, tables]
|
||||
import pkg/[godot, markdown]
|
||||
import
|
||||
godotapi/[
|
||||
rich_text_label, scroll_container, text_edit, theme, dynamic_font,
|
||||
dynamic_font_data, style_box_flat, main_loop
|
||||
]
|
||||
import core, globals, ui/editor
|
||||
import models/colors except Color
|
||||
|
||||
export scroll_container
|
||||
|
||||
|
@ -19,7 +22,7 @@ gdobj MarkdownLabel of ScrollContainer:
|
|||
var
|
||||
markdown* {.gd_export, hint: MultilineText.}: string
|
||||
old_markdown: string
|
||||
default_font* {.gd_export.}:DynamicFont
|
||||
default_font* {.gd_export.}: DynamicFont
|
||||
italic_font* {.gd_export.}: DynamicFont
|
||||
bold_font* {.gd_export.}: DynamicFont
|
||||
bold_italic_font* {.gd_export.}: DynamicFont
|
||||
|
@ -46,12 +49,8 @@ gdobj MarkdownLabel of ScrollContainer:
|
|||
self.current_label.visible = true
|
||||
state.nodes.game.bind_signals(self.current_label, "meta_clicked")
|
||||
|
||||
proc set_font_sizes =
|
||||
var size =
|
||||
if self.size > 0:
|
||||
self.size
|
||||
else:
|
||||
state.config.font_size
|
||||
proc set_font_sizes() =
|
||||
var size = if self.size > 0: self.size else: state.config.font_size
|
||||
|
||||
self.local_default_font.size = size
|
||||
self.local_italic_font.size = size
|
||||
|
@ -115,13 +114,14 @@ 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
|
||||
|
||||
method on_resized =
|
||||
method on_resized() =
|
||||
if not self.resized:
|
||||
self.set_font_sizes()
|
||||
self.resized = true
|
||||
|
@ -139,49 +139,45 @@ gdobj MarkdownLabel of ScrollContainer:
|
|||
self.needs_margin = false
|
||||
|
||||
if t of Heading:
|
||||
label.with(push_font self.local_header_font,
|
||||
push_color ir_black[keyword])
|
||||
label.with(
|
||||
push_font self.local_header_font, push_color ir_black[keyword]
|
||||
)
|
||||
self.render_markdown t
|
||||
label.with(pop, pop, newline)
|
||||
self.needs_margin = true
|
||||
|
||||
elif t of Em:
|
||||
label.push_font self.local_italic_font
|
||||
self.render_markdown t
|
||||
label.pop
|
||||
|
||||
elif t of Strong:
|
||||
label.push_font self.local_bold_font
|
||||
self.render_markdown t
|
||||
label.pop
|
||||
|
||||
elif t of CodeSpan:
|
||||
label.with(push_font self.local_mono_font, push_color ir_black[number],
|
||||
add_text t.doc)
|
||||
label.with(
|
||||
push_font self.local_mono_font,
|
||||
push_color ir_black[number],
|
||||
add_text t.doc,
|
||||
)
|
||||
self.render_markdown t
|
||||
label.with(pop, pop)
|
||||
|
||||
elif t of CodeBlock:
|
||||
self.needs_margin = false
|
||||
|
||||
var editor = self.add_text_edit()
|
||||
editor.text = t.doc[0..^2]
|
||||
editor.text = t.doc[0 ..^ 2]
|
||||
editor.visible = true
|
||||
self.container.add_child editor
|
||||
self.add_label()
|
||||
|
||||
elif t of Paragraph:
|
||||
self.render_markdown(t, inline_blocks = inline_blocks)
|
||||
if not inline_blocks:
|
||||
label.newline
|
||||
self.needs_margin = true
|
||||
|
||||
elif t of OL:
|
||||
self.render_markdown(t, 1)
|
||||
|
||||
elif t of UL:
|
||||
self.render_markdown(t)
|
||||
|
||||
elif t of LI:
|
||||
label.with(push_table 2, push_cell, push_font self.local_mono_font)
|
||||
|
||||
|
@ -193,7 +189,6 @@ gdobj MarkdownLabel of ScrollContainer:
|
|||
label.with(pop, pop, push_cell)
|
||||
self.render_markdown(t, inline_blocks = true)
|
||||
label.with(pop, pop, newline)
|
||||
|
||||
elif t of Link:
|
||||
let t = Link(t)
|
||||
label.push_color(ir_black[variable])
|
||||
|
@ -204,13 +199,10 @@ gdobj MarkdownLabel of ScrollContainer:
|
|||
label.pop
|
||||
label.pop
|
||||
label.pop
|
||||
|
||||
elif t of Text:
|
||||
label.add_text(t.doc.replace("\n", " "))
|
||||
|
||||
elif t of SoftBreak:
|
||||
label.add_text " "
|
||||
|
||||
else:
|
||||
self.render_markdown(t)
|
||||
|
||||
|
@ -229,7 +221,7 @@ gdobj MarkdownLabel of ScrollContainer:
|
|||
self.current_label = nil
|
||||
self.old_markdown = self.markdown
|
||||
var root = Document()
|
||||
discard markdown(self.markdown.dedent, root=root)
|
||||
discard markdown(self.markdown.dedent, root = root)
|
||||
self.needs_margin = false
|
||||
self.render_markdown(root)
|
||||
self.set_font_sizes()
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import godotapi / [viewport, camera, mesh_instance, material, camera,
|
||||
viewport_texture, image, resource_loader]
|
||||
import
|
||||
godotapi/[
|
||||
viewport, camera, mesh_instance, material, camera, viewport_texture, image,
|
||||
resource_loader
|
||||
]
|
||||
import godot
|
||||
import ".." / [core, globals]
|
||||
import ".."/[core, globals]
|
||||
|
||||
gdobj PreviewMaker of Viewport:
|
||||
var
|
||||
|
@ -26,7 +29,9 @@ gdobj PreviewMaker of Viewport:
|
|||
self.callback = nil
|
||||
self.skip_next = false
|
||||
|
||||
proc generate_block_preview*(material_name: string, callback: proc(preview: Image) {.gcsafe.}) =
|
||||
proc generate_block_preview*(
|
||||
material_name: string, callback: proc(preview: Image) {.gcsafe.}
|
||||
) =
|
||||
let material = load(\"res://materials/{material_name}.tres") as Material
|
||||
self.cube.visible = true
|
||||
self.bot.visible = false
|
||||
|
@ -37,7 +42,9 @@ gdobj PreviewMaker of Viewport:
|
|||
self.callback = callback
|
||||
self.skip_next = true
|
||||
|
||||
proc generate_object_preview*(object_name: string, callback: proc(preview: Image) {.gcsafe.}) =
|
||||
proc generate_object_preview*(
|
||||
object_name: string, callback: proc(preview: Image) {.gcsafe.}
|
||||
) =
|
||||
self.cube.visible = false
|
||||
self.bot.visible = true
|
||||
self.render_target_update_mode = UPDATE_ONCE
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import pkg / [godot]
|
||||
import godotapi / [margin_container, input_event, scene_tree]
|
||||
import ui / markdown_label
|
||||
import core, models / [states, colors]
|
||||
import pkg/[godot]
|
||||
import godotapi/[margin_container, input_event, scene_tree]
|
||||
import ui/markdown_label
|
||||
import core, models/[states, colors]
|
||||
|
||||
proc set_filter(self: Control, filter: int64) =
|
||||
self.mouse_filter = filter
|
||||
|
@ -20,7 +20,7 @@ gdobj RightPanel of MarginContainer:
|
|||
var label: MarkdownLabel
|
||||
var zid: ZID
|
||||
|
||||
method ready* =
|
||||
method ready*() =
|
||||
self.label = self.find_node("MarkdownLabel") as MarkdownLabel
|
||||
|
||||
state.status_message_value.changes:
|
||||
|
@ -40,10 +40,11 @@ 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)
|
||||
|
@ -62,10 +63,9 @@ 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 not (event of InputEventJoypadButton) or CommandMode notin state.local_flags:
|
||||
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
|
||||
self.get_tree().set_input_as_handled()
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import godotapi / [h_box_container, scene_tree, button, image_texture]
|
||||
import pkg / [godot]
|
||||
import godotapi/[h_box_container, scene_tree, button, image_texture]
|
||||
import pkg/[godot]
|
||||
import core
|
||||
import globals, ui/preview_maker
|
||||
|
||||
type
|
||||
PreviewResult = tuple[color: string, preview: Image]
|
||||
type PreviewResult = tuple[color: string, preview: Image]
|
||||
|
||||
gdobj Toolbar of HBoxContainer:
|
||||
var
|
||||
|
@ -28,11 +27,12 @@ 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:
|
||||
|
@ -47,23 +47,33 @@ gdobj Toolbar of HBoxContainer:
|
|||
if not self.waiting and self.blocks.len > 0:
|
||||
var color = self.blocks.pop()
|
||||
self.waiting = true
|
||||
self.preview_maker.generate_block_preview \"{color}-block-grid", proc(preview: Image) =
|
||||
self.preview_result = some (color: color, preview: preview)
|
||||
self.waiting = false
|
||||
self.preview_maker.generate_block_preview \"{color}-block-grid",
|
||||
proc(preview: Image) =
|
||||
self.preview_result = some (color: color, preview: preview)
|
||||
self.waiting = false
|
||||
if not self.waiting and self.blocks.len == 0 and self.objects.len > 0:
|
||||
let obj = self.objects.pop()
|
||||
self.waiting = true
|
||||
self.preview_maker.generate_object_preview obj, proc(preview: Image) =
|
||||
self.preview_result = some (color: obj, preview: preview)
|
||||
self.preview_maker.generate_object_preview obj,
|
||||
proc(preview: Image) =
|
||||
self.preview_result = some (color: obj, preview: preview)
|
||||
|
||||
method on_action_changed*(button_name: string) =
|
||||
state.tool_value.pause(self.zid):
|
||||
case button_name[7..^1]:
|
||||
of "code": state.tool = CodeMode
|
||||
of "blue": state.tool = BlueBlock
|
||||
of "red": state.tool = RedBlock
|
||||
of "green": state.tool = GreenBlock
|
||||
of "black": state.tool = BlackBlock
|
||||
of "white": state.tool = WhiteBlock
|
||||
of "brown": state.tool = BrownBlock
|
||||
of "bot": state.tool = PlaceBot
|
||||
case button_name[7 ..^ 1]
|
||||
of "code":
|
||||
state.tool = CodeMode
|
||||
of "blue":
|
||||
state.tool = BlueBlock
|
||||
of "red":
|
||||
state.tool = RedBlock
|
||||
of "green":
|
||||
state.tool = GreenBlock
|
||||
of "black":
|
||||
state.tool = BlackBlock
|
||||
of "white":
|
||||
state.tool = WhiteBlock
|
||||
of "brown":
|
||||
state.tool = BrownBlock
|
||||
of "bot":
|
||||
state.tool = PlaceBot
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import enu/types, enu/class_macros
|
||||
import macros except name
|
||||
import pkg / print
|
||||
import pkg/print
|
||||
import enu/base_api
|
||||
import enu/state_machine
|
||||
|
||||
let instance_global_by_default = false
|
||||
var move_mode = 1
|
||||
load_enu_script "potato_code.nim", Bot, speed, global, color
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
name potato(length = 5, width = 2, color = red, label = "hi", friendly = true)
|
||||
|
||||
- wander:
|
||||
-wander:
|
||||
forward 5
|
||||
turn right
|
||||
|
||||
- run_away(fast=true, msg="Help!") string:
|
||||
- say_hello() string:
|
||||
-run_away(fast = true, msg = "Help!") string:
|
||||
-say_hello() string:
|
||||
echo "hello"
|
||||
"goodbye"
|
||||
echo say_hello()
|
||||
|
@ -15,7 +15,7 @@ name potato(length = 5, width = 2, color = red, label = "hi", friendly = true)
|
|||
forward 100
|
||||
|
||||
block:
|
||||
- test_me:
|
||||
-test_me:
|
||||
echo "hi"
|
||||
|
||||
test_me()
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#proc quit*(exit_code = 0) = discard
|
||||
include robot
|
||||
|
||||
proc script_echo*(msg: string) = discard
|
||||
proc script_echo*(msg: string) =
|
||||
discard
|
||||
|
||||
proc echo(x: varargs[string, `$`]) =
|
||||
script_echo x.join
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
|
||||
#quit()
|
||||
|
||||
|
||||
var
|
||||
speed* = 1.0
|
||||
scale* = 1.0
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
name Box1
|
||||
# import typetraits
|
||||
#echo "SELF: ", me.type.name
|
||||
def box(length=10, height=20):
|
||||
def box(length = 10, height = 20):
|
||||
height.times:
|
||||
4.times:
|
||||
forward length
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name box2
|
||||
|
||||
def box(length=10, height=20):
|
||||
def box(length = 10, height = 20):
|
||||
height.times:
|
||||
4.times:
|
||||
forward length
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
place = "script 1 body"
|
||||
proc in_script_1_body*() = echo "in script 1 body"
|
||||
proc in_script_1_body*() =
|
||||
echo "in script 1 body"
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
place = "script 2 body"
|
||||
proc in_script_2_body*() = echo "in script 2 body"
|
||||
proc in_script_2_body*() =
|
||||
echo "in script 2 body"
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
proc callback*(name: string) = discard
|
||||
proc quit*(code = 0) = discard
|
||||
proc callback*(name: string) =
|
||||
discard
|
||||
|
||||
proc quit*(code = 0) =
|
||||
discard
|
||||
|
||||
echo "running test1"
|
||||
for i in 0..10:
|
||||
for i in 0 .. 10:
|
||||
echo "test1 ", i
|
||||
callback("test1")
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
proc callback*(name: string) = discard
|
||||
proc quit*(code = 0) = discard
|
||||
proc callback*(name: string) =
|
||||
discard
|
||||
|
||||
proc quit*(code = 0) =
|
||||
discard
|
||||
|
||||
echo "running test2"
|
||||
for i in 0..10:
|
||||
for i in 0 .. 10:
|
||||
echo "test2 ", i
|
||||
callback("test2")
|
||||
|
||||
|
|
|
@ -11,19 +11,21 @@ let
|
|||
e1.load("scripts", "test1", read_file script_dir & "/test1.nim", vmlib)
|
||||
e2.load("scripts", "test2", read_file script_dir & "/test2.nim", vmlib)
|
||||
|
||||
e1.expose "callback", proc(a: VmArgs): bool =
|
||||
assert a.get_string(0) == "test1"
|
||||
result = true
|
||||
e1.expose "callback",
|
||||
proc(a: VmArgs): bool =
|
||||
assert a.get_string(0) == "test1"
|
||||
result = true
|
||||
|
||||
e2.expose "callback", proc(a: VmArgs): bool =
|
||||
assert a.get_string(0) == "test2"
|
||||
result = true
|
||||
e2.expose "callback",
|
||||
proc(a: VmArgs): bool =
|
||||
assert a.get_string(0) == "test2"
|
||||
result = true
|
||||
|
||||
for _ in 0..1:
|
||||
for _ in 0 .. 1:
|
||||
assert e1.run()
|
||||
assert e2.run()
|
||||
|
||||
for _ in 0..9:
|
||||
for _ in 0 .. 9:
|
||||
assert e1.resume()
|
||||
assert e2.resume()
|
||||
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import engine/engine
|
||||
import core
|
||||
import std / [os, strutils]
|
||||
import std/[os, strutils]
|
||||
|
||||
var output = ""
|
||||
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 = """
|
||||
script_2 =
|
||||
"""
|
||||
import script_1
|
||||
var place = "script 2 header"
|
||||
proc in_script_2_header*() = echo "in script 2 header"
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import std / [sugar, os, monotimes]
|
||||
import std/[sugar, os, monotimes]
|
||||
import controllers/script_controllers, core, models
|
||||
|
||||
import libs / [interpreters, eval]
|
||||
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"
|
||||
state.config.script_dir =
|
||||
current_source_path().parent_dir / "scripts" / "instancing"
|
||||
state.config.lib_dir = current_source_path().parent_dir / ".." / ".." / "vmlib"
|
||||
|
||||
let controller = ScriptController.init
|
||||
|
@ -23,4 +25,3 @@ let bot2 = create("bot_script_2")
|
|||
state.units.add bot2
|
||||
let bot3 = create("bot_script_3")
|
||||
state.units.add bot3
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import enu / [types, base_api]
|
||||
import enu/[types, base_api]
|
||||
import pkg/print
|
||||
|
||||
let unit = Unit()
|
||||
register_active(unit)
|
||||
unit.seed = 122341
|
||||
|
||||
let f: float = 1..100
|
||||
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
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import engine/engine, core
|
||||
import std / [strformat, sugar, os]
|
||||
import std/[strformat, sugar, os]
|
||||
|
||||
let
|
||||
vmlib = "vmlib"
|
||||
|
@ -12,10 +12,11 @@ var
|
|||
e1.load("scripts", "bot", read_file script_dir & "/bots.nim", vmlib)
|
||||
e1.expose("yield_script", a => true)
|
||||
|
||||
e1.expose "script_echo", proc(a: VmArgs): bool =
|
||||
let str = a.get_string(0)
|
||||
output &= str
|
||||
echo str
|
||||
e1.expose "script_echo",
|
||||
proc(a: VmArgs): bool =
|
||||
let str = a.get_string(0)
|
||||
output &= str
|
||||
echo str
|
||||
|
||||
assert e1.run()
|
||||
while e1.resume():
|
||||
|
|
|
@ -1,41 +1,70 @@
|
|||
include core
|
||||
|
||||
type
|
||||
Direction = object
|
||||
type Direction = object
|
||||
#const skip_3d = false
|
||||
proc forward(steps = 1.0) = discard
|
||||
proc back(steps = 1.0) = discard
|
||||
proc left(steps = 1.0): Direction {.discardable.} = discard
|
||||
proc right(steps = 1.0): Direction {.discardable.} = discard
|
||||
proc l(steps = 1.0): Direction {.discardable.} = discard
|
||||
proc r(steps = 1.0): Direction {.discardable.} = discard
|
||||
proc forward(steps = 1.0) =
|
||||
discard
|
||||
|
||||
proc back(steps = 1.0) =
|
||||
discard
|
||||
|
||||
proc left(steps = 1.0): Direction {.discardable.} =
|
||||
discard
|
||||
|
||||
proc right(steps = 1.0): Direction {.discardable.} =
|
||||
discard
|
||||
|
||||
proc l(steps = 1.0): Direction {.discardable.} =
|
||||
discard
|
||||
|
||||
proc r(steps = 1.0): Direction {.discardable.} =
|
||||
discard
|
||||
|
||||
when not declared(skip_3d):
|
||||
proc up(steps = 1.0): Direction {.discardable.} = discard
|
||||
proc u(steps = 1.0): Direction {.discardable.} = discard
|
||||
proc down(steps = 1.0): Direction {.discardable.} = discard
|
||||
proc d(steps = 1.0): Direction {.discardable.} = discard
|
||||
proc up(steps = 1.0): Direction {.discardable.} =
|
||||
discard
|
||||
|
||||
proc u(steps = 1.0): Direction {.discardable.} =
|
||||
discard
|
||||
|
||||
proc down(steps = 1.0): Direction {.discardable.} =
|
||||
discard
|
||||
|
||||
proc d(steps = 1.0): Direction {.discardable.} =
|
||||
discard
|
||||
|
||||
proc turn(direction: proc(steps = 1.0): Direction, degrees = 90.0) =
|
||||
var axis = if direction == r: RIGHT
|
||||
elif direction == right: RIGHT
|
||||
elif direction == l: LEFT
|
||||
elif direction == left: LEFT
|
||||
else: vec3()
|
||||
var axis =
|
||||
if direction == r:
|
||||
RIGHT
|
||||
elif direction == right:
|
||||
RIGHT
|
||||
elif direction == l:
|
||||
LEFT
|
||||
elif direction == left:
|
||||
LEFT
|
||||
else:
|
||||
vec3()
|
||||
|
||||
when not declared(skip_3d):
|
||||
if axis == vec3():
|
||||
axis = if direction == u: UP
|
||||
elif direction == up: UP
|
||||
elif direction == d: DOWN
|
||||
elif direction == down: DOWN
|
||||
else: vec3()
|
||||
axis =
|
||||
if direction == u:
|
||||
UP
|
||||
elif direction == up:
|
||||
UP
|
||||
elif direction == d:
|
||||
DOWN
|
||||
elif direction == down:
|
||||
DOWN
|
||||
else:
|
||||
vec3()
|
||||
|
||||
assert axis != vec3(), "Invalid direction"
|
||||
echo "turning ", axis
|
||||
|
||||
proc t(direction: proc(steps = 1.0): Direction, degrees = 90.0) = turn(direction, degrees)
|
||||
proc t(direction: proc(steps = 1.0): Direction, degrees = 90.0) =
|
||||
turn(direction, degrees)
|
||||
|
||||
turn left
|
||||
t l
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import engine/engine
|
||||
import core
|
||||
import std / [os, strutils]
|
||||
import std/[os, strutils]
|
||||
|
||||
var output = ""
|
||||
let
|
||||
|
@ -8,12 +8,14 @@ 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 = """
|
||||
user_classes2 =
|
||||
"""
|
||||
type
|
||||
Type1* = object
|
||||
name*: string
|
||||
|
@ -21,12 +23,16 @@ let
|
|||
Type2* = object
|
||||
name*: string
|
||||
""".dedent
|
||||
script1 = prefix & """
|
||||
script1 =
|
||||
prefix &
|
||||
"""
|
||||
import user_classes
|
||||
let a = Type1(name: "type1")
|
||||
log "a=" & a.repr
|
||||
""".dedent
|
||||
script2 = prefix & """
|
||||
script2 =
|
||||
prefix &
|
||||
"""
|
||||
import user_classes
|
||||
let a = Type1(name: "type1", size: 5)
|
||||
let b = Type2(name: "type2")
|
||||
|
@ -38,10 +44,11 @@ e.load(script_dir, script_dir & "/user_classes.nim", user_classes1, vmlib)
|
|||
assert not e.run()
|
||||
|
||||
e.load(script_dir, "script", script1, vmlib)
|
||||
e.expose "log", proc(a: VmArgs): bool =
|
||||
let msg = a.get_string(0)
|
||||
echo msg
|
||||
output &= msg & "\n"
|
||||
e.expose "log",
|
||||
proc(a: VmArgs): bool =
|
||||
let msg = a.get_string(0)
|
||||
echo msg
|
||||
output &= msg & "\n"
|
||||
assert not e.run()
|
||||
|
||||
e.load(script_dir, script_dir & "/user_classes.nim", user_classes2, vmlib)
|
||||
|
@ -50,7 +57,8 @@ assert not e.run()
|
|||
e.load(script_dir, "script", script2, vmlib)
|
||||
assert not e.run()
|
||||
|
||||
assert output == """
|
||||
assert output ==
|
||||
"""
|
||||
a=(name: "type1")
|
||||
a=(name: "type1", size: 5)
|
||||
b=(name: "type2")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import enu/types, enu/class_macros
|
||||
import macros except name
|
||||
import pkg / print
|
||||
import pkg/print
|
||||
import enu/base_api
|
||||
import enu/state_machine
|
||||
|
||||
|
@ -8,4 +8,3 @@ let instance_global_by_default = false
|
|||
var move_mode = 1
|
||||
|
||||
load_enu_script "zombie_code.nim", Bot, speed, global, color
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name zombie(unit: Unit)
|
||||
|
||||
- hello(height=22):
|
||||
-hello(height = 22):
|
||||
echo height.type
|
||||
|
||||
hello()
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
import globals, os
|
||||
|
||||
proc echo(args:varargs[string, `$`]) =
|
||||
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",
|
||||
)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#proc quit*(exit_code = 0) = discard
|
||||
include robot
|
||||
|
||||
proc script_echo*(msg: string) = discard
|
||||
proc script_echo*(msg: string) =
|
||||
discard
|
||||
|
||||
proc echo(x: varargs[string, `$`]) =
|
||||
script_echo x.join
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
proc vec_proc*(vec: Vector3): Vector3 = discard
|
||||
proc vec_proc*(vec: Vector3): Vector3 =
|
||||
discard
|
||||
|
||||
name = "box"
|
||||
echo "in script"
|
||||
def box(length=10, height=20):
|
||||
def box(length = 10, height = 20):
|
||||
echo "box", " ", "box 2"
|
||||
height.times:
|
||||
4.times:
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
proc callback*(name: string) = discard
|
||||
proc quit*(code = 0) = discard
|
||||
proc callback*(name: string) =
|
||||
discard
|
||||
|
||||
proc quit*(code = 0) =
|
||||
discard
|
||||
|
||||
echo "running test1"
|
||||
for i in 0..10:
|
||||
for i in 0 .. 10:
|
||||
echo "test1 ", i
|
||||
callback("test1")
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
proc callback*(name: string) = discard
|
||||
proc quit*(code = 0) = discard
|
||||
proc callback*(name: string) =
|
||||
discard
|
||||
|
||||
proc quit*(code = 0) =
|
||||
discard
|
||||
|
||||
echo "running test2"
|
||||
for i in 0..10:
|
||||
for i in 0 .. 10:
|
||||
echo "test2 ", i
|
||||
callback("test2")
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import godot
|
||||
import godotapi / [objects]
|
||||
import engine / [engine, contexts]
|
||||
import godotapi/[objects]
|
||||
import engine/[engine, contexts]
|
||||
import core, world/bot_node, globals
|
||||
import os
|
||||
include helper
|
||||
import compiler / [vm, vmdef, options, lineinfos, ast, astalgo, renderer]
|
||||
import compiler/[vm, vmdef, options, lineinfos, ast, astalgo, renderer]
|
||||
|
||||
config.script_dir &= "/instancing"
|
||||
let b = gdnew[BotNode]()
|
||||
|
@ -12,13 +12,14 @@ let b = gdnew[BotNode]()
|
|||
let script = nim_filename().parent_dir & "/scripts/instancing/box.nim"
|
||||
b.paused = false
|
||||
b.load_script(script)
|
||||
b.engine.expose "vec_proc", proc(a: VmArgs): bool =
|
||||
let v = a.get_vec3(0)
|
||||
b.engine.expose "vec_proc",
|
||||
proc(a: VmArgs): bool =
|
||||
let v = a.get_vec3(0)
|
||||
|
||||
dump v
|
||||
let n = vec3(10.0, 9.0, 8.0).to_node
|
||||
a.set_result(n)
|
||||
result = false
|
||||
dump v
|
||||
let n = vec3(10.0, 9.0, 8.0).to_node
|
||||
a.set_result(n)
|
||||
result = false
|
||||
assert b.engine.run()
|
||||
|
||||
while b.running:
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import std/unittest
|
||||
import pkg / [print, model_citizen]
|
||||
import pkg/[print, model_citizen]
|
||||
import godot except print
|
||||
import godotapi/node
|
||||
import world/node_factories, models / [states, bots, types, builds]
|
||||
import world/node_factories, models/[states, bots, types, builds]
|
||||
|
||||
when is_main_module:
|
||||
proc tests =
|
||||
proc tests() =
|
||||
var state = GameState.init(Node)
|
||||
state.nodes.game = gdnew[Node]()
|
||||
state.nodes.data = gdnew[Node]()
|
||||
|
@ -20,4 +20,5 @@ when is_main_module:
|
|||
u2.units += u4
|
||||
u2.units -= u4
|
||||
u3.flags += Targeted
|
||||
|
||||
tests()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env nim r --warnings:off --hints:off
|
||||
# bits of the build process that don't work from NimScript
|
||||
|
||||
import std / [os, cpuinfo, strformat, httpclient]
|
||||
import pkg / [compiler/nimeval, cligen]
|
||||
import std/[os, cpuinfo, strformat, httpclient]
|
||||
import pkg/[compiler/nimeval, cligen]
|
||||
import godotapigen
|
||||
|
||||
include "../installer/export_presets.cfg.nimf"
|
||||
|
@ -10,11 +10,14 @@ include "../installer/Info.plist.nimf"
|
|||
|
||||
const stdlib = find_nim_std_lib_compile_time()
|
||||
|
||||
proc core_count = echo count_processors()
|
||||
proc core_count() =
|
||||
echo count_processors()
|
||||
|
||||
proc find_test(test = ""): string =
|
||||
try: ".last_test".read_file
|
||||
except: quit("Nothing to do")
|
||||
try:
|
||||
".last_test".read_file
|
||||
except:
|
||||
quit("Nothing to do")
|
||||
|
||||
proc save_test_file(file: string) =
|
||||
write_file(".last_test", file)
|
||||
|
@ -33,10 +36,9 @@ proc copy_stdlib(destination: string) =
|
|||
copy_dir join_path(stdlib, path), join_path(destination, path)
|
||||
|
||||
for file in @["system.nim", "stdlib.nimble", "system" / "compilation.nim"]:
|
||||
copy_file join_path(stdlib, file),
|
||||
join_path(destination, file)
|
||||
copy_file join_path(stdlib, file), join_path(destination, file)
|
||||
|
||||
proc run_tests =
|
||||
proc run_tests() =
|
||||
discard
|
||||
|
||||
proc generate_api(directory = "godotapi", json = "api.json") =
|
||||
|
@ -46,7 +48,15 @@ proc write_export_presets(enu_version: string) =
|
|||
write_file("app/export_presets.cfg", generate_export_presets(enu_version))
|
||||
|
||||
proc write_info_plist(enu_version: string) =
|
||||
write_file("dist/Enu.app/Contents/Info.plist", generate_info_plist(enu_version))
|
||||
write_file(
|
||||
"dist/Enu.app/Contents/Info.plist", generate_info_plist(enu_version)
|
||||
)
|
||||
|
||||
dispatch_multi [generate_api], [core_count], [copy_stdlib], [write_export_presets],
|
||||
[write_info_plist], [save_test_file], [build_last_test], [run_tests]
|
||||
dispatch_multi [generate_api],
|
||||
[core_count],
|
||||
[copy_stdlib],
|
||||
[write_export_presets],
|
||||
[write_info_plist],
|
||||
[save_test_file],
|
||||
[build_last_test],
|
||||
[run_tests]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import system except echo
|
||||
import std / [strformat, math, importutils, strutils, options, sets]
|
||||
import std/[strformat, math, importutils, strutils, options, sets]
|
||||
import random as rnd except rand
|
||||
import types, state_machine, base_bridge, base_bridge_private
|
||||
|
||||
|
@ -11,14 +11,18 @@ proc echo*(args: varargs[string, `$`]) =
|
|||
proc quit*(code = 0, msg = "") =
|
||||
exit(code, msg)
|
||||
|
||||
proc `position=`*(self: Unit, position: Vector3) = self.position_set(position)
|
||||
proc `position=`*(self: Unit, unit: Unit) = self.position_set(unit.position)
|
||||
proc `position=`*(self: Unit, position: Vector3) =
|
||||
self.position_set(position)
|
||||
|
||||
proc `position=`*(self: Unit, unit: Unit) =
|
||||
self.position_set(unit.position)
|
||||
|
||||
proc link_dependency*(dep: Unit) =
|
||||
if not dep.is_nil:
|
||||
link_dependency_impl(dep)
|
||||
|
||||
proc link_dependency*(dep: not Unit) = discard
|
||||
proc link_dependency*(dep: not Unit) =
|
||||
discard
|
||||
|
||||
proc `seed=`*(self: Unit, seed: int) =
|
||||
private_access Unit
|
||||
|
@ -85,26 +89,59 @@ template down*(self: Unit, steps = 1.0) =
|
|||
mixin wait, begin_move
|
||||
wait self.begin_move(DOWN, steps, move_mode)
|
||||
|
||||
template l*(self: Unit, steps = 1.0) = self.left(steps)
|
||||
template r*(self: Unit, steps = 1.0) = self.right(steps)
|
||||
template u*(self: Unit, steps = 1.0) = self.up(steps)
|
||||
template d*(self: Unit, steps = 1.0) = self.down(steps)
|
||||
template f*(self: Unit, steps = 1.0) = self.forward(steps)
|
||||
template b*(self: Unit, steps = 1.0) = self.back(steps)
|
||||
template l*(self: Unit, steps = 1.0) =
|
||||
self.left(steps)
|
||||
|
||||
template forward*(steps = 1.0) = enu_target.forward(steps)
|
||||
template back*(steps = 1.0) = enu_target.back(steps)
|
||||
template left*(steps = 1.0) = enu_target.left(steps)
|
||||
template right*(steps = 1.0) = enu_target.right(steps)
|
||||
template up*(steps = 1.0) = enu_target.up(steps)
|
||||
template down*(steps = 1.0) = enu_target.down(steps)
|
||||
template r*(self: Unit, steps = 1.0) =
|
||||
self.right(steps)
|
||||
|
||||
template l*(steps = 1.0) = enu_target.left(steps)
|
||||
template r*(steps = 1.0) = enu_target.right(steps)
|
||||
template u*(steps = 1.0) = enu_target.up(steps)
|
||||
template d*(steps = 1.0) = enu_target.down(steps)
|
||||
template f*(steps = 1.0) = enu_target.forward(steps)
|
||||
template b*(steps = 1.0) = enu_target.back(steps)
|
||||
template u*(self: Unit, steps = 1.0) =
|
||||
self.up(steps)
|
||||
|
||||
template d*(self: Unit, steps = 1.0) =
|
||||
self.down(steps)
|
||||
|
||||
template f*(self: Unit, steps = 1.0) =
|
||||
self.forward(steps)
|
||||
|
||||
template b*(self: Unit, steps = 1.0) =
|
||||
self.back(steps)
|
||||
|
||||
template forward*(steps = 1.0) =
|
||||
enu_target.forward(steps)
|
||||
|
||||
template back*(steps = 1.0) =
|
||||
enu_target.back(steps)
|
||||
|
||||
template left*(steps = 1.0) =
|
||||
enu_target.left(steps)
|
||||
|
||||
template right*(steps = 1.0) =
|
||||
enu_target.right(steps)
|
||||
|
||||
template up*(steps = 1.0) =
|
||||
enu_target.up(steps)
|
||||
|
||||
template down*(steps = 1.0) =
|
||||
enu_target.down(steps)
|
||||
|
||||
template l*(steps = 1.0) =
|
||||
enu_target.left(steps)
|
||||
|
||||
template r*(steps = 1.0) =
|
||||
enu_target.right(steps)
|
||||
|
||||
template u*(steps = 1.0) =
|
||||
enu_target.up(steps)
|
||||
|
||||
template d*(steps = 1.0) =
|
||||
enu_target.down(steps)
|
||||
|
||||
template f*(steps = 1.0) =
|
||||
enu_target.forward(steps)
|
||||
|
||||
template b*(steps = 1.0) =
|
||||
enu_target.back(steps)
|
||||
|
||||
template see*(target: Unit, less_than = 100.0): bool =
|
||||
enu_target.see(target, less_than)
|
||||
|
@ -128,7 +165,8 @@ template forward*(self: Unit, value: PositionOffset) =
|
|||
let steps = self.local_position.z - value.position.z + value.offset
|
||||
wait self.begin_move(FORWARD, steps, move_mode)
|
||||
|
||||
template forward*(offset: PositionOffset) = enu_target.forward(offset)
|
||||
template forward*(offset: PositionOffset) =
|
||||
enu_target.forward(offset)
|
||||
|
||||
proc back*(self: Unit, value: PositionOffset, move_mode: int) =
|
||||
let steps = self.position.z - value.position.z - value.offset
|
||||
|
@ -139,7 +177,8 @@ template back*(self: Unit, value: PositionOffset) =
|
|||
let steps = self.local_position.z - value.position.z - value.offset
|
||||
wait self.begin_move(FORWARD, steps, move_mode)
|
||||
|
||||
template back*(offset: PositionOffset) = enu_target.back(offset)
|
||||
template back*(offset: PositionOffset) =
|
||||
enu_target.back(offset)
|
||||
|
||||
proc left*(self: Unit, value: PositionOffset, move_mode: int) =
|
||||
let steps = self.local_position.x - value.position.x + value.offset
|
||||
|
@ -150,7 +189,8 @@ template left*(self: Unit, value: PositionOffset) =
|
|||
let steps = self.local_position.x - value.position.x + value.offset
|
||||
wait self.begin_move(LEFT, steps, move_mode)
|
||||
|
||||
template left*(offset: PositionOffset) = enu_target.left(offset)
|
||||
template left*(offset: PositionOffset) =
|
||||
enu_target.left(offset)
|
||||
|
||||
proc right*(self: Unit, value: PositionOffset, move_mode: int) =
|
||||
let steps = self.local_position.x - value.position.x - value.offset
|
||||
|
@ -161,7 +201,8 @@ template right*(self: Unit, value: PositionOffset) =
|
|||
let steps = self.local_position.x - value.position.x - value.offset
|
||||
wait self.begin_move(LEFT, steps, move_mode)
|
||||
|
||||
template right*(offset: PositionOffset) = enu_target.right(offset)
|
||||
template right*(offset: PositionOffset) =
|
||||
enu_target.right(offset)
|
||||
|
||||
proc down*(self: Unit, value: PositionOffset, move_mode: int) =
|
||||
let steps = self.local_position.y - value.position.y + value.offset
|
||||
|
@ -172,7 +213,8 @@ template down*(self: Unit, value: PositionOffset) =
|
|||
let steps = self.local_position.y - value.position.y + value.offset
|
||||
wait self.begin_move(DOWN, steps, move_mode)
|
||||
|
||||
template down*(offset: PositionOffset) = enu_target.down(offset)
|
||||
template down*(offset: PositionOffset) =
|
||||
enu_target.down(offset)
|
||||
|
||||
proc up*(self: Unit, value: PositionOffset, move_mode: int) =
|
||||
let steps = self.local_position.y - value.position.y - value.offset
|
||||
|
@ -183,7 +225,8 @@ template up*(self: Unit, value: PositionOffset) =
|
|||
let steps = self.local_position.y - value.position.y - value.offset
|
||||
wait self.begin_move(DOWN, steps, move_mode)
|
||||
|
||||
template up*(offset: PositionOffset) = enu_target.up(offset)
|
||||
template up*(offset: PositionOffset) =
|
||||
enu_target.up(offset)
|
||||
|
||||
type NegativeNode = ref object
|
||||
node: Unit
|
||||
|
@ -203,7 +246,8 @@ proc angle_to*(self: Unit, enu_target: Unit): float =
|
|||
self.angle_to(enu_target.position)
|
||||
|
||||
proc vec3(direction: Directions): Vector3 =
|
||||
result = case direction:
|
||||
result =
|
||||
case direction
|
||||
of Directions.forward, Directions.f: FORWARD
|
||||
of Directions.back, Directions.b: BACK
|
||||
of Directions.left, Directions.l: LEFT
|
||||
|
@ -340,8 +384,11 @@ proc near*(node: Unit | Vector3, less_than = 5.0): bool =
|
|||
proc far*(node: Unit | Vector3, greater_than = 100.0): bool =
|
||||
result = node.distance > greater_than
|
||||
|
||||
proc height*(self: Vector3): float = self.y
|
||||
proc height*(self: Unit): float = self.position.y
|
||||
proc height*(self: Vector3): float =
|
||||
self.y
|
||||
|
||||
proc height*(self: Unit): float =
|
||||
self.position.y
|
||||
|
||||
template go_home*() =
|
||||
enu_target.go_home
|
||||
|
@ -354,14 +401,14 @@ proc rng(): var Rand =
|
|||
var unit = active_unit()
|
||||
if unit.seed == 0:
|
||||
randomize()
|
||||
unit.seed = rnd. rand(int.high)
|
||||
unit.seed = rnd.rand(int.high)
|
||||
unit.rng = init_rand(unit.seed)
|
||||
unit.rng
|
||||
|
||||
proc rand*[T: int | float](range: Slice[T]): T =
|
||||
rnd.rand rng(),
|
||||
if range.a > range.b:
|
||||
range.b..range.a
|
||||
range.b .. range.a
|
||||
else:
|
||||
range
|
||||
|
||||
|
@ -378,10 +425,11 @@ converter float_slice_to_float*(range: Slice[float]): float =
|
|||
rand(range)
|
||||
|
||||
proc fuzzed*(self, range: float): float =
|
||||
result = if range > 0:
|
||||
self + (rand(0.0..range) - (range / 2.0))
|
||||
else:
|
||||
self
|
||||
result =
|
||||
if range > 0:
|
||||
self + (rand(0.0 .. range) - (range / 2.0))
|
||||
else:
|
||||
self
|
||||
|
||||
proc fuzzed*(self, range: Vector3): Vector3 =
|
||||
vec3(self.x.fuzzed(range.x), self.y.fuzzed(range.y), self.z.fuzzed(range.z))
|
||||
|
@ -393,23 +441,25 @@ proc fuzzed*(self: Vector3, x, y, z: float): Vector3 =
|
|||
self.fuzzed(vec3(x, y, z))
|
||||
|
||||
template times*(count: int, body: untyped): untyped =
|
||||
for x in 0..<count:
|
||||
for x in 0 ..< count:
|
||||
let first {.inject.} = (x == 0)
|
||||
let last {.inject.} = (x == count - 1)
|
||||
body
|
||||
|
||||
template times*(count: int, name: untyped, body: untyped): untyped =
|
||||
for name {.inject.} in 0..<count:
|
||||
for name {.inject.} in 0 ..< count:
|
||||
let first {.inject.} = (name == 0)
|
||||
let last {.inject.} = (name == count - 1)
|
||||
body
|
||||
|
||||
template x*(count: int, body: untyped): untyped = times(count, body)
|
||||
template x*(count: int, body: untyped): untyped =
|
||||
times(count, body)
|
||||
|
||||
macro dump*(x: typed): untyped =
|
||||
let s = x.toStrLit
|
||||
let r = quote do:
|
||||
echo(`s` & " = " & $`x`)
|
||||
let r =
|
||||
quote:
|
||||
echo(`s` & " = " & $`x`)
|
||||
return r
|
||||
|
||||
template cycle*[T](args: varargs[T]): T =
|
||||
|
@ -425,11 +475,11 @@ template cycle*[T](args: varargs[T]): T =
|
|||
args[positions[key]]
|
||||
|
||||
proc random*[T](args: varargs[T]): T =
|
||||
let i = rnd.rand(rng(), 0..(args.len - 1))
|
||||
let i = rnd.rand(rng(), 0 .. (args.len - 1))
|
||||
args[i]
|
||||
|
||||
proc contains*(max, chance: int): bool =
|
||||
var r = rnd.rand(rng(), 1..max)
|
||||
var r = rnd.rand(rng(), 1 .. max)
|
||||
result = r <= chance
|
||||
|
||||
template forever*(body) =
|
||||
|
@ -456,8 +506,11 @@ proc go*(unit: Unit) =
|
|||
active_unit().forward((position - active_unit().position).length, 2)
|
||||
active_unit().down(active_unit().height - height, 2)
|
||||
|
||||
proc even*(self: int): bool = self mod 2 == 0
|
||||
proc odd*(self: int): bool = not self.even
|
||||
proc even*(self: int): bool =
|
||||
self mod 2 == 0
|
||||
|
||||
proc odd*(self: int): bool =
|
||||
not self.even
|
||||
|
||||
template `\`*(s: string): string =
|
||||
var f = fmt(s)
|
||||
|
@ -466,18 +519,35 @@ template `\`*(s: string): string =
|
|||
f.remove_suffix("\n\n")
|
||||
f
|
||||
|
||||
template `?`*(self: ref): bool = not self.is_nil
|
||||
template `?`*(self: object): bool = self != self.type.default
|
||||
template `?`*[T](option: Option[T]): bool = option.is_some
|
||||
template `?`*(self: SomeNumber): bool = self != 0
|
||||
template `?`*(self: string): bool = self != ""
|
||||
template `?`*[T](self: open_array[T]): bool = self.len > 0
|
||||
template `?`*[T](self: set[T]): bool = self.card > 0
|
||||
template `?`*[T](self: HashSet[T]): bool = self.card > 0
|
||||
template `?`*(self: ref): bool =
|
||||
not self.is_nil
|
||||
|
||||
template `?`*(self: object): bool =
|
||||
self != self.type.default
|
||||
|
||||
template `?`*[T](option: Option[T]): bool =
|
||||
option.is_some
|
||||
|
||||
template `?`*(self: SomeNumber): bool =
|
||||
self != 0
|
||||
|
||||
template `?`*(self: string): bool =
|
||||
self != ""
|
||||
|
||||
template `?`*[T](self: open_array[T]): bool =
|
||||
self.len > 0
|
||||
|
||||
template `?`*[T](self: set[T]): bool =
|
||||
self.card > 0
|
||||
|
||||
template `?`*[T](self: HashSet[T]): bool =
|
||||
self.card > 0
|
||||
|
||||
proc `or`*[T: not bool](a, b: T): T =
|
||||
if ?a: result = a
|
||||
else: result = b
|
||||
if ?a:
|
||||
result = a
|
||||
else:
|
||||
result = b
|
||||
|
||||
template reset*(clear = false) =
|
||||
enu_target.reset(clear)
|
||||
|
|
|
@ -2,13 +2,20 @@ import types, vm_bridge_utils
|
|||
|
||||
# NOTE: overridden by ScriptController. Only for tests.
|
||||
var current_active_unit: Unit
|
||||
proc register_active_impl(self: Unit) = current_active_unit = self
|
||||
proc active_unit_impl(): Unit = current_active_unit
|
||||
proc register_active_impl(self: Unit) =
|
||||
current_active_unit = self
|
||||
|
||||
proc register_active*(self: Unit) = register_active_impl(self)
|
||||
proc active_unit*(): Unit = active_unit_impl()
|
||||
proc active_unit_impl(): Unit =
|
||||
current_active_unit
|
||||
|
||||
proc sees_impl*(self: Unit, target: Unit, less_than = 100.0): bool = discard
|
||||
proc register_active*(self: Unit) =
|
||||
register_active_impl(self)
|
||||
|
||||
proc active_unit*(): Unit =
|
||||
active_unit_impl()
|
||||
|
||||
proc sees_impl*(self: Unit, target: Unit, less_than = 100.0): bool =
|
||||
discard
|
||||
|
||||
bridged_to_host:
|
||||
proc write_stack_trace*()
|
||||
|
@ -40,8 +47,8 @@ bridged_to_host:
|
|||
proc press_action*(name: string)
|
||||
proc load_level*(level: string, world = "")
|
||||
proc reset_level*()
|
||||
proc level_name*: string
|
||||
proc world_name*: string
|
||||
proc level_name*(): string
|
||||
proc world_name*(): string
|
||||
|
||||
# TODO: These should be in base_bridge_private, but are currently needed outside of base_api.
|
||||
proc echo_console*(msg: string)
|
||||
|
|
|
@ -7,15 +7,34 @@ bridged_to_host:
|
|||
proc action_running*(self: Unit): bool
|
||||
proc `action_running=`*(self: Unit, value: bool)
|
||||
proc yield_script*(self: Unit)
|
||||
proc begin_move*(self: Unit, direction: Vector3, steps: float, move_mode: int)
|
||||
proc begin_move*(
|
||||
self: Unit, direction: Vector3, steps: float, move_mode: int
|
||||
)
|
||||
|
||||
proc begin_turn*(
|
||||
self: Unit, axis: Vector3, steps: float, lean: bool, move_mode: int)
|
||||
self: Unit, axis: Vector3, steps: float, lean: bool, move_mode: int
|
||||
)
|
||||
|
||||
proc sleep_impl*(seconds = 1.0)
|
||||
proc position_set*(self: Unit, position: Vector3)
|
||||
|
||||
proc new_markdown_sign*(self: Unit, instance: Sign, message: string,
|
||||
more = "", width = 1.0, height = 1.0, size = 32, billboard = false)
|
||||
proc new_markdown_sign*(
|
||||
self: Unit,
|
||||
instance: Sign,
|
||||
message: string,
|
||||
more = "",
|
||||
width = 1.0,
|
||||
height = 1.0,
|
||||
size = 32,
|
||||
billboard = false,
|
||||
)
|
||||
|
||||
proc update_markdown_sign*(self: Sign, message: string,
|
||||
more = "", width = 1.0, height = 1.0, size = 32, billboard = false)
|
||||
proc update_markdown_sign*(
|
||||
self: Sign,
|
||||
message: string,
|
||||
more = "",
|
||||
width = 1.0,
|
||||
height = 1.0,
|
||||
size = 32,
|
||||
billboard = false,
|
||||
)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import std / [strutils, math]
|
||||
import std/[strutils, math]
|
||||
import types, base_api, vm_bridge_utils
|
||||
|
||||
bridged_to_host:
|
||||
proc all_bots(): seq[Bot]
|
||||
proc play*(self: Bot, animation_name: string)
|
||||
|
||||
proc all*(_: type Bot): seq[Bot] = all_bots()
|
||||
proc all*(_: type Bot): seq[Bot] =
|
||||
all_bots()
|
||||
|
||||
proc walk*(self: Bot) =
|
||||
self.speed = 1.0
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import std / [strutils]
|
||||
import std/[strutils]
|
||||
import types, base_api, vm_bridge_utils
|
||||
|
||||
bridged_to_host:
|
||||
|
@ -14,7 +14,8 @@ bridged_to_host:
|
|||
proc `draw_position=`*(self: Build, unit: Unit) =
|
||||
self.draw_position = unit.position
|
||||
|
||||
proc all*(_: type Build): seq[Build] = all_builds()
|
||||
proc all*(_: type Build): seq[Build] =
|
||||
all_builds()
|
||||
|
||||
proc go_home*(self: Build) =
|
||||
self.rotation = 0
|
||||
|
@ -25,8 +26,7 @@ proc go_home*(self: Build) =
|
|||
self.down self.position.y - self.start_position.y, 2
|
||||
|
||||
proc fill_square*(self: Build, length = 1) =
|
||||
for l in 0..length:
|
||||
for i in 0..3:
|
||||
for l in 0 .. length:
|
||||
for i in 0 .. 3:
|
||||
self.forward(length - l, 2)
|
||||
self.right(1, 2)
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import std / [macros, strutils, sequtils, base64]
|
||||
import std/[macros, strutils, sequtils, base64]
|
||||
import types
|
||||
import base_api, macro_helpers
|
||||
|
||||
const private_props = ["lock"]
|
||||
const public_props = ["position", "start_position", "speed", "scale", "glow",
|
||||
"global", "seed", "color", "height", "show", "sign"]
|
||||
const public_props = [
|
||||
"position", "start_position", "speed", "scale", "glow", "global", "seed",
|
||||
"color", "height", "show", "sign"
|
||||
]
|
||||
|
||||
proc params_to_assignments(nodes: seq[NimNode]): NimNode =
|
||||
result = new_stmt_list()
|
||||
|
@ -28,8 +30,9 @@ proc params_to_ident_defs(nodes: seq[NimNode]): seq[NimNode] =
|
|||
elif node.kind == nnkExprColonExpr:
|
||||
result.add nnkIdentDefs.new_tree(node[0], node[1], new_empty_node())
|
||||
else:
|
||||
error("expected `my_param = 1`, `my_param: int` kind: " & $node.kind,
|
||||
node)
|
||||
error(
|
||||
"expected `my_param = 1`, `my_param: int` kind: " & $node.kind, node
|
||||
)
|
||||
|
||||
proc params_to_properties(nodes: seq[NimNode]): NimNode =
|
||||
result = new_nim_node(kind = nnkRecList)
|
||||
|
@ -39,14 +42,15 @@ proc params_to_properties(nodes: seq[NimNode]): NimNode =
|
|||
let prop = node[0]
|
||||
if prop.str_val notin ["global", "speed", "color"]:
|
||||
if node.kind == nnkExprEqExpr:
|
||||
result.add nnkIdentDefs.new_tree(node[0], new_call(ident"type",
|
||||
node[1]), empty)
|
||||
|
||||
result.add nnkIdentDefs.new_tree(
|
||||
node[0], new_call(ident"type", node[1]), empty
|
||||
)
|
||||
elif node.kind == nnkExprColonExpr:
|
||||
result.add nnkIdentDefs.new_tree(node[0], node[1], empty)
|
||||
else:
|
||||
error("expected `my_param = 1`, `my_param: int` kind: " & $node.kind,
|
||||
node)
|
||||
error(
|
||||
"expected `my_param = 1`, `my_param: int` kind: " & $node.kind, node
|
||||
)
|
||||
|
||||
proc params_to_accessors(type_name: NimNode, nodes: seq[NimNode]): NimNode =
|
||||
result = new_stmt_list()
|
||||
|
@ -56,10 +60,11 @@ proc params_to_accessors(type_name: NimNode, nodes: seq[NimNode]): NimNode =
|
|||
let getter = node[0]
|
||||
if getter.str_val notin ["global", "speed", "color"]:
|
||||
let setter = ident(getter.str_val & "=")
|
||||
let typ = if node.kind == nnkExprEqExpr:
|
||||
new_call(ident"type", node[1])
|
||||
else:
|
||||
node[1]
|
||||
let typ =
|
||||
if node.kind == nnkExprEqExpr:
|
||||
new_call(ident"type", node[1])
|
||||
else:
|
||||
node[1]
|
||||
|
||||
result.add quote do:
|
||||
proc `getter`*(self: `type_name`): `typ` =
|
||||
|
@ -73,15 +78,16 @@ proc params_to_accessors(type_name: NimNode, nodes: seq[NimNode]): NimNode =
|
|||
self.`getter` = value
|
||||
self.wake
|
||||
|
||||
proc build_ctors(name_str: string, type_name: NimNode, params: seq[NimNode]):
|
||||
NimNode =
|
||||
|
||||
var ctor_body = quote do:
|
||||
assert not instance.is_nil
|
||||
link_dependency(instance)
|
||||
result = `type_name`()
|
||||
result.seed = active_unit().seed
|
||||
new_instance(instance, result)
|
||||
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)
|
||||
|
||||
for param in params:
|
||||
let prop = param[0]
|
||||
|
@ -96,8 +102,10 @@ proc build_ctors(name_str: string, type_name: NimNode, params: seq[NimNode]):
|
|||
|
||||
var global = "global".ident
|
||||
if "global" notin var_names:
|
||||
params &= new_ident_defs(global, new_empty_node(),
|
||||
ident"instance_global_by_default")
|
||||
params &=
|
||||
new_ident_defs(
|
||||
global, new_empty_node(), ident"instance_global_by_default"
|
||||
)
|
||||
|
||||
ctor_body.add quote do:
|
||||
result.global = `global`
|
||||
|
@ -121,24 +129,29 @@ proc build_ctors(name_str: string, type_name: NimNode, params: seq[NimNode]):
|
|||
|
||||
# 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): tuple[name: string,
|
||||
params: seq[NimNode]] =
|
||||
|
||||
result = if name_node.kind == nnkIdent:
|
||||
proc extract_class_info(
|
||||
name_node: NimNode
|
||||
): tuple[name: string, params: seq[NimNode]] =
|
||||
result =
|
||||
if name_node.kind == nnkIdent:
|
||||
(name_node.str_val, @[])
|
||||
elif name_node.kind in [nnkCall, nnkCommand, nnkObjConstr]:
|
||||
name_node[0].expect_kind nnkIdent
|
||||
(name_node[0].str_val, name_node[1..^1])
|
||||
(name_node[0].str_val, name_node[1 ..^ 1])
|
||||
else:
|
||||
error("expected `name my_name` or `name my_name(my_param1 = 1, " &
|
||||
"my_param2 = 2, ...)`", name_node)
|
||||
error(
|
||||
"expected `name my_name` or `name my_name(my_param1 = 1, " &
|
||||
"my_param2 = 2, ...)`",
|
||||
name_node,
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
|
@ -153,8 +166,9 @@ proc build_class(name_node: NimNode, base_type: NimNode): NimNode =
|
|||
result = new_stmt_list()
|
||||
|
||||
let name_str = name
|
||||
var type_def = quote do:
|
||||
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)
|
||||
|
@ -176,18 +190,20 @@ proc pop_name_node(ast: NimNode): tuple[start: NimNode, name_node: NimNode] =
|
|||
for i, node in ast:
|
||||
if node.kind in [nnkCommand, nnkCall]:
|
||||
if node.len == 2 and node[1].kind in [nnkIdent, nnkCall, nnkObjConstr] and
|
||||
node[0].eq_ident(ident_name):
|
||||
|
||||
node[0].eq_ident(ident_name):
|
||||
result.name_node = node[1]
|
||||
ast.del(i)
|
||||
break
|
||||
result.start.add node
|
||||
for i, node in result.start:
|
||||
ast.del(i)
|
||||
|
||||
proc visit_tree(parent: NimNode, convert: open_array[string], receiver: string,
|
||||
alias: ptr seq[NimNode]) =
|
||||
ast.del(i)
|
||||
|
||||
proc visit_tree(
|
||||
parent: NimNode,
|
||||
convert: open_array[string],
|
||||
receiver: string,
|
||||
alias: ptr seq[NimNode],
|
||||
) =
|
||||
for i, node in parent:
|
||||
if node.kind in [nnkProcDef, nnkBlockStmt, nnkIfExpr, nnkIfStmt]:
|
||||
# The alias list should only live as long as a scope. We need to make a
|
||||
|
@ -203,9 +219,8 @@ proc visit_tree(parent: NimNode, convert: open_array[string], receiver: string,
|
|||
elif i == 2 and node notin alias[]:
|
||||
parent[i] = new_dot_expr(ident receiver, node)
|
||||
elif $node in convert and node notin alias[] and
|
||||
parent.kind != nnk_expr_eq_expr and not
|
||||
(parent.kind == nnk_dot_expr and i == 1):
|
||||
|
||||
parent.kind != nnk_expr_eq_expr and
|
||||
not (parent.kind == nnk_dot_expr and i == 1):
|
||||
parent[i] = new_dot_expr(ident receiver, node)
|
||||
visit_tree(node, convert, receiver, alias)
|
||||
|
||||
|
@ -213,9 +228,9 @@ proc visit_tree(parent: NimNode, convert: open_array[string], receiver: string,
|
|||
# Anything for `enu_target` must work for all units. `me` can be class specific.
|
||||
# This tries to take aliasing into account. If a variable called `speed` is
|
||||
# created, anywhere it's in scope won't get `me` prefixed.
|
||||
proc auto_insert_receiver(ast: NimNode, class_specific_props:
|
||||
open_array[string]): NimNode =
|
||||
|
||||
proc auto_insert_receiver(
|
||||
ast: NimNode, class_specific_props: open_array[string]
|
||||
): NimNode =
|
||||
var alias: seq[NimNode] = @[]
|
||||
visit_tree(ast, class_specific_props, "me", addr alias)
|
||||
visit_tree(ast, private_props, "me", addr alias)
|
||||
|
@ -225,24 +240,24 @@ proc auto_insert_receiver(ast: NimNode, class_specific_props:
|
|||
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:
|
||||
if parent.kind == nnkStmtList and node.kind == nnkPrefix and
|
||||
node[0] == ident"-":
|
||||
|
||||
node[0] == ident"-":
|
||||
if node[1].kind in [nnkIdent, nnkCall]:
|
||||
let new_proc = build_proc(node[1], transform_commands node[2])
|
||||
parent[i] = new_proc
|
||||
elif node[1].kind == nnkCommand:
|
||||
let new_proc = build_proc(node[1][0], transform_commands node[2],
|
||||
node[1][1])
|
||||
let new_proc =
|
||||
build_proc(node[1][0], transform_commands node[2], node[1][1])
|
||||
|
||||
parent[i] = new_proc
|
||||
else:
|
||||
|
@ -251,9 +266,12 @@ proc transform_commands(parent: NimNode): NimNode =
|
|||
parent[i] = transform_commands(node)
|
||||
parent
|
||||
|
||||
macro load_enu_script*(base64_code: string, file_name: string, base_type: untyped,
|
||||
class_specific_props: varargs[untyped]): untyped =
|
||||
|
||||
macro load_enu_script*(
|
||||
base64_code: string,
|
||||
file_name: string,
|
||||
base_type: untyped,
|
||||
class_specific_props: varargs[untyped],
|
||||
): untyped =
|
||||
var class_specific_props = class_specific_props.map_it($it)
|
||||
let file_name = file_name.str_val
|
||||
# `static_read` has been disabled for security, so we can't read the code
|
||||
|
@ -280,7 +298,6 @@ macro load_enu_script*(base64_code: string, file_name: string, base_type: untype
|
|||
let assignments = params_to_assignments(params)
|
||||
inner.add quote do:
|
||||
`assignments`
|
||||
|
||||
else:
|
||||
ast = ast.auto_insert_receiver(class_specific_props)
|
||||
result.add quote do:
|
||||
|
@ -302,4 +319,5 @@ macro load_enu_script*(base64_code: string, file_name: string, base_type: untype
|
|||
# If a new instance doesn't ever yield the interpreter can crash. Unsure
|
||||
# why, but probably fixable. Sleep before exit as a workaround.
|
||||
sleep 0
|
||||
|
||||
run_script(me, false)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
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:
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import std / [macros, strformat, strutils]
|
||||
import std/[macros, strformat, strutils]
|
||||
|
||||
proc parse_sig*(sig: NimNode, return_type = new_empty_node()): (string, seq[NimNode], NimNode) =
|
||||
proc parse_sig*(
|
||||
sig: NimNode, return_type = new_empty_node()
|
||||
): (string, seq[NimNode], NimNode) =
|
||||
var
|
||||
name = ""
|
||||
args = @[return_type]
|
||||
|
@ -9,7 +11,7 @@ proc parse_sig*(sig: NimNode, return_type = new_empty_node()): (string, seq[NimN
|
|||
name = $sig
|
||||
else:
|
||||
name = $sig[0]
|
||||
for i, arg in sig[1..^1]:
|
||||
for i, arg in sig[1 ..^ 1]:
|
||||
case arg.kind
|
||||
of nnkExprColonExpr:
|
||||
args.add new_ident_defs(arg[0], arg[1])
|
||||
|
|
|
@ -4,23 +4,44 @@ let player* = PlayerType()
|
|||
register_active(player)
|
||||
|
||||
bridged_to_host:
|
||||
proc tool*(self: PlayerType): Tools = discard
|
||||
proc `tool=`*(self: PlayerType, value: Tools) = discard
|
||||
proc tool*(self: PlayerType): Tools =
|
||||
discard
|
||||
|
||||
proc playing*(self: PlayerType): bool = discard
|
||||
proc `playing=`*(self: PlayerType, value: bool) = discard
|
||||
proc `tool=`*(self: PlayerType, value: Tools) =
|
||||
discard
|
||||
|
||||
proc flying*(self: PlayerType): bool = discard
|
||||
proc `flying=`*(self: PlayerType, value: bool) = discard
|
||||
proc playing*(self: PlayerType): bool =
|
||||
discard
|
||||
|
||||
proc running*(self: PlayerType): bool = discard
|
||||
proc `running=`*(self: PlayerType, value: bool) = discard
|
||||
proc `playing=`*(self: PlayerType, value: bool) =
|
||||
discard
|
||||
|
||||
proc god*(self: PlayerType): bool = discard
|
||||
proc `god=`*(self: PlayerType, value: bool) = discard
|
||||
proc flying*(self: PlayerType): bool =
|
||||
discard
|
||||
|
||||
proc coding*(self: PlayerType): Unit = discard
|
||||
proc `coding=`*(self: PlayerType, value: Unit) = discard
|
||||
proc `flying=`*(self: PlayerType, value: bool) =
|
||||
discard
|
||||
|
||||
proc open_sign*(self: PlayerType): Sign = discard
|
||||
proc `open_sign=`*(self: PlayerType, value: Sign) = discard
|
||||
proc running*(self: PlayerType): bool =
|
||||
discard
|
||||
|
||||
proc `running=`*(self: PlayerType, value: bool) =
|
||||
discard
|
||||
|
||||
proc god*(self: PlayerType): bool =
|
||||
discard
|
||||
|
||||
proc `god=`*(self: PlayerType, value: bool) =
|
||||
discard
|
||||
|
||||
proc coding*(self: PlayerType): Unit =
|
||||
discard
|
||||
|
||||
proc `coding=`*(self: PlayerType, value: Unit) =
|
||||
discard
|
||||
|
||||
proc open_sign*(self: PlayerType): Sign =
|
||||
discard
|
||||
|
||||
proc `open_sign=`*(self: PlayerType, value: Sign) =
|
||||
discard
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
type
|
||||
ErrorData* = tuple[id: int, msg: string]
|
||||
type ErrorData* = tuple[id: int, msg: string]
|
||||
|
||||
# :( there must be a way to do this with a table or an array
|
||||
proc to_exception*(self: ErrorData): ref Exception =
|
||||
case self.id:
|
||||
of 0: nil
|
||||
of 1: (ref NilAccessDefect)(msg: self.msg)
|
||||
of 2: (ref DivByZeroDefect)(msg: self.msg)
|
||||
of 3: (ref AssertionDefect)(msg: self.msg)
|
||||
of 4: (ref KeyError)(msg: self.msg)
|
||||
else: raise_assert "Unknown error id " & $self.id
|
||||
case self.id
|
||||
of 0:
|
||||
nil
|
||||
of 1:
|
||||
(ref NilAccessDefect)(msg: self.msg)
|
||||
of 2:
|
||||
(ref DivByZeroDefect)(msg: self.msg)
|
||||
of 3:
|
||||
(ref AssertionDefect)(msg: self.msg)
|
||||
of 4:
|
||||
(ref KeyError)(msg: self.msg)
|
||||
else:
|
||||
raise_assert "Unknown error id " & $self.id
|
||||
|
||||
proc from_exception*(self: ref Exception): ErrorData =
|
||||
if self == nil: (0, "")
|
||||
elif self of ref NilAccessDefect: (1, self.msg)
|
||||
elif self of ref DivByZeroDefect: (2, self.msg)
|
||||
elif self of ref AssertionDefect: (3, self.msg)
|
||||
elif self of ref KeyError: (4, self.msg)
|
||||
else: raise_assert "Unknown error type"
|
||||
if self == nil:
|
||||
(0, "")
|
||||
elif self of ref NilAccessDefect:
|
||||
(1, self.msg)
|
||||
elif self of ref DivByZeroDefect:
|
||||
(2, self.msg)
|
||||
elif self of ref AssertionDefect:
|
||||
(3, self.msg)
|
||||
elif self of ref KeyError:
|
||||
(4, self.msg)
|
||||
else:
|
||||
raise_assert "Unknown error type"
|
||||
|
|
|
@ -1,34 +1,60 @@
|
|||
import system except echo
|
||||
import std / [strutils, math, wrapnils, options]
|
||||
import std/[strutils, math, wrapnils, options]
|
||||
import types, base_api, vm_bridge_utils, base_bridge_private
|
||||
|
||||
bridged_to_host:
|
||||
proc message*(self: Sign): string = discard
|
||||
proc `message=`*(self: Sign, value: string) = discard
|
||||
proc message*(self: Sign): string =
|
||||
discard
|
||||
|
||||
proc more*(self: Sign): string = discard
|
||||
proc `more=`*(self: Sign, value: string) = discard
|
||||
proc `message=`*(self: Sign, value: string) =
|
||||
discard
|
||||
|
||||
proc width*(self: Sign): float = discard
|
||||
proc `width=`*(self: Sign, value: float) = discard
|
||||
proc more*(self: Sign): string =
|
||||
discard
|
||||
|
||||
proc height*(self: Sign): float = discard
|
||||
proc `height=`*(self: Sign, value: float) = discard
|
||||
proc `more=`*(self: Sign, value: string) =
|
||||
discard
|
||||
|
||||
proc size*(self: Sign): int = discard
|
||||
proc `size=`*(self: Sign, value: int) = discard
|
||||
proc width*(self: Sign): float =
|
||||
discard
|
||||
|
||||
proc open*(self: Sign): bool = discard
|
||||
proc `open=`*(self: Sign, value: bool) = discard
|
||||
proc `width=`*(self: Sign, value: float) =
|
||||
discard
|
||||
|
||||
proc billboard*(self: Sign): bool = discard
|
||||
proc `billboard=`*(self: Sign, value: bool) = discard
|
||||
proc height*(self: Sign): float =
|
||||
discard
|
||||
|
||||
proc say*(self: Unit, message: string, more = "", width = float.high,
|
||||
height = float.high, size = int.high, billboard = none(bool)
|
||||
): Sign {.discardable.} =
|
||||
proc `height=`*(self: Sign, value: float) =
|
||||
discard
|
||||
|
||||
let defaults: tuple[width: float, height: float, size: int, billboard: bool] =
|
||||
proc size*(self: Sign): int =
|
||||
discard
|
||||
|
||||
proc `size=`*(self: Sign, value: int) =
|
||||
discard
|
||||
|
||||
proc open*(self: Sign): bool =
|
||||
discard
|
||||
|
||||
proc `open=`*(self: Sign, value: bool) =
|
||||
discard
|
||||
|
||||
proc billboard*(self: Sign): bool =
|
||||
discard
|
||||
|
||||
proc `billboard=`*(self: Sign, value: bool) =
|
||||
discard
|
||||
|
||||
proc say*(
|
||||
self: Unit,
|
||||
message: string,
|
||||
more = "",
|
||||
width = float.high,
|
||||
height = float.high,
|
||||
size = int.high,
|
||||
billboard = none(bool),
|
||||
): Sign {.discardable.} =
|
||||
let defaults: tuple[width: float, height: float, size: int, billboard: bool] =
|
||||
if ?self.sign:
|
||||
(self.sign.width, self.sign.height, self.sign.size, self.sign.billboard)
|
||||
elif self of Bot:
|
||||
|
@ -36,24 +62,24 @@ proc say*(self: Unit, message: string, more = "", width = float.high,
|
|||
else:
|
||||
(2.0, 2.0, 250, false)
|
||||
|
||||
let
|
||||
let
|
||||
width = if width == float.high: defaults.width else: width
|
||||
height = if height == float.high: defaults.height else: height
|
||||
size = if size == int.high: defaults.size else: size
|
||||
billboard = billboard.get(defaults.billboard)
|
||||
|
||||
|
||||
if message == "":
|
||||
if ?self.sign:
|
||||
self.sign.show = false
|
||||
|
||||
elif self of Bot and ?self.sign:
|
||||
result = self.sign
|
||||
result.update_markdown_sign(message, more, width, height, size, billboard)
|
||||
result.show = true
|
||||
else:
|
||||
result = Sign()
|
||||
self.new_markdown_sign(result, message, more, width, height, size,
|
||||
billboard)
|
||||
self.new_markdown_sign(
|
||||
result, message, more, width, height, size, billboard
|
||||
)
|
||||
|
||||
self.sign = result
|
||||
|
||||
|
@ -61,7 +87,13 @@ proc say*(self: Unit, message: string, more = "", width = float.high,
|
|||
result.position = result.position + (UP * (height - 1.0))
|
||||
elif self of Bot:
|
||||
result.position = result.position + (UP * 2) + (LEFT * 1)
|
||||
|
||||
template say*(message: string, more = "", width = float.high,
|
||||
height = float.high, size = int.high, billboard = none(bool)): Sign =
|
||||
|
||||
template say*(
|
||||
message: string,
|
||||
more = "",
|
||||
width = float.high,
|
||||
height = float.high,
|
||||
size = int.high,
|
||||
billboard = none(bool),
|
||||
): Sign =
|
||||
enu_target.say(message, more, width, height, size, billboard)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import std / [macros, strformat, strutils, sequtils, tables]
|
||||
import std/[macros, strformat, strutils, sequtils, tables]
|
||||
import types, macro_helpers, base_api
|
||||
|
||||
proc current_loop(value: Loop = nil): Loop =
|
||||
|
@ -11,15 +11,14 @@ proc advance*(ctx: Context, frame: Frame = nil): bool =
|
|||
## advance state machine. Returns true if statemachine is still running.
|
||||
## It's ok to run this from within the statemachine, but Halt exceptions
|
||||
## must be allowed to propagate.
|
||||
let
|
||||
stack = ctx.stack
|
||||
let stack = ctx.stack
|
||||
for i, stack_frame in stack:
|
||||
let active = i + 1 == stack.len
|
||||
try:
|
||||
discard stack_frame.manager(active)
|
||||
except Halt:
|
||||
# shrink the stack to the current position
|
||||
ctx.stack = ctx.stack[0..i]
|
||||
ctx.stack = ctx.stack[0 .. i]
|
||||
if stack_frame.action == nil and stack_frame == ctx.stack[^1]:
|
||||
discard ctx.stack.pop()
|
||||
if frame != stack_frame:
|
||||
|
@ -58,6 +57,7 @@ template loop_body(body: untyped) =
|
|||
while true:
|
||||
body
|
||||
return true
|
||||
|
||||
validate_loop()
|
||||
frame.manager = manager
|
||||
ctx.stack.add frame
|
||||
|
@ -103,36 +103,35 @@ template loop_body(body: untyped) =
|
|||
macro loop*(body: untyped) =
|
||||
discard current_loop(Loop())
|
||||
result = new_stmt_list()
|
||||
let body = if body.kind == nnkNilLit: new_stmt_list() else: body
|
||||
let body =
|
||||
if body.kind == nnkNilLit:
|
||||
new_stmt_list()
|
||||
else:
|
||||
body
|
||||
result.add(get_ast loop_body(body))
|
||||
result = new_block_stmt(result)
|
||||
|
||||
macro loop*(sig: untyped, body: untyped): untyped =
|
||||
macro loop*(sig: untyped, body: untyped): untyped =
|
||||
var (name, params, vars) = sig.parse_sig
|
||||
|
||||
let proc_body = quote do:
|
||||
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())
|
||||
|
||||
result.add new_proc(
|
||||
name = ident(name),
|
||||
params = params,
|
||||
body = proc_body
|
||||
)
|
||||
result.add new_proc(name = ident(name), params = params, body = proc_body)
|
||||
|
||||
macro smart_call*(call: untyped) =
|
||||
var call_without_ctx = call.copy_nim_tree
|
||||
call_without_ctx.del call_without_ctx.len - 1
|
||||
result = quote do:
|
||||
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
|
||||
|
@ -171,11 +170,13 @@ proc transition(from_state, to_state, body, immediate: NimNode): NimNode =
|
|||
let ctx_arg = new_nim_node(nnkExprEqExpr)
|
||||
ctx_arg.add(ident"ctx")
|
||||
ctx_arg.add(ident"ctx")
|
||||
if to_state.kind == nnkInfix and to_state[0] == ident"as" and to_state[2].kind == nnkIdent:
|
||||
if to_state.kind == nnkInfix and to_state[0] == ident"as" and
|
||||
to_state[2].kind == nnkIdent:
|
||||
to_state_name = $to_state[2]
|
||||
if to_state[1].kind == nnkIdent:
|
||||
let search_name = $to_state[1]
|
||||
if search_name in current_loop().states and not current_loop().states[search_name].is_nil:
|
||||
if search_name in current_loop().states and
|
||||
not current_loop().states[search_name].is_nil:
|
||||
to_state = current_loop().states[search_name]
|
||||
else:
|
||||
to_state = new_call(to_state[1], ctx_arg)
|
||||
|
@ -185,7 +186,8 @@ proc transition(from_state, to_state, body, immediate: NimNode): NimNode =
|
|||
current_loop().states[to_state_name] = copy_nim_tree to_state
|
||||
elif to_state.kind == nnkIdent:
|
||||
to_state_name = $to_state
|
||||
if to_state_name in current_loop().states and not current_loop().states[to_state_name].is_nil:
|
||||
if to_state_name in current_loop().states and
|
||||
not current_loop().states[to_state_name].is_nil:
|
||||
to_state = current_loop().states[to_state_name]
|
||||
else:
|
||||
to_state = new_call(to_state, ctx_arg) #
|
||||
|
@ -194,7 +196,6 @@ proc transition(from_state, to_state, body, immediate: NimNode): NimNode =
|
|||
to_state.add(ctx_arg)
|
||||
else:
|
||||
error "to_state must be identifier or call", to_state
|
||||
|
||||
else:
|
||||
to_state_name = "nil"
|
||||
to_state = new_stmt_list()
|
||||
|
@ -207,29 +208,35 @@ proc transition(from_state, to_state, body, immediate: NimNode): NimNode =
|
|||
includes_str = includes.join(",")
|
||||
excludes_str = excludes.join(",")
|
||||
|
||||
result = quote do:
|
||||
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
|
||||
if `to_state_name` != "nil":
|
||||
frame.action = action
|
||||
else:
|
||||
frame.action = nil
|
||||
raise (ref Halt)()
|
||||
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
|
||||
|
||||
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")
|
||||
|
@ -238,14 +245,13 @@ macro `->`*(from_state: untyped, to_state: untyped, body: untyped = nil) =
|
|||
result = transition(from_state, to_state, body, ident"false")
|
||||
|
||||
when is_main_module:
|
||||
proc task1 =
|
||||
proc task1() =
|
||||
echo "task1"
|
||||
|
||||
proc task2 =
|
||||
proc task2() =
|
||||
echo "task2"
|
||||
|
||||
var
|
||||
count = 0
|
||||
var count = 0
|
||||
loop:
|
||||
inc count
|
||||
echo "count: " & $count
|
||||
|
@ -261,6 +267,7 @@ when is_main_module:
|
|||
|
||||
proc forward(length = 1) =
|
||||
echo "forward ", length
|
||||
|
||||
proc turn_right() =
|
||||
discard
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import std / [strutils, strformat, macros, math, hashes, tables, random]
|
||||
import std/[strutils, strformat, macros, math, hashes, tables, random]
|
||||
|
||||
var global_default* = false
|
||||
|
||||
|
@ -6,11 +6,21 @@ const yes* = true
|
|||
const no* = false
|
||||
|
||||
type
|
||||
Vector3* = tuple
|
||||
x, y, z: float
|
||||
Vector3* = tuple[x, y, z: float]
|
||||
|
||||
Directions* = enum
|
||||
up, u, down, d, left, l, right, r, forward, f, back, b
|
||||
up
|
||||
u
|
||||
down
|
||||
d
|
||||
left
|
||||
l
|
||||
right
|
||||
r
|
||||
forward
|
||||
f
|
||||
back
|
||||
b
|
||||
|
||||
Unit* = ref object of RootObj
|
||||
id: int
|
||||
|
@ -32,7 +42,13 @@ type
|
|||
Sign* = ref object of Unit
|
||||
|
||||
Colors* = enum
|
||||
eraser, blue, red, green, black, white, brown
|
||||
eraser
|
||||
blue
|
||||
red
|
||||
green
|
||||
black
|
||||
white
|
||||
brown
|
||||
|
||||
PlayerType* = ref object of Unit
|
||||
|
||||
|
@ -40,7 +56,7 @@ type
|
|||
stack*: seq[Frame]
|
||||
|
||||
Frame* = ref object
|
||||
manager*: proc(active: bool):bool
|
||||
manager*: proc(active: bool): bool
|
||||
action*: proc()
|
||||
|
||||
Halt* = object of CatchableError
|
||||
|
@ -50,10 +66,17 @@ type
|
|||
from_states*: seq[(string, NimNode)]
|
||||
|
||||
Tools* = enum
|
||||
CodeMode, BlueBlock, RedBlock, GreenBlock, BlackBlock, WhiteBlock,
|
||||
BrownBlock, PlaceBot
|
||||
CodeMode
|
||||
BlueBlock
|
||||
RedBlock
|
||||
GreenBlock
|
||||
BlackBlock
|
||||
WhiteBlock
|
||||
BrownBlock
|
||||
PlaceBot
|
||||
|
||||
proc vec3*(x, y, z: float): Vector3 {.inline.} = (x:x, y:y, z:z)
|
||||
proc vec3*(x, y, z: float): Vector3 {.inline.} =
|
||||
(x: x, y: y, z: z)
|
||||
|
||||
const
|
||||
UP* = vec3(0, 1, 0)
|
||||
|
@ -67,7 +90,7 @@ const
|
|||
|
||||
const EPSILON = 0.00001'f32
|
||||
|
||||
proc isEqualApprox*(a, b: float32): bool {.inline, noinit.} =
|
||||
proc isEqualApprox*(a, b: float32): bool {.inline, noinit.} =
|
||||
abs(a - b) < EPSILON
|
||||
|
||||
proc isEqualApprox*(a, b: float64): bool {.inline, noinit.} =
|
||||
|
@ -93,8 +116,6 @@ proc stepify*(value, step: float32): float32 {.inline, noinit.} =
|
|||
|
||||
# Vector3 math. https://github.com/pragmagic/godot-nim/blob/7fb22f69af92aa916e56dba14ba3938fc7fa1dd1/godot/core/vector3.nim
|
||||
|
||||
|
||||
|
||||
proc `+`*(a, b: Vector3): Vector3 {.inline.} =
|
||||
result.x = a.x + b.x
|
||||
result.y = a.y + b.y
|
||||
|
@ -120,20 +141,20 @@ proc `*`*(a, b: Vector3): Vector3 {.inline.} =
|
|||
result.y = a.y * b.y
|
||||
result.z = a.z * b.z
|
||||
|
||||
proc `*=`*(a: var Vector3, b: Vector3) {.inline.}=
|
||||
proc `*=`*(a: var Vector3, b: Vector3) {.inline.} =
|
||||
a.x *= b.x
|
||||
a.y *= b.y
|
||||
a.z *= b.z
|
||||
|
||||
proc `*`*(a: Vector3; b: float32): Vector3 {.inline.} =
|
||||
proc `*`*(a: Vector3, b: float32): Vector3 {.inline.} =
|
||||
result.x = a.x * b
|
||||
result.y = a.y * b
|
||||
result.z = a.z * b
|
||||
|
||||
proc `*`*(b: float32; a: Vector3): Vector3 {.inline.} =
|
||||
proc `*`*(b: float32, a: Vector3): Vector3 {.inline.} =
|
||||
a * b
|
||||
|
||||
proc `*=`*(a: var Vector3; b: float32) {.inline.} =
|
||||
proc `*=`*(a: var Vector3, b: float32) {.inline.} =
|
||||
a.x *= b
|
||||
a.y *= b
|
||||
a.z *= b
|
||||
|
@ -143,17 +164,17 @@ proc `/`*(a, b: Vector3): Vector3 =
|
|||
result.y = a.y / b.y
|
||||
result.z = a.z / b.z
|
||||
|
||||
proc `/=`*(a: var Vector3; b: Vector3) {.inline.} =
|
||||
proc `/=`*(a: var Vector3, b: Vector3) {.inline.} =
|
||||
a.x /= b.x
|
||||
a.y /= b.y
|
||||
a.z /= b.z
|
||||
|
||||
proc `/`*(a: Vector3; b: float32): Vector3 =
|
||||
proc `/`*(a: Vector3, b: float32): Vector3 =
|
||||
result.x = a.x / b
|
||||
result.y = a.y / b
|
||||
result.z = a.z / b
|
||||
|
||||
proc `/=`*(a: var Vector3; b: float32) {.inline.} =
|
||||
proc `/=`*(a: var Vector3, b: float32) {.inline.} =
|
||||
a.x /= b
|
||||
a.y /= b
|
||||
a.z /= b
|
||||
|
@ -229,7 +250,8 @@ proc cross*(self, other: Vector3): Vector3 {.inline.} =
|
|||
vec3(
|
||||
self.y * other.z - self.z * other.y,
|
||||
self.z * other.x - self.x * other.z,
|
||||
self.x * other.y - self.y * other.x)
|
||||
self.x * other.y - self.y * other.x,
|
||||
)
|
||||
|
||||
proc dot*(self, other: Vector3): float32 {.inline.} =
|
||||
self.x * other.x + self.y * other.y + self.z * other.z
|
||||
|
@ -238,7 +260,7 @@ proc abs*(self: Vector3): Vector3 {.inline.} =
|
|||
vec3(abs(self.x), abs(self.y), abs(self.z))
|
||||
|
||||
proc sign*(self: Vector3): Vector3 {.inline.} =
|
||||
vec3(sign(self.x), sign(self.y), sign(self.z))
|
||||
vec3(sign(self.x), sign(self.y), sign(self.z))
|
||||
|
||||
proc floor*(self: Vector3): Vector3 {.inline.} =
|
||||
vec3(floor(self.x), floor(self.y), floor(self.z))
|
||||
|
@ -250,7 +272,7 @@ proc lerp*(self: Vector3, other: Vector3, t: float32): Vector3 {.inline.} =
|
|||
vec3(
|
||||
self.x + t * (other.x - self.x),
|
||||
self.y + t * (other.y - self.y),
|
||||
self.z + t * (other.z - self.z)
|
||||
self.z + t * (other.z - self.z),
|
||||
)
|
||||
|
||||
proc distanceTo*(self, other: Vector3): float32 {.inline.} =
|
||||
|
@ -282,8 +304,7 @@ proc snapped*(self: Vector3, other: Vector3): Vector3 =
|
|||
result = self
|
||||
result.snap(other)
|
||||
|
||||
proc cubicInterpolate*(self, b, preA, postB: Vector3;
|
||||
t: float32): Vector3 =
|
||||
proc cubicInterpolate*(self, b, preA, postB: Vector3, t: float32): Vector3 =
|
||||
let p0 = preA
|
||||
let p1 = self
|
||||
let p2 = b
|
||||
|
@ -292,10 +313,11 @@ proc cubicInterpolate*(self, b, preA, postB: Vector3;
|
|||
let t2 = t * t
|
||||
let t3 = t2 * t
|
||||
|
||||
result = 0.5 * ((p1 * 2.0) +
|
||||
(-p0 + p2) * t +
|
||||
(2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 +
|
||||
(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3)
|
||||
result =
|
||||
0.5 * (
|
||||
(p1 * 2.0) + (-p0 + p2) * t + (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 +
|
||||
(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3
|
||||
)
|
||||
|
||||
proc moveToward*(vFrom, to: Vector3, delta: float32): Vector3 =
|
||||
let
|
||||
|
@ -312,5 +334,7 @@ proc moveToward*(vFrom, to: Vector3, delta: float32): Vector3 =
|
|||
converter vec3_to_bool*(v: Vector3): bool =
|
||||
v != vec3(0, 0, 0)
|
||||
|
||||
proc init*[T: Exception](kind: type[T], message: string, parent: ref Exception = nil): ref Exception =
|
||||
proc init*[T: Exception](
|
||||
kind: type[T], message: string, parent: ref Exception = nil
|
||||
): ref Exception =
|
||||
(ref kind)(msg: message, parent: parent)
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import std / [macros, strutils]
|
||||
import shared / errors
|
||||
import std/[macros, strutils]
|
||||
import shared/errors
|
||||
|
||||
proc get_last_error_impl(): ErrorData =
|
||||
raise_assert "get_last_error_impl must be implemented by host"
|
||||
|
||||
proc check_errors* =
|
||||
proc check_errors*() =
|
||||
let err = get_last_error_impl().to_exception
|
||||
if err != nil:
|
||||
raise err
|
||||
|
||||
proc get_name(node: NimNode): string =
|
||||
result = case node.kind:
|
||||
result =
|
||||
case node.kind
|
||||
of nnk_ident:
|
||||
$node.str_val
|
||||
of nnk_postfix:
|
||||
|
@ -30,21 +31,23 @@ 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 do:
|
||||
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:
|
||||
for defs in params[1..^1]:
|
||||
call.add(defs[0..^3])
|
||||
node.body = if params[0].kind == nnk_empty:
|
||||
quote do:
|
||||
`call`
|
||||
check_errors()
|
||||
else:
|
||||
quote do:
|
||||
result = `call`
|
||||
check_errors()
|
||||
for defs in params[1 ..^ 1]:
|
||||
call.add(defs[0 ..^ 3])
|
||||
node.body =
|
||||
if params[0].kind == nnk_empty:
|
||||
quote:
|
||||
`call`
|
||||
check_errors()
|
||||
else:
|
||||
quote:
|
||||
result = `call`
|
||||
check_errors()
|
||||
|
||||
defs.add def
|
||||
calls.add node
|
||||
|
@ -58,7 +61,8 @@ when is_main_module:
|
|||
Vector3 = object
|
||||
Colors = object
|
||||
|
||||
proc check_errors = discard
|
||||
proc check_errors() =
|
||||
discard
|
||||
|
||||
bridged_to_host:
|
||||
proc write_stack_trace*()
|
||||
|
|
|
@ -3,7 +3,8 @@ let menu* = me
|
|||
speed = 0
|
||||
lock = true
|
||||
|
||||
let overview = \"""
|
||||
let overview =
|
||||
\"""
|
||||
|
||||
World `{world_name()}`
|
||||
|
||||
|
@ -11,7 +12,8 @@ Level `{level_name()}`
|
|||
|
||||
"""
|
||||
|
||||
let details = \"""
|
||||
let details =
|
||||
\"""
|
||||
|
||||
# Menu
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ var test_mode = false
|
|||
var bot_id = ""
|
||||
var maze: MazeType
|
||||
|
||||
proc get_bot: Bot =
|
||||
proc get_bot(): Bot =
|
||||
# each time the code for the bot is reloaded we need to
|
||||
# grab a new reference to it. Eventually an easy way to
|
||||
# do this will be built in.
|
||||
|
@ -16,13 +16,13 @@ proc get_bot: Bot =
|
|||
elif not test_mode and bot.id == bot_id:
|
||||
return bot
|
||||
|
||||
- setup:
|
||||
-setup:
|
||||
reset()
|
||||
position = position + LEFT
|
||||
speed = 10
|
||||
turn player
|
||||
|
||||
- approach:
|
||||
-approach:
|
||||
sleep 2
|
||||
say "# - Hi!", width = 1.0
|
||||
sleep 2
|
||||
|
@ -33,10 +33,12 @@ proc get_bot: Bot =
|
|||
say "- Welcome to **Enu**!", width = 2.0
|
||||
sleep 1.5
|
||||
say "- Click this text for a short tutorial.", "# Welcome to Enu!"
|
||||
while not sign.open: turn(player)
|
||||
while not sign.open:
|
||||
turn(player)
|
||||
|
||||
- movement_info:
|
||||
say "# . . .", """
|
||||
-movement_info:
|
||||
say "# . . .",
|
||||
"""
|
||||
|
||||
# Welcome to Enu!
|
||||
|
||||
|
@ -62,14 +64,16 @@ controller, or by holding down left `alt/option (⌥)` on your keyboard.
|
|||
|
||||
Sneak behind the `Bot` to continue the tutorial.
|
||||
|
||||
""", width = 0.6
|
||||
""",
|
||||
width = 0.6
|
||||
|
||||
menu.show = true
|
||||
while me.angle_to(player).abs notin 150..210:
|
||||
while me.angle_to(player).abs notin 150 .. 210:
|
||||
sleep 0.5
|
||||
|
||||
- flying_info:
|
||||
say "- Good!", """
|
||||
-flying_info:
|
||||
say "- Good!",
|
||||
"""
|
||||
|
||||
# Jumping and Flying
|
||||
|
||||
|
@ -85,24 +89,28 @@ 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
|
||||
""",
|
||||
width = 1.4
|
||||
|
||||
speed = 1
|
||||
turn player
|
||||
sign.open = true
|
||||
while not player.flying: sleep()
|
||||
while not player.flying:
|
||||
sleep()
|
||||
|
||||
- flying_done:
|
||||
-flying_done:
|
||||
say "- Now drop!"
|
||||
while player.flying: sleep()
|
||||
while player.flying:
|
||||
sleep()
|
||||
|
||||
- good_job:
|
||||
-good_job:
|
||||
sign.open = false
|
||||
say "- Good Job!", "# Good Job!"
|
||||
sleep 2
|
||||
|
||||
- tool_info:
|
||||
say "- Switch to `Place Bot`", """
|
||||
-tool_info:
|
||||
say "- Switch to `Place Bot`",
|
||||
"""
|
||||
|
||||
# Changing tools
|
||||
|
||||
|
@ -120,14 +128,17 @@ friendly robots. Tools `2` - `7` are the `blue`, `red`, `green`, `black`,
|
|||
|
||||
Select the `Place Bot` tool to continue the tutorial.
|
||||
|
||||
""", width = 2.5
|
||||
""",
|
||||
width = 2.5
|
||||
|
||||
sign.open = true
|
||||
while player.tool != PlaceBot: sleep()
|
||||
while player.tool != PlaceBot:
|
||||
sleep()
|
||||
sleep 0.5
|
||||
|
||||
- bot_info:
|
||||
say "- Place a `Bot`", """
|
||||
-bot_info:
|
||||
say "- Place a `Bot`",
|
||||
"""
|
||||
|
||||
# Bots
|
||||
|
||||
|
@ -138,7 +149,8 @@ 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
|
||||
""",
|
||||
width = 2.0
|
||||
|
||||
sign.open = true
|
||||
var last_bot_frame = frame_count()
|
||||
|
@ -151,20 +163,21 @@ Place a `Bot` on the ground to continue the tutorial.
|
|||
last_bot_frame = bot.frame_created
|
||||
bot_id = bot.id
|
||||
|
||||
- draw_maze:
|
||||
-draw_maze:
|
||||
var bot = get_bot()
|
||||
turn bot
|
||||
maze = Maze.new(global = true)
|
||||
maze.show = true
|
||||
maze.rotation = 0
|
||||
maze.position = bot.position + (LEFT * 4.0) + (FORWARD * 1.0)
|
||||
maze.position = bot.position + (LEFT * 4.0) + (FORWARD * 1.0)
|
||||
|
||||
while not maze.finished:
|
||||
sleep 0.5
|
||||
sleep 1
|
||||
|
||||
- code_info:
|
||||
say "- Coding", """
|
||||
-code_info:
|
||||
say "- Coding",
|
||||
"""
|
||||
|
||||
# Coding Enu
|
||||
|
||||
|
@ -182,8 +195,9 @@ Switch to the `Code` tool to continue the tutorial.
|
|||
turn player
|
||||
sleep 0.5
|
||||
|
||||
- open_code:
|
||||
sign.more = """
|
||||
-open_code:
|
||||
sign.more =
|
||||
"""
|
||||
|
||||
# Coding Enu
|
||||
|
||||
|
@ -201,8 +215,9 @@ Open the code for your `Bot` to continue the tutorial.
|
|||
turn player
|
||||
sleep 0.5
|
||||
|
||||
- bot_navigation:
|
||||
sign.more = """
|
||||
-bot_navigation:
|
||||
sign.more =
|
||||
"""
|
||||
|
||||
# Coding Bots
|
||||
|
||||
|
@ -246,11 +261,12 @@ Good luck! We'll conclude this tutorial when your `Bot` goes past the
|
|||
bot = get_bot()
|
||||
turn bot
|
||||
|
||||
- win:
|
||||
-win:
|
||||
maze.won = true
|
||||
say "- Great Job!", "# Great Job!"
|
||||
sleep 15
|
||||
say "- All Done!", """
|
||||
say "- All Done!",
|
||||
"""
|
||||
|
||||
# Great Job!
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ name Confetti(arms = 31, count = 99)
|
|||
if not is_instance:
|
||||
show = false
|
||||
quit()
|
||||
|
||||
|
||||
speed = 0
|
||||
var arm_speed = 0.7
|
||||
|
||||
|
@ -12,9 +12,9 @@ arms.times:
|
|||
let h = cycle(3, 4, 5, 4)
|
||||
up h
|
||||
turn 360 / arms
|
||||
|
||||
|
||||
Streamer.new(speed = arm_speed, count = me.count)
|
||||
|
||||
|
||||
arm_speed += 1.0 / arms
|
||||
down h
|
||||
back 2
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
name Maze(finished = false, won = false)
|
||||
lock = true
|
||||
|
||||
proc make_sign: Sign =
|
||||
proc make_sign(): Sign =
|
||||
say("` `", width = 10.0, height = 3.0, size = 2600)
|
||||
|
||||
drawing = true
|
||||
|
@ -94,13 +94,15 @@ 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)
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ count.times:
|
|||
if 1 in 3:
|
||||
speed = base_speed
|
||||
color = random(green, black, blue, white, red, green, white, green)
|
||||
|
||||
|
||||
drawing = true
|
||||
back 3
|
||||
left 1
|
||||
|
@ -19,6 +19,6 @@ count.times:
|
|||
drawing = false
|
||||
speed = 0
|
||||
forward 2
|
||||
forward 1..4
|
||||
turn -35..35
|
||||
lean -45..35
|
||||
forward 1 .. 4
|
||||
turn -35 .. 35
|
||||
lean -45 .. 35
|
||||
|
|
|
@ -4,7 +4,8 @@ speed = 0
|
|||
show = false
|
||||
lock = true
|
||||
|
||||
let overview = """
|
||||
let overview =
|
||||
"""
|
||||
# ` ` Menu
|
||||
|
||||
- `Reset Tutorial`
|
||||
|
@ -13,7 +14,8 @@ let overview = """
|
|||
|
||||
"""
|
||||
|
||||
let details = """
|
||||
let details =
|
||||
"""
|
||||
|
||||
# Menu
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ name Win
|
|||
if not is_instance:
|
||||
show = false
|
||||
quit()
|
||||
|
||||
|
||||
seed = 7
|
||||
lock = true
|
||||
move me
|
||||
|
@ -10,4 +10,4 @@ speed = 0.02
|
|||
|
||||
Confetti.new(arms = 30, count = 124)
|
||||
forever:
|
||||
turn right
|
||||
turn right
|
||||
|
|
|
@ -11,21 +11,20 @@ let hellos = [
|
|||
let goodbyes = [
|
||||
"Uh, I gotta get going. Uh, it's good to see you, Phil.",
|
||||
"Whoa-ho-ho! Watch out for that first step! It's a doozy!",
|
||||
"Where are we going?",
|
||||
"Phil, this is the best day of my life."
|
||||
"Where are we going?", "Phil, this is the best day of my life."
|
||||
]
|
||||
|
||||
- wander:
|
||||
-wander:
|
||||
speed = 3
|
||||
forward 2..10
|
||||
turn -45..45
|
||||
forward 2 .. 10
|
||||
turn -45 .. 45
|
||||
|
||||
- chase:
|
||||
-chase:
|
||||
speed = 6
|
||||
turn player
|
||||
forward 5
|
||||
|
||||
- caught:
|
||||
-caught:
|
||||
say ""
|
||||
sleep 2
|
||||
say random(goodbyes)
|
||||
|
@ -35,22 +34,22 @@ loop:
|
|||
nil -> wander
|
||||
(go_home, wander_home) -> wander
|
||||
caught -> go_home
|
||||
|
||||
|
||||
if start_position.far(20):
|
||||
# `go_home`, but we want to be able to interrupt and
|
||||
# switch to `chase` if the player is near. Alias to
|
||||
# `wander_home`, which switches the same as `wander`.
|
||||
wander -> go_home as wander_home
|
||||
|
||||
|
||||
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:
|
||||
# this will be called when the command switches
|
||||
say cycle(hellos)
|
||||
|
||||
|
||||
if player.far(20):
|
||||
chase -> go_home
|
||||
|
||||
|
||||
if player.near(3):
|
||||
chase -> caught
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
lock = true
|
||||
|
||||
let text = """
|
||||
let text =
|
||||
"""
|
||||
|
||||
# Towers. Too Many Towers.
|
||||
|
||||
|
@ -10,4 +11,4 @@ This is an example of a [Prototype](https://ē.nu/docs/coding/concepts.html).
|
|||
|
||||
"""
|
||||
|
||||
say text, text, width = 3, height = 3
|
||||
say text, text, width = 3, height = 3
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
lock = true
|
||||
turn left
|
||||
|
||||
let text = """
|
||||
let text =
|
||||
"""
|
||||
|
||||
# Annoying Robot
|
||||
|
||||
|
@ -11,4 +12,4 @@ This is an example of a [Command Loop](https://ē.nu/docs/command_loops.html).
|
|||
|
||||
"""
|
||||
|
||||
say text, text, height = 4
|
||||
say text, text, height = 4
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
lock = true
|
||||
|
||||
turn right
|
||||
let text = """
|
||||
let text =
|
||||
"""
|
||||
|
||||
# ???
|
||||
|
||||
|
|
|
@ -4,4 +4,4 @@ height.times:
|
|||
sides.times:
|
||||
forward length
|
||||
turn 360 / sides + twist
|
||||
up 1
|
||||
up 1
|
||||
|
|
|
@ -3,12 +3,12 @@ seed = 213
|
|||
speed = 0
|
||||
turn right
|
||||
10.times:
|
||||
forward 20..40
|
||||
turn 20..50
|
||||
forward 20 .. 40
|
||||
turn 20 .. 50
|
||||
tower.new(
|
||||
height = 40..100,
|
||||
length = 5..10,
|
||||
sides = 3..10,
|
||||
twist = -2..2,
|
||||
color = cycle(red, green, blue, black)
|
||||
)
|
||||
height = 40 .. 100,
|
||||
length = 5 .. 10,
|
||||
sides = 3 .. 10,
|
||||
twist = -2 .. 2,
|
||||
color = cycle(red, green, blue, black),
|
||||
)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
lock = true
|
||||
|
||||
turn 180
|
||||
let blurb = "Here are some things you can build with Enu. Play with their code, or build something new from scratch."
|
||||
let blurb =
|
||||
"Here are some things you can build with Enu. Play with their code, or build something new from scratch."
|
||||
|
||||
let text = \"""
|
||||
let text =
|
||||
\"""
|
||||
|
||||
# Examples
|
||||
|
||||
|
@ -15,7 +17,8 @@ let text = \"""
|
|||
|
||||
"""
|
||||
|
||||
let more = \"""
|
||||
let more =
|
||||
\"""
|
||||
|
||||
# Menu
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue