mirror of https://github.com/dsrw/enu.git
267 lines
6.1 KiB
Nim
267 lines
6.1 KiB
Nim
import types
|
|
export types
|
|
|
|
import pkg/model_citizen/utils
|
|
import std/[sequtils, strutils, sugar, macros, asyncfutures, importutils]
|
|
export utils, sequtils, strutils, sugar, importutils
|
|
|
|
### Globals ###
|
|
|
|
const enu_version* = static_exec("git describe --tags HEAD")
|
|
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]
|
|
|
|
export with, sets, tables, pretty, flatty
|
|
|
|
proc minutes*(m: float | int): Duration {.inline.} =
|
|
init_duration(seconds = int(m * 60))
|
|
|
|
### Debug
|
|
|
|
export dump
|
|
|
|
import pkg/chronicles
|
|
export chronicles
|
|
|
|
template nim_filename*(): string =
|
|
instantiation_info(full_paths = true).filename
|
|
|
|
### options ###
|
|
|
|
import options
|
|
export options
|
|
|
|
proc `||=`*[T](opt: var Option[T], val: T): T {.discardable.} =
|
|
if not opt.is_some:
|
|
opt = some(val)
|
|
result = val
|
|
else:
|
|
result = opt.get()
|
|
|
|
proc `||`*[T](a: Option[T], b: T): T =
|
|
if ?a: a.get else: b
|
|
|
|
proc `||`*[T](a, b: T): T =
|
|
if ?a: a else: b
|
|
|
|
converter from_option*[T](val: Option[T]): T =
|
|
val.get()
|
|
|
|
proc optional_get*[T](self: var HashSet[T], key: T): Option[T] =
|
|
if key in self:
|
|
result = some(self[key])
|
|
else:
|
|
result = none(T)
|
|
|
|
### Vector3 ###
|
|
|
|
import core/godotcoretypes, core/vector3, core/vector2, math
|
|
|
|
const
|
|
UP* = vec3(0, 1, 0)
|
|
DOWN* = vec3(0, -1, 0)
|
|
BACK* = vec3(0, 0, 1)
|
|
FORWARD* = vec3(0, 0, -1)
|
|
RIGHT* = vec3(1, 0, 0)
|
|
LEFT* = vec3(-1, 0, 0)
|
|
|
|
proc vec3*(x, y, z: int): Vector3 {.inline.} =
|
|
vec3(x.float, y.float, z.float)
|
|
|
|
proc vec3*(x: int | float): Vector3 {.inline.} =
|
|
vec3(x, x, x)
|
|
|
|
proc vec2*(x: int | float): Vector2 {.inline.} =
|
|
vec2(x, x)
|
|
|
|
proc trunc*(self: Vector3): Vector3 {.inline.} =
|
|
vec3(trunc(self.x), trunc(self.y), trunc(self.z))
|
|
|
|
proc inverse_normalized*(self: Vector3): Vector3 {.inline.} =
|
|
(self - vec3(self.length, self.length, self.length)) * -1
|
|
|
|
proc first*[T](arr: open_array[T], test: proc(x: T): bool): Option[T] =
|
|
for item in arr:
|
|
if test(item):
|
|
return some(item)
|
|
|
|
proc round*(v: Vector3): Vector3 {.inline.} =
|
|
vec3(v.x.round(), v.y.round(), v.z.round())
|
|
|
|
proc is_axis_aligned*(v: Vector3): bool {.inline.} =
|
|
v in [UP, DOWN, LEFT, RIGHT, FORWARD, BACK]
|
|
|
|
# Basis
|
|
|
|
proc `x=`*(self: var Basis, value: Vector3) {.inline.} =
|
|
self.elements[0].x = value.x
|
|
self.elements[1].x = value.y
|
|
self.elements[2].x = value.z
|
|
|
|
proc `y=`*(self: var Basis, value: Vector3) {.inline.} =
|
|
self.elements[0].y = value.x
|
|
self.elements[1].y = value.y
|
|
self.elements[2].y = value.z
|
|
|
|
proc `z=`*(self: var Basis, value: Vector3) {.inline.} =
|
|
self.elements[0].z = value.x
|
|
self.elements[1].z = value.y
|
|
self.elements[2].z = value.z
|
|
|
|
proc surrounding*(point: Vector3): seq[Vector3] =
|
|
collect(new_seq):
|
|
for x in 0 .. 2:
|
|
for y in 0 .. 2:
|
|
for z in 0 .. 2:
|
|
point + vec3(x - 1, y - 1, z - 1)
|
|
|
|
# math
|
|
|
|
const CMP_EPSILON = 0.00001
|
|
proc roughly_zero[T](s: T): bool =
|
|
abs(s) < CMP_EPSILON
|
|
|
|
proc lerp*(self, other, t: float): float {.inline.} =
|
|
self + t * (other - self)
|
|
|
|
proc wrap*[T](value, min, max: T): float =
|
|
let range = max - min
|
|
if range.roughly_zero:
|
|
min
|
|
else:
|
|
value - (range * floor((value - min) / range))
|
|
|
|
# output
|
|
|
|
when not defined(no_godot):
|
|
import pkg/godot
|
|
|
|
default_chronicles_stream.output.writer = proc(
|
|
logLevel: LogLevel, msg: LogOutputStr
|
|
) {.gcsafe.} =
|
|
when defined(release):
|
|
godot.print msg
|
|
else:
|
|
echo msg
|
|
|
|
# misc
|
|
|
|
proc init*(_: type Future, T: type, proc_name = ""): Future[T] =
|
|
return new_future[T](proc_name)
|
|
|
|
import pkg/core/transforms
|
|
export transforms
|
|
|
|
import pkg/godot
|
|
|
|
import pkg/model_citizen
|
|
export model_citizen
|
|
|
|
proc global_from*(self: Vector3, unit: Unit): Vector3 =
|
|
result = self
|
|
var unit = unit
|
|
while unit != nil:
|
|
result += unit.transform.origin
|
|
unit = unit.parent
|
|
|
|
proc local_to*(self: Vector3, unit: Unit): Vector3 =
|
|
result = self
|
|
var unit = unit
|
|
while unit != nil:
|
|
result -= unit.transform.origin
|
|
unit = unit.parent
|
|
|
|
proc `+=`*(self: ZenValue[string], str: string) =
|
|
self.value = self.value & str
|
|
|
|
proc origin*(self: ZenValue[Transform]): Vector3 =
|
|
self.value.origin
|
|
|
|
proc `origin=`*(self: ZenValue[Transform], value: Vector3) =
|
|
var transform = self.value
|
|
transform.origin = value
|
|
self.value = transform
|
|
|
|
proc basis*(self: ZenValue[Transform]): Basis =
|
|
self.value.basis
|
|
|
|
proc `basis=`*(self: ZenValue[Transform], value: Basis) =
|
|
var transform = self.value
|
|
transform.basis = value
|
|
self.value = transform
|
|
|
|
proc init*(_: type Basis): Basis =
|
|
init_basis()
|
|
|
|
proc init*(_: type Transform, origin = vec3()): Transform =
|
|
result = init_transform()
|
|
result.origin = origin
|
|
|
|
proc init*(_: type Code, nim: string): Code =
|
|
Code(owner: state.worker_ctx_name, nim: nim)
|
|
|
|
proc update_action_index*(state: GameState, change: int) =
|
|
var index = int(state.tool) + change
|
|
if index < 0:
|
|
index = int Tools.high
|
|
elif index > int Tools.high:
|
|
index = int Tools.low
|
|
|
|
state.tool = Tools(index)
|
|
|
|
template watch*[T, O](zen: Zen[T, O], unit: untyped, body: untyped) =
|
|
when unit is Unit:
|
|
mixin thread_ctx
|
|
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`."
|
|
.}
|
|
|
|
template watch*[T, O](zen: Zen[T, O], body: untyped) =
|
|
when compiles(self.model):
|
|
watch(zen, self.model, body)
|
|
else:
|
|
watch(zen, self, body)
|
|
|
|
# from https://forum.nim-lang.org/t/5481#34239
|
|
macro enum_fields*(n: typed): untyped =
|
|
let impl = get_type(n)
|
|
expect_kind impl[1], nnk_enum_ty
|
|
result = nnk_bracket.new_tree()
|
|
for f in impl[1]:
|
|
case f.kind
|
|
of nnk_sym, nnk_ident:
|
|
result.add new_lit(f.str_val)
|
|
else:
|
|
discard
|
|
|
|
template value*(self: ZenValue, body: untyped) {.dirty.} =
|
|
block:
|
|
var value = self.value
|
|
with value:
|
|
body
|
|
self.value = value
|
|
|
|
var deferred {.threadvar.}: seq[proc() {.closure, gcsafe.}]
|
|
template after_boop*(body: untyped) =
|
|
deferred.add proc() =
|
|
body
|
|
|
|
proc run_deferred*() =
|
|
for fn in deferred:
|
|
fn()
|
|
deferred.set_len(0)
|