Compare commits
423 Commits
b566cf20b5
...
main
Author | SHA1 | Date | |
---|---|---|---|
75890b1a71 | |||
73db3e57dc | |||
2975aa2dfb | |||
6726faf719 | |||
183dfa6be5 | |||
e974194af0 | |||
8607aa48ec | |||
f6600dfbda | |||
bdabd04388 | |||
0e075ec334 | |||
b256fc903a | |||
b52ecaeaa0 | |||
37e46e9a7e | |||
a472e6af52 | |||
66b2f04d9d | |||
90f4097070 | |||
829ff4780c | |||
8e15c9ec3c | |||
474ea84a77 | |||
7b8b9416ba | |||
8ed8158ae6 | |||
48e3a4c233 | |||
56530f9864 | |||
f86f3dd41a | |||
adae6be7e5 | |||
cd3033f9c4 | |||
e11e63f273 | |||
75737b738f | |||
ce2c2513aa | |||
36c0af9953 | |||
826622cd58 | |||
78b6a26de9 | |||
5f7b8bac6d | |||
6d6230c6a1 | |||
c07e16490e | |||
f5e55bb997 | |||
1e6e323fe1 | |||
dbf9599fe5 | |||
923cd81571 | |||
733a1786ab | |||
a03e1d885d | |||
67feb5974a | |||
5be4ed4645 | |||
4a41f47a58 | |||
35bb26705a | |||
13bc71a28d | |||
b97a155de4 | |||
5df80addeb | |||
787977b747 | |||
f90b973d86 | |||
32675c012c | |||
a97515e948 | |||
ed8e826b94 | |||
4e5ff9433c | |||
55829a1bef | |||
119bd52c51 | |||
5abd1ced1c | |||
80db96672d | |||
2f6f7852be | |||
307d5552f6 | |||
5911cbd980 | |||
e47b761a2c | |||
844283c2fb | |||
09eac707c3 | |||
5e89710458 | |||
4bc1feb826 | |||
1c3973c6a2 | |||
da5bdb4fae | |||
ed2afec5a7 | |||
6812c7c13d | |||
8c0f43ec34 | |||
23fbd45564 | |||
a36459397e | |||
5f3920fdba | |||
f57525cea6 | |||
6b2901be28 | |||
9f0d15b9f6 | |||
b46331e08d | |||
d2938da8e2 | |||
9134e51817 | |||
d66eda1894 | |||
a88392b9e9 | |||
05f85062e8 | |||
d5aec5e6e1 | |||
62866d33ae | |||
ce7240d423 | |||
7a38f7bcf3 | |||
affaf7f557 | |||
a223506a5f | |||
98d7d76a42 | |||
814269ab0c | |||
d76ea06470 | |||
dc6b298532 | |||
f25e27b102 | |||
dd4fc45be3 | |||
85e47dd677 | |||
a020b92824 | |||
b6347996f9 | |||
66c525a0d4 | |||
70fab28158 | |||
80a4ae3d0e | |||
bd3b090f6f | |||
6eb0730c52 | |||
a231d650f2 | |||
e15975bfaa | |||
b67bc92857 | |||
991196f7c8 | |||
0b89c90ad7 | |||
3d51c8c48f | |||
21d8e2c5a5 | |||
f805bf3f92 | |||
5228fa7e41 | |||
f044a75ffe | |||
723ccf1126 | |||
6bd3afe9b2 | |||
d90bf4cbe2 | |||
48f34f4623 | |||
9c007f34df | |||
a1f4599efd | |||
4c1a8e087a | |||
a2b1f1820a | |||
85ec8d3366 | |||
7eebc7a2d7 | |||
2b26fad983 | |||
47799deb8b | |||
1cd4bfa638 | |||
9beef7686e | |||
cee344c7c1 | |||
18a76649b9
|
|||
88a4876d91
|
|||
835edd737c | |||
9a486fa912
|
|||
d4ce6ab9ec | |||
24b417c287 | |||
5a83381ae1 | |||
793bd850f6 | |||
29d163216c | |||
ffc3badc50 | |||
bedfe0cdfb | |||
927f284fda | |||
2616549f88 | |||
cc4f7f7417 | |||
f81c583319 | |||
0df0a9226f | |||
af1b9caedc | |||
72d1941091 | |||
8a58336d16 | |||
1818532ec9 | |||
e9f8dbebbf | |||
c81f95e571 | |||
5ba11dc584 | |||
f2aded9046 | |||
d6aaef3f68 | |||
322fbf6bbd | |||
5a7d7433d1 | |||
037548436d | |||
5e27845e55 | |||
3bf8d7bedb | |||
85d7d54eed | |||
8c248cb3fb | |||
145b040a0f | |||
559ff9fedc | |||
990135105a | |||
7040d6f218 | |||
cb88b4bcc5 | |||
8110789b3a | |||
0eadeb7e9d | |||
3d10e1782a | |||
d9df3f9b04 | |||
d9d7072c86 | |||
3733b53cc5 | |||
b6b436e1b7 | |||
02b5ac4cc3 | |||
4efe80bb5a | |||
f3a2dc9063 | |||
53c43a8f34 | |||
507bff6ed8 | |||
00636d65a9 | |||
42253fc58a | |||
c6cbf941a2 | |||
277d1b2e10 | |||
46955a19c1 | |||
9ab3e942cd | |||
c7bb317ead | |||
241e72be1a | |||
f4fccc08c4 | |||
2286cdefeb | |||
00ada15dbc | |||
96b6b7e70b | |||
ccfdfd8a35 | |||
7284bb726a | |||
87b33c2e0c | |||
732a3579b0 | |||
2f629433aa | |||
6d58e964bc | |||
0014458dbb | |||
9112630330 | |||
108810d68a | |||
2c94efb796 | |||
11ec35bc8a | |||
2120f6876c | |||
dfd888a80a | |||
695784301d | |||
98d19495a2 | |||
0929aa9be4 | |||
898c11bbdf | |||
aeabb17f86 | |||
24e8dc052d | |||
3f264ca0ad | |||
a7557fceb4 | |||
bd89c4b938 | |||
7074e7499a | |||
2e29cfcfe2 | |||
74d7190c62 | |||
4b2a22bf3c | |||
630c6fb5d4 | |||
d7c744a6ca | |||
f8d7aa8a07 | |||
ea2bbf5de0 | |||
637343db5b | |||
fedf1b5ef3 | |||
732b61e207 | |||
6cb166522e | |||
916e433753 | |||
458b44d0b0 | |||
8de4a1f09b | |||
ac93d114c9 | |||
37b0f4e3a6 | |||
fad11041bc | |||
bbd654a569 | |||
7c33107585 | |||
f625dde8d1 | |||
166ae43981 | |||
6ef3cf1a3a | |||
d84e5f8610 | |||
6a87119c70 | |||
791ab628ca | |||
20394eed6c | |||
a54657e7be | |||
3bdee30e7b | |||
0e4abaae3c | |||
cf8227d7d3 | |||
f365cff590 | |||
77ff5c7f25 | |||
bf3eb50b55 | |||
6a029b7e79 | |||
fad46137a0 | |||
7a403c766e | |||
b0e718876a | |||
785b507330 | |||
d884a9026f | |||
9ddc5c4a66 | |||
16bd49b42e | |||
dd158dee01 | |||
ab3c032313 | |||
34a3de73c6 | |||
597168c282 | |||
37cd8cf2cf | |||
cb5f207761 | |||
6e421543c4 | |||
c97d9b2568 | |||
e281ba593c | |||
a20be2c523 | |||
b20e7202fe | |||
045d2764fa | |||
53917b05b7 | |||
0dc0a18019 | |||
2df5616410 | |||
3f9906a918 | |||
8a5d639f95 | |||
40aef0a1f9 | |||
2de536210b | |||
449d4d3c32 | |||
8233f31269 | |||
45b8b21ec3 | |||
4659cf2aef | |||
db530ca3a0 | |||
f0dfd5627a | |||
0da1e413aa | |||
8c165974c7 | |||
1ba33bdc26 | |||
760515c551 | |||
9d0a2cab81 | |||
d5b42fa242 | |||
851ab80292 | |||
688d71953a | |||
63abf3d374 | |||
80c77424e2 | |||
82d4f21a4b | |||
3990f78a74 | |||
f0ad9b9a8a | |||
ea0af5159f | |||
5059802d09 | |||
b7cb37c06a | |||
664f123a85 | |||
2351d4114c | |||
86bf16b680 | |||
dbe6217e24 | |||
b037d7a0b9 | |||
e984e95fa8 | |||
4ed3764c1d | |||
6d19d2d819 | |||
5bce3e5238 | |||
6298394957 | |||
eefd53a630 | |||
87ae1a7312 | |||
3052bb693a | |||
5f6c8dd8e6 | |||
c694dfff82 | |||
b6ca9bedb4 | |||
8d67e44009 | |||
192907a0db | |||
e8b02570a2 | |||
46e077ba63 | |||
41d0e24780 | |||
777a06a002 | |||
313108092b | |||
83e2dc5468 | |||
951d9c76c8 | |||
f3848d2d52 | |||
8c401eda75 | |||
5c89c55b3e | |||
5b05386bb0 | |||
b0549612a9 | |||
6463ac3dd7 | |||
e914cad0dd | |||
a9d9936cb7 | |||
4dd028aeae | |||
d7a119a592 | |||
cb6c1df0be | |||
3bfa86066e | |||
7c0bf39f12 | |||
4f2b8ccd01 | |||
472a0657f3 | |||
0f368e2700 | |||
33471b4c46 | |||
edcb7fc39c | |||
f9a8448782 | |||
6d5732cc2b | |||
62d738cbbe | |||
f4a3298906 | |||
8ec5a96333 | |||
4277852fc5 | |||
dc2535358e | |||
190eb1f107 | |||
e06c879869 | |||
e7ed72dfc0 | |||
c4c097f050 | |||
1d34c91106 | |||
0d81236331 | |||
f9bb6412b7 | |||
b18f6f1d87 | |||
2f94e17852 | |||
26a2bf293f | |||
4b0d584b7e | |||
19215d5795 | |||
1c97053675 | |||
ee7fc42fbc | |||
cd9c65212d | |||
ccaef34d61 | |||
833f7dbc53 | |||
a7feb7b61b | |||
4be27816c2 | |||
d794ca862f | |||
26c75ffd7c | |||
e4da4a8b7f | |||
963d549eed | |||
9121da0675 | |||
6464d14b3e | |||
eff9fe6918 | |||
d11143ac86 | |||
1d35a3859b | |||
9da26638c8 | |||
a22bcfd97e | |||
a527036436 | |||
b390e9db23 | |||
5a08c01208 | |||
eff2d9c5e1 | |||
8aecc2bd06 | |||
1296d41ad7 | |||
48f63fc9df | |||
c49789f1f4 | |||
a7b09b9f39 | |||
399b199266 | |||
73b6ab047d | |||
024f17de91 | |||
92de2c00c0 | |||
b683594013 | |||
7e409fc14a | |||
aa3cab87d2 | |||
1dc0dea762 | |||
7f56ed8421 | |||
119b706638 | |||
f2bbc1863e | |||
768daf1f54 | |||
139394c6de | |||
446402c2e0 | |||
f7a718003e | |||
f087bf1f7f | |||
19bf88d44e | |||
3535a185df | |||
d34516c4ee | |||
b295c5920c | |||
f7f27119e1 | |||
ffab6a3924 | |||
82bad550e5 | |||
19b9812b3e | |||
c8a65f2894 | |||
f0d3f6778c | |||
da98c0941b | |||
d884cd45d9 | |||
d2422735e6 | |||
ed93072371 | |||
9329d3c2be | |||
ef5d609f4a | |||
64433cbe18 | |||
f96d521af2 | |||
1a7322dccf | |||
e70366f82f | |||
7886650339 | |||
3a57833ac1 | |||
667b599c19 | |||
cfc9ac9583 |
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{c,h,py}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[CMakeListst.txt]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 8
|
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -2,3 +2,5 @@
|
|||||||
*.ogg filter=lfs diff=lfs merge=lfs -text
|
*.ogg filter=lfs diff=lfs merge=lfs -text
|
||||||
*.xm filter=lfs diff=lfs merge=lfs -text
|
*.xm filter=lfs diff=lfs merge=lfs -text
|
||||||
*.tga filter=lfs diff=lfs merge=lfs -text
|
*.tga filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
||||||
|
text=auto eol=lf
|
||||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -2,13 +2,17 @@
|
|||||||
*
|
*
|
||||||
!*.*
|
!*.*
|
||||||
!*/
|
!*/
|
||||||
|
!bin/*
|
||||||
!Makefile
|
!Makefile
|
||||||
|
!LICENSE
|
||||||
|
!COPYING
|
||||||
|
|
||||||
**/*.exe
|
**/*.exe
|
||||||
**/*.html
|
**/*.html
|
||||||
**/*.js
|
**/*.js
|
||||||
**/*.wasm
|
**/*.wasm
|
||||||
**/*.wasm.map
|
**/*.wasm.map
|
||||||
|
**/*.data
|
||||||
**/*.so
|
**/*.so
|
||||||
**/*.dll
|
**/*.dll
|
||||||
**/*.7z
|
**/*.7z
|
||||||
@ -22,8 +26,8 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
.cache/
|
.cache/
|
||||||
.build/
|
build/
|
||||||
.build-web/
|
build-web/
|
||||||
build/
|
build/
|
||||||
out/
|
out/
|
||||||
|
|
||||||
|
230
CMakeLists.txt
230
CMakeLists.txt
@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.21)
|
|||||||
|
|
||||||
project(townengine LANGUAGES C)
|
project(townengine LANGUAGES C)
|
||||||
|
|
||||||
|
set(CMAKE_MESSAGE_LOG_LEVEL "WARNING")
|
||||||
|
set(CMAKE_INSTALL_MESSAGE NEVER)
|
||||||
|
|
||||||
|
# TODO: test whether webgl 1 is good enough.
|
||||||
|
|
||||||
# SDL dependencies
|
# SDL dependencies
|
||||||
# for whatever reason Emscripten has SDL2 config, but not actual SDL2 port by default
|
# for whatever reason Emscripten has SDL2 config, but not actual SDL2 port by default
|
||||||
if(NOT EMSCRIPTEN)
|
if(NOT EMSCRIPTEN)
|
||||||
@ -9,11 +14,11 @@ if(NOT EMSCRIPTEN)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# CMake actually has no default configuration and it's toolchain file dependent, set debug if not specified
|
# CMake actually has no default configuration and it's toolchain file dependent, set debug if not specified
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if(NOT DEFINED CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT TWN_SANITIZE AND CMAKE_BUILD_TYPE MATCHES Debug)
|
if(NOT DEFINED TWN_SANITIZE AND CMAKE_BUILD_TYPE MATCHES Debug)
|
||||||
set(TWN_SANITIZE ON)
|
set(TWN_SANITIZE ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -23,17 +28,19 @@ set(TWN_TARGET townengine CACHE INTERNAL "")
|
|||||||
set(TWN_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "")
|
set(TWN_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "")
|
||||||
|
|
||||||
# feature configuration, set them with -DFEATURE=ON/OFF in cli
|
# feature configuration, set them with -DFEATURE=ON/OFF in cli
|
||||||
option(TWN_FEATURE_DYNLIB_GAME "Enable dynamic library loading support" ON)
|
option(TWN_USE_AMALGAM "Enable use of twn_amalgam.c as a single compilation unit" ON)
|
||||||
|
option(TWN_FEATURE_DYNLIB_GAME "Enable dynamic library loading support" ON)
|
||||||
|
set(TWN_OUT_DIR ${CMAKE_SOURCE_DIR} CACHE PATH "Artifact destination")
|
||||||
|
|
||||||
# todo: figure out how to compile for dynamic linking instead
|
# todo: figure out how to compile for dynamic linking instead?
|
||||||
if(EMSCRIPTEN)
|
if(HAIKU OR EMSCRIPTEN)
|
||||||
if(TWN_FEATURE_DYNLIB_GAME)
|
if(TWN_FEATURE_DYNLIB_GAME)
|
||||||
message(WARNING "TWN_FEATURE_DYNLIB_GAME is set, but not supported - it is turned off")
|
message(WARNING "TWN_FEATURE_DYNLIB_GAME is set, but not supported - it is turned off")
|
||||||
set(TWN_FEATURE_DYNLIB_GAME OFF CACHE INTERNAL "")
|
set(TWN_FEATURE_DYNLIB_GAME OFF CACHE INTERNAL "")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(HAIKU)
|
if(HAIKU OR EMSCRIPTEN)
|
||||||
if(TWN_SANITIZE)
|
if(TWN_SANITIZE)
|
||||||
message(WARNING "TWN_SANITIZE is set, but not supported - it is turned off")
|
message(WARNING "TWN_SANITIZE is set, but not supported - it is turned off")
|
||||||
set(TWN_SANITIZE OFF CACHE INTERNAL "")
|
set(TWN_SANITIZE OFF CACHE INTERNAL "")
|
||||||
@ -41,45 +48,30 @@ if(HAIKU)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# add -fPIC globally so that it's linked well
|
# add -fPIC globally so that it's linked well
|
||||||
add_compile_options($<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:-fPIC>)
|
add_compile_options($<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:-fPIC>
|
||||||
|
-fvisibility=hidden)
|
||||||
|
|
||||||
set(PHYSFS_BUILD_SHARED FALSE)
|
set(PHYSFS_BUILD_SHARED FALSE CACHE INTERNAL "")
|
||||||
set(PHYSFS_DISABLE_INSTALL TRUE)
|
set(PHYSFS_DISABLE_INSTALL TRUE CACHE INTERNAL "")
|
||||||
set(PHYSFS_TARGETNAME_UNINSTALL "physfs_uninstall")
|
set(PHYSFS_TARGETNAME_UNINSTALL "physfs_uninstall" CACHE INTERNAL "")
|
||||||
set(PHYSFS_ARCHIVE_GRP OFF)
|
set(PHYSFS_ARCHIVE_GRP OFF CACHE BOOL "")
|
||||||
set(PHYSFS_ARCHIVE_WAD OFF)
|
set(PHYSFS_ARCHIVE_WAD OFF CACHE INTERNAL "")
|
||||||
set(PHYSFS_ARCHIVE_HOG OFF)
|
set(PHYSFS_ARCHIVE_HOG OFF CACHE INTERNAL "")
|
||||||
set(PHYSFS_ARCHIVE_MVL OFF)
|
set(PHYSFS_ARCHIVE_MVL OFF CACHE INTERNAL "")
|
||||||
set(PHYSFS_ARCHIVE_QPAK OFF)
|
set(PHYSFS_ARCHIVE_QPAK OFF CACHE INTERNAL "")
|
||||||
set(PHYSFS_ARCHIVE_SLB OFF)
|
set(PHYSFS_ARCHIVE_SLB OFF CACHE INTERNAL "")
|
||||||
set(PHYSFS_ARCHIVE_ISO9660 OFF)
|
set(PHYSFS_ARCHIVE_ISO9660 OFF CACHE INTERNAL "")
|
||||||
set(PHYSFS_ARCHIVE_VDF OFF)
|
set(PHYSFS_ARCHIVE_VDF OFF CACHE INTERNAL "")
|
||||||
|
set(PHYSFS_ARCHIVE_7Z OFF CACHE INTERNAL "")
|
||||||
add_subdirectory(third-party/physfs ${CMAKE_CURRENT_BINARY_DIR}/third-party/physfs SYSTEM)
|
add_subdirectory(third-party/physfs ${CMAKE_CURRENT_BINARY_DIR}/third-party/physfs SYSTEM)
|
||||||
add_subdirectory(third-party/libxm ${CMAKE_CURRENT_BINARY_DIR}/third-party/libxm SYSTEM)
|
add_subdirectory(third-party/libxm ${CMAKE_CURRENT_BINARY_DIR}/third-party/libxm SYSTEM)
|
||||||
|
|
||||||
|
set(TWN_RENDERING_API OPENGL_15)
|
||||||
if(LINUX)
|
|
||||||
set(SYSTEM_SOURCE_FILES
|
|
||||||
src/system/linux/twn_elf.c
|
|
||||||
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:src/game_object/twn_linux_game_object.c>)
|
|
||||||
elseif(WIN32)
|
|
||||||
set(SYSTEM_SOURCE_FILES
|
|
||||||
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:src/game_object/twn_win32_game_object.c>)
|
|
||||||
else()
|
|
||||||
set(SYSTEM_SOURCE_FILES)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(EMSCRIPTEN)
|
|
||||||
set(TWN_RENDERING_API WEBGL1)
|
|
||||||
else()
|
|
||||||
set(TWN_RENDERING_API OPENGL_15)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(TWN_RENDERING_API MATCHES OPENGL_15)
|
if(TWN_RENDERING_API MATCHES OPENGL_15)
|
||||||
set(SYSTEM_SOURCE_FILES ${SYSTEM_SOURCE_FILES}
|
set(SYSTEM_SOURCE_FILES ${SYSTEM_SOURCE_FILES}
|
||||||
src/rendering/twn_gl_any_rendering.c
|
src/rendering/twn_gl_any_rendering.c
|
||||||
src/rendering/twn_gl_15_rendering.c
|
src/rendering/twn_gl_15_rendering.c)
|
||||||
src/rendering/twn_gl_15_gpu_texture.c)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(TWN_THIRD_PARTY_SOURCE_FILES
|
set(TWN_THIRD_PARTY_SOURCE_FILES
|
||||||
@ -88,31 +80,47 @@ set(TWN_THIRD_PARTY_SOURCE_FILES
|
|||||||
third-party/tomlc99/toml.c
|
third-party/tomlc99/toml.c
|
||||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/src/glad.c>)
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/src/glad.c>)
|
||||||
|
|
||||||
set(TWN_SOURCE_FILES
|
set(TWN_NONOPT_SOURCE_FILES
|
||||||
src/twn_loop.c
|
src/twn_stb.c
|
||||||
src/twn_main.c
|
|
||||||
src/twn_context.c include/twn_context.h
|
src/twn_context.c include/twn_context.h
|
||||||
src/twn_audio.c include/twn_audio.h
|
src/twn_audio.c include/twn_audio.h
|
||||||
src/twn_util.c include/twn_util.h
|
src/twn_util.c include/twn_util.h
|
||||||
src/twn_input.c include/twn_input.h
|
src/twn_input.c include/twn_input.h
|
||||||
src/twn_camera.c include/twn_camera.h
|
|
||||||
src/twn_textures.c src/twn_textures_c.h
|
src/twn_loop.c src/twn_loop_c.h
|
||||||
|
src/twn_camera.c src/twn_camera_c.h
|
||||||
|
src/twn_textures.c src/twn_textures_c.h
|
||||||
|
src/twn_filewatch.c src/twn_filewatch_c.h
|
||||||
|
src/twn_filewatch.c src/twn_filewatch_c.h
|
||||||
|
src/twn_timer.c src/twn_timer_c.h
|
||||||
|
src/twn_workers.c src/twn_workers_c.h
|
||||||
|
|
||||||
src/rendering/twn_draw.c src/rendering/twn_draw_c.h
|
src/rendering/twn_draw.c src/rendering/twn_draw_c.h
|
||||||
|
src/rendering/twn_quads.c
|
||||||
src/rendering/twn_sprites.c
|
src/rendering/twn_sprites.c
|
||||||
|
src/rendering/twn_rects.c
|
||||||
src/rendering/twn_text.c
|
src/rendering/twn_text.c
|
||||||
src/rendering/twn_triangles.c
|
src/rendering/twn_triangles.c
|
||||||
|
src/rendering/twn_billboards.c
|
||||||
src/rendering/twn_circles.c
|
src/rendering/twn_circles.c
|
||||||
src/rendering/twn_skybox.c
|
src/rendering/twn_skybox.c
|
||||||
src/rendering/twn_fog.c
|
src/rendering/twn_models.c
|
||||||
|
src/rendering/twn_lines.c
|
||||||
|
)
|
||||||
|
|
||||||
|
set(TWN_SOURCE_FILES
|
||||||
|
$<IF:$<BOOL:${TWN_USE_AMALGAM}>,src/twn_amalgam.c src/twn_stb.c,${TWN_NONOPT_SOURCE_FILES}>
|
||||||
|
|
||||||
# for dynamic load based solution main is compiled in a separate target
|
# for dynamic load based solution main is compiled in a separate target
|
||||||
|
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:src/game_object/twn_dynamic_game_object.c>
|
||||||
$<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:src/twn_main.c
|
$<$<NOT:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>>:src/twn_main.c
|
||||||
src/game_object/twn_static_game_object.c>
|
src/game_object/twn_static_game_object.c>
|
||||||
|
|
||||||
${SYSTEM_SOURCE_FILES})
|
${SYSTEM_SOURCE_FILES})
|
||||||
|
|
||||||
list(TRANSFORM TWN_SOURCE_FILES PREPEND ${TWN_ROOT_DIR}/)
|
list(TRANSFORM TWN_SOURCE_FILES PREPEND ${TWN_ROOT_DIR}/)
|
||||||
|
source_group(TREE ${TWN_ROOT_DIR} FILES ${TWN_NONOPT_SOURCE_FILES})
|
||||||
|
|
||||||
add_library(twn_third_parties STATIC ${TWN_THIRD_PARTY_SOURCE_FILES})
|
add_library(twn_third_parties STATIC ${TWN_THIRD_PARTY_SOURCE_FILES})
|
||||||
|
|
||||||
@ -123,8 +131,6 @@ else()
|
|||||||
add_library(${TWN_TARGET} STATIC ${TWN_SOURCE_FILES} ${twn_third_parties})
|
add_library(${TWN_TARGET} STATIC ${TWN_SOURCE_FILES} ${twn_third_parties})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
source_group(TREE ${TWN_ROOT_DIR} FILES ${TWN_SOURCE_FILES})
|
|
||||||
|
|
||||||
set_target_properties(${TWN_TARGET} PROPERTIES
|
set_target_properties(${TWN_TARGET} PROPERTIES
|
||||||
C_STANDARD 11
|
C_STANDARD 11
|
||||||
C_STANDARD_REQUIRED ON
|
C_STANDARD_REQUIRED ON
|
||||||
@ -133,7 +139,8 @@ set_target_properties(${TWN_TARGET} PROPERTIES
|
|||||||
# precompile commonly used not-so-small headers
|
# precompile commonly used not-so-small headers
|
||||||
target_precompile_headers(${TWN_TARGET} PRIVATE
|
target_precompile_headers(${TWN_TARGET} PRIVATE
|
||||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include/glad/glad.h>
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include/glad/glad.h>
|
||||||
third-party/stb/stb_ds.h)
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:${SDL2_INCLUDE_DIR}/SDL.h>
|
||||||
|
third-party/physfs/src/physfs.h)
|
||||||
|
|
||||||
|
|
||||||
function(give_options_without_warnings target)
|
function(give_options_without_warnings target)
|
||||||
@ -144,44 +151,77 @@ function(give_options_without_warnings target)
|
|||||||
-fno-signed-zeros
|
-fno-signed-zeros
|
||||||
-fno-trapping-math
|
-fno-trapping-math
|
||||||
-freciprocal-math
|
-freciprocal-math
|
||||||
|
# TODO: require packaging for web case
|
||||||
$<$<BOOL:${EMSCRIPTEN}>:-sUSE_SDL=2>)
|
$<$<BOOL:${EMSCRIPTEN}>:-sUSE_SDL=2>)
|
||||||
|
|
||||||
set(BUILD_FLAGS_RELEASE
|
set(BUILD_FLAGS_RELEASE
|
||||||
-O3
|
-O3
|
||||||
-flto=auto
|
-flto=$<IF:$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>,thin,auto>
|
||||||
-mavx -mavx2
|
$<$<STREQUAL:${CMAKE_SYSTEM_PROCESSOR},AMD64>:-sse2 -mavx -mavx2>
|
||||||
-Wl,--gc-sections
|
$<$<BOOL:${EMSCRIPTEN}>:-msimd128 -mrelaxed-simd>
|
||||||
-fdata-sections
|
|
||||||
-ffunction-sections
|
|
||||||
-funroll-loops
|
-funroll-loops
|
||||||
-fomit-frame-pointer
|
-fomit-frame-pointer
|
||||||
-s)
|
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-s>)
|
||||||
|
|
||||||
set(BUILD_FLAGS_DEBUG
|
set(BUILD_FLAGS_DEBUG
|
||||||
-O0
|
-O0
|
||||||
-g3
|
-g3
|
||||||
-gdwarf
|
-gdwarf
|
||||||
-fno-omit-frame-pointer
|
-fno-omit-frame-pointer
|
||||||
$<$<BOOL:${TWN_SANITIZE}>:-fstack-protector-all -fsanitize=undefined -fsanitize=address>
|
$<$<BOOL:${TWN_SANITIZE}>:-fstack-protector-all -fsanitize=undefined -fsanitize=address>)
|
||||||
$<$<BOOL:${EMSCRIPTEN}>:-gsource-map>)
|
|
||||||
|
set(LINK_FLAGS
|
||||||
|
-Bsymbolic-functions
|
||||||
|
|
||||||
|
$<$<BOOL:${EMSCRIPTEN}>:-sLEGACY_GL_EMULATION -sGL_FFP_ONLY -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2
|
||||||
|
-sENVIRONMENT=web -sDEFAULT_TO_CXX=0>
|
||||||
|
$<$<BOOL:${EMSCRIPTEN}>:--preload-file ${TWN_OUT_DIR}/data@data -sALLOW_MEMORY_GROWTH>
|
||||||
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:-Wl,--as-needed>
|
||||||
|
$<$<BOOL:${LINUX}>:-Wl,--hash-style=gnu>)
|
||||||
|
|
||||||
|
set(LINK_FLAGS_RELEASE
|
||||||
|
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>:-Wl,--strip-all>
|
||||||
|
${BUILD_FLAGS_RELEASE})
|
||||||
|
|
||||||
|
if (CMAKE_C_COMPILER_LINKER_ID MATCHES GNU OR CMAKE_C_COMPILER_LINKER_ID MATCHES GNUgold)
|
||||||
|
set(THINLTO_USAGE "-plugin-opt,")
|
||||||
|
endif()
|
||||||
|
if (CMAKE_C_COMPILER_LINKER_ID MATCHES LLD)
|
||||||
|
set(THINLTO_USAGE "--thinlto-")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (THINLTO_USAGE)
|
||||||
|
set(BUILD_SHARED_LIBRARY_FLAGS_RELEASE
|
||||||
|
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>:-Wl,${THINLTO_USAGE}cache-dir=${CMAKE_CURRENT_BINARY_DIR}/linker-cache/>
|
||||||
|
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>:-Wl,${THINLTO_USAGE}cache-policy=prune_after=30m>)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_options(${target} PUBLIC
|
target_compile_options(${target} PUBLIC
|
||||||
${BUILD_FLAGS}
|
${BUILD_FLAGS}
|
||||||
$<$<CONFIG:Release>:${BUILD_FLAGS_RELEASE}>
|
$<$<CONFIG:Release>:${BUILD_FLAGS_RELEASE}>
|
||||||
$<$<CONFIG:Debug>:${BUILD_FLAGS_DEBUG}>
|
$<$<CONFIG:Debug>:${BUILD_FLAGS_DEBUG}>)
|
||||||
$<$<BOOL:${LINUX}>:-Wl,-rpath,$ORIGIN/>)
|
|
||||||
|
|
||||||
target_link_options(${target} PUBLIC
|
target_link_options(${target} PUBLIC
|
||||||
${BUILD_FLAGS}
|
${BUILD_FLAGS}
|
||||||
# -Wl,--no-undefined # TODO: use later for implementing no-libc
|
${LINK_FLAGS}
|
||||||
$<$<CONFIG:Release>:${BUILD_FLAGS_RELEASE}>
|
$<$<CONFIG:Release>:${LINK_FLAGS_RELEASE}>
|
||||||
$<$<CONFIG:Debug>:${BUILD_FLAGS_DEBUG}>
|
$<$<CONFIG:Debug>:${BUILD_FLAGS_DEBUG}>)
|
||||||
$<$<BOOL:${LINUX}>:-Wl,-rpath,$ORIGIN/>
|
|
||||||
-Bsymbolic-functions
|
get_target_property(target_type ${target} TYPE)
|
||||||
-Wl,--hash-style=gnu)
|
if (target_type MATCHES SHARED_LIBRARY)
|
||||||
|
target_compile_options(${target} PUBLIC
|
||||||
|
$<$<CONFIG:Release>:${BUILD_SHARED_LIBRARY_FLAGS_RELEASE}>)
|
||||||
|
|
||||||
|
target_link_options(${target} PUBLIC
|
||||||
|
$<$<CONFIG:Release>:${BUILD_SHARED_LIBRARY_FLAGS_RELEASE}>)
|
||||||
|
elseif(CMAKE_BUILD_TYPE MATCHES Release)
|
||||||
|
target_compile_options(${target} PUBLIC
|
||||||
|
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Clang>:-mllvm=--enable-gvn-hoist>)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(${target} PRIVATE
|
target_compile_definitions(${target} PRIVATE
|
||||||
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:TWN_FEATURE_DYNLIB_GAME>)
|
$<$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>:TWN_FEATURE_DYNLIB_GAME>
|
||||||
|
_GNU_SOURCE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
@ -193,7 +233,9 @@ function(give_options target)
|
|||||||
-Wno-padded
|
-Wno-padded
|
||||||
-Wno-declaration-after-statement
|
-Wno-declaration-after-statement
|
||||||
-Wno-unsafe-buffer-usage
|
-Wno-unsafe-buffer-usage
|
||||||
-Wno-unused-command-line-argument)
|
-Wno-unused-command-line-argument
|
||||||
|
-Wno-covered-switch-default
|
||||||
|
-Wno-disabled-macro-expansion)
|
||||||
|
|
||||||
set(WARNING_FLAGS
|
set(WARNING_FLAGS
|
||||||
-Wall
|
-Wall
|
||||||
@ -205,6 +247,7 @@ function(give_options target)
|
|||||||
-Werror=vla
|
-Werror=vla
|
||||||
-Wno-missing-field-initializers
|
-Wno-missing-field-initializers
|
||||||
-Wunused-result
|
-Wunused-result
|
||||||
|
-Wno-pre-c11-compat
|
||||||
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-Wcast-align=strict>)
|
$<$<STREQUAL:${CMAKE_C_COMPILER_ID},Gnu>:-Wcast-align=strict>)
|
||||||
|
|
||||||
target_compile_options(${target} PRIVATE
|
target_compile_options(${target} PRIVATE
|
||||||
@ -221,8 +264,9 @@ function(include_deps target)
|
|||||||
third-party/physfs/extras
|
third-party/physfs/extras
|
||||||
third-party/libxm/include
|
third-party/libxm/include
|
||||||
third-party/stb
|
third-party/stb
|
||||||
third-party/x-watcher
|
third-party/dmon
|
||||||
third-party/tomlc99
|
third-party/tomlc99
|
||||||
|
third-party/fast_obj
|
||||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include>)
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:third-party/glad/include>)
|
||||||
|
|
||||||
list(TRANSFORM THIRD_PARTY_INCLUDES PREPEND ${TWN_ROOT_DIR}/)
|
list(TRANSFORM THIRD_PARTY_INCLUDES PREPEND ${TWN_ROOT_DIR}/)
|
||||||
@ -236,19 +280,40 @@ endfunction()
|
|||||||
function(link_deps target)
|
function(link_deps target)
|
||||||
target_link_libraries(${target} PUBLIC
|
target_link_libraries(${target} PUBLIC
|
||||||
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2>
|
$<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2>
|
||||||
|
$<$<NOT:$<OR:$<BOOL:${TWN_FEATURE_DYNLIB_GAME}>,$<BOOL:${EMSCRIPTEN}>>>:SDL2::SDL2main>
|
||||||
physfs-static
|
physfs-static
|
||||||
xms)
|
xms)
|
||||||
target_include_directories(${target} PUBLIC ${SDL2_INCLUDE_DIRS})
|
target_include_directories(${target} PUBLIC ${SDL2_INCLUDE_DIRS})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
function(use_townengine target sources output_directory)
|
function(put_townengine output_directory)
|
||||||
|
cmake_path(GET TWN_OUT_DIR STEM LAST_ONLY target)
|
||||||
|
|
||||||
|
add_executable(${target} ${TWN_ROOT_DIR}/src/twn_main.c)
|
||||||
|
target_link_options(${target} PRIVATE $<$<BOOL:${LINUX}>:-Wl,-rpath,$ORIGIN/>)
|
||||||
|
set_target_properties(${TWN_TARGET} PROPERTIES
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY ${output_directory})
|
||||||
|
set_target_properties(${target} PROPERTIES
|
||||||
|
OUTPUT_NAME ${target}
|
||||||
|
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${output_directory})
|
||||||
|
give_options(${target})
|
||||||
|
include_deps(${target})
|
||||||
|
link_deps(${target})
|
||||||
|
target_link_libraries(${target} PUBLIC ${TWN_TARGET})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
|
function(use_townengine sources output_directory)
|
||||||
|
cmake_path(GET TWN_OUT_DIR STEM LAST_ONLY target)
|
||||||
|
|
||||||
if(TWN_FEATURE_DYNLIB_GAME)
|
if(TWN_FEATURE_DYNLIB_GAME)
|
||||||
# game shared library, for reloading
|
# game shared library, for reloading
|
||||||
add_library(${target}_game SHARED ${sources})
|
add_library(${target}_game SHARED ${sources})
|
||||||
give_options(${target}_game)
|
give_options(${target}_game)
|
||||||
include_deps(${target}_game)
|
include_deps(${target}_game)
|
||||||
target_link_libraries(${target}_game PUBLIC $<$<NOT:$<BOOL:${EMSCRIPTEN}>>:SDL2::SDL2> ${TWN_TARGET})
|
target_link_libraries(${target}_game PUBLIC ${TWN_TARGET})
|
||||||
set_target_properties(${target}_game PROPERTIES
|
set_target_properties(${target}_game PROPERTIES
|
||||||
OUTPUT_NAME game
|
OUTPUT_NAME game
|
||||||
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
||||||
@ -257,6 +322,8 @@ function(use_townengine target sources output_directory)
|
|||||||
# launcher binary, loads game and engine shared library
|
# launcher binary, loads game and engine shared library
|
||||||
add_executable(${target} ${TWN_ROOT_DIR}/src/twn_main.c)
|
add_executable(${target} ${TWN_ROOT_DIR}/src/twn_main.c)
|
||||||
|
|
||||||
|
target_link_options(${target} PRIVATE $<$<BOOL:${LINUX}>:-Wl,-rpath,$ORIGIN/>)
|
||||||
|
|
||||||
# todo: copy instead?
|
# todo: copy instead?
|
||||||
# put libtownengine.so alongside the binary
|
# put libtownengine.so alongside the binary
|
||||||
set_target_properties(${TWN_TARGET} PROPERTIES
|
set_target_properties(${TWN_TARGET} PROPERTIES
|
||||||
@ -267,6 +334,7 @@ function(use_townengine target sources output_directory)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set_target_properties(${target} PROPERTIES
|
set_target_properties(${target} PROPERTIES
|
||||||
|
OUTPUT_NAME ${target}
|
||||||
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
LIBRARY_OUTPUT_DIRECTORY ${output_directory}
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${output_directory})
|
RUNTIME_OUTPUT_DIRECTORY ${output_directory})
|
||||||
|
|
||||||
@ -275,7 +343,7 @@ function(use_townengine target sources output_directory)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_options(${target} PRIVATE
|
target_compile_options(${target} PRIVATE
|
||||||
$<$<BOOL:${EMSCRIPTEN}>:--shell-file ${TWN_ROOT_DIR}/shell_minimal.html>)
|
$<$<BOOL:${EMSCRIPTEN}>:--shell-file ${TWN_ROOT_DIR}/src/shell_minimal.html>)
|
||||||
|
|
||||||
# system libraries
|
# system libraries
|
||||||
find_library(MATH_LIBRARY m)
|
find_library(MATH_LIBRARY m)
|
||||||
@ -304,10 +372,18 @@ link_deps(twn_third_parties)
|
|||||||
give_options(${TWN_TARGET})
|
give_options(${TWN_TARGET})
|
||||||
include_deps(${TWN_TARGET})
|
include_deps(${TWN_TARGET})
|
||||||
link_deps(${TWN_TARGET})
|
link_deps(${TWN_TARGET})
|
||||||
target_link_libraries(${TWN_TARGET} PUBLIC twn_third_parties)
|
|
||||||
target_include_directories(${TWN_TARGET} PRIVATE ${TWN_ROOT_DIR}/src)
|
target_include_directories(${TWN_TARGET} PRIVATE ${TWN_ROOT_DIR}/src)
|
||||||
|
target_link_libraries(${TWN_TARGET} PUBLIC
|
||||||
|
twn_third_parties
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/font.o)
|
||||||
|
|
||||||
# move compie_commands.json into root directory so that it plays nicer with code editors without any configuration
|
# embed resources
|
||||||
add_custom_target(copy-compile-commands ALL
|
# TODO: think of a portable way to compress/decompress them
|
||||||
${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/compile_commands.json
|
add_custom_command(
|
||||||
${TWN_ROOT_DIR})
|
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/font.o
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E env CMAKE_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} sh bin/prep-embed.sh
|
||||||
|
DEPENDS share/assets/Dernyns256.ttf)
|
||||||
|
|
||||||
|
add_custom_target(asset-compilation ALL DEPENDS
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/font.o)
|
||||||
|
@ -6,11 +6,11 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNBUILDDIR})
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
state.h
|
state.h
|
||||||
)
|
)
|
||||||
|
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[[deps]]
|
[[deps]]
|
||||||
source = "../../../common-data"
|
source = "../../../data"
|
||||||
name = "common-data"
|
name = "common-data"
|
||||||
|
@ -5,7 +5,6 @@ app_id = "bunnymark"
|
|||||||
dev_id = "morshy"
|
dev_id = "morshy"
|
||||||
|
|
||||||
[game]
|
[game]
|
||||||
base_render_width = 640
|
resolution = [ 640, 480 ]
|
||||||
base_render_height = 480
|
|
||||||
|
|
||||||
[engine]
|
[engine]
|
||||||
|
@ -13,40 +13,44 @@
|
|||||||
#define RIGHT_CLICK_ADD 500
|
#define RIGHT_CLICK_ADD 500
|
||||||
|
|
||||||
|
|
||||||
void handle_input(void)
|
static void handle_input(void)
|
||||||
{
|
{
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
if (ctx.mouse_window_position.y <= 60)
|
if (ctx.mouse_position.y <= 60)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (input_is_action_pressed("add_a_bit"))
|
if (input_action_pressed("add_a_bit"))
|
||||||
{ // Left click
|
{ // Left click
|
||||||
for (int i = 0; i < LEFT_CLICK_ADD; i++)
|
for (int i = 0; i < LEFT_CLICK_ADD; i++)
|
||||||
{
|
{
|
||||||
if (state->bunniesCount < MAX_BUNNIES)
|
if (state->bunniesCount < MAX_BUNNIES)
|
||||||
{
|
{
|
||||||
state->bunnies[state->bunniesCount].position = input_get_action_position("add_a_bit");
|
state->bunnies[state->bunniesCount].position = input_action_position("add_a_bit");
|
||||||
state->bunnies[state->bunniesCount].speed.x = (float)(rand() % 500 - 250) / 60.0;
|
state->bunnies[state->bunniesCount].speed.x = (float)(rand() % 500 - 250) / 60.0f;
|
||||||
state->bunnies[state->bunniesCount].speed.y = (float)(rand() % 500 - 250) / 60.0;
|
state->bunnies[state->bunniesCount].speed.y = (float)(rand() % 500 - 250) / 60.0f;
|
||||||
state->bunnies[state->bunniesCount].color =
|
state->bunnies[state->bunniesCount].color =
|
||||||
(Color){rand() % 190 + 50, rand() % 160 + 80, rand() % 140 + 100, 255};
|
(Color){(uint8_t)(state->bunniesCount % 190 + 50),
|
||||||
|
(uint8_t)((state->bunniesCount + 120) % 160 + 80),
|
||||||
|
(uint8_t)((state->bunniesCount + 65) % 140 + 100), 255};
|
||||||
state->bunniesCount++;
|
state->bunniesCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_is_action_pressed("add_a_lot"))
|
if (input_action_pressed("add_a_lot"))
|
||||||
{ // Right click
|
{ // Right click
|
||||||
for (int i = 0; i < RIGHT_CLICK_ADD; i++)
|
for (int i = 0; i < RIGHT_CLICK_ADD; i++)
|
||||||
{
|
{
|
||||||
if (state->bunniesCount < MAX_BUNNIES)
|
if (state->bunniesCount < MAX_BUNNIES)
|
||||||
{
|
{
|
||||||
state->bunnies[state->bunniesCount].position = input_get_action_position("add_a_lot");
|
state->bunnies[state->bunniesCount].position = input_action_position("add_a_lot");
|
||||||
state->bunnies[state->bunniesCount].speed.x = (float)(rand() % 500 - 250) / 60.0;
|
state->bunnies[state->bunniesCount].speed.x = (float)(rand() % 500 - 250) / 60.0f;
|
||||||
state->bunnies[state->bunniesCount].speed.y = (float)(rand() % 500 - 250) / 60.0;
|
state->bunnies[state->bunniesCount].speed.y = (float)(rand() % 500 - 250) / 60.0f;
|
||||||
state->bunnies[state->bunniesCount].color =
|
state->bunnies[state->bunniesCount].color =
|
||||||
(Color){rand() % 190 + 50, rand() % 160 + 80, rand() % 140 + 100, 255};
|
(Color){(uint8_t)(state->bunniesCount % 190 + 50),
|
||||||
|
(uint8_t)((state->bunniesCount + 120) % 160 + 80),
|
||||||
|
(uint8_t)((state->bunniesCount + 65) % 140 + 100), 255};
|
||||||
state->bunniesCount++;
|
state->bunniesCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,43 +66,37 @@ void game_tick(void)
|
|||||||
{ // First tick, initalizing data
|
{ // First tick, initalizing data
|
||||||
// Allocating State struct to store data there
|
// Allocating State struct to store data there
|
||||||
if (!ctx.udata)
|
if (!ctx.udata)
|
||||||
ctx.udata = ccalloc(1, sizeof(State));
|
ctx.udata = calloc(1, sizeof(State));
|
||||||
|
|
||||||
input_add_action("add_a_bit");
|
|
||||||
input_bind_action_control("add_a_bit", CONTROL_LEFT_MOUSE);
|
|
||||||
|
|
||||||
input_add_action("add_a_lot");
|
|
||||||
input_bind_action_control("add_a_lot", CONTROL_RIGHT_MOUSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
State *state = ctx.udata;
|
input_action("add_a_bit", "LCLICK");
|
||||||
|
input_action("add_a_lot", "RCLICK");
|
||||||
|
|
||||||
const double delta =
|
State *state = ctx.udata;
|
||||||
(double)(ctx.delta_time) / 1000.0; // Receiving floating point delta value (diving by 1000 based on vibe)
|
|
||||||
|
|
||||||
for (int i = 0; i < state->bunniesCount; i++)
|
for (int i = 0; i < state->bunniesCount; i++)
|
||||||
{
|
{
|
||||||
state->bunnies[i].position.x += state->bunnies[i].speed.x;
|
state->bunnies[i].position.x += state->bunnies[i].speed.x;
|
||||||
state->bunnies[i].position.y += state->bunnies[i].speed.y;
|
state->bunnies[i].position.y += state->bunnies[i].speed.y;
|
||||||
|
|
||||||
if (((state->bunnies[i].position.x + BUNNY_W / 2) > ctx.base_draw_w) ||
|
if (((state->bunnies[i].position.x + (float)BUNNY_W / 2) > (float)ctx.resolution.x) ||
|
||||||
((state->bunnies[i].position.x + BUNNY_W / 2) < 0))
|
((state->bunnies[i].position.x + (float)BUNNY_W / 2) < 0))
|
||||||
state->bunnies[i].speed.x *= -1;
|
state->bunnies[i].speed.x *= -1;
|
||||||
if (((state->bunnies[i].position.y + BUNNY_H / 2) > ctx.base_draw_h) ||
|
if (((state->bunnies[i].position.y + (float)BUNNY_H / 2) > (float)ctx.resolution.y) ||
|
||||||
((state->bunnies[i].position.y + BUNNY_H / 2 - 60) < 0))
|
((state->bunnies[i].position.y + (float)BUNNY_H / 2 - 60) < 0))
|
||||||
state->bunnies[i].speed.y *= -1;
|
state->bunnies[i].speed.y *= -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_input();
|
handle_input();
|
||||||
|
|
||||||
// Clear window with Gray color (set the background color this way)
|
// Clear window with Gray color (set the background color this way)
|
||||||
draw_rectangle((Rect){0, 0, ctx.base_draw_w, ctx.base_draw_h}, GRAY);
|
draw_rectangle((Rect){0, 0, (float)ctx.resolution.x, (float)ctx.resolution.y}, GRAY);
|
||||||
|
|
||||||
for (int i = 0; i < state->bunniesCount; i++)
|
for (int i = 0; i < state->bunniesCount; i++)
|
||||||
{ // Draw each bunny based on their position and color, also scale accordingly
|
{ // Draw each bunny based on their position and color, also scale accordingly
|
||||||
m_sprite(m_set(path, "wabbit_alpha.png"),
|
m_sprite(m_set(texture, "wabbit_alpha.png"),
|
||||||
m_set(rect, ((Rect){.x = (int)state->bunnies[i].position.x,
|
m_set(rect, ((Rect){.x = state->bunnies[i].position.x,
|
||||||
.y = (int)state->bunnies[i].position.y,
|
.y = state->bunnies[i].position.y,
|
||||||
.w = BUNNY_W * SPRITE_SCALE,
|
.w = BUNNY_W * SPRITE_SCALE,
|
||||||
.h = BUNNY_H * SPRITE_SCALE})),
|
.h = BUNNY_H * SPRITE_SCALE})),
|
||||||
m_opt(color, (state->bunnies[i].color)), m_opt(stretch, true), );
|
m_opt(color, (state->bunnies[i].color)), m_opt(stretch, true), );
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
|
|
||||||
#define MAX_BUNNIES 100000 // 100K bunnies limit
|
#define MAX_BUNNIES 500000 // 100K bunnies limit
|
||||||
#define BUNNY_W 26
|
#define BUNNY_W 26
|
||||||
#define BUNNY_H 37
|
#define BUNNY_H 37
|
||||||
#define SPRITE_SCALE 1
|
#define SPRITE_SCALE 1
|
||||||
|
1
apps/demos/crawl/data/assets/LICENSES
Normal file
1
apps/demos/crawl/data/assets/LICENSES
Normal file
@ -0,0 +1 @@
|
|||||||
|
castledoors.png - https://opengameart.org/content/castle-door - CC-BY 3.0
|
BIN
apps/demos/crawl/data/assets/brick.png
(Stored with Git LFS)
Normal file
BIN
apps/demos/crawl/data/assets/brick.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/demos/crawl/data/assets/castledoors.png
(Stored with Git LFS)
Normal file
BIN
apps/demos/crawl/data/assets/castledoors.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/demos/crawl/data/assets/lever.png
(Stored with Git LFS)
Normal file
BIN
apps/demos/crawl/data/assets/lever.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/demos/crawl/data/assets/mossy_rock.png
(Stored with Git LFS)
Normal file
BIN
apps/demos/crawl/data/assets/mossy_rock.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/demos/crawl/data/assets/pebbles.png
(Stored with Git LFS)
Normal file
BIN
apps/demos/crawl/data/assets/pebbles.png
(Stored with Git LFS)
Normal file
Binary file not shown.
37
apps/demos/crawl/data/levels/00.lvl
Normal file
37
apps/demos/crawl/data/levels/00.lvl
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
@map
|
||||||
|
-- Grid of symbols defined by @classes section.
|
||||||
|
#####
|
||||||
|
#.@.#
|
||||||
|
#...#
|
||||||
|
##.######
|
||||||
|
#...X...#
|
||||||
|
#./.#####
|
||||||
|
#####
|
||||||
|
|
||||||
|
|
||||||
|
@classes
|
||||||
|
-- Defines classes under symbols, which could have properties attached.
|
||||||
|
# stone_wall
|
||||||
|
wall_texture : /assets/brick.png
|
||||||
|
solid : 1
|
||||||
|
. stone_floor
|
||||||
|
tile_texture : /assets/mossy_rock.png
|
||||||
|
@ player_spawn
|
||||||
|
unique : 1
|
||||||
|
face : south
|
||||||
|
hold : torch
|
||||||
|
tile_texture : /assets/mossy_rock.png
|
||||||
|
X door
|
||||||
|
open_on_signal : sg_torch0
|
||||||
|
tile_texture : /assets/mossy_rock.png
|
||||||
|
face : horizon
|
||||||
|
face_texture : /assets/castledoors.png
|
||||||
|
/ lever
|
||||||
|
on_interact_emit : sg_torch0
|
||||||
|
tile_texture : /assets/mossy_rock.png
|
||||||
|
face : observer
|
||||||
|
face_texture : /assets/lever.png
|
||||||
|
|
||||||
|
@meta
|
||||||
|
-- Arbitrary sections could be defined with value pairs.
|
||||||
|
description : Test Level! Just two square rooms and a tunnel opened by lever.
|
71
apps/demos/crawl/data/scripts/game.lua
Normal file
71
apps/demos/crawl/data/scripts/game.lua
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
require("level")
|
||||||
|
require("render")
|
||||||
|
|
||||||
|
function lerp(a, b, x)
|
||||||
|
return a + ((b - a) * x)
|
||||||
|
end
|
||||||
|
|
||||||
|
function qlerp(a, b, x)
|
||||||
|
return lerp(a, b, x * x)
|
||||||
|
end
|
||||||
|
|
||||||
|
function game_tick()
|
||||||
|
if ctx.udata == nil then
|
||||||
|
ctx.udata = {
|
||||||
|
level = load_level("levels/00.lvl")
|
||||||
|
}
|
||||||
|
ctx.udata.player = {
|
||||||
|
position = ctx.udata.level.classes.player_spawn.position,
|
||||||
|
position_lerp = ctx.udata.level.classes.player_spawn.position,
|
||||||
|
direction = { x = 1, y = 0, z = 0 },
|
||||||
|
direction_lerp = { x = 1, y = 0, z = 0 },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
input_action { control = "A", name = "turn_left" }
|
||||||
|
input_action { control = "D", name = "turn_right" }
|
||||||
|
input_action { control = "W", name = "walk_forward" }
|
||||||
|
input_action { control = "S", name = "walk_backward" }
|
||||||
|
|
||||||
|
if input_action_just_released { name = "turn_left" } then
|
||||||
|
ctx.udata.player.direction = { x = ctx.udata.player.direction.z,
|
||||||
|
y = ctx.udata.player.direction.y,
|
||||||
|
z =-ctx.udata.player.direction.x }
|
||||||
|
end
|
||||||
|
|
||||||
|
if input_action_just_released { name = "turn_right" } then
|
||||||
|
ctx.udata.player.direction = { x =-ctx.udata.player.direction.z,
|
||||||
|
y = ctx.udata.player.direction.y,
|
||||||
|
z = ctx.udata.player.direction.x }
|
||||||
|
end
|
||||||
|
|
||||||
|
local move = { x = 0, y = 0 }
|
||||||
|
if input_action_just_released { name = "walk_forward" } then
|
||||||
|
move = { x = move.x + ctx.udata.player.direction.x, y = move.y + ctx.udata.player.direction.z }
|
||||||
|
end
|
||||||
|
if input_action_just_released { name = "walk_backward" } then
|
||||||
|
move = { x = move.x - ctx.udata.player.direction.x, y = move.y - ctx.udata.player.direction.z }
|
||||||
|
end
|
||||||
|
|
||||||
|
if ctx.udata.level.grid[ctx.udata.player.position.y + move.y][ctx.udata.player.position.x + move.x].solid ~= nil then
|
||||||
|
move = { x = 0, y = 0 }
|
||||||
|
end
|
||||||
|
|
||||||
|
ctx.udata.player.position = { x = ctx.udata.player.position.x + move.x, y = ctx.udata.player.position.y + move.y }
|
||||||
|
|
||||||
|
ctx.udata.player.position_lerp.x = qlerp(ctx.udata.player.position_lerp.x, ctx.udata.player.position.x, ctx.frame_duration * 30)
|
||||||
|
ctx.udata.player.position_lerp.y = qlerp(ctx.udata.player.position_lerp.y, ctx.udata.player.position.y, ctx.frame_duration * 30)
|
||||||
|
ctx.udata.player.direction_lerp.x = qlerp(ctx.udata.player.direction_lerp.x, ctx.udata.player.direction.x, ctx.frame_duration * 40)
|
||||||
|
ctx.udata.player.direction_lerp.z = qlerp(ctx.udata.player.direction_lerp.z, ctx.udata.player.direction.z, ctx.frame_duration * 40)
|
||||||
|
|
||||||
|
draw_camera {
|
||||||
|
position = {
|
||||||
|
x = ctx.udata.player.position_lerp.x + 0.5 - ctx.udata.player.direction.x / 2,
|
||||||
|
y = 0.5,
|
||||||
|
z = ctx.udata.player.position_lerp.y + 0.5 - ctx.udata.player.direction.z / 2,
|
||||||
|
},
|
||||||
|
direction = ctx.udata.player.direction_lerp,
|
||||||
|
}
|
||||||
|
|
||||||
|
render_dungeon(ctx.udata.level)
|
||||||
|
end
|
91
apps/demos/crawl/data/scripts/level.lua
Normal file
91
apps/demos/crawl/data/scripts/level.lua
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
function load_level(file)
|
||||||
|
local f = file_read { file = file }
|
||||||
|
|
||||||
|
local result = {
|
||||||
|
-- templates to fill the grid with
|
||||||
|
classes = {
|
||||||
|
-- predefined empty tile
|
||||||
|
void = { },
|
||||||
|
},
|
||||||
|
-- symbol to class lookup table
|
||||||
|
glossary = {
|
||||||
|
[" "] = "void",
|
||||||
|
},
|
||||||
|
-- grid consists of expanded classes, of size dimensions
|
||||||
|
grid = {},
|
||||||
|
-- map consists of original rows of symbols that the grid is constructed from
|
||||||
|
map = {},
|
||||||
|
-- maximum extends of the map, unspecified tiles are filled with "void"
|
||||||
|
size = { x = 0, y = 0 },
|
||||||
|
}
|
||||||
|
|
||||||
|
-- iterate over lines
|
||||||
|
local section, subsection = "none", "none"
|
||||||
|
local from = 1
|
||||||
|
local start, limit = string.find(f, "\n", from)
|
||||||
|
while start do
|
||||||
|
local line = string.sub(f, from, start - 1)
|
||||||
|
-- skip over
|
||||||
|
if #line == 0 or line:find("^%-%-%s*") then
|
||||||
|
goto skip
|
||||||
|
-- start new section
|
||||||
|
elseif line:find("^@%g+") then
|
||||||
|
section = line:sub(2); subsection = "none"
|
||||||
|
-- decode map one line at a time
|
||||||
|
elseif section == "map" then
|
||||||
|
if result.size.x < #line then
|
||||||
|
result.size.x = #line
|
||||||
|
end
|
||||||
|
result.map[#result.map + 1] = line
|
||||||
|
-- templates to expand
|
||||||
|
elseif section == "classes" then
|
||||||
|
-- properties
|
||||||
|
if line:find("^ %g+") then
|
||||||
|
local _, _, property, value = line:find("^ (%g+)%s?:%s?(.*)")
|
||||||
|
result.classes[subsection][property] = value
|
||||||
|
goto skip
|
||||||
|
end
|
||||||
|
local symbol, classname = line:sub(1,1), line:sub(3)
|
||||||
|
result.classes[classname] = {
|
||||||
|
symbol = symbol,
|
||||||
|
}
|
||||||
|
result.glossary[symbol] = classname
|
||||||
|
subsection = classname
|
||||||
|
elseif section ~= "none" then
|
||||||
|
local _, _, property, value = line:find("^(%g+)%s?:%s?(.*)")
|
||||||
|
if result[section] == nil then
|
||||||
|
result[section] = {}
|
||||||
|
end
|
||||||
|
result[section][property] = value
|
||||||
|
end
|
||||||
|
::skip::
|
||||||
|
from = limit + 1
|
||||||
|
start, limit = string.find(f, "\n", from)
|
||||||
|
end
|
||||||
|
-- post process, expand map to grid
|
||||||
|
for y = 1, #result.map do
|
||||||
|
result.grid[y] = {}
|
||||||
|
for x = 1, result.size.x do
|
||||||
|
-- past defined for line
|
||||||
|
local symbol
|
||||||
|
if x > #result.map[y] then
|
||||||
|
symbol = " "
|
||||||
|
else
|
||||||
|
symbol = result.map[y]:sub(x,x)
|
||||||
|
end
|
||||||
|
local class = result.classes[result.glossary[symbol]]
|
||||||
|
if class["unique"] ~= nil then
|
||||||
|
class.position = { x = x, y = y }
|
||||||
|
end
|
||||||
|
result.grid[y][x] = class
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result.size.y = #result.map
|
||||||
|
|
||||||
|
print(result.meta.description)
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
88
apps/demos/crawl/data/scripts/render.lua
Normal file
88
apps/demos/crawl/data/scripts/render.lua
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
-- if this is too wasteful, one could check nerby tiles to see whether faces could be visible
|
||||||
|
-- more robust solution would be to travel the level from observer point of view
|
||||||
|
function render_dungeon(dungeon)
|
||||||
|
for y = 1, dungeon.size.y do
|
||||||
|
for x = 1, dungeon.size.x do
|
||||||
|
if dungeon.grid[y][x].wall_texture ~= nil then
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].wall_texture,
|
||||||
|
v3 = { x = x, y = 1, z = y },
|
||||||
|
v2 = { x = x, y = 0, z = y },
|
||||||
|
v1 = { x = x + 1, y = 0, z = y },
|
||||||
|
v0 = { x = x + 1, y = 1, z = y },
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].wall_texture,
|
||||||
|
v3 = { x = x + 1, y = 1, z = y },
|
||||||
|
v2 = { x = x + 1, y = 0, z = y },
|
||||||
|
v1 = { x = x + 1, y = 0, z = y + 1 },
|
||||||
|
v0 = { x = x + 1, y = 1, z = y + 1 },
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].wall_texture,
|
||||||
|
v3 = { x = x + 1, y = 1, z = y + 1 },
|
||||||
|
v2 = { x = x + 1, y = 0, z = y + 1 },
|
||||||
|
v1 = { x = x, y = 0, z = y + 1 },
|
||||||
|
v0 = { x = x, y = 1, z = y + 1 },
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].wall_texture,
|
||||||
|
v3 = { x = x, y = 1, z = y + 1 },
|
||||||
|
v2 = { x = x, y = 0, z = y + 1 },
|
||||||
|
v1 = { x = x, y = 0, z = y },
|
||||||
|
v0 = { x = x, y = 1, z = y },
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif dungeon.grid[y][x].tile_texture ~= nil then
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].tile_texture,
|
||||||
|
v0 = { x = x + 1, y = 0, z = y },
|
||||||
|
v1 = { x = x, y = 0, z = y },
|
||||||
|
v2 = { x = x, y = 0, z = y + 1 },
|
||||||
|
v3 = { x = x + 1, y = 0, z = y + 1},
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].tile_texture,
|
||||||
|
v3 = { x = x + 1, y = 1, z = y },
|
||||||
|
v2 = { x = x, y = 1, z = y },
|
||||||
|
v1 = { x = x, y = 1, z = y + 1 },
|
||||||
|
v0 = { x = x + 1, y = 1, z = y + 1},
|
||||||
|
texture_region = { w = 128, h = 128 },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
if dungeon.grid[y][x].face_texture ~= nil then
|
||||||
|
if dungeon.grid[y][x].face == "horizon" then
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].face_texture,
|
||||||
|
v3 = { x = x + 1, y = 1, z = y },
|
||||||
|
v2 = { x = x + 1, y = 0, z = y },
|
||||||
|
v1 = { x = x + 1, y = 0, z = y + 1 },
|
||||||
|
v0 = { x = x + 1, y = 1, z = y + 1 },
|
||||||
|
texture_region = { w = 64, h = 64 },
|
||||||
|
}
|
||||||
|
draw_quad {
|
||||||
|
texture = dungeon.grid[y][x].face_texture,
|
||||||
|
v3 = { x = x, y = 1, z = y + 1 },
|
||||||
|
v2 = { x = x, y = 0, z = y + 1 },
|
||||||
|
v1 = { x = x, y = 0, z = y },
|
||||||
|
v0 = { x = x, y = 1, z = y },
|
||||||
|
texture_region = { w = 64, h = 64 },
|
||||||
|
}
|
||||||
|
|
||||||
|
elseif dungeon.grid[y][x].face == "observer" then
|
||||||
|
draw_billboard {
|
||||||
|
texture = dungeon.grid[y][x].face_texture,
|
||||||
|
position = { x = x + 0.5, y = 0.5, z = y + 0.5 },
|
||||||
|
size = { x = 0.5, y = 0.5 },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
27
apps/demos/crawl/data/twn.toml
Normal file
27
apps/demos/crawl/data/twn.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# This file contains everything about the engine and your game that can be
|
||||||
|
# configured before it runs.
|
||||||
|
#
|
||||||
|
# Optional settings are commented out, with their default values shown.
|
||||||
|
# Invalid values in these settings will be ignored.
|
||||||
|
|
||||||
|
# Data about your game as an application
|
||||||
|
[about]
|
||||||
|
title = "Template"
|
||||||
|
developer = "You"
|
||||||
|
app_id = "template"
|
||||||
|
dev_id = "you"
|
||||||
|
|
||||||
|
# Game runtime details
|
||||||
|
[game]
|
||||||
|
resolution = [ 640, 480 ]
|
||||||
|
interpreter = "$TWNROOT/apps/twnlua"
|
||||||
|
#debug = true
|
||||||
|
|
||||||
|
# Engine tweaks. You probably don't need to change these
|
||||||
|
[engine]
|
||||||
|
#ticks_per_second = 60 # minimum of 8
|
||||||
|
#keybind_slots = 3 # minimum of 1
|
||||||
|
#texture_atlas_size = 2048 # minimum of 32
|
||||||
|
#font_texture_size = 2048 # minimum of 1024
|
||||||
|
#font_oversampling = 4 # minimum of 0
|
||||||
|
#font_filtering = "linear" # possible values: "nearest", "linear"
|
@ -6,7 +6,7 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNBUILDDIR})
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
@ -20,4 +20,4 @@ set(SOURCE_FILES
|
|||||||
scenes/ingame.c scenes/ingame.h
|
scenes/ingame.c scenes/ingame.h
|
||||||
)
|
)
|
||||||
|
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
use_townengine("${SOURCE_FILES}" ${TWN_OUT_DIR})
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[[deps]]
|
[[deps]]
|
||||||
source = "../../common-data" # where does it come from, might be an url
|
source = "../../../data" # where does it come from, might be an url
|
||||||
name = "common-data" # should be globally unique
|
name = "common-data" # should be globally unique
|
||||||
|
@ -5,7 +5,6 @@ app_id = "platformer-demo"
|
|||||||
dev_id = "townengine-team"
|
dev_id = "townengine-team"
|
||||||
|
|
||||||
[game]
|
[game]
|
||||||
base_render_width = 640
|
resolution = [ 640, 360 ]
|
||||||
base_render_height = 360
|
|
||||||
|
|
||||||
[engine]
|
[engine]
|
||||||
|
@ -12,52 +12,21 @@
|
|||||||
void game_tick(void) {
|
void game_tick(void) {
|
||||||
if (ctx.initialization_needed) {
|
if (ctx.initialization_needed) {
|
||||||
if (!ctx.udata) {
|
if (!ctx.udata) {
|
||||||
ctx.udata = ccalloc(1, sizeof (State));
|
ctx.udata = calloc(1, sizeof (State));
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
state->ctx = &ctx;
|
state->ctx = &ctx;
|
||||||
state->scene = title_scene(state);
|
state->scene = title_scene(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
input_add_action("debug_toggle");
|
|
||||||
input_bind_action_control("debug_toggle", CONTROL_BACKSPACE);
|
|
||||||
|
|
||||||
input_add_action("debug_dump_atlases");
|
|
||||||
input_bind_action_control("debug_dump_atlases", CONTROL_HOME);
|
|
||||||
|
|
||||||
input_add_action("player_left");
|
|
||||||
input_bind_action_control("player_left", CONTROL_A);
|
|
||||||
|
|
||||||
input_add_action("player_right");
|
|
||||||
input_bind_action_control("player_right", CONTROL_D);
|
|
||||||
|
|
||||||
input_add_action("player_forward");
|
|
||||||
input_bind_action_control("player_forward", CONTROL_W);
|
|
||||||
|
|
||||||
input_add_action("player_backward");
|
|
||||||
input_bind_action_control("player_backward", CONTROL_S);
|
|
||||||
|
|
||||||
input_add_action("player_jump");
|
|
||||||
input_bind_action_control("player_jump", CONTROL_SPACE);
|
|
||||||
|
|
||||||
input_add_action("player_run");
|
|
||||||
input_bind_action_control("player_run", CONTROL_LSHIFT);
|
|
||||||
|
|
||||||
input_add_action("ui_accept");
|
|
||||||
input_bind_action_control("ui_accept", CONTROL_RETURN);
|
|
||||||
|
|
||||||
input_add_action("mouse_capture_toggle");
|
|
||||||
input_bind_action_control("mouse_capture_toggle", CONTROL_ESCAPE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
if (input_is_action_just_pressed("debug_toggle")) {
|
input_action("debug_toggle", "BACKSPACE");
|
||||||
ctx.debug = !ctx.debug;
|
input_action("debug_dump_atlases", "HOME");
|
||||||
}
|
|
||||||
|
|
||||||
if (input_is_action_just_pressed("debug_dump_atlases")) {
|
if (input_action_just_pressed("debug_toggle")) {
|
||||||
textures_dump_atlases();
|
ctx.debug = !ctx.debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->scene->tick(state);
|
state->scene->tick(state);
|
||||||
|
@ -11,28 +11,28 @@
|
|||||||
|
|
||||||
|
|
||||||
static void update_timers(Player *player) {
|
static void update_timers(Player *player) {
|
||||||
tick_timer(&player->jump_air_timer);
|
player->jump_air_timer = player->jump_air_timer - 1 <= 0 ? 0 : player->jump_air_timer - 1;
|
||||||
tick_timer(&player->jump_coyote_timer);
|
player->jump_coyote_timer = player->jump_coyote_timer - 1 <= 0 ? 0 : player->jump_coyote_timer - 1;
|
||||||
tick_timer(&player->jump_buffer_timer);
|
player->jump_buffer_timer = player->jump_buffer_timer - 1 <= 0 ? 0 : player->jump_buffer_timer - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void input_move(Player *player) {
|
static void input_move(Player *player) {
|
||||||
/* apply horizontal damping when the player stops moving */
|
/* apply horizontal damping when the player stops moving */
|
||||||
/* in other words, make it decelerate to a standstill */
|
/* in other words, make it decelerate to a standstill */
|
||||||
if (!input_is_action_pressed("player_left") &&
|
if (!input_action_pressed("player_left") &&
|
||||||
!input_is_action_pressed("player_right"))
|
!input_action_pressed("player_right"))
|
||||||
{
|
{
|
||||||
player->dx *= player->horizontal_damping;
|
player->dx *= player->horizontal_damping;
|
||||||
}
|
}
|
||||||
|
|
||||||
int input_dir = 0;
|
int input_dir = 0;
|
||||||
if (input_is_action_pressed("player_left"))
|
if (input_action_pressed("player_left"))
|
||||||
input_dir = -1;
|
input_dir = -1;
|
||||||
if (input_is_action_pressed("player_right"))
|
if (input_action_pressed("player_right"))
|
||||||
input_dir = 1;
|
input_dir = 1;
|
||||||
if (input_is_action_pressed("player_left") &&
|
if (input_action_pressed("player_left") &&
|
||||||
input_is_action_pressed("player_right"))
|
input_action_pressed("player_right"))
|
||||||
input_dir = 0;
|
input_dir = 0;
|
||||||
|
|
||||||
player->dx += (float)input_dir * player->run_horizontal_speed;
|
player->dx += (float)input_dir * player->run_horizontal_speed;
|
||||||
@ -56,7 +56,7 @@ static void jump(Player *player) {
|
|||||||
static void input_jump(Player *player) {
|
static void input_jump(Player *player) {
|
||||||
player->current_gravity_multiplier = player->jump_default_multiplier;
|
player->current_gravity_multiplier = player->jump_default_multiplier;
|
||||||
|
|
||||||
if (input_is_action_just_pressed("player_jump")) {
|
if (input_action_just_pressed("player_jump")) {
|
||||||
player->jump_air_timer = 0;
|
player->jump_air_timer = 0;
|
||||||
player->jump_buffer_timer = player->jump_buffer_ticks;
|
player->jump_buffer_timer = player->jump_buffer_ticks;
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ static void input_jump(Player *player) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_is_action_pressed("player_jump")) {
|
if (input_action_pressed("player_jump")) {
|
||||||
if (player->action != PLAYER_ACTION_GROUND && player->jump_air_timer > 0) {
|
if (player->action != PLAYER_ACTION_GROUND && player->jump_air_timer > 0) {
|
||||||
player->current_gravity_multiplier = player->jump_boosted_multiplier;
|
player->current_gravity_multiplier = player->jump_boosted_multiplier;
|
||||||
player->dy += player->jump_force_increase;
|
player->dy += player->jump_force_increase;
|
||||||
@ -147,7 +147,7 @@ static bool corner_correct(Player *player, Rect collision) {
|
|||||||
|
|
||||||
static void calc_collisions_x(Player *player) {
|
static void calc_collisions_x(Player *player) {
|
||||||
Rect collision;
|
Rect collision;
|
||||||
bool is_colliding = world_find_intersect_frect(player->world, player->collider_x, &collision);
|
bool is_colliding = world_find_rect_intersects(player->world, player->collider_x, &collision);
|
||||||
if (!is_colliding) return;
|
if (!is_colliding) return;
|
||||||
|
|
||||||
float player_center_x = player->collider_x.x + (player->collider_x.w / 2);
|
float player_center_x = player->collider_x.x + (player->collider_x.w / 2);
|
||||||
@ -164,7 +164,7 @@ static void calc_collisions_x(Player *player) {
|
|||||||
|
|
||||||
static void calc_collisions_y(Player *player) {
|
static void calc_collisions_y(Player *player) {
|
||||||
Rect collision;
|
Rect collision;
|
||||||
bool is_colliding = world_find_intersect_frect(player->world, player->collider_y, &collision);
|
bool is_colliding = world_find_rect_intersects(player->world, player->collider_y, &collision);
|
||||||
if (!is_colliding) return;
|
if (!is_colliding) return;
|
||||||
|
|
||||||
float player_center_y = player->collider_y.y + (player->collider_y.h / 2);
|
float player_center_y = player->collider_y.y + (player->collider_y.h / 2);
|
||||||
@ -203,7 +203,7 @@ static void calc_collisions_y(Player *player) {
|
|||||||
|
|
||||||
|
|
||||||
Player *player_create(World *world) {
|
Player *player_create(World *world) {
|
||||||
Player *player = cmalloc(sizeof *player);
|
Player *player = malloc(sizeof *player);
|
||||||
|
|
||||||
*player = (Player) {
|
*player = (Player) {
|
||||||
.world = world,
|
.world = world,
|
||||||
@ -255,6 +255,10 @@ static void drawdef(Player *player) {
|
|||||||
draw_circle((Vec2) { 256, 128 },
|
draw_circle((Vec2) { 256, 128 },
|
||||||
24,
|
24,
|
||||||
(Color) { 255, 0, 0, 255 });
|
(Color) { 255, 0, 0, 255 });
|
||||||
|
|
||||||
|
draw_circle((Vec2) { 304, 128 },
|
||||||
|
24,
|
||||||
|
(Color) { 255, 0, 0, 255 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,28 +11,20 @@
|
|||||||
static void ingame_tick(State *state) {
|
static void ingame_tick(State *state) {
|
||||||
SceneIngame *scn = (SceneIngame *)state->scene;
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
|
input_action("player_left", "A");
|
||||||
|
input_action("player_right", "D");
|
||||||
|
input_action("player_forward", "W");
|
||||||
|
input_action("player_backward", "S");
|
||||||
|
input_action("player_jump", "SPACE");
|
||||||
|
input_action("player_run", "LSHIFT");
|
||||||
|
|
||||||
|
draw_camera_2d((Vec2){ scn->player->rect.x + scn->player->rect.w / 2 - ctx.resolution.x / 2,
|
||||||
|
scn->player->rect.y + scn->player->rect.h / 2 - ctx.resolution.y / 2 },
|
||||||
|
0, 1
|
||||||
|
);
|
||||||
|
|
||||||
world_drawdef(scn->world);
|
world_drawdef(scn->world);
|
||||||
player_calc(scn->player);
|
player_calc(scn->player);
|
||||||
|
|
||||||
const Vec3 right = m_vec_norm(m_vec_cross(scn->cam.target, scn->cam.up));
|
|
||||||
const float speed = 0.04f; /* TODO: put this in a better place */
|
|
||||||
if (input_is_action_pressed("player_left"))
|
|
||||||
scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(right, speed));
|
|
||||||
|
|
||||||
if (input_is_action_pressed("player_right"))
|
|
||||||
scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(right, speed));
|
|
||||||
|
|
||||||
if (input_is_action_pressed("player_forward"))
|
|
||||||
scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
|
|
||||||
|
|
||||||
if (input_is_action_pressed("player_backward"))
|
|
||||||
scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
|
|
||||||
|
|
||||||
if (input_is_action_pressed("player_jump"))
|
|
||||||
scn->cam.pos.y += speed;
|
|
||||||
|
|
||||||
if (input_is_action_pressed("player_run"))
|
|
||||||
scn->cam.pos.y -= speed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -47,14 +39,12 @@ static void ingame_end(State *state) {
|
|||||||
Scene *ingame_scene(State *state) {
|
Scene *ingame_scene(State *state) {
|
||||||
(void)state;
|
(void)state;
|
||||||
|
|
||||||
SceneIngame *new_scene = ccalloc(1, sizeof *new_scene);
|
SceneIngame *new_scene = calloc(1, sizeof *new_scene);
|
||||||
new_scene->base.tick = ingame_tick;
|
new_scene->base.tick = ingame_tick;
|
||||||
new_scene->base.end = ingame_end;
|
new_scene->base.end = ingame_end;
|
||||||
|
|
||||||
new_scene->world = world_create();
|
new_scene->world = world_create();
|
||||||
new_scene->player = player_create(new_scene->world);
|
new_scene->player = player_create(new_scene->world);
|
||||||
|
|
||||||
new_scene->cam = (Camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 };
|
|
||||||
|
|
||||||
return (Scene *)new_scene;
|
return (Scene *)new_scene;
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,6 @@ typedef struct SceneIngame {
|
|||||||
World *world;
|
World *world;
|
||||||
Player *player;
|
Player *player;
|
||||||
|
|
||||||
Camera cam;
|
|
||||||
|
|
||||||
/* TODO: put this in a better place */
|
/* TODO: put this in a better place */
|
||||||
float yaw;
|
float yaw;
|
||||||
float pitch;
|
float pitch;
|
||||||
|
@ -5,33 +5,32 @@
|
|||||||
|
|
||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
static void title_tick(State *state) {
|
static void title_tick(State *state) {
|
||||||
SceneTitle *scn = (SceneTitle *)state->scene;
|
SceneTitle *scn = (SceneTitle *)state->scene;
|
||||||
(void)scn;
|
(void)scn;
|
||||||
|
|
||||||
if (input_is_action_just_pressed("ui_accept")) {
|
input_action("ui_accept", "ENTER");
|
||||||
|
|
||||||
|
if (input_action_just_pressed("ui_accept")) {
|
||||||
switch_to(state, ingame_scene);
|
switch_to(state, ingame_scene);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
m_sprite("/assets/title.png", ((Rect) {
|
m_sprite("/assets/title.png", ((Rect) {
|
||||||
((float)ctx.base_draw_w / 2) - ((float)320 / 2), 64, 320, 128 }));
|
((float)ctx.resolution.x / 2) - ((float)320 / 2), 64, 320, 128 }));
|
||||||
|
|
||||||
|
|
||||||
/* draw the tick count as an example of dynamic text */
|
/* draw the tick count as an example of dynamic text */
|
||||||
size_t text_str_len = snprintf(NULL, 0, "%lu", state->ctx->tick_count) + 1;
|
size_t text_str_len = snprintf(NULL, 0, "%llu", (unsigned long long)state->ctx->frame_number) + 1;
|
||||||
char *text_str = cmalloc(text_str_len);
|
char *text_str = malloc(text_str_len);
|
||||||
snprintf(text_str, text_str_len, "%lu", state->ctx->tick_count);
|
snprintf(text_str, text_str_len, "%llu", (unsigned long long)state->ctx->frame_number);
|
||||||
|
|
||||||
const char *font = "fonts/kenney-pixel.ttf";
|
const char *font = "fonts/kenney-pixel.ttf";
|
||||||
int text_h = 32;
|
float text_h = 32;
|
||||||
int text_w = draw_text_width(text_str, text_h, font);
|
float text_w = draw_text_width(text_str, text_h, font);
|
||||||
|
|
||||||
draw_rectangle(
|
draw_rectangle(
|
||||||
(Rect) {
|
(Rect) {
|
||||||
@ -64,7 +63,7 @@ static void title_end(State *state) {
|
|||||||
Scene *title_scene(State *state) {
|
Scene *title_scene(State *state) {
|
||||||
(void)state;
|
(void)state;
|
||||||
|
|
||||||
SceneTitle *new_scene = ccalloc(1, sizeof *new_scene);
|
SceneTitle *new_scene = calloc(1, sizeof *new_scene);
|
||||||
new_scene->base.tick = title_tick;
|
new_scene->base.tick = title_tick;
|
||||||
new_scene->base.end = title_end;
|
new_scene->base.end = title_end;
|
||||||
|
|
||||||
|
@ -12,11 +12,11 @@ static void update_tiles(struct World *world) {
|
|||||||
for (size_t row = 0; row < world->tilemap_height; ++row) {
|
for (size_t row = 0; row < world->tilemap_height; ++row) {
|
||||||
for (size_t col = 0; col < world->tilemap_width; ++col) {
|
for (size_t col = 0; col < world->tilemap_width; ++col) {
|
||||||
world->tiles[(row * world->tilemap_width) + col] = (struct Tile) {
|
world->tiles[(row * world->tilemap_width) + col] = (struct Tile) {
|
||||||
.rect = (Recti) {
|
.rect = (Rect) {
|
||||||
.x = (int)col * world->tile_size,
|
.x = (float)(col * world->tile_size),
|
||||||
.y = (int)row * world->tile_size,
|
.y = (float)(row * world->tile_size),
|
||||||
.w = world->tile_size,
|
.w = (float)world->tile_size,
|
||||||
.h = world->tile_size,
|
.h = (float)world->tile_size,
|
||||||
},
|
},
|
||||||
.type = world->tilemap[row][col],
|
.type = world->tilemap[row][col],
|
||||||
};
|
};
|
||||||
@ -25,10 +25,10 @@ static void update_tiles(struct World *world) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Vec2i to_grid_location(struct World *world, float x, float y) {
|
static Vec2 to_grid_location(struct World *world, float x, float y) {
|
||||||
return (Vec2i) {
|
return (Vec2) {
|
||||||
.x = (int)floor(x / (float)world->tile_size),
|
.x = floor(x / (float)world->tile_size),
|
||||||
.y = (int)floor(y / (float)world->tile_size),
|
.y = floor(y / (float)world->tile_size),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,14 +39,13 @@ static void drawdef_debug(struct World *world) {
|
|||||||
for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
|
for (size_t i = 0; i < world->tilemap_height * world->tilemap_width; ++i) {
|
||||||
if (world->tiles[i].type == TILE_TYPE_VOID) continue;
|
if (world->tiles[i].type == TILE_TYPE_VOID) continue;
|
||||||
|
|
||||||
draw_rectangle(to_frect(world->tiles[i].rect),
|
draw_rectangle(world->tiles[i].rect, (Color) { 255, 0, 255, 128 });
|
||||||
(Color) { 255, 0, 255, 128 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct World *world_create(void) {
|
struct World *world_create(void) {
|
||||||
struct World *world = cmalloc(sizeof *world);
|
struct World *world = malloc(sizeof *world);
|
||||||
|
|
||||||
*world = (struct World) {
|
*world = (struct World) {
|
||||||
.tiles = NULL,
|
.tiles = NULL,
|
||||||
@ -59,9 +58,9 @@ struct World *world_create(void) {
|
|||||||
/* create the tilemap */
|
/* create the tilemap */
|
||||||
/* it simply stores what's in each tile as a 2d array */
|
/* it simply stores what's in each tile as a 2d array */
|
||||||
/* on its own, it's entirely unrelated to drawing or logic */
|
/* on its own, it's entirely unrelated to drawing or logic */
|
||||||
world->tilemap = cmalloc(sizeof *world->tilemap * world->tilemap_height);
|
world->tilemap = malloc(sizeof *world->tilemap * world->tilemap_height);
|
||||||
for (size_t i = 0; i < world->tilemap_height; ++i) {
|
for (size_t i = 0; i < world->tilemap_height; ++i) {
|
||||||
world->tilemap[i] = cmalloc(sizeof **world->tilemap * world->tilemap_width);
|
world->tilemap[i] = malloc(sizeof **world->tilemap * world->tilemap_width);
|
||||||
|
|
||||||
for (size_t j = 0; j < world->tilemap_width; ++j) {
|
for (size_t j = 0; j < world->tilemap_width; ++j) {
|
||||||
world->tilemap[i][j] = TILE_TYPE_VOID;
|
world->tilemap[i][j] = TILE_TYPE_VOID;
|
||||||
@ -82,7 +81,7 @@ struct World *world_create(void) {
|
|||||||
/* the tiles array contains data meant to be used by other logic */
|
/* the tiles array contains data meant to be used by other logic */
|
||||||
/* most importantly, it is used to draw the tiles */
|
/* most importantly, it is used to draw the tiles */
|
||||||
const size_t tile_count = world->tilemap_height * world->tilemap_width;
|
const size_t tile_count = world->tilemap_height * world->tilemap_width;
|
||||||
world->tiles = cmalloc(sizeof *world->tiles * tile_count);
|
world->tiles = malloc(sizeof *world->tiles * tile_count);
|
||||||
update_tiles(world);
|
update_tiles(world);
|
||||||
|
|
||||||
return world;
|
return world;
|
||||||
@ -106,14 +105,14 @@ void world_drawdef(struct World *world) {
|
|||||||
if (world->tiles[i].type == TILE_TYPE_VOID)
|
if (world->tiles[i].type == TILE_TYPE_VOID)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
m_sprite("/assets/white.png", to_frect(world->tiles[i].rect));
|
m_sprite("/assets/white.png", world->tiles[i].rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawdef_debug(world);
|
drawdef_debug(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool world_find_intersect_frect(struct World *world, Rect rect, Rect *intersection) {
|
bool world_find_rect_intersects(struct World *world, Rect rect, Rect *intersection) {
|
||||||
bool is_intersecting = false;
|
bool is_intersecting = false;
|
||||||
|
|
||||||
const size_t tile_count = world->tilemap_height * world->tilemap_width;
|
const size_t tile_count = world->tilemap_height * world->tilemap_width;
|
||||||
@ -121,44 +120,12 @@ bool world_find_intersect_frect(struct World *world, Rect rect, Rect *intersecti
|
|||||||
if (world->tiles[i].type == TILE_TYPE_VOID)
|
if (world->tiles[i].type == TILE_TYPE_VOID)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Rect tile_frect = {
|
Rect const tile_frect = world->tiles[i].rect;
|
||||||
.x = (float)(world->tiles[i].rect.x),
|
|
||||||
.y = (float)(world->tiles[i].rect.y),
|
|
||||||
.w = (float)(world->tiles[i].rect.w),
|
|
||||||
.h = (float)(world->tiles[i].rect.h),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (intersection == NULL) {
|
is_intersecting = rect_intersects(rect, tile_frect);
|
||||||
Rect temp;
|
|
||||||
is_intersecting = overlap_frect(&rect, &tile_frect, &temp);
|
|
||||||
} else {
|
|
||||||
is_intersecting = overlap_frect(&rect, &tile_frect, intersection);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_intersecting)
|
if (intersection)
|
||||||
break;
|
*intersection = rect_overlap(rect, tile_frect);
|
||||||
}
|
|
||||||
|
|
||||||
return is_intersecting;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool world_find_intersect_rect(struct World *world, Recti rect, Recti *intersection) {
|
|
||||||
bool is_intersecting = false;
|
|
||||||
|
|
||||||
const size_t tile_count = world->tilemap_height * world->tilemap_width;
|
|
||||||
for (size_t i = 0; i < tile_count; ++i) {
|
|
||||||
if (world->tiles[i].type == TILE_TYPE_VOID)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Recti *tile_rect = &world->tiles[i].rect;
|
|
||||||
|
|
||||||
if (intersection == NULL) {
|
|
||||||
Recti temp;
|
|
||||||
is_intersecting = overlap_rect(&rect, tile_rect, &temp);
|
|
||||||
} else {
|
|
||||||
is_intersecting = overlap_rect(&rect, tile_rect, intersection);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_intersecting)
|
if (is_intersecting)
|
||||||
break;
|
break;
|
||||||
@ -169,20 +136,20 @@ bool world_find_intersect_rect(struct World *world, Recti rect, Recti *intersect
|
|||||||
|
|
||||||
|
|
||||||
bool world_is_tile_at(struct World *world, float x, float y) {
|
bool world_is_tile_at(struct World *world, float x, float y) {
|
||||||
Vec2i position_in_grid = to_grid_location(world, x, y);
|
Vec2 position_in_grid = to_grid_location(world, x, y);
|
||||||
return world->tilemap[position_in_grid.y][position_in_grid.x] != TILE_TYPE_VOID;
|
return world->tilemap[(int32_t)position_in_grid.y][(int32_t)position_in_grid.x] != TILE_TYPE_VOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void world_place_tile(struct World *world, float x, float y) {
|
void world_place_tile(struct World *world, float x, float y) {
|
||||||
Vec2i position_in_grid = to_grid_location(world, x, y);
|
Vec2 position_in_grid = to_grid_location(world, x, y);
|
||||||
world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_SOLID;
|
world->tilemap[(int32_t)position_in_grid.y][(int32_t)position_in_grid.x] = TILE_TYPE_SOLID;
|
||||||
update_tiles(world);
|
update_tiles(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void world_remove_tile(struct World *world, float x, float y) {
|
void world_remove_tile(struct World *world, float x, float y) {
|
||||||
Vec2i position_in_grid = to_grid_location(world, x, y);
|
Vec2 position_in_grid = to_grid_location(world, x, y);
|
||||||
world->tilemap[position_in_grid.y][position_in_grid.x] = TILE_TYPE_VOID;
|
world->tilemap[(int32_t)position_in_grid.y][(int32_t)position_in_grid.x] = TILE_TYPE_VOID;
|
||||||
update_tiles(world);
|
update_tiles(world);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ typedef enum TileType {
|
|||||||
|
|
||||||
|
|
||||||
typedef struct Tile {
|
typedef struct Tile {
|
||||||
Recti rect;
|
Rect rect;
|
||||||
TileType type;
|
TileType type;
|
||||||
} Tile;
|
} Tile;
|
||||||
|
|
||||||
@ -34,8 +34,7 @@ typedef struct World {
|
|||||||
World *world_create(void);
|
World *world_create(void);
|
||||||
void world_destroy(World *world);
|
void world_destroy(World *world);
|
||||||
void world_drawdef(World *world);
|
void world_drawdef(World *world);
|
||||||
bool world_find_intersect_frect(World *world, Rect rect, Rect *intersection);
|
bool world_find_rect_intersects(World *world, Rect rect, Rect *intersection);
|
||||||
bool world_find_intersect_rect(World *world, Recti rect, Recti *intersection);
|
|
||||||
bool world_is_tile_at(World *world, float x, float y);
|
bool world_is_tile_at(World *world, float x, float y);
|
||||||
void world_place_tile(World *world, float x, float y);
|
void world_place_tile(World *world, float x, float y);
|
||||||
void world_remove_tile(World *world, float x, float y);
|
void world_remove_tile(World *world, float x, float y);
|
||||||
|
@ -6,15 +6,14 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNBUILDDIR})
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
state.h
|
state.h
|
||||||
|
|
||||||
scenes/scene.c scenes/scene.h
|
scenes/scene.c scenes/scene.h
|
||||||
scenes/title.c scenes/title.h
|
|
||||||
scenes/ingame.c scenes/ingame.h
|
scenes/ingame.c scenes/ingame.h
|
||||||
)
|
)
|
||||||
|
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[[deps]]
|
[[deps]]
|
||||||
source = "../../common-data" # where does it come from, might be an url
|
source = "../../../data" # where does it come from, might be an url
|
||||||
name = "common-data" # should be globally unique
|
name = "common-data" # should be globally unique
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
[about]
|
[about]
|
||||||
title = "Serene Scenery"
|
title = "Serene Scenery"
|
||||||
developer = "Townengine Team"
|
developer = "Townengine Team"
|
||||||
app_id = "platformer-demo"
|
app_id = "scenery-demo"
|
||||||
dev_id = "townengine-team"
|
dev_id = "townengine-team"
|
||||||
|
|
||||||
[game]
|
[game]
|
||||||
base_render_width = 640
|
resolution = [ 640, 360 ]
|
||||||
base_render_height = 360
|
|
||||||
|
|
||||||
[engine]
|
[engine]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "scenes/scene.h"
|
#include "scenes/scene.h"
|
||||||
#include "scenes/title.h"
|
#include "scenes/ingame.h"
|
||||||
|
|
||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
|
|
||||||
@ -13,52 +13,21 @@
|
|||||||
void game_tick(void) {
|
void game_tick(void) {
|
||||||
if (ctx.initialization_needed) {
|
if (ctx.initialization_needed) {
|
||||||
if (!ctx.udata) {
|
if (!ctx.udata) {
|
||||||
ctx.udata = ccalloc(1, sizeof (State));
|
ctx.udata = calloc(1, sizeof (State));
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
state->ctx = &ctx;
|
state->ctx = &ctx;
|
||||||
state->scene = title_scene(state);
|
state->scene = ingame_scene(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
input_add_action("debug_toggle");
|
|
||||||
input_bind_action_control("debug_toggle", CONTROL_BACKSPACE);
|
|
||||||
|
|
||||||
input_add_action("debug_dump_atlases");
|
|
||||||
input_bind_action_control("debug_dump_atlases", CONTROL_HOME);
|
|
||||||
|
|
||||||
input_add_action("player_left");
|
|
||||||
input_bind_action_control("player_left", CONTROL_A);
|
|
||||||
|
|
||||||
input_add_action("player_right");
|
|
||||||
input_bind_action_control("player_right", CONTROL_D);
|
|
||||||
|
|
||||||
input_add_action("player_forward");
|
|
||||||
input_bind_action_control("player_forward", CONTROL_W);
|
|
||||||
|
|
||||||
input_add_action("player_backward");
|
|
||||||
input_bind_action_control("player_backward", CONTROL_S);
|
|
||||||
|
|
||||||
input_add_action("player_jump");
|
|
||||||
input_bind_action_control("player_jump", CONTROL_SPACE);
|
|
||||||
|
|
||||||
input_add_action("player_run");
|
|
||||||
input_bind_action_control("player_run", CONTROL_LSHIFT);
|
|
||||||
|
|
||||||
input_add_action("ui_accept");
|
|
||||||
input_bind_action_control("ui_accept", CONTROL_RETURN);
|
|
||||||
|
|
||||||
input_add_action("mouse_capture_toggle");
|
|
||||||
input_bind_action_control("mouse_capture_toggle", CONTROL_ESCAPE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
if (input_is_action_just_pressed("debug_toggle")) {
|
input_action("debug_toggle", "BACKSPACE");
|
||||||
ctx.debug = !ctx.debug;
|
input_action("debug_dump_atlases", "HOME");
|
||||||
}
|
|
||||||
|
|
||||||
if (input_is_action_just_pressed("debug_dump_atlases")) {
|
if (input_action_just_pressed("debug_toggle")) {
|
||||||
textures_dump_atlases();
|
ctx.debug = !ctx.debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->scene->tick(state);
|
state->scene->tick(state);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "ingame.h"
|
#include "ingame.h"
|
||||||
#include "title.h"
|
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
|
|
||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
@ -12,84 +11,560 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
static void ingame_tick(State *state) {
|
#define TERRAIN_FREQUENCY 0.15f
|
||||||
|
#define TERRAIN_RADIUS 128
|
||||||
|
#define GRASS_RADIUS 16
|
||||||
|
#define TERRAIN_DISTANCE (TERRAIN_RADIUS * 2)
|
||||||
|
#define HALF_TERRAIN_DISTANCE ((float)TERRAIN_DISTANCE / 2)
|
||||||
|
#define PLAYER_HEIGHT 0.6f
|
||||||
|
#define TREE_DENSITY 0.03f
|
||||||
|
|
||||||
|
#define G_CONST 10.0f
|
||||||
|
|
||||||
|
/* TODO: pregenerate grid of levels of detail */
|
||||||
|
static float heightmap[TERRAIN_DISTANCE][TERRAIN_DISTANCE];
|
||||||
|
|
||||||
|
|
||||||
|
/* vehicle sim ! */
|
||||||
|
/* https://www.youtube.com/watch?v=pwbwFdWBkU0 */
|
||||||
|
/* representation is a "jelly" box with spring configuration trying to make it coherent */
|
||||||
|
|
||||||
|
/* == springs == */
|
||||||
|
/* damped spring: F = -kx - cv */
|
||||||
|
/* x = length(p1-p0) - at_rest_length */
|
||||||
|
/* v = dot(v1 - v0, unit(p1-p0)) */
|
||||||
|
/* F = (-kx - cv) * unit(p1-p0) */
|
||||||
|
/* v += (F/m)*t */
|
||||||
|
/* one points gains positive F, other negative F, to come together */
|
||||||
|
|
||||||
|
/* == ground interaction == */
|
||||||
|
/* if point is under terrain, then apply this: */
|
||||||
|
/* x = y difference on point and ground */
|
||||||
|
/* -x(n) = x * normal */
|
||||||
|
/* -v(n) = dot(v, normal) */
|
||||||
|
/* -F = (-kx(n)-cv(n)) * normal */
|
||||||
|
|
||||||
|
/* == friction == */
|
||||||
|
/* v(o)/F(o) are perpendicular to slope (x - x(n)) */
|
||||||
|
/* if v(o) == 0, then */
|
||||||
|
/* -- at rest, static friction overcomes */
|
||||||
|
/* if F(o) <= f(s)*x(n), F(o) = 0 */
|
||||||
|
/* else, F(o) -= f(k) * x(n) */
|
||||||
|
/* else if length(v(o) + (F(o)/m)*t) <= (f(k)*x(n)*t), v(o) = 0 */
|
||||||
|
/* else, F = -unit(v(o)*f(k)*x(n)) */
|
||||||
|
|
||||||
|
#define VEHICLE_MASS 200.0f
|
||||||
|
#define VEHICLE_LENGTH 3.0f
|
||||||
|
#define VEHICLE_WIDTH 1.7f
|
||||||
|
#define VEHICLE_HEIGHT 1.3f
|
||||||
|
/* spring constant */
|
||||||
|
#define VEHICLE_SPRING_K 22000.0f
|
||||||
|
#define VEHICLE_SPRING_K_SHOCK 18000.0f
|
||||||
|
#define VEHICLE_SPRING_GK 70000.0f
|
||||||
|
/* damping constant */
|
||||||
|
#define VEHICLE_SPRING_C 800.0f
|
||||||
|
#define VEHICLE_SPRING_C_SHOCK 500.0f
|
||||||
|
#define VEHICLE_SPRING_GC 100.0f
|
||||||
|
#define VEHICLE_FRICTION_S 1000.0f
|
||||||
|
#define VEHICLE_FRICTION_K 110.0f
|
||||||
|
#define VEHICLE_FRICTION_V 4000.0f
|
||||||
|
|
||||||
|
/* TODO: shock springs, that are more loose, which are used to simulate the wheels */
|
||||||
|
/* initial, ideal corner positions */
|
||||||
|
static const Vec3 vbpi[8] = {
|
||||||
|
[0] = { 0, 0, 0 },
|
||||||
|
[1] = { VEHICLE_LENGTH, 0, 0 },
|
||||||
|
[2] = { VEHICLE_LENGTH, 0, VEHICLE_WIDTH },
|
||||||
|
[3] = { 0, 0, VEHICLE_WIDTH },
|
||||||
|
[4] = { 0, VEHICLE_HEIGHT, 0 },
|
||||||
|
[5] = { VEHICLE_LENGTH, VEHICLE_HEIGHT, 0 },
|
||||||
|
[6] = { VEHICLE_LENGTH, VEHICLE_HEIGHT, VEHICLE_WIDTH },
|
||||||
|
[7] = { 0, VEHICLE_HEIGHT, VEHICLE_WIDTH },
|
||||||
|
};
|
||||||
|
/* corner positions in simulation */
|
||||||
|
static Vec3 vbp[8] = { vbpi[0], vbpi[1], vbpi[2], vbpi[3],
|
||||||
|
vbpi[4], vbpi[5], vbpi[6], vbpi[7], };
|
||||||
|
/* corner velocities */
|
||||||
|
static Vec3 vbv[8];
|
||||||
|
/* springs */
|
||||||
|
static uint8_t vbs[28][2] = {
|
||||||
|
{0, 1}, {4, 5}, {0, 4},
|
||||||
|
{1, 2}, {5, 6}, {1, 5},
|
||||||
|
{2, 3}, {6, 7}, {2, 6},
|
||||||
|
{3, 0}, {7, 4}, {3, 7},
|
||||||
|
|
||||||
|
{0, 2}, {0, 5}, {0, 7},
|
||||||
|
{1, 3}, {1, 6}, {1, 4},
|
||||||
|
{4, 6}, {2, 7}, {2, 5},
|
||||||
|
{5, 7}, {3, 4}, {3, 6},
|
||||||
|
|
||||||
|
{0, 6}, {1, 7}, {2, 4}, {3, 5},
|
||||||
|
};
|
||||||
|
/* ackermann steering geometry */
|
||||||
|
static float vehicle_turning_extend;
|
||||||
|
static float vehicle_turning_speed = 0.12f;
|
||||||
|
static float vehicle_turning_extend_limit = VEHICLE_WIDTH * 1.75f;
|
||||||
|
|
||||||
|
static float height_at(SceneIngame *scn, Vec2 position);
|
||||||
|
static Vec3 normal_at(SceneIngame *scn, Vec2 position);
|
||||||
|
|
||||||
|
|
||||||
|
static inline float clampf(float f, float min, float max) {
|
||||||
|
const float t = f < min ? min : f;
|
||||||
|
return t > max ? max : t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_vehicle(SceneIngame *scn) {
|
||||||
|
for (size_t i = 0; i < 12; ++i)
|
||||||
|
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){255, 255, 255, 255});
|
||||||
|
for (size_t i = 12; i < 24; ++i)
|
||||||
|
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){200, 200, 200, 255});
|
||||||
|
for (size_t i = 24; i < 28; ++i)
|
||||||
|
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){255, 125, 125, 255});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_vehicle(SceneIngame *scn) {
|
||||||
|
/* apply gravity */
|
||||||
|
Vec3 Facc[8] = {0};
|
||||||
|
|
||||||
|
/* steering */
|
||||||
|
bool steered = false;
|
||||||
|
if (input_action_pressed("player_left")) {
|
||||||
|
vehicle_turning_extend -= vehicle_turning_speed;
|
||||||
|
steered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_pressed("player_right")) {
|
||||||
|
vehicle_turning_extend += vehicle_turning_speed;
|
||||||
|
steered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!steered)
|
||||||
|
vehicle_turning_extend -= copysignf(vehicle_turning_speed * 0.9f, vehicle_turning_extend);
|
||||||
|
|
||||||
|
vehicle_turning_extend = clampf(vehicle_turning_extend, -vehicle_turning_extend_limit, vehicle_turning_extend_limit);
|
||||||
|
if (fabsf(vehicle_turning_extend) <= 0.11f)
|
||||||
|
vehicle_turning_extend = 0;
|
||||||
|
|
||||||
|
Vec3 const Fg = { .y = -VEHICLE_MASS * G_CONST };
|
||||||
|
for (size_t i = 0; i < 8; ++i)
|
||||||
|
Facc[i] = vec3_add(Facc[i], Fg);
|
||||||
|
|
||||||
|
/* apply springs */
|
||||||
|
for (size_t i = 0; i < 28; ++i) {
|
||||||
|
Vec3 const p0 = vbp[vbs[i][0]];
|
||||||
|
Vec3 const p1 = vbp[vbs[i][1]];
|
||||||
|
Vec3 const v0 = vbv[vbs[i][0]];
|
||||||
|
Vec3 const v1 = vbv[vbs[i][1]];
|
||||||
|
Vec3 const pd = vec3_sub(p1, p0);
|
||||||
|
Vec3 const pn = vec3_norm(pd);
|
||||||
|
/* TODO: length at rest could be precalculated */
|
||||||
|
float const lar = vec3_length(vec3_sub(vbpi[vbs[i][1]], vbpi[vbs[i][0]]));
|
||||||
|
float const x = vec3_length(pd) - lar;
|
||||||
|
float const v = vec3_dot(vec3_sub(v1, v0), pn);
|
||||||
|
float const spring_k = i == 2 | i == 5 || i == 8 || i == 11 ? VEHICLE_SPRING_K_SHOCK : VEHICLE_SPRING_K;
|
||||||
|
float const spring_c = i == 2 | i == 5 || i == 8 || i == 11 ? VEHICLE_SPRING_C_SHOCK : VEHICLE_SPRING_C;
|
||||||
|
Vec3 const Fs = vec3_scale(pn, -spring_k * x - spring_c * v);
|
||||||
|
Facc[vbs[i][0]] = vec3_sub(Facc[vbs[i][0]], Fs);
|
||||||
|
Facc[vbs[i][1]] = vec3_add(Facc[vbs[i][1]], Fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* spring and friction against the ground */
|
||||||
|
for (size_t i = 0; i < 8; ++i) {
|
||||||
|
Vec3 const p = vbp[i];
|
||||||
|
Vec3 const v = vbv[i];
|
||||||
|
float const h = height_at(scn, (Vec2){ p.x, p.z });
|
||||||
|
Vec3 const fwd = vec3_norm(vec3_sub(vbp[1], vbp[0]));
|
||||||
|
if (h >= p.y) {
|
||||||
|
/* back wheel processing: acceleration */
|
||||||
|
if (i == 0 || i == 3) {
|
||||||
|
float scale = 0;
|
||||||
|
if (scn->camera_mode == 2 && input_action_pressed("player_forward"))
|
||||||
|
scale += 1;
|
||||||
|
if (scn->camera_mode == 2 && input_action_pressed("player_backward"))
|
||||||
|
scale -= 1;
|
||||||
|
Facc[i] = vec3_add(Facc[i], vec3_scale(fwd, 6500 * scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* normal force, for displacement */
|
||||||
|
Vec3 const n = normal_at(scn, (Vec2){ p.x, p.z });
|
||||||
|
float const xn = (h - p.y) * n.y;
|
||||||
|
float const vn = vec3_dot(v, n);
|
||||||
|
Vec3 const Fn = vec3_scale(n, -VEHICLE_SPRING_GK * xn - VEHICLE_SPRING_GC * vn);
|
||||||
|
Facc[i] = vec3_sub(Facc[i], Fn);
|
||||||
|
|
||||||
|
/* friction force, perpendicular to normal force */
|
||||||
|
/* TODO: is it right? aren't vn+vol should be = |v| */
|
||||||
|
Vec3 const von = vec3_norm(vec3_cross(n, vec3_cross(v, n)));
|
||||||
|
Vec3 const vo = vec3_scale(vec3_scale(von, vec3_length(v) - vn), -1);
|
||||||
|
float const vol = vec3_length(vo);
|
||||||
|
Vec3 const Fon = vec3_norm(vec3_cross(n, vec3_cross(Facc[i], n)));
|
||||||
|
Vec3 Fo = vec3_scale(vec3_scale(Fon, vec3_length(Facc[i]) - vec3_dot(Facc[i], n)), -1);
|
||||||
|
/* portion of total force along the surface */
|
||||||
|
float const Fol = vec3_length(Fo);
|
||||||
|
float const fkxn = VEHICLE_FRICTION_K * xn;
|
||||||
|
/* at rest, might want to start moving */
|
||||||
|
if (fabsf(0.0f - vol) <= 0.0001f) {
|
||||||
|
/* cannot overcome static friction, force along the surface is zeroed */
|
||||||
|
if (Fol <= VEHICLE_FRICTION_S * xn) { Fo = vec3_scale(Fo, -1);}
|
||||||
|
/* resist the force by friction, while starting to move */
|
||||||
|
else { Fo = vec3_sub(Fo, vec3_scale(von, fkxn));}
|
||||||
|
/* not at rest, stop accelerating along the surface */
|
||||||
|
} else if (vol + (Fol / VEHICLE_MASS) * ctx.frame_duration <= fkxn * ctx.frame_duration * 2) {
|
||||||
|
/* ugh ... */
|
||||||
|
vbv[i] = vec3_add(v, vo);
|
||||||
|
/* just apply friction */
|
||||||
|
} else {
|
||||||
|
Fo = vec3_scale(von, -fkxn * 400);
|
||||||
|
}
|
||||||
|
Facc[i] = vec3_add(Facc[i], Fo);
|
||||||
|
|
||||||
|
/* rear wheel friction */
|
||||||
|
if (i == 0 || i == 3) {
|
||||||
|
Vec3 const pn = vec3_cross(fwd, n);
|
||||||
|
Vec3 const Fp = vec3_scale(pn, vec3_dot(v, pn) * -VEHICLE_FRICTION_V);
|
||||||
|
Facc[i] = vec3_add(Facc[i], Fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* front wheel processing */
|
||||||
|
if (i == 1 || i == 2) {
|
||||||
|
/* steering influences "center of turning", which is a point */
|
||||||
|
/* laying on line defined by rear axle */
|
||||||
|
/* front arms are rotated to be perpendicular to center of turning, */
|
||||||
|
/* which then are used to dissipate forces, thus providing control */
|
||||||
|
Vec3 const rear_bar = vec3_sub(vbp[0], vbp[3]);
|
||||||
|
Vec3 const rear_center = vec3_scale(vec3_add(vbp[0], vbp[3]), 0.5);
|
||||||
|
Vec3 a, b, r;
|
||||||
|
if (i == 1) {
|
||||||
|
a = vec3_sub(vbp[3], vbp[2]);
|
||||||
|
b = vec3_sub(rear_center, vbp[2]);
|
||||||
|
r = vbp[2];
|
||||||
|
} else {
|
||||||
|
a = vec3_sub(vbp[0], vbp[1]);
|
||||||
|
b = vec3_sub(rear_center, vbp[1]);
|
||||||
|
r = vbp[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
float const arm_angle = vec3_angle(a, b);
|
||||||
|
Vec3 const turn_center = vec3_add(rear_center, vec3_scale(vec3_norm(rear_bar), vehicle_turning_extend));
|
||||||
|
Vec3 const arm = vec3_sub(r, turn_center);
|
||||||
|
Vec3 const n = vec3_norm(vec3_cross(a, b));
|
||||||
|
Vec3 const wheel = vec3_norm(vec3_rotate(arm, -arm_angle, n));
|
||||||
|
Vec3 const p = vec3_norm(vec3_cross(wheel, n));
|
||||||
|
draw_line_3d(r, vec3_add(r, p), 1, (Color){0,255,255,255});
|
||||||
|
|
||||||
|
Vec3 const Fp = vec3_scale(p, vec3_dot(v, p) * -VEHICLE_FRICTION_V);
|
||||||
|
Facc[i] = vec3_add(Facc[i], Fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 vd = vec3_scale(vec3_scale(Facc[i], (1.0f / VEHICLE_MASS)), ctx.frame_duration);
|
||||||
|
vbv[i] = vec3_add(vbv[i], vd);
|
||||||
|
vbp[i] = vec3_add(vbp[i], vec3_scale(vbv[i], ctx.frame_duration));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_vehicle_mode(State *state) {
|
||||||
SceneIngame *scn = (SceneIngame *)state->scene;
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
if (input_is_mouse_captured()) {
|
Vec3 const top_center = vec3_sub(vbp[4], vec3_scale(vec3_sub(vbp[4], vbp[6]), 1.0f / 2.0f));
|
||||||
const float sensitivity = 0.6f; /* TODO: put this in a better place */
|
// Vec3 const front_center = vec3_add(vbp[4], vec3_scale(vec3_sub(vbp[4], vbp[7]), 1.0f / 2.0f));
|
||||||
scn->yaw += (float)ctx.mouse_relative_position.x * sensitivity;
|
// Vec3 const facing_direction = vec3_sub(top_center, front_center);
|
||||||
scn->pitch -= (float)ctx.mouse_relative_position.y * sensitivity;
|
|
||||||
scn->pitch = clampf(scn->pitch, -89.0f, 89.0f);
|
|
||||||
|
|
||||||
const float yaw_rad = scn->yaw * (float)DEG2RAD;
|
float yawc, yaws, pitchc, pitchs;
|
||||||
const float pitch_rad = scn->pitch * (float)DEG2RAD;
|
sincosf(scn->yaw + (float)M_PI_2, &yaws, &yawc);
|
||||||
|
sincosf(scn->pitch, &pitchs, &pitchc);
|
||||||
|
|
||||||
scn->cam.target = m_vec_norm(((Vec3){
|
Vec3 const looking_direction = vec3_norm(((Vec3){
|
||||||
cosf(yaw_rad) * cosf(pitch_rad),
|
yawc * pitchc,
|
||||||
sinf(pitch_rad),
|
pitchs,
|
||||||
sinf(yaw_rad) * cosf(pitch_rad)
|
yaws * pitchc,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
Vec3 const orbit = vec3_sub(top_center, vec3_scale(looking_direction, 7.5));
|
||||||
|
|
||||||
|
draw_camera(orbit, looking_direction, (Vec3){0,1,0}, (float)M_PI_2 * 0.8f, 1, TERRAIN_RADIUS * sqrtf(3));
|
||||||
|
|
||||||
|
scn->looking_direction = looking_direction;
|
||||||
|
|
||||||
|
scn->pos = top_center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_fly_mode(State *state) {
|
||||||
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
|
DrawCameraFromPrincipalAxesResult dir_and_up =
|
||||||
|
draw_camera_from_principal_axes(scn->pos, scn->roll, scn->pitch, scn->yaw, (float)M_PI_2 * 0.8f, 1, TERRAIN_RADIUS * sqrtf(3));
|
||||||
|
|
||||||
|
scn->looking_direction = dir_and_up.direction;
|
||||||
|
|
||||||
|
const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up));
|
||||||
|
const float speed = 0.1f; /* TODO: put this in a better place */
|
||||||
|
if (input_action_pressed("player_left"))
|
||||||
|
scn->pos = vec3_sub(scn->pos, m_vec_scale(right, speed));
|
||||||
|
|
||||||
|
if (input_action_pressed("player_right"))
|
||||||
|
scn->pos = vec3_add(scn->pos, m_vec_scale(right, speed));
|
||||||
|
|
||||||
|
if (input_action_pressed("player_forward"))
|
||||||
|
scn->pos = vec3_add(scn->pos, m_vec_scale(dir_and_up.direction, speed));
|
||||||
|
|
||||||
|
if (input_action_pressed("player_backward"))
|
||||||
|
scn->pos = vec3_sub(scn->pos, m_vec_scale(dir_and_up.direction, speed));
|
||||||
|
|
||||||
|
if (input_action_pressed("player_jump"))
|
||||||
|
scn->pos.y += speed;
|
||||||
|
|
||||||
|
if (input_action_pressed("player_run"))
|
||||||
|
scn->pos.y -= speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: could be baked in map format */
|
||||||
|
static Vec3 normal_at(SceneIngame *scn, Vec2 position) {
|
||||||
|
int const x = (int)(floorf(position.x - scn->world_center.x));
|
||||||
|
int const y = (int)(floorf(position.y - scn->world_center.y));
|
||||||
|
|
||||||
|
float const height0 = heightmap[x][y];
|
||||||
|
float const height1 = heightmap[x + 1][y];
|
||||||
|
float const height2 = heightmap[x][y + 1];
|
||||||
|
|
||||||
|
Vec3 const a = { .x = 1, .y = height0 - height1, .z = 0 };
|
||||||
|
Vec3 const b = { .x = 0, .y = height0 - height2, .z = -1 };
|
||||||
|
|
||||||
|
return vec3_norm(vec3_cross(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: don't operate on triangles, instead interpolate on quads */
|
||||||
|
static float height_at(SceneIngame *scn, Vec2 position) {
|
||||||
|
int const x = (int)(floorf(position.x - scn->world_center.x));
|
||||||
|
int const y = (int)(floorf(position.y - scn->world_center.y));
|
||||||
|
|
||||||
|
float const height0 = heightmap[x][y];
|
||||||
|
float const height1 = heightmap[x + 1][y];
|
||||||
|
float const height2 = heightmap[x][y + 1];
|
||||||
|
float const height3 = heightmap[x + 1][y + 1];
|
||||||
|
|
||||||
|
Vec2 incell = { position.x - floorf(position.x), position.y - floorf(position.y) };
|
||||||
|
|
||||||
|
float const weight0 = (1 - incell.x) * (1 - incell.y);
|
||||||
|
float const weight1 = ( incell.x) * (1 - incell.y);
|
||||||
|
float const weight2 = (1 - incell.x) * ( incell.y);
|
||||||
|
float const weight3 = ( incell.x) * ( incell.y);
|
||||||
|
|
||||||
|
return (height0 * weight0 + height1 * weight1 + height2 * weight2 + height3 * weight3) / (weight0 + weight1 + weight2 + weight3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_ground_mode(State *state) {
|
||||||
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
|
DrawCameraFromPrincipalAxesResult dir_and_up =
|
||||||
|
draw_camera_from_principal_axes(scn->pos, scn->roll, scn->pitch, scn->yaw, (float)M_PI_2 * 0.8f, 1, TERRAIN_RADIUS * sqrtf(3));
|
||||||
|
|
||||||
|
scn->looking_direction = dir_and_up.direction;
|
||||||
|
|
||||||
|
dir_and_up.direction.y = 0;
|
||||||
|
dir_and_up.direction = vec3_norm(dir_and_up.direction);
|
||||||
|
|
||||||
|
const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up));
|
||||||
|
const float speed = 0.20f; /* TODO: put this in a better place */
|
||||||
|
|
||||||
|
Vec3 target = scn->pos;
|
||||||
|
|
||||||
|
/* gravity */
|
||||||
|
{
|
||||||
|
float const height = height_at(scn, (Vec2){scn->pos.x, scn->pos.z});
|
||||||
|
|
||||||
|
if (target.y > height + PLAYER_HEIGHT)
|
||||||
|
target.y = target.y - 0.6f;
|
||||||
|
|
||||||
|
if (target.y < height + PLAYER_HEIGHT)
|
||||||
|
target.y = height + PLAYER_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Vec3 right = m_vec_norm(m_vec_cross(scn->cam.target, scn->cam.up));
|
/* movement */
|
||||||
const float speed = 0.04f; /* TODO: put this in a better place */
|
{
|
||||||
if (input_is_action_pressed("player_left"))
|
Vec3 direction = {0, 0, 0};
|
||||||
scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(right, speed));
|
|
||||||
|
|
||||||
if (input_is_action_pressed("player_right"))
|
if (input_action_pressed("player_left"))
|
||||||
scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(right, speed));
|
direction = m_vec_sub(direction, m_vec_scale(right, speed));
|
||||||
|
|
||||||
if (input_is_action_pressed("player_forward"))
|
if (input_action_pressed("player_right"))
|
||||||
scn->cam.pos = vec3_add(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
|
direction = m_vec_add(direction, m_vec_scale(right, speed));
|
||||||
|
|
||||||
if (input_is_action_pressed("player_backward"))
|
if (input_action_pressed("player_forward"))
|
||||||
scn->cam.pos = vec3_sub(scn->cam.pos, m_vec_scale(scn->cam.target, speed));
|
direction = m_vec_add(direction, m_vec_scale(dir_and_up.direction, speed));
|
||||||
|
|
||||||
if (input_is_action_pressed("player_jump"))
|
if (input_action_pressed("player_backward"))
|
||||||
scn->cam.pos.y += speed;
|
direction = m_vec_sub(direction, m_vec_scale(dir_and_up.direction, speed));
|
||||||
|
|
||||||
if (input_is_action_pressed("player_run"))
|
target = m_vec_add(target, direction);
|
||||||
scn->cam.pos.y -= speed;
|
|
||||||
|
|
||||||
/* toggle mouse capture with end key */
|
|
||||||
if (input_is_action_just_pressed("mouse_capture_toggle")) {
|
|
||||||
input_set_mouse_captured(!input_is_mouse_captured());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_camera(&scn->cam);
|
/* interpolate */
|
||||||
|
scn->pos.x = (target.x - scn->pos.x) * (0.13f / 0.9f) + scn->pos.x;
|
||||||
|
scn->pos.y = (target.y - scn->pos.y) * (0.13f / 0.9f) + scn->pos.y;
|
||||||
|
scn->pos.z = (target.z - scn->pos.z) * (0.13f / 0.9f) + scn->pos.z;
|
||||||
|
}
|
||||||
|
|
||||||
#define TERRAIN_FREQUENCY 0.1f
|
|
||||||
|
|
||||||
for (int ly = 64; ly--;) {
|
static void generate_terrain(SceneIngame *scn) {
|
||||||
for (int lx = 64; lx--;) {
|
for (int ly = 0; ly < TERRAIN_DISTANCE; ly++) {
|
||||||
float x = SDL_truncf(scn->cam.pos.x + 32 - lx);
|
for (int lx = 0; lx < TERRAIN_DISTANCE; lx++) {
|
||||||
float y = SDL_truncf(scn->cam.pos.z + 32 - ly);
|
float x = floorf(scn->pos.x - HALF_TERRAIN_DISTANCE + (float)lx);
|
||||||
|
float y = floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly);
|
||||||
|
|
||||||
float d0 = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
float height = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 1;
|
||||||
float d1 = stb_perlin_noise3((float)(x + 1) * TERRAIN_FREQUENCY, (float)y * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
height += stb_perlin_noise3((float)x * TERRAIN_FREQUENCY / 10, (float)y * TERRAIN_FREQUENCY / 10, 0, 0, 0, 0) * 20 - 1;
|
||||||
float d2 = stb_perlin_noise3((float)(x + 1) * TERRAIN_FREQUENCY, (float)(y - 1) * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
|
||||||
float d3 = stb_perlin_noise3((float)x * TERRAIN_FREQUENCY, (float)(y - 1) * TERRAIN_FREQUENCY, 0, 0, 0, 0) * 3 - 6;
|
|
||||||
|
|
||||||
draw_triangle("/assets/grass.png",
|
heightmap[lx][ly] = height;
|
||||||
(Vec3){ (float)x, d0, (float)y },
|
|
||||||
(Vec3){ (float)x + 1, d1, (float)y },
|
|
||||||
(Vec3){ (float)x, d3, (float)y - 1 },
|
|
||||||
(Vec2){ 128, 128 },
|
|
||||||
(Vec2){ 128, 0 },
|
|
||||||
(Vec2){ 0, 128 });
|
|
||||||
|
|
||||||
draw_triangle("/assets/grass.png",
|
|
||||||
(Vec3){ (float)x + 1, d1, (float)y },
|
|
||||||
(Vec3){ (float)x + 1, d2, (float)y - 1 },
|
|
||||||
(Vec3){ (float)x, d3, (float)y - 1 },
|
|
||||||
(Vec2){ 128, 0 },
|
|
||||||
(Vec2){ 0, 0 },
|
|
||||||
(Vec2){ 0, 128 });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scn->world_center = (Vec2){ floorf(scn->pos.x - HALF_TERRAIN_DISTANCE), floorf(scn->pos.z - HALF_TERRAIN_DISTANCE) };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int32_t ceil_sqrt(int32_t const n) {
|
||||||
|
int32_t res = 1;
|
||||||
|
while(res * res < n)
|
||||||
|
res++;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint32_t adler32(const void *buf, size_t buflength) {
|
||||||
|
const uint8_t *buffer = (const uint8_t*)buf;
|
||||||
|
|
||||||
|
uint32_t s1 = 1;
|
||||||
|
uint32_t s2 = 0;
|
||||||
|
|
||||||
|
for (size_t n = 0; n < buflength; n++) {
|
||||||
|
s1 = (s1 + buffer[n]) % 65521;
|
||||||
|
s2 = (s2 + s1) % 65521;
|
||||||
|
}
|
||||||
|
return (s2 << 16) | s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_terrain(SceneIngame *scn) {
|
||||||
|
/* used to cull invisible tiles over field of view (to horizon) */
|
||||||
|
Vec2 const d = vec2_norm((Vec2){ .x = scn->looking_direction.x, .y = scn->looking_direction.z });
|
||||||
|
float const c = cosf((float)M_PI_2 * 0.8f * 0.8f);
|
||||||
|
|
||||||
|
/* draw terrain in circle */
|
||||||
|
int32_t const rsi = (int32_t)TERRAIN_RADIUS * (int32_t)TERRAIN_RADIUS;
|
||||||
|
for (int32_t iy = -(int32_t)TERRAIN_RADIUS; iy <= (int32_t)TERRAIN_RADIUS - 1; ++iy) {
|
||||||
|
int32_t const dx = ceil_sqrt(rsi - (iy + (iy <= 0)) * (iy + (iy <= 0)));
|
||||||
|
for (int32_t ix = -dx; ix < dx - 1; ++ix) {
|
||||||
|
int32_t lx = ix + TERRAIN_RADIUS;
|
||||||
|
int32_t ly = iy + TERRAIN_RADIUS;
|
||||||
|
|
||||||
|
float x = (float)(floorf(scn->pos.x - HALF_TERRAIN_DISTANCE + (float)lx));
|
||||||
|
float y = (float)(floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly));
|
||||||
|
|
||||||
|
/* cull tiles outside of vision */
|
||||||
|
if (vec2_dot(vec2_norm((Vec2){x - scn->pos.x + d.x * 2, y - scn->pos.z + d.y * 2}), d) < c)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float d0 = heightmap[lx][ly];
|
||||||
|
float d1 = heightmap[lx + 1][ly];
|
||||||
|
float d2 = heightmap[lx + 1][ly - 1];
|
||||||
|
float d3 = heightmap[lx][ly - 1];
|
||||||
|
|
||||||
|
draw_quad("/assets/grass2.png",
|
||||||
|
(Vec3){ (float)x, d0, (float)y },
|
||||||
|
(Vec3){ (float)x + 1, d1, (float)y },
|
||||||
|
(Vec3){ (float)x + 1, d2, (float)y - 1 },
|
||||||
|
(Vec3){ (float)x, d3, (float)y - 1 },
|
||||||
|
(Rect){ .w = 128, .h = 128 },
|
||||||
|
(Color){255, 255, 255, 255});
|
||||||
|
|
||||||
|
if (((float)(adler32(&((Vec2){x, y}), sizeof (Vec2)) % 100) / 100) <= TREE_DENSITY)
|
||||||
|
draw_billboard("/assets/trreez.png",
|
||||||
|
(Vec3){ (float)x, d0 + 1.95f, (float)y },
|
||||||
|
(Vec2){2.f, 2.f},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255, 255, 255, 255}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t const rsi_g = (int32_t)GRASS_RADIUS * (int32_t)GRASS_RADIUS;
|
||||||
|
for (int32_t iy = -(int32_t)GRASS_RADIUS; iy <= (int32_t)GRASS_RADIUS - 1; ++iy) {
|
||||||
|
int32_t const dx = ceil_sqrt(rsi_g - (iy + (iy <= 0)) * (iy + (iy <= 0)));
|
||||||
|
for (int32_t ix = -dx; ix < dx; ++ix) {
|
||||||
|
int32_t lx = ix + TERRAIN_RADIUS;
|
||||||
|
int32_t ly = iy + TERRAIN_RADIUS;
|
||||||
|
|
||||||
|
float x = (float)(floorf(scn->pos.x - HALF_TERRAIN_DISTANCE + (float)lx));
|
||||||
|
float y = (float)(floorf(scn->pos.z - HALF_TERRAIN_DISTANCE + (float)ly));
|
||||||
|
|
||||||
|
float d = heightmap[lx][ly];
|
||||||
|
|
||||||
|
draw_billboard("/assets/grasses/25.png",
|
||||||
|
(Vec3){
|
||||||
|
(float)x + (float)((adler32(&((Vec2){x, y}), sizeof (Vec2))) % 32) / 64.0f,
|
||||||
|
d + 0.2f,
|
||||||
|
(float)y + (float)((adler32(&((Vec2){y, x}), sizeof (Vec2))) % 32) / 64.0f
|
||||||
|
},
|
||||||
|
(Vec2){0.4f, 0.4f},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255, 255, 255, 255}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ingame_tick(State *state) {
|
||||||
|
SceneIngame *scn = (SceneIngame *)state->scene;
|
||||||
|
|
||||||
|
input_action("player_left", "A");
|
||||||
|
input_action("player_right", "D");
|
||||||
|
input_action("player_forward", "W");
|
||||||
|
input_action("player_backward", "S");
|
||||||
|
input_action("player_jump", "SPACE");
|
||||||
|
input_action("player_run", "LSHIFT");
|
||||||
|
input_action("mouse_capture_toggle", "ESCAPE");
|
||||||
|
input_action("toggle_camera_mode", "C");
|
||||||
|
|
||||||
|
if (scn->mouse_captured) {
|
||||||
|
const float sensitivity = 0.4f * (float)(M_PI / 180); /* TODO: put this in a better place */
|
||||||
|
scn->yaw += (float)ctx.mouse_movement.x * sensitivity;
|
||||||
|
scn->pitch -= (float)ctx.mouse_movement.y * sensitivity;
|
||||||
|
scn->pitch = clampf(scn->pitch, (float)-M_PI * 0.49f, (float)M_PI * 0.49f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("toggle_camera_mode"))
|
||||||
|
scn->camera_mode = scn->camera_mode == 2 ? 0 : scn->camera_mode + 1;
|
||||||
|
|
||||||
|
if (scn->camera_mode == 1) {
|
||||||
|
process_fly_mode(state);
|
||||||
|
} else if (scn->camera_mode == 0) {
|
||||||
|
process_ground_mode(state);
|
||||||
|
} else if (scn->camera_mode) {
|
||||||
|
process_vehicle_mode(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* toggle mouse capture with end key */
|
||||||
|
if (input_action_just_pressed("mouse_capture_toggle"))
|
||||||
|
scn->mouse_captured = !scn->mouse_captured;
|
||||||
|
|
||||||
|
ctx.mouse_capture = scn->mouse_captured;
|
||||||
|
|
||||||
|
generate_terrain(scn);
|
||||||
|
process_vehicle(scn);
|
||||||
|
|
||||||
|
draw_terrain(scn);
|
||||||
|
draw_vehicle(scn);
|
||||||
|
|
||||||
draw_skybox("/assets/miramar/miramar_*.tga");
|
draw_skybox("/assets/miramar/miramar_*.tga");
|
||||||
draw_fog(0.9, 1.0, 0.05, (Color){ 140, 147, 160, 255 });
|
|
||||||
|
ctx.fog_color = (Color){ 140, 147, 160, 255 };
|
||||||
|
ctx.fog_density = 0.015f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -101,17 +576,17 @@ static void ingame_end(State *state) {
|
|||||||
Scene *ingame_scene(State *state) {
|
Scene *ingame_scene(State *state) {
|
||||||
(void)state;
|
(void)state;
|
||||||
|
|
||||||
SceneIngame *new_scene = ccalloc(1, sizeof *new_scene);
|
SceneIngame *new_scene = calloc(1, sizeof *new_scene);
|
||||||
new_scene->base.tick = ingame_tick;
|
new_scene->base.tick = ingame_tick;
|
||||||
new_scene->base.end = ingame_end;
|
new_scene->base.end = ingame_end;
|
||||||
|
|
||||||
new_scene->cam = (Camera){ .pos = { 32, 0, 1 }, .up = { 0, 1, 0 }, .fov = (float)M_PI_2 };
|
new_scene->mouse_captured = true;
|
||||||
|
|
||||||
m_audio(m_set(path, "music/mod65.xm"),
|
m_audio(m_set(path, "music/woah.ogg"),
|
||||||
m_opt(channel, "soundtrack"),
|
m_opt(channel, "soundtrack"),
|
||||||
m_opt(repeat, true));
|
m_opt(repeat, true));
|
||||||
|
|
||||||
input_set_mouse_captured(true);
|
new_scene->pos = (Vec3){ 0.1f, 0.0, 0.1f };
|
||||||
|
|
||||||
return (Scene *)new_scene;
|
return (Scene *)new_scene;
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,22 @@
|
|||||||
#include "../state.h"
|
#include "../state.h"
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
|
||||||
typedef struct SceneIngame {
|
typedef struct SceneIngame {
|
||||||
Scene base;
|
Scene base;
|
||||||
|
|
||||||
Camera cam;
|
Vec3 looking_direction;
|
||||||
|
Vec2 world_center;
|
||||||
|
|
||||||
/* TODO: put this in a better place */
|
Vec3 pos;
|
||||||
float yaw;
|
float yaw;
|
||||||
float pitch;
|
float pitch;
|
||||||
float roll;
|
float roll;
|
||||||
|
|
||||||
|
bool mouse_captured;
|
||||||
|
int camera_mode;
|
||||||
} SceneIngame;
|
} SceneIngame;
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
#include "title.h"
|
|
||||||
#include "ingame.h"
|
|
||||||
|
|
||||||
#include "twn_game_api.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
|
|
||||||
static void title_tick(State *state) {
|
|
||||||
SceneTitle *scn = (SceneTitle *)state->scene;
|
|
||||||
(void)scn;
|
|
||||||
|
|
||||||
if (input_is_action_just_pressed("ui_accept")) {
|
|
||||||
switch_to(state, ingame_scene);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
m_sprite("/assets/title.png", ((Rect) {
|
|
||||||
((float)ctx.base_draw_w / 2) - ((float)320 / 2), 64, 320, 128 }));
|
|
||||||
|
|
||||||
|
|
||||||
/* draw the tick count as an example of dynamic text */
|
|
||||||
size_t text_str_len = snprintf(NULL, 0, "%lu", state->ctx->tick_count) + 1;
|
|
||||||
char *text_str = cmalloc(text_str_len);
|
|
||||||
snprintf(text_str, text_str_len, "%lu", state->ctx->tick_count);
|
|
||||||
|
|
||||||
const char *font = "/fonts/kenney-pixel.ttf";
|
|
||||||
int text_h = 32;
|
|
||||||
int text_w = draw_text_width(text_str, text_h, font);
|
|
||||||
|
|
||||||
draw_rectangle(
|
|
||||||
(Rect) {
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.w = (float)text_w,
|
|
||||||
.h = (float)text_h,
|
|
||||||
},
|
|
||||||
(Color) { 0, 0, 0, 255 }
|
|
||||||
);
|
|
||||||
draw_text(text_str, (Vec2){ 0, 0 }, text_h, (Color) { 255, 255, 255, 255 }, font);
|
|
||||||
|
|
||||||
free(text_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void title_end(State *state) {
|
|
||||||
free(state->scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Scene *title_scene(State *state) {
|
|
||||||
(void)state;
|
|
||||||
|
|
||||||
SceneTitle *new_scene = ccalloc(1, sizeof *new_scene);
|
|
||||||
new_scene->base.tick = title_tick;
|
|
||||||
new_scene->base.end = title_end;
|
|
||||||
|
|
||||||
return (Scene *)new_scene;
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#ifndef TITLE_H
|
|
||||||
#define TITLE_H
|
|
||||||
|
|
||||||
#include "../state.h"
|
|
||||||
#include "scene.h"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct SceneTitle {
|
|
||||||
Scene base;
|
|
||||||
} SceneTitle;
|
|
||||||
|
|
||||||
|
|
||||||
Scene *title_scene(State *state);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,16 +1,16 @@
|
|||||||
cmake_minimum_required(VERSION 3.21)
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
|
||||||
project(template LANGUAGES C)
|
project(circle-raster LANGUAGES C)
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNBUILDDIR})
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
state.h
|
state.h
|
||||||
)
|
)
|
||||||
|
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
@ -13,8 +13,7 @@ dev_id = "you"
|
|||||||
|
|
||||||
# Game runtime details
|
# Game runtime details
|
||||||
[game]
|
[game]
|
||||||
base_render_width = 640
|
resolution = [ 640, 480 ]
|
||||||
base_render_height = 480
|
|
||||||
#debug = true
|
#debug = true
|
||||||
|
|
||||||
# Engine tweaks. You probably don't need to change these
|
# Engine tweaks. You probably don't need to change these
|
147
apps/examples/circle-raster/game.c
Normal file
147
apps/examples/circle-raster/game.c
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#include "twn_game_api.h"
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Emits `x` and `y` for every intersecting cell */
|
||||||
|
/* We snap position to the nearest corner, which means there's no aliasing */
|
||||||
|
/* It works great for integer radii */
|
||||||
|
#define m_iter_circle_pixels(p_center_x, p_center_y, p_radius) \
|
||||||
|
for (float y = (p_center_y + ceilf(p_radius)) - 1; y > (p_center_y - ceilf(p_radius)) - 1; --y) \
|
||||||
|
for (float x = p_center_x - ceilf(sqrtf(p_radius * p_radius - (y - p_center_y + (y <= p_center_y)) * (y - p_center_y + (y <= p_center_y)))); x < p_center_x + ceilf(sqrtf(p_radius * p_radius - (y - p_center_y + (y <= p_center_y)) * (y - p_center_y + (y <= p_center_y)))); ++x)
|
||||||
|
|
||||||
|
|
||||||
|
static int32_t ceil_sqrt(int32_t const n) {
|
||||||
|
int32_t res = 1;
|
||||||
|
#pragma clang loop unroll_count(8)
|
||||||
|
while(res * res < n)
|
||||||
|
res++;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void benchmark(struct state *state) {
|
||||||
|
volatile float x, y;
|
||||||
|
|
||||||
|
profile_start("float");
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; ++i) {
|
||||||
|
float const rs = state->r * state->r;
|
||||||
|
float const cr = ceilf(state->r);
|
||||||
|
for (float iy = -cr; iy <= cr - 1; ++iy) {
|
||||||
|
float const dx = ceilf(sqrtf(rs - (iy + (iy <= 0)) * (iy + (iy <= 0))));
|
||||||
|
for (float ix = -dx; ix < dx; ++ix) {
|
||||||
|
x = ix;
|
||||||
|
y = iy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profile_end("float");
|
||||||
|
|
||||||
|
profile_start("int32_t");
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; ++i) {
|
||||||
|
int32_t const rsi = (int32_t)state->r * (int32_t)state->r;
|
||||||
|
for (int32_t iy = -(int32_t)state->r; iy <= (int32_t)state->r - 1; ++iy) {
|
||||||
|
int32_t const dx = ceil_sqrt(rsi - (iy + (iy <= 0)) * (iy + (iy <= 0)));
|
||||||
|
for (int32_t ix = -dx; ix < dx; ++ix) {
|
||||||
|
x = (float)ix;
|
||||||
|
y = (float)iy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profile_end("int32_t");
|
||||||
|
|
||||||
|
profile_start("int32_t acc");
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; ++i) {
|
||||||
|
int32_t const rsi = (int32_t)state->r * (int32_t)state->r;
|
||||||
|
int32_t acc = 1;
|
||||||
|
for (int32_t iy = (int32_t)state->r - 1; iy >= 0; --iy) {
|
||||||
|
while (acc * acc < rsi - iy * iy) acc++;
|
||||||
|
for (int32_t ix = -acc; ix < acc; ++ix) {
|
||||||
|
/* lower portion */
|
||||||
|
x = (float)ix;
|
||||||
|
y = (float)iy;
|
||||||
|
/* upper portion */
|
||||||
|
x = (float)ix;
|
||||||
|
y = (float)-iy - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profile_end("int32_t acc");
|
||||||
|
|
||||||
|
profile_start("int32_t acc precalc");
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; ++i) {
|
||||||
|
int32_t const rsi = (int32_t)state->r * (int32_t)state->r;
|
||||||
|
int32_t acc = (int32_t)(sqrtf(state->r * state->r - (state->r - 1) * (state->r - 1)));
|
||||||
|
for (int32_t iy = (int32_t)state->r - 1; iy >= 0; --iy) {
|
||||||
|
while (acc * acc < rsi - iy * iy) acc++;
|
||||||
|
for (int32_t ix = -acc; ix < acc; ++ix) {
|
||||||
|
/* lower portion */
|
||||||
|
x = (float)ix;
|
||||||
|
y = (float)iy;
|
||||||
|
/* upper portion */
|
||||||
|
x = (float)ix;
|
||||||
|
y = (float)(int32_t)(~(uint32_t)iy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
profile_end("int32_t acc precalc");
|
||||||
|
|
||||||
|
(void)x; (void)y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void game_tick(void) {
|
||||||
|
if (ctx.initialization_needed) {
|
||||||
|
if (!ctx.udata) {
|
||||||
|
ctx.udata = calloc(1, sizeof (struct state));
|
||||||
|
struct state *state = ctx.udata;
|
||||||
|
state->r = 24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct state *state = ctx.udata;
|
||||||
|
|
||||||
|
Vec2 const mouse_snap = {floorf(ctx.mouse_position.x / 8) * 8, floorf(ctx.mouse_position.y / 8) * 8};
|
||||||
|
|
||||||
|
input_action("up", "LCLICK");
|
||||||
|
input_action("down", "RCLICK");
|
||||||
|
|
||||||
|
if (input_action_pressed("up"))
|
||||||
|
state->r += 1;
|
||||||
|
if (input_action_pressed("down") && state->r > 2)
|
||||||
|
state->r -= 1;
|
||||||
|
|
||||||
|
draw_circle(mouse_snap, state->r * 8, (Color){125, 125, 125, 125});
|
||||||
|
|
||||||
|
int32_t const rsi = (int32_t)state->r * (int32_t)state->r;
|
||||||
|
int32_t acc = 1;
|
||||||
|
for (int32_t iy = (int32_t)state->r - 1; iy >= 0; --iy) {
|
||||||
|
while (acc * acc < rsi - iy * iy) acc++;
|
||||||
|
for (int32_t ix = -acc; ix < acc; ++ix) {
|
||||||
|
/* lower portion */
|
||||||
|
draw_box((Rect){mouse_snap.x + (float)ix * 8, mouse_snap.y + (float)iy * 8, 8, 8}, 1, (Color){125, 125, 0, 255});
|
||||||
|
/* upper portion */
|
||||||
|
draw_box((Rect){mouse_snap.x + (float)ix * 8, mouse_snap.y + (float)(-iy - 1) * 8, 8, 8}, 1, (Color){125, 125, 0, 255});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* uncomment to see performance difference between variants */
|
||||||
|
// benchmark(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void game_end(void) {
|
||||||
|
/* do your deinitialization here */
|
||||||
|
struct state *state = ctx.udata;
|
||||||
|
free(state);
|
||||||
|
}
|
11
apps/examples/circle-raster/state.h
Normal file
11
apps/examples/circle-raster/state.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef STATE_H
|
||||||
|
#define STATE_H
|
||||||
|
|
||||||
|
#include "twn_game_api.h"
|
||||||
|
|
||||||
|
struct state {
|
||||||
|
float r;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
16
apps/templates/c/CMakeLists.txt
Normal file
16
apps/templates/c/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(twngame LANGUAGES C)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
|
set(SOURCE_FILES
|
||||||
|
game.c
|
||||||
|
state.h
|
||||||
|
)
|
||||||
|
|
||||||
|
cmake_path(GET CMAKE_SOURCE_DIR STEM LAST_ONLY GAME_PROJECT_NAME)
|
||||||
|
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
27
apps/templates/c/data/twn.toml
Normal file
27
apps/templates/c/data/twn.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# This file contains everything about the engine and your game that can be
|
||||||
|
# configured before it runs.
|
||||||
|
#
|
||||||
|
# Optional settings are commented out, with their default values shown.
|
||||||
|
# Invalid values in these settings will be ignored.
|
||||||
|
|
||||||
|
# Data about your game as an application
|
||||||
|
[about]
|
||||||
|
title = "Template"
|
||||||
|
developer = "You"
|
||||||
|
app_id = "template"
|
||||||
|
dev_id = "you"
|
||||||
|
|
||||||
|
# Game runtime details
|
||||||
|
[game]
|
||||||
|
resolution = [ 640, 480 ]
|
||||||
|
background_color = [ 255, 125, 0, 255 ]
|
||||||
|
#debug = true
|
||||||
|
|
||||||
|
# Engine tweaks. You probably don't need to change these
|
||||||
|
[engine]
|
||||||
|
#ticks_per_second = 60 # minimum of 8
|
||||||
|
#keybind_slots = 3 # minimum of 1
|
||||||
|
#texture_atlas_size = 2048 # minimum of 32
|
||||||
|
#font_texture_size = 2048 # minimum of 1024
|
||||||
|
#font_oversampling = 4 # minimum of 0
|
||||||
|
#font_filtering = "linear" # possible values: "nearest", "linear"
|
@ -1,7 +1,7 @@
|
|||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
|
||||||
#include <malloc.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
void game_tick(void) {
|
void game_tick(void) {
|
||||||
@ -10,7 +10,7 @@ void game_tick(void) {
|
|||||||
if (ctx.initialization_needed) {
|
if (ctx.initialization_needed) {
|
||||||
/* application data could be stored in ctx.udata and retrieved anywhere */
|
/* application data could be stored in ctx.udata and retrieved anywhere */
|
||||||
if (!ctx.udata)
|
if (!ctx.udata)
|
||||||
ctx.udata = ccalloc(1, sizeof (struct state));
|
ctx.udata = calloc(1, sizeof (struct state));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* a lot of data is accessible from `ctx`, look into `townengine/context.h` for more */
|
/* a lot of data is accessible from `ctx`, look into `townengine/context.h` for more */
|
17
apps/templates/lua/.gitignore
vendored
Normal file
17
apps/templates/lua/.gitignore
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# ignore executables
|
||||||
|
*
|
||||||
|
!*.*
|
||||||
|
!*/
|
||||||
|
|
||||||
|
**/*.so
|
||||||
|
**/*.dll
|
||||||
|
**/*.exe
|
||||||
|
**/*.trace
|
||||||
|
**/*.js
|
||||||
|
**/*.wasm
|
||||||
|
**/*.wasm.map
|
||||||
|
**/*.data
|
||||||
|
**/*.html
|
||||||
|
|
||||||
|
data/scripts/twnapi.lua
|
||||||
|
build/
|
7
apps/templates/lua/data/scripts/game.lua
Normal file
7
apps/templates/lua/data/scripts/game.lua
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
-- called every frame, with constant delta time
|
||||||
|
function game_tick()
|
||||||
|
-- ctx.udata persists on code reload
|
||||||
|
if ctx.udata == nil then
|
||||||
|
ctx.udata = {}
|
||||||
|
end
|
||||||
|
end
|
27
apps/templates/lua/data/twn.toml
Normal file
27
apps/templates/lua/data/twn.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# This file contains everything about the engine and your game that can be
|
||||||
|
# configured before it runs.
|
||||||
|
#
|
||||||
|
# Optional settings are commented out, with their default values shown.
|
||||||
|
# Invalid values in these settings will be ignored.
|
||||||
|
|
||||||
|
# Data about your game as an application
|
||||||
|
[about]
|
||||||
|
title = "Template"
|
||||||
|
developer = "You"
|
||||||
|
app_id = "template"
|
||||||
|
dev_id = "you"
|
||||||
|
|
||||||
|
# Game runtime details
|
||||||
|
[game]
|
||||||
|
resolution = [ 640, 480 ]
|
||||||
|
interpreter = "$TWNROOT/apps/twnlua"
|
||||||
|
#debug = true
|
||||||
|
|
||||||
|
# Engine tweaks. You probably don't need to change these
|
||||||
|
[engine]
|
||||||
|
#ticks_per_second = 60 # minimum of 8
|
||||||
|
#keybind_slots = 3 # minimum of 1
|
||||||
|
#texture_atlas_size = 2048 # minimum of 32
|
||||||
|
#font_texture_size = 2048 # minimum of 1024
|
||||||
|
#font_oversampling = 4 # minimum of 0
|
||||||
|
#font_filtering = "linear" # possible values: "nearest", "linear"
|
19
apps/templates/zig/.gitignore
vendored
Normal file
19
apps/templates/zig/.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# ignore executables
|
||||||
|
*
|
||||||
|
!*.*
|
||||||
|
!*/
|
||||||
|
|
||||||
|
**/*.so
|
||||||
|
**/*.dll
|
||||||
|
**/*.exe
|
||||||
|
**/*.trace
|
||||||
|
**/*.js
|
||||||
|
**/*.wasm
|
||||||
|
**/*.wasm.map
|
||||||
|
**/*.data
|
||||||
|
**/*.html
|
||||||
|
|
||||||
|
data/scripts/twnapi.lua
|
||||||
|
build/
|
||||||
|
.zig-cache/
|
||||||
|
zig-out/
|
26
apps/templates/zig/CMakeLists.txt
Normal file
26
apps/templates/zig/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(twngame LANGUAGES C)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
|
cmake_path(GET CMAKE_SOURCE_DIR STEM LAST_ONLY GAME_PROJECT_NAME)
|
||||||
|
put_townengine(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
file(GLOB_RECURSE zig-sources ${CMAKE_CURRENT_SOURCE_DIR}/src/*.zig)
|
||||||
|
|
||||||
|
# TODO: support static build
|
||||||
|
# TODO: propagate release switches
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/libgame.so
|
||||||
|
COMMAND env zig build
|
||||||
|
DEPENDS ${TWN_TARGET} ${zig-sources}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(
|
||||||
|
zig-step ALL
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgame.so
|
||||||
|
)
|
58
apps/templates/zig/build.zig
Normal file
58
apps/templates/zig/build.zig
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
// Although this function looks imperative, note that its job is to
|
||||||
|
// declaratively construct a build graph that will be executed by an external
|
||||||
|
// runner.
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard optimization options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
|
||||||
|
// set a preferred release mode, allowing the user to decide how to optimize.
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
// This creates a "module", which represents a collection of source files alongside
|
||||||
|
// some compilation options, such as optimization mode and linked system libraries.
|
||||||
|
// Every executable or library we compile will be based on one or more modules.
|
||||||
|
const lib_mod = b.createModule(.{
|
||||||
|
// `root_source_file` is the Zig "entry point" of the module. If a module
|
||||||
|
// only contains e.g. external object files, you can make this `null`.
|
||||||
|
// In this case the main source file is merely a path, however, in more
|
||||||
|
// complicated build scripts, this could be a generated file.
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.link_libc = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
lib_mod.addIncludePath(b.path("../../../"));
|
||||||
|
lib_mod.addIncludePath(b.path("../../../include/"));
|
||||||
|
lib_mod.addLibraryPath(b.path("./"));
|
||||||
|
|
||||||
|
// Now, we will create a static library based on the module we created above.
|
||||||
|
// This creates a `std.Build.Step.Compile`, which is the build step responsible
|
||||||
|
// for actually invoking the compiler.
|
||||||
|
const lib = b.addLibrary(.{
|
||||||
|
.linkage = .dynamic,
|
||||||
|
.name = "game",
|
||||||
|
.root_module = lib_mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
lib.linkSystemLibrary("townengine");
|
||||||
|
|
||||||
|
// This declares intent for the library to be installed into the standard
|
||||||
|
// location when the user invokes the "install" step (the default step when
|
||||||
|
// running `zig build`).
|
||||||
|
const install_artifact = b.addInstallArtifact(lib, .{
|
||||||
|
.dest_dir = .{
|
||||||
|
.override = .{
|
||||||
|
.custom = "../",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
b.getInstallStep().dependOn(&install_artifact.step);
|
||||||
|
}
|
27
apps/templates/zig/data/twn.toml
Normal file
27
apps/templates/zig/data/twn.toml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# This file contains everything about the engine and your game that can be
|
||||||
|
# configured before it runs.
|
||||||
|
#
|
||||||
|
# Optional settings are commented out, with their default values shown.
|
||||||
|
# Invalid values in these settings will be ignored.
|
||||||
|
|
||||||
|
# Data about your game as an application
|
||||||
|
[about]
|
||||||
|
title = "Zig Awesomeness"
|
||||||
|
developer = "notwanp"
|
||||||
|
app_id = "yourzigthing"
|
||||||
|
dev_id = "definatelynotwanp"
|
||||||
|
|
||||||
|
# Game runtime details
|
||||||
|
[game]
|
||||||
|
resolution = [ 640, 480 ]
|
||||||
|
background_color = [ 255, 125, 0, 255 ]
|
||||||
|
#debug = true
|
||||||
|
|
||||||
|
# Engine tweaks. You probably don't need to change these
|
||||||
|
[engine]
|
||||||
|
#ticks_per_second = 60 # minimum of 8
|
||||||
|
#keybind_slots = 3 # minimum of 1
|
||||||
|
#texture_atlas_size = 2048 # minimum of 32
|
||||||
|
#font_texture_size = 2048 # minimum of 1024
|
||||||
|
#font_oversampling = 4 # minimum of 0
|
||||||
|
#font_filtering = "linear" # possible values: "nearest", "linear"
|
24
apps/templates/zig/src/root.zig
Normal file
24
apps/templates/zig/src/root.zig
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("twn_game_api.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
export fn game_tick() void {
|
||||||
|
tick() catch |err| {
|
||||||
|
std.log.err(@errorName(err));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn game_end() void {
|
||||||
|
end() catch |err| {
|
||||||
|
std.log.err(@errorName(err));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick() !void {
|
||||||
|
if (c.ctx.initialization_needed) {
|
||||||
|
std.debug.print("lmao\n", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end() !void {}
|
16
apps/tools/twndel/CMakeLists.txt
Normal file
16
apps/tools/twndel/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(twndel LANGUAGES C)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
|
set(SOURCE_FILES
|
||||||
|
tool.c
|
||||||
|
state.h
|
||||||
|
)
|
||||||
|
|
||||||
|
cmake_path(GET CMAKE_SOURCE_DIR STEM LAST_ONLY GAME_PROJECT_NAME)
|
||||||
|
use_townengine("${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
BIN
apps/tools/twndel/data/bong.ogg
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/bong.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/camera.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/camera.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/center.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/center.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/click.wav
Normal file
BIN
apps/tools/twndel/data/click.wav
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/drop.ogg
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/drop.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/grab.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/grab.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/point.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/point.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/pop.wav
Normal file
BIN
apps/tools/twndel/data/pop.wav
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/rip.wav
Normal file
BIN
apps/tools/twndel/data/rip.wav
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/selectin.ogg
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/selectin.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/selectout.ogg
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/selectout.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/snip.wav
Normal file
BIN
apps/tools/twndel/data/snip.wav
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/spooncap.wav
Normal file
BIN
apps/tools/twndel/data/spooncap.wav
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/switch.ogg
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/switch.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
12
apps/tools/twndel/data/twn.toml
Normal file
12
apps/tools/twndel/data/twn.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[about]
|
||||||
|
title = "Townengine Modeling Tool"
|
||||||
|
developer = "twnteam"
|
||||||
|
app_id = "twndel"
|
||||||
|
dev_id = "twnteam"
|
||||||
|
|
||||||
|
# Game runtime details
|
||||||
|
[game]
|
||||||
|
resolution = [ 640, 480 ]
|
||||||
|
background_color = [ 255, 125, 0, 255 ]
|
||||||
|
|
||||||
|
[engine]
|
BIN
apps/tools/twndel/data/x.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/x.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/y.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/y.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
apps/tools/twndel/data/z.png
(Stored with Git LFS)
Normal file
BIN
apps/tools/twndel/data/z.png
(Stored with Git LFS)
Normal file
Binary file not shown.
118
apps/tools/twndel/state.h
Normal file
118
apps/tools/twndel/state.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#ifndef STATE_H
|
||||||
|
#define STATE_H
|
||||||
|
|
||||||
|
#include "twn_game_api.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define POINTS_PER_METER 128
|
||||||
|
#define UNDO_STACK_SIZE 32
|
||||||
|
#define CAMERA_FOV ((float)M_PI_2 * 0.75f)
|
||||||
|
|
||||||
|
#define POINT_LIMIT 65534
|
||||||
|
#define FACE_LIMIT 2048
|
||||||
|
#define OBJECT_LIMIT 16
|
||||||
|
#define TEXTURE_LIMIT 32
|
||||||
|
|
||||||
|
#define INVALID_POINT (POINT_LIMIT+1)
|
||||||
|
#define INVALID_FACE (FACE_LIMIT+1)
|
||||||
|
#define INVALID_OBJECT (OBJECT_LIMIT+1)
|
||||||
|
#define INVALID_TEXTURE (TEXTURE_LIMIT+1)
|
||||||
|
|
||||||
|
#define CAMERA_ROTATION_SPEED 0.04f
|
||||||
|
#define CAMERA_TRANSLATION_SPEED 0.04f
|
||||||
|
#define SELECTION_SPHERE_RADIUS 32
|
||||||
|
/* should be an odd number */
|
||||||
|
#define SNAP_LINES_SHOW 7
|
||||||
|
#define SNAP_LINES_WIDTH 1.0f
|
||||||
|
#define SNAP_LINES_COLOR ((Color){200,200,200,150})
|
||||||
|
|
||||||
|
typedef struct Operation {
|
||||||
|
enum {
|
||||||
|
OPERATION_MOVE_POINT,
|
||||||
|
OPERATION_SET_TEXTURE,
|
||||||
|
OPERATION_TRIANGULATE,
|
||||||
|
} kind;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint16_t point;
|
||||||
|
int16_t delta_x;
|
||||||
|
int16_t delta_y;
|
||||||
|
int16_t delta_z;
|
||||||
|
uint8_t object;
|
||||||
|
} move_point;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint16_t face;
|
||||||
|
int16_t delta_texture;
|
||||||
|
uint8_t object;
|
||||||
|
} set_texture;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint16_t old_face;
|
||||||
|
uint16_t new_face;
|
||||||
|
uint8_t object;
|
||||||
|
} triangulate;
|
||||||
|
} data;
|
||||||
|
|
||||||
|
bool chained;
|
||||||
|
} Operation;
|
||||||
|
|
||||||
|
typedef struct Point {
|
||||||
|
int16_t x, y, z;
|
||||||
|
} Point;
|
||||||
|
|
||||||
|
/* TODO: store topology in terms on edge connections? might be bad, as it's stateful */
|
||||||
|
/* triangles have p3 = INVALID_POINT */
|
||||||
|
/* lines have p2, p3 = INVALID_POINT */
|
||||||
|
/* billboards have p1, p2, p3 = INVALID_POINT */
|
||||||
|
typedef struct Face {
|
||||||
|
uint16_t p[4];
|
||||||
|
/* texture origin, as point on face plane, in absolute coordinates */
|
||||||
|
int16_t tex_x, tex_y;
|
||||||
|
uint8_t texture;
|
||||||
|
uint8_t tex_scale;
|
||||||
|
} Face;
|
||||||
|
|
||||||
|
typedef struct Object {
|
||||||
|
char *name;
|
||||||
|
bool is_invisible;
|
||||||
|
Point position;
|
||||||
|
char *textures[TEXTURE_LIMIT + 1];
|
||||||
|
uint8_t textures_sz;
|
||||||
|
Point rotation;
|
||||||
|
Face faces[FACE_LIMIT];
|
||||||
|
uint16_t faces_sz;
|
||||||
|
} Object;
|
||||||
|
|
||||||
|
typedef struct State {
|
||||||
|
Operation op_stack[UNDO_STACK_SIZE];
|
||||||
|
uint32_t op_stack_ptr;
|
||||||
|
bool op_active;
|
||||||
|
|
||||||
|
Vec3 active_center;
|
||||||
|
Vec3 camera_position;
|
||||||
|
Vec3 camera_direction;
|
||||||
|
float camera_zoom;
|
||||||
|
bool camera_is_orthographic;
|
||||||
|
/* defaults to wireframe */
|
||||||
|
bool solid_display_mode;
|
||||||
|
/* positions skipped */
|
||||||
|
uint8_t grid_snap_granularity;
|
||||||
|
|
||||||
|
Point points[POINT_LIMIT];
|
||||||
|
uint16_t points_sz;
|
||||||
|
|
||||||
|
Object objects[OBJECT_LIMIT];
|
||||||
|
uint8_t objects_sz;
|
||||||
|
|
||||||
|
/* which axes are blocked and which are not */
|
||||||
|
/* order: x, y, z */
|
||||||
|
bool axis_mask[3];
|
||||||
|
char *current_texture;
|
||||||
|
|
||||||
|
uint8_t current_hovered_obj;
|
||||||
|
uint16_t current_hovered_face;
|
||||||
|
} State;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
972
apps/tools/twndel/tool.c
Normal file
972
apps/tools/twndel/tool.c
Normal file
@ -0,0 +1,972 @@
|
|||||||
|
#include "twn_game_api.h"
|
||||||
|
#include "state.h"
|
||||||
|
#include "twn_vec.h"
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
/* planned features: */
|
||||||
|
/* grid-based, bounded space (65536 / POINTS_PER_METER meters), which allows for efficient storage */
|
||||||
|
/* 65534 point limit, 16 object limit, 2048 faces per object, 32 textures per object */
|
||||||
|
/* triangles and quads only */
|
||||||
|
/* support for billboards and flat two sided quads */
|
||||||
|
/* texture painting */
|
||||||
|
/* bones with mesh animations, snapping to grid, with no weights */
|
||||||
|
/* billboard render to specified angles */
|
||||||
|
/* 1 point light primitive lighting */
|
||||||
|
/* live edited textures are capped at 128x128 */
|
||||||
|
|
||||||
|
/* assumptions: */
|
||||||
|
/* up is always (0,1,0) */
|
||||||
|
/* preallocations everywhere */
|
||||||
|
|
||||||
|
static State state;
|
||||||
|
static bool init;
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t new_object(const char *name) {
|
||||||
|
if (state.objects_sz >= OBJECT_LIMIT)
|
||||||
|
return INVALID_OBJECT;
|
||||||
|
state.objects_sz++;
|
||||||
|
state.objects[state.objects_sz-1].name = SDL_strdup(name);
|
||||||
|
return state.objects_sz-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint16_t new_point(int16_t x, int16_t y, int16_t z) {
|
||||||
|
if (state.points_sz >= POINT_LIMIT)
|
||||||
|
return INVALID_POINT;
|
||||||
|
state.points_sz++;
|
||||||
|
state.points[state.points_sz-1] = (Point){x, y, z};
|
||||||
|
return state.points_sz-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint16_t push_face(uint8_t object,
|
||||||
|
uint16_t p0, uint16_t p1, uint16_t p2, uint16_t p3,
|
||||||
|
uint8_t texture, uint8_t tex_scale, int16_t tex_x, int16_t tex_y)
|
||||||
|
{
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
o->faces_sz++;
|
||||||
|
o->faces[o->faces_sz-1] = (Face) {
|
||||||
|
.p = {p0, p1, p2, p3},
|
||||||
|
.texture = texture,
|
||||||
|
.tex_scale = tex_scale,
|
||||||
|
.tex_x = tex_x,
|
||||||
|
.tex_y = tex_y,
|
||||||
|
};
|
||||||
|
return o->faces_sz-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t push_texture(uint8_t object, char *texture) {
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
|
||||||
|
/* check whether it's already here */
|
||||||
|
for (uint8_t i = 0; i < o->textures_sz; ++i)
|
||||||
|
if (SDL_strcmp(o->textures[i], texture) == 0)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
o->textures_sz++;
|
||||||
|
o->textures[o->textures_sz-1] = SDL_strdup(texture);
|
||||||
|
return o->textures_sz-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: use tombstones instead? it would be easier to maintain, by a lot */
|
||||||
|
/* note: make sure nothing depends on none */
|
||||||
|
static void pop_face(uint8_t object, uint16_t face) {
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
if (face != o->faces_sz-1)
|
||||||
|
o->faces[face] = o->faces[o->faces_sz-1];
|
||||||
|
o->faces_sz--;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void push_operation(Operation operation, bool active) {
|
||||||
|
state.op_stack_ptr++;
|
||||||
|
uint8_t op = state.op_stack_ptr % UNDO_STACK_SIZE;
|
||||||
|
state.op_stack[op] = operation;
|
||||||
|
state.op_active = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void extend_operation(Operation operation) {
|
||||||
|
uint8_t op = state.op_stack_ptr % UNDO_STACK_SIZE;
|
||||||
|
Operation ext = state.op_stack[op];
|
||||||
|
ext.chained = true;
|
||||||
|
state.op_stack[op] = operation;
|
||||||
|
push_operation(ext, state.op_active);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline Vec3 point_to_vec3(uint8_t object, uint16_t point) {
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
return (Vec3){ (o->position.x + state.points[point].x) / (float)POINTS_PER_METER,
|
||||||
|
(o->position.y + state.points[point].y) / (float)POINTS_PER_METER,
|
||||||
|
(o->position.z + state.points[point].z) / (float)POINTS_PER_METER };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: something is wrong, figure out how to introduce rotation to this. */
|
||||||
|
static inline Vec2 project_texture_coordinate(Vec2 origin, Vec3 plane, Vec3 point, float scale) {
|
||||||
|
Vec3 right = vec3_norm(vec3_cross(plane, fabsf(0.0f - plane.y) < 1e-9f ? (Vec3){0,1,0} : (Vec3){1,0,0}));
|
||||||
|
Vec3 up = vec3_norm(vec3_cross(right, plane));
|
||||||
|
Vec2 pp = { vec3_dot(point, right), vec3_dot(point, up) };
|
||||||
|
return vec2_scale(vec2_sub(origin, pp), scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void render_object(uint8_t object) {
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
|
||||||
|
if (o->is_invisible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||||
|
Face *f = &o->faces[fi];
|
||||||
|
|
||||||
|
if (f->p[2] != INVALID_POINT) {
|
||||||
|
Vec3 p0 = point_to_vec3(object, f->p[0]);
|
||||||
|
Vec3 p1 = point_to_vec3(object, f->p[1]);
|
||||||
|
Vec3 p2 = point_to_vec3(object, f->p[2]);
|
||||||
|
|
||||||
|
if (state.solid_display_mode) {
|
||||||
|
Vec3 n = vec3_norm(vec3_cross(vec3_sub(p1, p0), vec3_sub(p2, p0)));
|
||||||
|
Vec2 to = { f->tex_x / (float)POINTS_PER_METER, f->tex_y / (float)POINTS_PER_METER, };
|
||||||
|
|
||||||
|
Vec2 tul = project_texture_coordinate(to, n, p0, (float)f->tex_scale);
|
||||||
|
Vec2 tdl = project_texture_coordinate(to, n, p1, (float)f->tex_scale);
|
||||||
|
Vec2 tdr = project_texture_coordinate(to, n, p2, (float)f->tex_scale);
|
||||||
|
|
||||||
|
draw_triangle(o->textures[f->texture],
|
||||||
|
p0, p1, p2,
|
||||||
|
tul, tdl, tdr,
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
(Color){255,255,255,255});
|
||||||
|
|
||||||
|
if (f->p[3] != INVALID_POINT) {
|
||||||
|
Vec3 p3 = point_to_vec3(object, f->p[3]);
|
||||||
|
Vec2 tur = project_texture_coordinate(to, n, p3, (float)f->tex_scale);
|
||||||
|
draw_triangle(o->textures[f->texture],
|
||||||
|
p2, p3, p0,
|
||||||
|
tdr, tur, tul,
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
(Color){255,255,255,255});
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
draw_line_3d(p0, p1, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(p1, p2, 1, (Color){255,255,255,255});
|
||||||
|
|
||||||
|
if (f->p[3] == INVALID_POINT)
|
||||||
|
draw_line_3d(p2, p0, 1, (Color){255,255,255,255});
|
||||||
|
else {
|
||||||
|
Vec3 p3 = point_to_vec3(object, f->p[3]);
|
||||||
|
draw_line_3d(p2, p3, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(p3, p0, 1, (Color){255,255,255,255});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else
|
||||||
|
SDL_assert_always(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t new_cube(Point pos, Point size) {
|
||||||
|
uint8_t object = new_object("cube");
|
||||||
|
|
||||||
|
uint16_t p0 = new_point(pos.x - size.x / 2, pos.y - size.y / 2, pos.z - size.z / 2);
|
||||||
|
uint16_t p1 = new_point(pos.x - size.x / 2, pos.y + size.y / 2, pos.z - size.z / 2);
|
||||||
|
uint16_t p2 = new_point(pos.x + size.x / 2, pos.y + size.y / 2, pos.z - size.z / 2);
|
||||||
|
uint16_t p3 = new_point(pos.x + size.x / 2, pos.y - size.y / 2, pos.z - size.z / 2);
|
||||||
|
uint16_t p4 = new_point(pos.x - size.x / 2, pos.y - size.y / 2, pos.z + size.z / 2);
|
||||||
|
uint16_t p5 = new_point(pos.x - size.x / 2, pos.y + size.y / 2, pos.z + size.z / 2);
|
||||||
|
uint16_t p6 = new_point(pos.x + size.x / 2, pos.y + size.y / 2, pos.z + size.z / 2);
|
||||||
|
uint16_t p7 = new_point(pos.x + size.x / 2, pos.y - size.y / 2, pos.z + size.z / 2);
|
||||||
|
|
||||||
|
uint8_t tex = push_texture(object, "/data/placeholder.png");
|
||||||
|
push_face(object, p2, p3, p0, p1, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
push_face(object, p5, p4, p7, p6, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
push_face(object, p1, p0, p4, p5, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
push_face(object, p6, p7, p3, p2, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
push_face(object, p2, p1, p5, p6, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
push_face(object, p0, p3, p7, p4, tex, 128, pos.x - size.x / 2, pos.y - size.y / 2);
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_camera_rotation(void) {
|
||||||
|
float horizontal_rotation = 0;
|
||||||
|
float vertical_rotation = 0;
|
||||||
|
|
||||||
|
if (input_action_pressed("camera_rotate_left"))
|
||||||
|
horizontal_rotation -= CAMERA_ROTATION_SPEED;
|
||||||
|
if (input_action_pressed("camera_rotate_right"))
|
||||||
|
horizontal_rotation += CAMERA_ROTATION_SPEED;
|
||||||
|
if (input_action_pressed("camera_rotate_up"))
|
||||||
|
vertical_rotation -= CAMERA_ROTATION_SPEED;
|
||||||
|
if (input_action_pressed("camera_rotate_down"))
|
||||||
|
vertical_rotation += CAMERA_ROTATION_SPEED;
|
||||||
|
|
||||||
|
Vec3 front = vec3_cross(state.camera_direction, (Vec3){0,1,0});
|
||||||
|
Vec3 local_position = vec3_sub(state.active_center, state.camera_position);
|
||||||
|
Vec3 new_local_position = vec3_rotate(local_position, horizontal_rotation, (Vec3){0,1,0});
|
||||||
|
|
||||||
|
state.camera_direction = vec3_rotate(state.camera_direction, horizontal_rotation, (Vec3){0,1,0});
|
||||||
|
Vec3 new_rot = vec3_rotate(state.camera_direction, vertical_rotation, front);
|
||||||
|
|
||||||
|
/* only apply if it's in limits */
|
||||||
|
float d = vec3_dot(new_rot, (Vec3){0,-1,0});
|
||||||
|
if (fabsf(d) <= 0.999f) {
|
||||||
|
new_local_position = vec3_rotate(new_local_position, vertical_rotation, front);
|
||||||
|
state.camera_direction = new_rot;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.camera_position = vec3_sub(state.active_center, new_local_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_camera_translation(void) {
|
||||||
|
Vec3 right = vec3_norm(vec3_cross(state.camera_direction, (Vec3){0,1,0}));
|
||||||
|
Vec3 up = vec3_norm(vec3_cross(state.camera_direction, right));
|
||||||
|
Vec3 was = state.camera_position;
|
||||||
|
|
||||||
|
if (input_action_pressed("camera_rotate_left"))
|
||||||
|
state.camera_position = vec3_sub(state.camera_position, vec3_scale(right, CAMERA_TRANSLATION_SPEED));
|
||||||
|
if (input_action_pressed("camera_rotate_right"))
|
||||||
|
state.camera_position = vec3_add(state.camera_position, vec3_scale(right, CAMERA_TRANSLATION_SPEED));
|
||||||
|
if (input_action_pressed("camera_rotate_up"))
|
||||||
|
state.camera_position = vec3_sub(state.camera_position, vec3_scale(up, CAMERA_TRANSLATION_SPEED));
|
||||||
|
if (input_action_pressed("camera_rotate_down"))
|
||||||
|
state.camera_position = vec3_add(state.camera_position, vec3_scale(up, CAMERA_TRANSLATION_SPEED));
|
||||||
|
|
||||||
|
state.active_center = vec3_add(state.active_center, vec3_sub(state.camera_position, was));
|
||||||
|
|
||||||
|
draw_billboard("/data/camera.png",
|
||||||
|
vec3_add(state.camera_position, vec3_scale(state.camera_direction, vec3_length(state.camera_position))),
|
||||||
|
(Vec2){0.2f,0.2f},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
/* show relation to origin */
|
||||||
|
draw_billboard("/data/center.png",
|
||||||
|
(Vec3){0},
|
||||||
|
(Vec2){0.1f,0.1f},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
draw_line_3d((Vec3){0}, state.active_center, 1, (Color){255,255,255,255});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_camera_movement(void) {
|
||||||
|
input_action("camera_rotate_left", "A");
|
||||||
|
input_action("camera_rotate_right", "D");
|
||||||
|
input_action("camera_rotate_up", "W");
|
||||||
|
input_action("camera_rotate_down", "S");
|
||||||
|
input_action("camera_lock_rotation", "SPACE");
|
||||||
|
|
||||||
|
if (input_action_pressed("camera_lock_rotation")) {
|
||||||
|
process_camera_translation();
|
||||||
|
} else {
|
||||||
|
process_camera_rotation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline DrawCameraUnprojectResult unproject_point(Vec2 point) {
|
||||||
|
return draw_camera_unproject(
|
||||||
|
point,
|
||||||
|
state.camera_position,
|
||||||
|
state.camera_direction,
|
||||||
|
(Vec3){0, 1, 0},
|
||||||
|
state.camera_is_orthographic ? 0 : CAMERA_FOV,
|
||||||
|
state.camera_zoom,
|
||||||
|
100 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool find_closest_point(uint8_t* object_result, uint16_t *point_result) {
|
||||||
|
DrawCameraUnprojectResult pos_and_ray = unproject_point(ctx.mouse_position);
|
||||||
|
|
||||||
|
/* step over every selectable object and find points closest to the view ray */
|
||||||
|
/* by constructing triangles and finding their height, from perpendicular */
|
||||||
|
uint16_t closest_point = INVALID_POINT;
|
||||||
|
uint8_t closest_obj = INVALID_OBJECT;
|
||||||
|
float closest_distance = INFINITY;
|
||||||
|
|
||||||
|
for (uint8_t obj = 0; obj < state.objects_sz; ++obj) {
|
||||||
|
Object *o = &state.objects[obj];
|
||||||
|
|
||||||
|
if (o->is_invisible)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* TODO: is it possible to skip repeated points? does it matter? */
|
||||||
|
/* as we limit the point could we could actually have bool array preallocated for this */
|
||||||
|
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||||
|
Face *f = &o->faces[fi];
|
||||||
|
for (uint16_t pi = 0; pi < 4; ++pi) {
|
||||||
|
if (f->p[pi] == INVALID_POINT) break;
|
||||||
|
Vec3 p = point_to_vec3(obj, f->p[pi]);
|
||||||
|
Vec3 d = vec3_sub(pos_and_ray.position, p);
|
||||||
|
Vec3 b = vec3_cross(d, pos_and_ray.direction);
|
||||||
|
float ray_dist = vec3_length(b);
|
||||||
|
if (ray_dist > ((float)SELECTION_SPHERE_RADIUS / POINTS_PER_METER))
|
||||||
|
continue;
|
||||||
|
float dist = vec3_length(vec3_sub(pos_and_ray.position, vec3_add(p, b)));
|
||||||
|
if (dist < closest_distance) {
|
||||||
|
closest_distance = dist;
|
||||||
|
closest_obj = obj;
|
||||||
|
closest_point = f->p[pi];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closest_point == INVALID_POINT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (object_result)
|
||||||
|
*object_result = closest_obj;
|
||||||
|
if (point_result)
|
||||||
|
*point_result = closest_point;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* o = vector origin */
|
||||||
|
/* v = vector direction, normalized */
|
||||||
|
/* p = any point on plane */
|
||||||
|
/* n = normal of a plane */
|
||||||
|
static bool vector_plane_intersection(Vec3 o, Vec3 v, Vec3 p, Vec3 n, Vec3 *out) {
|
||||||
|
float dot = vec3_dot(n, v);
|
||||||
|
if (fabsf(dot) > FLT_EPSILON) {
|
||||||
|
Vec3 w = vec3_sub(o, p);
|
||||||
|
float fac = -vec3_dot(n, w) / dot;
|
||||||
|
*out = vec3_add(o, vec3_scale(v, fac));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/* vector and plane are perpendicular, assume that it lies exactly on it */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool find_closest_face(uint8_t* object_result, uint16_t *face_result) {
|
||||||
|
DrawCameraUnprojectResult cam = unproject_point(ctx.mouse_position);
|
||||||
|
|
||||||
|
uint8_t closest_obj = INVALID_OBJECT;
|
||||||
|
uint16_t closest_face = INVALID_FACE;
|
||||||
|
float closest_distance = INFINITY;
|
||||||
|
|
||||||
|
for (uint8_t oi = 0; oi < state.objects_sz; ++oi) {
|
||||||
|
Object *o = &state.objects[oi];
|
||||||
|
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||||
|
Face *f = &o->faces[fi];
|
||||||
|
|
||||||
|
if (f->p[1] == INVALID_POINT) continue;
|
||||||
|
|
||||||
|
Vec3 p0 = point_to_vec3(oi, f->p[0]);
|
||||||
|
Vec3 p1 = point_to_vec3(oi, f->p[1]);
|
||||||
|
Vec3 p2 = point_to_vec3(oi, f->p[2]);
|
||||||
|
|
||||||
|
Vec3 n = vec3_norm(vec3_cross(vec3_sub(p1, p0), vec3_sub(p2, p0)));
|
||||||
|
|
||||||
|
/* culling */
|
||||||
|
if (vec3_dot(state.camera_direction, n) >= 0) continue;
|
||||||
|
|
||||||
|
Vec3 i;
|
||||||
|
if (!vector_plane_intersection(cam.position, cam.direction, p0, n, &i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float dist = vec3_length(vec3_sub(p0, i));
|
||||||
|
if (dist >= closest_distance) continue;
|
||||||
|
|
||||||
|
/* left normals are used to determine whether point lies to the left for all forming lines */
|
||||||
|
Vec3 ln0 = vec3_norm(vec3_cross(n, vec3_sub(p1, p0)));
|
||||||
|
if (vec3_dot(ln0, vec3_sub(p0, i)) >= 0) continue;
|
||||||
|
|
||||||
|
Vec3 ln1 = vec3_norm(vec3_cross(n, vec3_sub(p2, p1)));
|
||||||
|
if (vec3_dot(ln1, vec3_sub(p1, i)) >= 0) continue;
|
||||||
|
|
||||||
|
if (f->p[3] == INVALID_POINT) {
|
||||||
|
/* triangle */
|
||||||
|
Vec3 ln2 = vec3_norm(vec3_cross(n, vec3_sub(p0, p2)));
|
||||||
|
if (vec3_dot(ln2, vec3_sub(p2, i)) >= 0) continue;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* quad */
|
||||||
|
Vec3 p3 = point_to_vec3(oi, f->p[3]);
|
||||||
|
Vec3 ln2 = vec3_norm(vec3_cross(n, vec3_sub(p3, p2)));
|
||||||
|
if (vec3_dot(ln2, vec3_sub(p2, i)) >= 0) continue;
|
||||||
|
|
||||||
|
Vec3 ln3 = vec3_norm(vec3_cross(n, vec3_sub(p0, p3)));
|
||||||
|
if (vec3_dot(ln3, vec3_sub(p3, i)) >= 0) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
closest_distance = dist;
|
||||||
|
closest_face = fi;
|
||||||
|
closest_obj = oi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closest_face == INVALID_FACE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (object_result)
|
||||||
|
*object_result = closest_obj;
|
||||||
|
if (face_result)
|
||||||
|
*face_result = closest_face;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void show_snap_lines(Vec3 p) {
|
||||||
|
float step = 1.0f / ((float)POINTS_PER_METER / state.grid_snap_granularity);
|
||||||
|
int const lines_per_side = (SNAP_LINES_SHOW - 1) / 2;
|
||||||
|
|
||||||
|
for (int l = -lines_per_side; l <= lines_per_side; ++l) {
|
||||||
|
if (!state.axis_mask[0]) {
|
||||||
|
Vec3 c = vec3_add(p, vec3_scale((Vec3){1,0,0}, step * (float)l));
|
||||||
|
draw_line_3d(
|
||||||
|
vec3_add(c, vec3_scale((Vec3){0,0,1}, SNAP_LINES_WIDTH)),
|
||||||
|
vec3_add(c, vec3_scale((Vec3){0,0,1}, -SNAP_LINES_WIDTH)),
|
||||||
|
1,
|
||||||
|
SNAP_LINES_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.axis_mask[1]) {
|
||||||
|
Vec3 axis = fabsf(vec3_dot(state.camera_direction, (Vec3){0,0,1})) >= 0.5f ? (Vec3){1,0,0} : (Vec3){0,0,1};
|
||||||
|
Vec3 c = vec3_add(p, vec3_scale((Vec3){0,1,0}, step * (float)l));
|
||||||
|
draw_line_3d(
|
||||||
|
vec3_add(c, vec3_scale(axis, SNAP_LINES_WIDTH)),
|
||||||
|
vec3_add(c, vec3_scale(axis, -SNAP_LINES_WIDTH)),
|
||||||
|
1,
|
||||||
|
SNAP_LINES_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.axis_mask[2]) {
|
||||||
|
Vec3 c = vec3_add(p, vec3_scale((Vec3){0,0,1}, step * (float)l));
|
||||||
|
draw_line_3d(
|
||||||
|
vec3_add(c, vec3_scale((Vec3){1,0,0}, SNAP_LINES_WIDTH)),
|
||||||
|
vec3_add(c, vec3_scale((Vec3){1,0,0}, -SNAP_LINES_WIDTH)),
|
||||||
|
1,
|
||||||
|
SNAP_LINES_COLOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool process_operation_move_point(Operation *op) {
|
||||||
|
/* finish dragging around */
|
||||||
|
/* TODO: dont keep empty ops on stack? */
|
||||||
|
if (input_action_just_released("select")) {
|
||||||
|
state.op_active = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float size = sinf(ctx.frame_number / 10) * 0.05f + 0.05f;
|
||||||
|
draw_billboard("/data/grab.png",
|
||||||
|
point_to_vec3(op->data.move_point.object, op->data.move_point.point),
|
||||||
|
(Vec2){size, size},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
DrawCameraUnprojectResult cam = unproject_point(ctx.mouse_position);
|
||||||
|
Vec3 p = point_to_vec3(op->data.move_point.object, op->data.move_point.point);
|
||||||
|
bool point_moved = false;
|
||||||
|
|
||||||
|
/* figure out which planes are angled acutely against the viewing direction */
|
||||||
|
bool y_closer_than_horizon = fabsf(vec3_dot(state.camera_direction, (Vec3){0,1,0})) >= 0.5f;
|
||||||
|
|
||||||
|
Vec3 plane_normal_x = y_closer_than_horizon ? (Vec3){0,1,0} : (Vec3){0,0,1};
|
||||||
|
Vec3 plane_normal_z = y_closer_than_horizon ? (Vec3){0,1,0} : (Vec3){1,0,0};
|
||||||
|
|
||||||
|
/* show snapping in lines */
|
||||||
|
show_snap_lines(p);
|
||||||
|
|
||||||
|
Vec3 s;
|
||||||
|
if (!state.axis_mask[0]) {
|
||||||
|
if (vector_plane_intersection(cam.position, cam.direction, p, plane_normal_x, &s)) {
|
||||||
|
int16_t xch = (int16_t)(roundf(vec3_dot(vec3_sub(s, p), (Vec3){1,0,0}) * (float)POINTS_PER_METER));
|
||||||
|
xch -= xch % state.grid_snap_granularity;
|
||||||
|
state.points[op->data.move_point.point].x += xch;
|
||||||
|
op->data.move_point.delta_x += xch;
|
||||||
|
if (xch != 0) point_moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.axis_mask[1]) {
|
||||||
|
if (vector_plane_intersection(cam.position, cam.direction, p, (Vec3){0,0,1}, &s)) {
|
||||||
|
int16_t ych = (int16_t)(roundf(vec3_dot(vec3_sub(s, p), (Vec3){0,1,0}) * (float)POINTS_PER_METER));
|
||||||
|
ych -= ych % state.grid_snap_granularity;
|
||||||
|
state.points[op->data.move_point.point].y += ych;
|
||||||
|
op->data.move_point.delta_y += ych;
|
||||||
|
if (ych != 0) point_moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.axis_mask[2]) {
|
||||||
|
if (vector_plane_intersection(cam.position, cam.direction, p, plane_normal_z, &s)) {
|
||||||
|
int16_t zch = (int16_t)(roundf(vec3_dot(vec3_sub(s, p), (Vec3){0,0,1}) * (float)POINTS_PER_METER));
|
||||||
|
zch -= zch % state.grid_snap_granularity;
|
||||||
|
state.points[op->data.move_point.point].z += zch;
|
||||||
|
op->data.move_point.delta_z += zch;
|
||||||
|
if (zch != 0) point_moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (point_moved) {
|
||||||
|
audio_play("/data/bong.ogg", NULL, false, 0.12f, 0.0f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void reverse_operation_move_point(Operation *op) {
|
||||||
|
SDL_assert(op->kind == OPERATION_MOVE_POINT);
|
||||||
|
state.points[op->data.move_point.point].x -= op->data.move_point.delta_x;
|
||||||
|
state.points[op->data.move_point.point].y -= op->data.move_point.delta_y;
|
||||||
|
state.points[op->data.move_point.point].z -= op->data.move_point.delta_z;
|
||||||
|
audio_play("/data/drop.ogg", NULL, false, 0.4f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void reverse_operation_set_texture(Operation *op) {
|
||||||
|
SDL_assert(op->kind == OPERATION_SET_TEXTURE);
|
||||||
|
Object *o = &state.objects[op->data.set_texture.object];
|
||||||
|
o->faces[op->data.set_texture.face].texture += op->data.set_texture.delta_texture;
|
||||||
|
audio_play("/data/drop.ogg", NULL, false, 0.4f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void reverse_triangulation(Operation *op) {
|
||||||
|
SDL_assert(op->kind == OPERATION_TRIANGULATE);
|
||||||
|
Object *o = &state.objects[op->data.set_texture.object];
|
||||||
|
Face *fn = &o->faces[op->data.triangulate.new_face];
|
||||||
|
Face *fo = &o->faces[op->data.triangulate.old_face];
|
||||||
|
fo->p[3] = fo->p[2];
|
||||||
|
fo->p[2] = fn->p[1];
|
||||||
|
pop_face(op->data.set_texture.object, op->data.triangulate.new_face);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: reverse of this */
|
||||||
|
static void try_subdividing_from_moving(uint8_t object, uint16_t point) {
|
||||||
|
Object *o = &state.objects[object];
|
||||||
|
bool not_first = false;
|
||||||
|
|
||||||
|
for (uint16_t fi = 0; fi < o->faces_sz; ++fi) {
|
||||||
|
Face *f = &o->faces[fi];
|
||||||
|
if (f->p[3] == INVALID_POINT) continue;
|
||||||
|
for (uint16_t pi = 0; pi < 4; ++pi) {
|
||||||
|
if (f->p[pi] == point) {
|
||||||
|
Face new0 = *f;
|
||||||
|
new0.p[0] = f->p[pi];
|
||||||
|
new0.p[1] = f->p[(pi + 1) % 4];
|
||||||
|
new0.p[2] = f->p[(pi + 3) % 4];
|
||||||
|
new0.p[3] = INVALID_POINT;
|
||||||
|
|
||||||
|
uint16_t newf = push_face(
|
||||||
|
object,
|
||||||
|
f->p[(pi + 1) % 4],
|
||||||
|
f->p[(pi + 2) % 4],
|
||||||
|
f->p[(pi + 3) % 4],
|
||||||
|
INVALID_POINT,
|
||||||
|
f->texture,
|
||||||
|
f->tex_scale,
|
||||||
|
f->tex_x,
|
||||||
|
f->tex_y);
|
||||||
|
|
||||||
|
*f = new0;
|
||||||
|
|
||||||
|
extend_operation((Operation){
|
||||||
|
.kind = OPERATION_TRIANGULATE,
|
||||||
|
.data = { .triangulate = { .new_face = newf, .old_face = fi, .object = object} },
|
||||||
|
.chained = not_first,
|
||||||
|
});
|
||||||
|
|
||||||
|
not_first = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_undo(void) {
|
||||||
|
if (state.op_active)
|
||||||
|
state.op_active = false;
|
||||||
|
|
||||||
|
/* TODO: checks and defined limit */
|
||||||
|
Operation *op = &state.op_stack[state.op_stack_ptr % UNDO_STACK_SIZE];
|
||||||
|
state.op_stack_ptr--;
|
||||||
|
|
||||||
|
switch (op->kind) {
|
||||||
|
case OPERATION_MOVE_POINT: {
|
||||||
|
reverse_operation_move_point(op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPERATION_SET_TEXTURE: {
|
||||||
|
reverse_operation_set_texture(op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPERATION_TRIANGULATE: {
|
||||||
|
reverse_triangulation(op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
(void)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pop another if they're chained together */
|
||||||
|
if (op->chained) process_undo();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_operations(void) {
|
||||||
|
if (input_action_just_pressed("undo"))
|
||||||
|
process_undo();
|
||||||
|
|
||||||
|
if (!state.op_active) {
|
||||||
|
/* point dragging */
|
||||||
|
if (!state.current_texture) {
|
||||||
|
uint16_t point_select; uint8_t obj_select;
|
||||||
|
if (find_closest_point(&obj_select, &point_select)) {
|
||||||
|
draw_billboard("/data/point.png",
|
||||||
|
point_to_vec3(obj_select, point_select),
|
||||||
|
(Vec2){0.05f, 0.05f},
|
||||||
|
(Rect){0},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
if (input_action_just_pressed("select"))
|
||||||
|
push_operation((Operation){
|
||||||
|
.kind = OPERATION_MOVE_POINT,
|
||||||
|
.data = { .move_point = { .point = point_select, .object = obj_select } },
|
||||||
|
}, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* texture setting */
|
||||||
|
} else {
|
||||||
|
uint8_t obj_select; uint16_t face_select;
|
||||||
|
if (find_closest_face(&obj_select, &face_select)) {
|
||||||
|
state.current_hovered_face = face_select;
|
||||||
|
state.current_hovered_obj = obj_select;
|
||||||
|
|
||||||
|
if (input_action_just_pressed("rotate")) {
|
||||||
|
Face *f = &state.objects[obj_select].faces[face_select];
|
||||||
|
int16_t tex_x = f->tex_x;
|
||||||
|
int16_t tex_y = f->tex_y;
|
||||||
|
f->tex_x = -tex_y;
|
||||||
|
f->tex_y = tex_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_pressed("select") && state.current_texture) {
|
||||||
|
uint8_t new_tex = push_texture(obj_select, state.current_texture);
|
||||||
|
uint8_t cur_tex = state.objects[obj_select].faces[face_select].texture;
|
||||||
|
|
||||||
|
if (new_tex != cur_tex) {
|
||||||
|
state.objects[obj_select].faces[face_select].texture = new_tex;
|
||||||
|
audio_play("/data/bong.ogg", NULL, false, 0.5f, 0.0f);
|
||||||
|
|
||||||
|
push_operation((Operation){
|
||||||
|
.kind = OPERATION_SET_TEXTURE,
|
||||||
|
.data = { .set_texture = {
|
||||||
|
.face = face_select,
|
||||||
|
.object = obj_select,
|
||||||
|
.delta_texture = cur_tex - new_tex,
|
||||||
|
}},
|
||||||
|
}, false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.op_active) {
|
||||||
|
Operation *op = &state.op_stack[state.op_stack_ptr % UNDO_STACK_SIZE];
|
||||||
|
switch (op->kind) {
|
||||||
|
case OPERATION_MOVE_POINT: {
|
||||||
|
bool update = process_operation_move_point(op);
|
||||||
|
|
||||||
|
if (update)
|
||||||
|
try_subdividing_from_moving(op->data.move_point.object, op->data.move_point.point);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OPERATION_SET_TEXTURE:
|
||||||
|
case OPERATION_TRIANGULATE:
|
||||||
|
default:
|
||||||
|
(void)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_axes(void) {
|
||||||
|
/* axis helpers */
|
||||||
|
/* idea: double selection of axes for diagonal edits */
|
||||||
|
draw_line_3d(state.active_center, (Vec3){256,0,0}, 1, state.axis_mask[0] ? (Color){0,0,0,75} : (Color){255,0,0,125});
|
||||||
|
draw_billboard("/data/x.png",
|
||||||
|
vec3_add(state.active_center, (Vec3){2,0,0}),
|
||||||
|
(Vec2){0.1f, 0.1f},
|
||||||
|
(Rect){0},
|
||||||
|
state.axis_mask[0] ? (Color){0,0,0,255} : (Color){255,0,0,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
draw_line_3d(state.active_center, (Vec3){0,256,0}, 1, state.axis_mask[1] ? (Color){0,0,0,75} : (Color){75,125,25,125});
|
||||||
|
draw_billboard("/data/y.png",
|
||||||
|
vec3_add(state.active_center, (Vec3){0,1.5f,0}),
|
||||||
|
(Vec2){0.1f, 0.1f},
|
||||||
|
(Rect){0},
|
||||||
|
state.axis_mask[1] ? (Color){0,0,0,255} : (Color){75,125,25,255},
|
||||||
|
false);
|
||||||
|
|
||||||
|
draw_line_3d(state.active_center, (Vec3){0,0,256}, 1, state.axis_mask[2] ? (Color){0,0,0,75} : (Color){0,0,255,125});
|
||||||
|
draw_billboard("/data/z.png",
|
||||||
|
vec3_add(state.active_center, (Vec3){0,0,2}),
|
||||||
|
(Vec2){0.1f, 0.1f},
|
||||||
|
(Rect){0},
|
||||||
|
state.axis_mask[2] ? (Color){0,0,0,255} : (Color){0,0,255,255},
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void draw_hovered_face_border(void) {
|
||||||
|
if (state.current_hovered_obj == INVALID_OBJECT || state.current_hovered_face == INVALID_FACE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Object *o = &state.objects[state.current_hovered_obj];
|
||||||
|
Face *f = &o->faces[state.current_hovered_face];
|
||||||
|
|
||||||
|
if (f->p[3] != INVALID_POINT) {
|
||||||
|
Vec3 p0 = point_to_vec3(state.current_hovered_obj, f->p[0]);
|
||||||
|
Vec3 p1 = point_to_vec3(state.current_hovered_obj, f->p[1]);
|
||||||
|
Vec3 p2 = point_to_vec3(state.current_hovered_obj, f->p[2]);
|
||||||
|
Vec3 p3 = point_to_vec3(state.current_hovered_obj, f->p[3]);
|
||||||
|
Vec3 center = vec3_scale(vec3_add(p2, p0), 0.5);
|
||||||
|
float size = sinf(ctx.frame_number / 10) * 0.05f + 0.1f;
|
||||||
|
Vec3 c0 = vec3_add(p0, vec3_scale(vec3_sub(p0, center), size));
|
||||||
|
Vec3 c1 = vec3_add(p1, vec3_scale(vec3_sub(p1, center), size));
|
||||||
|
Vec3 c2 = vec3_add(p2, vec3_scale(vec3_sub(p2, center), size));
|
||||||
|
Vec3 c3 = vec3_add(p3, vec3_scale(vec3_sub(p3, center), size));
|
||||||
|
draw_line_3d(c0, c1, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(c1, c2, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(c2, c3, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(c3, c0, 1, (Color){255,255,255,255});
|
||||||
|
} else {
|
||||||
|
Vec3 p0 = point_to_vec3(state.current_hovered_obj, f->p[0]);
|
||||||
|
Vec3 p1 = point_to_vec3(state.current_hovered_obj, f->p[1]);
|
||||||
|
Vec3 p2 = point_to_vec3(state.current_hovered_obj, f->p[2]);
|
||||||
|
Vec3 center = vec3_scale(vec3_add(p0, vec3_add(p1, p2)), 0.33f);
|
||||||
|
float size = sinf(ctx.frame_number / 10) * 0.05f + 0.1f;
|
||||||
|
Vec3 c0 = vec3_add(p0, vec3_scale(vec3_sub(p0, center), size));
|
||||||
|
Vec3 c1 = vec3_add(p1, vec3_scale(vec3_sub(p1, center), size));
|
||||||
|
Vec3 c2 = vec3_add(p2, vec3_scale(vec3_sub(p2, center), size));
|
||||||
|
draw_line_3d(c0, c1, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(c1, c2, 1, (Color){255,255,255,255});
|
||||||
|
draw_line_3d(c2, c0, 1, (Color){255,255,255,255});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void display_texture_selection(void) {
|
||||||
|
String list = file_read("/data/assets/", ":images");
|
||||||
|
if (!list.data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
draw_rectangle((Rect){0, 480 - 50, 640, 480}, (Color){0,0,0,126});
|
||||||
|
|
||||||
|
char *selected = NULL;
|
||||||
|
char *saveptr = NULL;
|
||||||
|
int count = 0;
|
||||||
|
char const *part = SDL_strtokr(list.data, "\n", &saveptr);
|
||||||
|
do {
|
||||||
|
Rect box = (Rect){(float)count * 50, 480 - 48, 48, 48};
|
||||||
|
draw_sprite(part,
|
||||||
|
box,
|
||||||
|
(Rect){0,0,64,64},
|
||||||
|
(Color){255,255,255,255},
|
||||||
|
0, false, false, true);
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (state.current_texture && SDL_strcmp(part, state.current_texture) == 0)
|
||||||
|
draw_box(box, 1, (Color){255,255,255,255});
|
||||||
|
|
||||||
|
if (rect_intersects(box, (Rect){ctx.mouse_position.x, ctx.mouse_position.y, 1, 1}))
|
||||||
|
selected = SDL_strdup(part);
|
||||||
|
|
||||||
|
} while ((part = SDL_strtokr(NULL, "\n", &saveptr)));
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
draw_text(selected, (Vec2){0, 480 - 48 - 24}, 24, (Color){255,255,255,255}, NULL);
|
||||||
|
if (input_action_just_pressed("select")) {
|
||||||
|
if (state.current_texture) SDL_free(state.current_texture);
|
||||||
|
state.current_texture = selected;
|
||||||
|
} else
|
||||||
|
SDL_free(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void process_camera_inputs(void) {
|
||||||
|
if (input_action_just_pressed("toggle_display_mode")) {
|
||||||
|
audio_play("/data/click.wav", NULL, false, 0.7f, 0.0f);
|
||||||
|
state.solid_display_mode = !state.solid_display_mode;
|
||||||
|
if (state.current_texture) {
|
||||||
|
SDL_free(state.current_texture);
|
||||||
|
state.current_texture = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("toggle_projection")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = !state.camera_is_orthographic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("toggle_x_axis")) {
|
||||||
|
state.axis_mask[0] = 0; state.axis_mask[1] = 1; state.axis_mask[2] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("toggle_y_axis")) {
|
||||||
|
state.axis_mask[0] = 1; state.axis_mask[1] = 0; state.axis_mask[2] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("toggle_z_axis")) {
|
||||||
|
state.axis_mask[0] = 1; state.axis_mask[1] = 1; state.axis_mask[2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: determine bounding box for this? */
|
||||||
|
/* TODO: no idea whether it's all correct in terms of directions. */
|
||||||
|
if (input_action_just_pressed("camera_front")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){0,0,-1};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){0,0,2});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("camera_back")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){0,0,1};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){0,0,-2});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("camera_left")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){1,0,0};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){-2,0,0});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_action_just_pressed("camera_right")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){-1,0,0};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){2,0,0});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: broken */
|
||||||
|
if (input_action_just_pressed("camera_above")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){0,-1,0};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){0,2,0});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: broken */
|
||||||
|
if (input_action_just_pressed("camera_below")) {
|
||||||
|
audio_play("/data/pop.wav", NULL, false, 0.8f, 0.0f);
|
||||||
|
state.camera_is_orthographic = true;
|
||||||
|
state.camera_direction = (Vec3){0,1,0};
|
||||||
|
state.camera_position = vec3_add(state.active_center, (Vec3){0,-2,0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void game_tick(void) {
|
||||||
|
if (!init) {
|
||||||
|
/* default state */
|
||||||
|
new_cube((Point){0,0,0}, (Point){POINTS_PER_METER,POINTS_PER_METER,POINTS_PER_METER});
|
||||||
|
state.camera_position = (Vec3){2,1,2};
|
||||||
|
state.camera_direction = vec3_norm(((Vec3){-2,-1,-2}));
|
||||||
|
state.camera_zoom = 0.5f;
|
||||||
|
state.grid_snap_granularity = 16;
|
||||||
|
state.axis_mask[1] = 1; state.axis_mask[2] = 1;
|
||||||
|
init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.current_hovered_face = INVALID_FACE;
|
||||||
|
state.current_hovered_obj = INVALID_OBJECT;
|
||||||
|
|
||||||
|
input_action("toggle_display_mode", "Q");
|
||||||
|
input_action("toggle_projection", "TAB");
|
||||||
|
|
||||||
|
input_action("toggle_x_axis", "Z");
|
||||||
|
input_action("toggle_y_axis", "X");
|
||||||
|
input_action("toggle_z_axis", "C");
|
||||||
|
|
||||||
|
input_action("select", "LCLICK");
|
||||||
|
input_action("rotate", "R");
|
||||||
|
input_action("undo", "F");
|
||||||
|
|
||||||
|
input_action("camera_front", "KP0");
|
||||||
|
input_action("camera_back", "KP1");
|
||||||
|
input_action("camera_left", "KP2");
|
||||||
|
input_action("camera_right", "KP3");
|
||||||
|
input_action("camera_above", "KP4");
|
||||||
|
input_action("camera_below", "KP5");
|
||||||
|
|
||||||
|
process_camera_inputs();
|
||||||
|
process_camera_movement();
|
||||||
|
process_operations();
|
||||||
|
|
||||||
|
/* helpres */
|
||||||
|
draw_axes();
|
||||||
|
draw_hovered_face_border();
|
||||||
|
|
||||||
|
for (uint8_t obj = 0; obj < state.objects_sz; ++obj)
|
||||||
|
render_object(obj);
|
||||||
|
|
||||||
|
if (state.solid_display_mode)
|
||||||
|
display_texture_selection();
|
||||||
|
|
||||||
|
draw_text("twndel\x03", (Vec2){0, 2}, 32, (Color){255,255,255,200}, NULL);
|
||||||
|
draw_camera(
|
||||||
|
state.camera_position,
|
||||||
|
state.camera_direction,
|
||||||
|
(Vec3){0, 1, 0},
|
||||||
|
state.camera_is_orthographic ? 0 : CAMERA_FOV,
|
||||||
|
state.camera_zoom,
|
||||||
|
100
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void game_end(void) {
|
||||||
|
for (uint8_t obj = 0; obj < state.objects_sz; ++obj) {
|
||||||
|
Object *o = &state.objects[obj];
|
||||||
|
for (uint8_t t = 0; t < o->textures_sz; ++t)
|
||||||
|
SDL_free(o->textures[t]);
|
||||||
|
SDL_free(o->name);
|
||||||
|
}
|
||||||
|
if (state.current_texture) {
|
||||||
|
SDL_free(state.current_texture);
|
||||||
|
state.current_texture = NULL;
|
||||||
|
}
|
||||||
|
}
|
2
apps/twnlua/.gitignore
vendored
Normal file
2
apps/twnlua/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
luabind.c
|
||||||
|
data/scripts/twnapi.lua
|
@ -1,77 +1,36 @@
|
|||||||
cmake_minimum_required(VERSION 3.21)
|
cmake_minimum_required(VERSION 3.30)
|
||||||
|
|
||||||
project(twnlua LANGUAGES C)
|
project(twnlua LANGUAGES C)
|
||||||
|
|
||||||
|
find_package(Python3 COMPONENTS Interpreter)
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory($ENV{TWNROOT} $ENV{TWNBUILDDIR})
|
add_subdirectory($ENV{TWNROOT} ${CMAKE_BINARY_DIR}/twn)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/game.c
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json ${FLAGS} > ${CMAKE_CURRENT_SOURCE_DIR}/luabind.c
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindgen.py $ENV{TWNROOT}/share/twn_api.json
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${TWN_OUT_DIR}/data/scripts/twnapi.lua
|
||||||
|
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/docgen.py $ENV{TWNROOT}/share/twn_api.json > ${TWN_OUT_DIR}/data/scripts/twnapi.lua
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/docgen.py $ENV{TWNROOT}/share/twn_api.json
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(
|
||||||
|
twnlua_docgen ALL
|
||||||
|
DEPENDS ${TWN_OUT_DIR}/data/scripts/twnapi.lua
|
||||||
|
)
|
||||||
|
|
||||||
|
add_compile_definitions(LUA_32BITS=1)
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
game.c
|
game.c
|
||||||
state.h
|
minilua.c
|
||||||
|
|
||||||
lua/src/lapi.c
|
|
||||||
lua/src/lapi.h
|
|
||||||
lua/src/lauxlib.c
|
|
||||||
lua/src/lauxlib.h
|
|
||||||
lua/src/lbaselib.c
|
|
||||||
lua/src/lcode.c
|
|
||||||
lua/src/lcode.h
|
|
||||||
lua/src/lcorolib.c
|
|
||||||
lua/src/lctype.c
|
|
||||||
lua/src/lctype.h
|
|
||||||
lua/src/ldblib.c
|
|
||||||
lua/src/ldebug.c
|
|
||||||
lua/src/ldebug.h
|
|
||||||
lua/src/ldo.c
|
|
||||||
lua/src/ldo.h
|
|
||||||
lua/src/ldump.c
|
|
||||||
lua/src/lfunc.c
|
|
||||||
lua/src/lfunc.h
|
|
||||||
lua/src/lgc.c
|
|
||||||
lua/src/lgc.h
|
|
||||||
lua/src/linit.c
|
|
||||||
lua/src/liolib.c
|
|
||||||
lua/src/ljumptab.h
|
|
||||||
lua/src/llex.c
|
|
||||||
lua/src/llex.h
|
|
||||||
lua/src/llimits.h
|
|
||||||
lua/src/lmathlib.c
|
|
||||||
lua/src/lmem.c
|
|
||||||
lua/src/lmem.h
|
|
||||||
lua/src/loadlib.c
|
|
||||||
lua/src/lobject.c
|
|
||||||
lua/src/lobject.h
|
|
||||||
lua/src/lopcodes.c
|
|
||||||
lua/src/lopcodes.h
|
|
||||||
lua/src/lopnames.h
|
|
||||||
lua/src/loslib.c
|
|
||||||
lua/src/lparser.c
|
|
||||||
lua/src/lparser.h
|
|
||||||
lua/src/lprefix.h
|
|
||||||
lua/src/lstate.c
|
|
||||||
lua/src/lstate.h
|
|
||||||
lua/src/lstring.c
|
|
||||||
lua/src/lstring.h
|
|
||||||
lua/src/lstrlib.c
|
|
||||||
lua/src/ltable.c
|
|
||||||
lua/src/ltable.h
|
|
||||||
lua/src/ltablib.c
|
|
||||||
lua/src/ltm.c
|
|
||||||
lua/src/ltm.h
|
|
||||||
lua/src/lua.h
|
|
||||||
lua/src/luaconf.h
|
|
||||||
lua/src/lualib.h
|
|
||||||
lua/src/lundump.c
|
|
||||||
lua/src/lundump.h
|
|
||||||
lua/src/lutf8lib.c
|
|
||||||
lua/src/lvm.c
|
|
||||||
lua/src/lvm.h
|
|
||||||
lua/src/lzio.c
|
|
||||||
lua/src/lzio.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
use_townengine("${SOURCE_FILES}" ${TWN_OUT_DIR})
|
||||||
use_townengine(${PROJECT_NAME} "${SOURCE_FILES}" ${CMAKE_CURRENT_SOURCE_DIR})
|
|
||||||
|
62
apps/twnlua/README.md
Normal file
62
apps/twnlua/README.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# MiniLua
|
||||||
|
|
||||||
|
This is Lua contained in a single header to be bundled in C/C++ applications with ease.
|
||||||
|
[Lua](https://www.lua.org/) is a powerful, efficient, lightweight, embeddable scripting language.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define LUA_IMPL
|
||||||
|
#include "minilua.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
lua_State *L = luaL_newstate();
|
||||||
|
if(L == NULL)
|
||||||
|
return -1;
|
||||||
|
luaL_openlibs(L);
|
||||||
|
luaL_loadstring(L, "print 'hello world'");
|
||||||
|
lua_call(L, 0, 0);
|
||||||
|
lua_close(L);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Copy `minilua.h` into your C or C++ project, include it anywhere you want to use Lua API.
|
||||||
|
Then do the following in *one* C file to implement Lua:
|
||||||
|
```c
|
||||||
|
#define LUA_IMPL
|
||||||
|
#include "minilua.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
By default it detects the system platform to use, however you can explicitly define one.
|
||||||
|
|
||||||
|
Note that almost no modification was made in the Lua implementation code,
|
||||||
|
thus there are some C variable names that may collide with your code,
|
||||||
|
therefore it is best to declare the Lua implementation in dedicated C file.
|
||||||
|
|
||||||
|
Optionally provide the following defines:
|
||||||
|
- `LUA_MAKE_LUA` - implement the Lua command line REPL
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
For documentation on how to use Lua read its [official manual](https://www.lua.org/manual/).
|
||||||
|
|
||||||
|
## Updates
|
||||||
|
|
||||||
|
- **25-Jul-2024**: Updated to Lua 5.4.7.
|
||||||
|
- **13-Nov-2023**: Updated to Lua 5.4.6.
|
||||||
|
- **28-Jan-2022**: Updated to Lua 5.4.4.
|
||||||
|
- **31-Mar-2021**: Updated to Lua 5.4.3.
|
||||||
|
- **03-Dec-2020**: Updated to Lua 5.4.2.
|
||||||
|
- **27-Nov-2020**: Library created, using Lua 5.4.2-rc1.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
This library tries to keep up with latest official Lua release.
|
||||||
|
The header is generated using the bash script `gen.sh` all modifications done is there.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Same license as Lua, the MIT license, see LICENSE.txt for information.
|
191
apps/twnlua/bindgen.py
Executable file
191
apps/twnlua/bindgen.py
Executable file
@ -0,0 +1,191 @@
|
|||||||
|
#!/bin/env python3
|
||||||
|
|
||||||
|
import sys, json
|
||||||
|
|
||||||
|
with open(sys.argv[1], 'r') if sys.argv[1] != "-" else sys.stdin as f:
|
||||||
|
api_source = f.read()
|
||||||
|
|
||||||
|
api = json.loads(api_source)
|
||||||
|
|
||||||
|
|
||||||
|
def default(parameter):
|
||||||
|
basetype = parameter["type"].rsplit(' *', 1)[0]
|
||||||
|
if parameter["type"] == "float":
|
||||||
|
s = str(parameter["default"])
|
||||||
|
return s + 'f' if '.' in s else s + '.f'
|
||||||
|
elif parameter["type"] == "bool":
|
||||||
|
return "true" if parameter["default"] else "false"
|
||||||
|
elif parameter["type"] == "char *":
|
||||||
|
if parameter["default"] == {}:
|
||||||
|
return "NULL"
|
||||||
|
else: return '"' + parameter["default"] + '"'
|
||||||
|
elif basetype in api["types"]:
|
||||||
|
if parameter["type"].endswith(" *"):
|
||||||
|
if parameter["default"] == {}:
|
||||||
|
return "NULL"
|
||||||
|
else:
|
||||||
|
return "&(%s){\n%s\n }" % \
|
||||||
|
(parameter["type"], ", \n".join(" .%s = %s" % (n, v) for n, v in parameter["default"].items()))
|
||||||
|
else:
|
||||||
|
return "(%s){\n%s\n }" % \
|
||||||
|
(parameter["type"], ", \n".join(" .%s = %s" % (n, v) for n, v in parameter["default"].items()))
|
||||||
|
raise BaseException("Unhandled default value of type '%s'" % parameter["type"])
|
||||||
|
|
||||||
|
|
||||||
|
def to_table(typedesc, variable, indent = 0):
|
||||||
|
binding = ' ' * indent + "lua_createtable(L, 0, %i);\n" % len(typedesc["fields"])
|
||||||
|
for field in typedesc["fields"]:
|
||||||
|
if field["type"] == "float" or field["type"] == "uint8_t":
|
||||||
|
binding += ' ' * indent + "lua_pushnumber(L, (float)(%s));\n" % (variable + ".%s" % field["name"])
|
||||||
|
elif field["type"] == "bool":
|
||||||
|
binding += ' ' * indent + "lua_pushboolean(L, (%s));\n" % (variable + ".%s" % field["name"])
|
||||||
|
elif field["type"] in api["types"]:
|
||||||
|
binding += to_table(api["types"][field["type"]], variable + ".%s" % field["name"], indent + 4)
|
||||||
|
else:
|
||||||
|
raise BaseException("Unhandled return field type '%s'" % (field["type"]))
|
||||||
|
binding += ' ' * indent + "lua_setfield(L, -2, \"%s\");\n" % field["name"]
|
||||||
|
return binding
|
||||||
|
|
||||||
|
|
||||||
|
def from_table(typedesc, variable, indent = 0):
|
||||||
|
binding = ""
|
||||||
|
for field in typedesc["fields"]:
|
||||||
|
binding += ' ' * indent + "lua_getfield(L, -1, \"%s\");\n" % field["name"]
|
||||||
|
if field["type"] == "float" or field["type"] == "uint8_t":
|
||||||
|
binding += ' ' * indent + "%s = (%s)(lua_tonumber(L, -1));\n" % (variable + ".%s" % field["name"], field["type"])
|
||||||
|
elif field["type"] == "bool":
|
||||||
|
binding += ' ' * indent + "%s = lua_toboolean(L, -1);\n" % (variable + ".%s" % field["name"])
|
||||||
|
elif field["type"] in api["types"]:
|
||||||
|
binding += from_table(api["types"][field["type"]], variable + ".%s" % field["name"], indent + 4)
|
||||||
|
else:
|
||||||
|
raise BaseException("Unhandled return field type '%s'" % (field["type"]))
|
||||||
|
binding += ' ' * indent + "lua_pop(L, 1);\n"
|
||||||
|
return binding
|
||||||
|
|
||||||
|
|
||||||
|
print('#include "twn_game_api.h"\n')
|
||||||
|
print('/* assumed to be included from game.c, where minilua.h is already present */\n')
|
||||||
|
|
||||||
|
bindings, used_converters = [], {}
|
||||||
|
for procedure, procedure_desc in api["procedures"].items():
|
||||||
|
binding = "static int binding_%s(lua_State *L) {\n" % procedure
|
||||||
|
binding += " luaL_checktype(L, 1, LUA_TTABLE);\n"
|
||||||
|
|
||||||
|
if "params" in procedure_desc:
|
||||||
|
for parameter in procedure_desc["params"]:
|
||||||
|
basetype = parameter["type"].rsplit(' *', 1)[0]
|
||||||
|
|
||||||
|
if parameter["type"].endswith("*") and not parameter["type"] == "char *":
|
||||||
|
binding += " %s %s_value;\n" % (basetype, parameter["name"])
|
||||||
|
binding += " %s %s;\n" % (parameter["type"] if not parameter["type"].endswith("*") else 'const ' + parameter["type"], parameter["name"])
|
||||||
|
binding += " lua_getfield(L, 1, \"%s\");\n" % parameter["name"]
|
||||||
|
|
||||||
|
if "default" in parameter and not parameter["type"] in ("float", "bool"):
|
||||||
|
binding += " if (lua_isnoneornil(L, -1))\n"
|
||||||
|
binding += " %s = %s;\n" % (parameter["name"], default(parameter))
|
||||||
|
binding += " else\n "
|
||||||
|
|
||||||
|
if parameter["type"] == "float":
|
||||||
|
if "default" in parameter:
|
||||||
|
binding += " int is_%s_num;\n" % parameter["name"]
|
||||||
|
binding += " %s = (float)(lua_tonumberx(L, -1, &is_%s_num));\n" % (parameter["name"], parameter["name"]);
|
||||||
|
binding += " if (!is_%s_num) %s = %s;\n" % (parameter["name"], parameter["name"], default(parameter))
|
||||||
|
else:
|
||||||
|
binding += " %s = (float)(lua_tonumber(L, -1));\n" % (parameter["name"]);
|
||||||
|
elif parameter["type"] == "bool":
|
||||||
|
binding += " %s = lua_toboolean(L, -1);\n" % (parameter["name"]);
|
||||||
|
elif parameter["type"] == "char *":
|
||||||
|
binding += " %s = lua_tostring(L, -1);\n" % (parameter["name"]);
|
||||||
|
elif basetype in api["types"]:
|
||||||
|
used_converters[basetype] = api["types"][basetype]
|
||||||
|
if parameter["type"].endswith(" *"):
|
||||||
|
binding += " { %s_value = to_%s(L); %s = &%s_value; }\n" % (parameter["name"], basetype.lower(), parameter["name"], parameter["name"]);
|
||||||
|
else:
|
||||||
|
binding += " %s = to_%s(L);\n" % (parameter["name"], basetype.lower());
|
||||||
|
else:
|
||||||
|
raise BaseException("Unhandled parameter type '%s'" % (parameter["type"]))
|
||||||
|
|
||||||
|
if "return" in procedure_desc:
|
||||||
|
if procedure_desc["return"] == "bool":
|
||||||
|
binding += " lua_pushboolean(L, (int)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
|
elif procedure_desc["return"] == "float":
|
||||||
|
binding += " lua_pushnumber(L, (float)(%s(%s)));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
|
elif procedure_desc["return"] == "char *":
|
||||||
|
binding += " lua_pushstring(L, %s(%s));\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
|
elif procedure_desc["return"] == "String":
|
||||||
|
binding += " String result = %s(%s);\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
|
binding += " lua_pushlstring(L, result.data, (int)result.length);\n"
|
||||||
|
elif type(procedure_desc["return"]) is dict or procedure_desc["return"] in api["types"]:
|
||||||
|
type_desc = procedure_desc["return"] if type(procedure_desc["return"]) is dict else api["types"][procedure_desc["return"]]
|
||||||
|
binding += " %s result = %s(%s);\n" % (type_desc["c_type"], procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
|
binding += to_table(type_desc, "result", 4)
|
||||||
|
else:
|
||||||
|
raise BaseException("Unhandled return type '%s'" % (procedure_desc["return"]))
|
||||||
|
binding += " return 1;\n}\n"
|
||||||
|
else:
|
||||||
|
binding += " %s(%s);\n" % (procedure, ", ".join(param["name"] for param in procedure_desc["params"]))
|
||||||
|
binding += " return 0;\n}\n"
|
||||||
|
|
||||||
|
bindings += [binding]
|
||||||
|
|
||||||
|
|
||||||
|
storages, converters, initializers, deinitializers = [], [], [], []
|
||||||
|
|
||||||
|
for typename, typedesc in used_converters.items():
|
||||||
|
if "no_convert" in typedesc and typedesc["no_convert"]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
converter = "static %s to_%s(lua_State *L) {\n" % (typename, typename.lower())
|
||||||
|
converter += " %s %s;\n" % (typename, typename.lower());
|
||||||
|
|
||||||
|
if "fields" in typedesc:
|
||||||
|
for idx, field in enumerate(typedesc["fields"]):
|
||||||
|
converter += " lua_getfield(L, -%i, \"%s\");\n" % (idx + 1, field["name"]);
|
||||||
|
if field["type"] == "float":
|
||||||
|
converter += " %s.%s = (float)lua_tonumber(L, -1);\n" % (typename.lower(), field["name"]);
|
||||||
|
elif field["type"] == "uint8_t":
|
||||||
|
converter += " %s.%s = (uint8_t)lua_tointeger(L, -1);\n" % (typename.lower(), field["name"]);
|
||||||
|
else:
|
||||||
|
raise BaseException("Unhandled converter field type '%s'" % (field["type"]))
|
||||||
|
converter += " lua_pop(L, %i);\n" % len(typedesc["fields"]);
|
||||||
|
|
||||||
|
converter += " return %s;\n}\n" % (typename.lower())
|
||||||
|
converters += [converter]
|
||||||
|
|
||||||
|
|
||||||
|
print('\n'.join(storages))
|
||||||
|
print('\n'.join(converters))
|
||||||
|
print('\n'.join(bindings))
|
||||||
|
|
||||||
|
|
||||||
|
loader = "extern void bindgen_load_%s(lua_State *L);\n" % api["name"]
|
||||||
|
loader += "void bindgen_load_%s(lua_State *L) {\n" % api["name"]
|
||||||
|
for procedure, procedure_desc in api["procedures"].items():
|
||||||
|
loader += " lua_pushcfunction(L, binding_%s);\n" % procedure
|
||||||
|
loader += " lua_setglobal(L, \"%s\");\n" % procedure
|
||||||
|
|
||||||
|
loader += "}\n"
|
||||||
|
print(loader)
|
||||||
|
|
||||||
|
|
||||||
|
unloader = "extern void bindgen_unload_%s(lua_State *L);\n" % api["name"]
|
||||||
|
unloader += "void bindgen_unload_%s(lua_State *L) {\n (void)L;\n" % api["name"]
|
||||||
|
unloader += '\n'.join(deinitializers)
|
||||||
|
unloader += "\n}\n"
|
||||||
|
print(unloader)
|
||||||
|
|
||||||
|
|
||||||
|
# exceptions for the base townengine api
|
||||||
|
# TODO: is there a way to generalize it? or rather, is there any need to do so?
|
||||||
|
if api["name"] == "twn":
|
||||||
|
contexter = "extern void bindgen_build_context(lua_State *L);\n"
|
||||||
|
contexter += "void bindgen_build_context(lua_State *L) {\n"
|
||||||
|
contexter += to_table(api["types"]["Context"], "ctx", 4)
|
||||||
|
contexter += "}\n\n"
|
||||||
|
|
||||||
|
contexter += "extern void bindgen_upload_context(lua_State *L);\n"
|
||||||
|
contexter += "void bindgen_upload_context(lua_State *L) {\n"
|
||||||
|
contexter += from_table(api["types"]["Context"], "ctx", 4)
|
||||||
|
contexter += "}"
|
||||||
|
|
||||||
|
print(contexter)
|
@ -1,3 +1,3 @@
|
|||||||
[[deps]]
|
[[deps]]
|
||||||
source = "../../common-data" # where does it come from, might be an url
|
source = "../../data" # where does it come from, might be an url
|
||||||
name = "common-data" # should be globally unique
|
name = "common-data" # should be globally unique
|
||||||
|
@ -4,13 +4,41 @@ offset = { x = 0, y = 0 }
|
|||||||
angle = 0
|
angle = 0
|
||||||
|
|
||||||
function game_tick()
|
function game_tick()
|
||||||
rectangle {
|
if ctx.udata == nil then
|
||||||
rect = { x = 0, y = 0, w = 640, h = 360 },
|
ctx.udata = {
|
||||||
color = { r = 127, g = 0, b = 127, a = 255 },
|
frame_count = 0,
|
||||||
|
nest = {
|
||||||
|
frame_count = 0,
|
||||||
|
},
|
||||||
|
arr = { [0] = 0 },
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
draw_text {
|
||||||
|
string = tostring(ctx.udata.frame_count),
|
||||||
|
position = { x = 0, y = 0 },
|
||||||
|
font = "/fonts/kenney-pixel.ttf",
|
||||||
}
|
}
|
||||||
|
|
||||||
sprite {
|
draw_text {
|
||||||
path = "/assets/title.png",
|
string = tostring(ctx.udata.nest.frame_count),
|
||||||
|
position = { x = 0, y = 14 },
|
||||||
|
font = "/fonts/kenney-pixel.ttf",
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_text {
|
||||||
|
string = tostring(ctx.udata.arr[0]),
|
||||||
|
position = { x = 0, y = 28 },
|
||||||
|
font = "/fonts/kenney-pixel.ttf",
|
||||||
|
}
|
||||||
|
|
||||||
|
input_action {
|
||||||
|
name = "press",
|
||||||
|
control = "A"
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_sprite {
|
||||||
|
texture = "/assets/title.png",
|
||||||
rect = {
|
rect = {
|
||||||
x = 320 - (320 / 2),
|
x = 320 - (320 / 2),
|
||||||
y = 180 - (128 / 2),
|
y = 180 - (128 / 2),
|
||||||
@ -19,11 +47,17 @@ function game_tick()
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
text {
|
if input_action_pressed { name = "press" } then
|
||||||
string = "IT KEEPS HAPPENING",
|
draw_text {
|
||||||
position = offset,
|
string = "it never happened",
|
||||||
font = "/fonts/kenney-pixel.ttf",
|
position = offset,
|
||||||
}
|
font = "/fonts/kenney-pixel.ttf",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
ctx.udata.frame_count = ctx.udata.frame_count + 1
|
||||||
|
ctx.udata.nest.frame_count = ctx.udata.nest.frame_count + 1
|
||||||
|
ctx.udata.arr[0] = ctx.udata.arr[0] + 1
|
||||||
|
|
||||||
offset.x = ORIGIN.x + (math.cos(angle) * RADIUS)
|
offset.x = ORIGIN.x + (math.cos(angle) * RADIUS)
|
||||||
offset.y = ORIGIN.y + (math.sin(angle) * RADIUS)
|
offset.y = ORIGIN.y + (math.sin(angle) * RADIUS)
|
||||||
|
@ -5,7 +5,7 @@ app_id = "twnlua"
|
|||||||
dev_id = "somebody"
|
dev_id = "somebody"
|
||||||
|
|
||||||
[game]
|
[game]
|
||||||
base_render_width = 640
|
resolution = [ 640, 360 ]
|
||||||
base_render_height = 360
|
background_color = [ 127, 0, 127, 255 ]
|
||||||
|
|
||||||
[engine]
|
[engine]
|
||||||
|
55
apps/twnlua/docgen.py
Executable file
55
apps/twnlua/docgen.py
Executable file
@ -0,0 +1,55 @@
|
|||||||
|
#!/bin/env python3
|
||||||
|
|
||||||
|
import sys, json
|
||||||
|
|
||||||
|
with open(sys.argv[1], 'r') if sys.argv[1] != "-" else sys.stdin as f:
|
||||||
|
api_source = f.read()
|
||||||
|
|
||||||
|
api = json.loads(api_source)
|
||||||
|
|
||||||
|
def to_lua_type_annot(typedesc):
|
||||||
|
if type(typedesc) is dict:
|
||||||
|
return r'{ %s }' % ','.join('%s: %s' % (f["name"], to_lua_type_annot(f["type"])) for f in typedesc["fields"])
|
||||||
|
basetype = typedesc.rsplit(' *', 1)[0]
|
||||||
|
if typedesc == "char *":
|
||||||
|
return "string"
|
||||||
|
elif basetype == "float":
|
||||||
|
return "number"
|
||||||
|
elif basetype == "bool":
|
||||||
|
return "boolean"
|
||||||
|
elif basetype == "Vec2":
|
||||||
|
return r"{ x: number, y: number }"
|
||||||
|
elif basetype == "Vec3":
|
||||||
|
return r"{ x: number, y: number, z: number }"
|
||||||
|
elif basetype == "Color":
|
||||||
|
return r"{ r: number, g: number, b: number, a: number }"
|
||||||
|
elif basetype == "Rect":
|
||||||
|
return r"{ x: number, y: number, w: number, h: number }"
|
||||||
|
else:
|
||||||
|
return "unknown"
|
||||||
|
# raise BaseException("Unhandled type for annotation: %s" % typedesc)
|
||||||
|
|
||||||
|
print("---@meta twn")
|
||||||
|
print("---@diagnostic disable")
|
||||||
|
|
||||||
|
type_annotations = {}
|
||||||
|
type_annotations["ctx"] = r"{ %s, udata: table }" % \
|
||||||
|
', '.join("%s: %s" % (f["name"], to_lua_type_annot(f["type"])) for f in api["types"]["Context"]["fields"])
|
||||||
|
|
||||||
|
for annot in type_annotations:
|
||||||
|
print("---@type " + type_annotations[annot])
|
||||||
|
print(r"%s = nil" % annot)
|
||||||
|
|
||||||
|
procedure_annotations = {}
|
||||||
|
for procedure, procedure_desc in api["procedures"].items():
|
||||||
|
procedure_annotations[procedure] = {}
|
||||||
|
procedure_annotations[procedure]["params"] = r"{ %s }" % \
|
||||||
|
', '.join("%s: %s" % (p["name"], to_lua_type_annot(p["type"]) + '?' * ("default" in p)) for p in procedure_desc["params"])
|
||||||
|
if "return" in procedure_desc:
|
||||||
|
procedure_annotations[procedure]["return"] = to_lua_type_annot(procedure_desc["return"])
|
||||||
|
|
||||||
|
for annot in procedure_annotations:
|
||||||
|
print("---@param args " + procedure_annotations[annot]["params"])
|
||||||
|
if "return" in procedure_annotations[annot]:
|
||||||
|
print("---@return " + procedure_annotations[annot]["return"])
|
||||||
|
print("function %s(args) end" % annot)
|
@ -1,259 +1,191 @@
|
|||||||
#include "twn_game_api.h"
|
#include "twn_game_api.h"
|
||||||
|
|
||||||
|
/* TODO: actually move it back it its own file, it doesn't give any compilation benefits */
|
||||||
|
#include "minilua.h"
|
||||||
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
#include "luabind.c"
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include <lua.h>
|
#define UDATA_NESTING_LIMIT 128
|
||||||
#include <lualib.h>
|
|
||||||
#include <lauxlib.h>
|
|
||||||
|
|
||||||
#include <malloc.h>
|
|
||||||
|
/* generated by bindgen.py */
|
||||||
|
void bindgen_load_twn(lua_State *L);
|
||||||
|
void bindgen_unload_twn(lua_State *L);
|
||||||
|
void bindgen_build_context(lua_State *L);
|
||||||
|
void bindgen_upload_context(lua_State *L);
|
||||||
|
|
||||||
|
|
||||||
/* require will go through physicsfs exclusively so that scripts can be in the data dir */
|
/* require will go through physicsfs exclusively so that scripts can be in the data dir */
|
||||||
|
/* TODO: allow for bytecode files */
|
||||||
|
/* TODO: support .lua suffixes files? */
|
||||||
static int physfs_loader(lua_State *L) {
|
static int physfs_loader(lua_State *L) {
|
||||||
const char *name = luaL_checkstring(L, 1);
|
const char *name = luaL_checkstring(L, 1);
|
||||||
char *final_path = NULL;
|
|
||||||
SDL_asprintf(&final_path, "%s.lua", name);
|
|
||||||
|
|
||||||
if (!file_exists(final_path)) {
|
static const char *name_breaker = NULL;
|
||||||
|
if (name_breaker && SDL_strcmp(name, name_breaker) == 0) {
|
||||||
|
log_string(name_breaker, "Recursive load on itself from lua module");
|
||||||
|
return 0;
|
||||||
|
} name_breaker = name;
|
||||||
|
|
||||||
|
/* replace dots with path slashes */
|
||||||
|
char *path_copy = SDL_strdup(name);
|
||||||
|
char *ch = NULL;
|
||||||
|
while ((ch = SDL_strchr(path_copy, '.')))
|
||||||
|
*ch = '/';
|
||||||
|
|
||||||
|
char *final_path = NULL;
|
||||||
|
SDL_asprintf(&final_path, "/scripts/%s.lua", path_copy);
|
||||||
|
SDL_free(path_copy);
|
||||||
|
|
||||||
|
if (SDL_strcmp(file_read(final_path, ":exists").data, "yes") == 0) {
|
||||||
char *error_message = NULL;
|
char *error_message = NULL;
|
||||||
SDL_asprintf(&error_message, "could not find module %s in filesystem", name);
|
SDL_asprintf(&error_message, "could not find module %s in filesystem", name);
|
||||||
lua_pushstring(L, error_message);
|
lua_pushstring(L, error_message);
|
||||||
free(error_message);
|
SDL_free(error_message);
|
||||||
|
|
||||||
free(final_path);
|
SDL_free(final_path);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *buf = NULL;
|
char *final_path_binary = NULL;
|
||||||
int64_t buf_size = file_to_bytes(final_path, &buf);
|
SDL_asprintf(&final_path_binary, "%s%s", final_path, ":binary");
|
||||||
free(final_path);
|
|
||||||
|
|
||||||
luaL_loadbuffer(L, (char *)buf, buf_size, name);
|
String file = file_read(final_path, ":binary");
|
||||||
free(buf);
|
SDL_free(final_path);
|
||||||
|
|
||||||
return 1;
|
/* TODO: use reader interface for streaming instead */
|
||||||
|
int const result = luaL_loadbuffer(L, file.data, (size_t)file.length, name);
|
||||||
|
if (result != LUA_OK)
|
||||||
|
log_string(lua_tostring(L, -1), NULL);
|
||||||
|
|
||||||
|
return result == LUA_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Rect table_to_rect(lua_State *L, int idx, const char *name) {
|
/* WARN! experimental and will probably be removed */
|
||||||
/* types are checked here to help prevent unexpected results */
|
/* it is an attempt to ease memory usage problems posed by using lua, partially successful */
|
||||||
Rect rect;
|
static void *custom_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
|
||||||
int is_num;
|
(void)ud;
|
||||||
|
|
||||||
lua_getfield(L, idx, "x");
|
/* small allocations are placed in slots, as there's a big chance they will not need to be resized */
|
||||||
rect.x = (float)lua_tonumberx(L, -1, &is_num);
|
static char slots[1024][128];
|
||||||
if (!is_num)
|
static int16_t free_slots[1024] = { [0] = -1 };
|
||||||
luaL_error(L, "bad field 'x' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
|
static size_t free_slot_count = 1024;
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
lua_getfield(L, idx, "y");
|
if (free_slots[0] == -1)
|
||||||
rect.y = (float)lua_tonumberx(L, -1, &is_num);
|
for (int i = 0; i < 1024; i++)
|
||||||
if (!is_num)
|
free_slots[i] = (int16_t)i;
|
||||||
luaL_error(L, "bad field 'y' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
lua_getfield(L, idx, "w");
|
if (nsize == 0) {
|
||||||
rect.w = (float)lua_tonumberx(L, -1, &is_num);
|
if (ptr && (char *)ptr >= &slots[0][0] && (char *)ptr <= &slots[1024-1][128-1])
|
||||||
if (!is_num)
|
free_slots[free_slot_count++] = (int16_t)(((uintptr_t)ptr - (uintptr_t)slots) / 128);
|
||||||
luaL_error(L, "bad field 'w' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
|
else if (osize)
|
||||||
lua_pop(L, 1);
|
SDL_free(ptr);
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
if (!ptr && nsize <= 128 && free_slot_count > 0) {
|
||||||
|
/* use a slot */
|
||||||
|
return slots[free_slots[--free_slot_count]];
|
||||||
|
}
|
||||||
|
|
||||||
lua_getfield(L, idx, "h");
|
if ((char *)ptr >= &slots[0][0] && (char *)ptr <= &slots[1024-1][128-1]) {
|
||||||
rect.h = (float)lua_tonumberx(L, -1, &is_num);
|
/* still fits */
|
||||||
if (!is_num)
|
if (nsize <= 128)
|
||||||
luaL_error(L, "bad field 'h' in '%s' (number expected, got %s)", name, luaL_typename(L, -1));
|
return ptr;
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
return rect;
|
/* move from slot to dynamic memory */
|
||||||
|
void *mem = SDL_malloc(nsize);
|
||||||
|
SDL_memcpy(mem, ptr, osize);
|
||||||
|
free_slots[free_slot_count++] = (int16_t)(((uintptr_t)ptr - (uintptr_t)slots) / 128);
|
||||||
|
return mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_realloc(ptr, nsize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Color table_to_color(lua_State *L, int idx) {
|
static void exchange_lua_states(lua_State *from, lua_State *to, int level, int index) {
|
||||||
Color color;
|
if (level >= UDATA_NESTING_LIMIT) {
|
||||||
|
log_string("ctx.udata nesting limit is reached", NULL);
|
||||||
lua_getfield(L, idx, "r");
|
return;
|
||||||
color.r = (uint8_t)lua_tointeger(L, -1);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
lua_getfield(L, idx, "g");
|
|
||||||
color.g = (uint8_t)lua_tointeger(L, -1);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
lua_getfield(L, idx, "b");
|
|
||||||
color.b = (uint8_t)lua_tointeger(L, -1);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
lua_getfield(L, idx, "a");
|
|
||||||
color.a = (uint8_t)lua_tointeger(L, -1);
|
|
||||||
lua_pop(L, 1);
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* sprite(data [table]) */
|
|
||||||
/* data should contain the following fields: */
|
|
||||||
/*
|
|
||||||
* path [string]
|
|
||||||
* rect [table]
|
|
||||||
* texture_region [table], optional
|
|
||||||
* color [table], optional
|
|
||||||
* rotation [number], optional
|
|
||||||
* flip_x [boolean], optional
|
|
||||||
* flip_y [boolean], optional
|
|
||||||
* stretch [boolean], optional
|
|
||||||
*/
|
|
||||||
static int b_sprite(lua_State *L) {
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
|
|
||||||
DrawSpriteArgs args = { 0 };
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "path");
|
|
||||||
const char *field_path = lua_tostring(L, -1);
|
|
||||||
if (field_path == NULL)
|
|
||||||
luaL_error(L, "bad field 'path' in 'data' (string expected, got %s)", luaL_typename(L, -1));
|
|
||||||
args.path = field_path;
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "rect");
|
|
||||||
if (!lua_istable(L, -1))
|
|
||||||
luaL_error(L, "bad field 'rect' in 'data' (table expected, got %s)", luaL_typename(L, -1));
|
|
||||||
args.rect = table_to_rect(L, -1, "data.rect");
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "texture_region");
|
|
||||||
if (lua_istable(L, -1)) {
|
|
||||||
args.texture_region_opt = table_to_rect(L, -1, "data.texture_region");
|
|
||||||
args.texture_region_opt_set = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_getfield(L, 1, "color");
|
/* TODO: use arrays for optimized paths */
|
||||||
if (lua_istable(L, -1)) {
|
/* TODO: preallocate table records */
|
||||||
args.color_opt = table_to_color(L, -1);
|
switch (lua_type(from, index)) {
|
||||||
args.color_opt_set = true;
|
case LUA_TTABLE:
|
||||||
|
lua_newtable(to);
|
||||||
|
lua_pushnil(from); /* first key */
|
||||||
|
while (lua_next(from, index - 1) != 0) {
|
||||||
|
/* 'key' at index -2 and 'value' at index -1 */
|
||||||
|
exchange_lua_states(from, to, level + 1, -2);
|
||||||
|
exchange_lua_states(from, to, level + 1, -1);
|
||||||
|
lua_settable(to, index - 2);
|
||||||
|
/* removes 'value'; keeps 'key' for next iteration */
|
||||||
|
lua_pop(from, 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LUA_TNUMBER:
|
||||||
|
lua_pushnumber(to, lua_tonumber(from, index));
|
||||||
|
break;
|
||||||
|
case LUA_TBOOLEAN:
|
||||||
|
lua_pushboolean(to, lua_toboolean(from, index));
|
||||||
|
break;
|
||||||
|
case LUA_TSTRING:
|
||||||
|
lua_pushstring(to, lua_tostring(from, index));
|
||||||
|
break;
|
||||||
|
case LUA_TNIL:
|
||||||
|
lua_pushnil(to);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* TODO: provide a path and type of it for better diagnostic */
|
||||||
|
log_string("Unserializable udata found and is ignored", NULL);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_getfield(L, 1, "rotation");
|
|
||||||
if (lua_isnumber(L, -1)) {
|
|
||||||
args.rotation_opt = (float)lua_tonumber(L, -1);
|
|
||||||
args.rotation_opt_set = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "flip_x");
|
|
||||||
if (lua_isboolean(L, -1)) {
|
|
||||||
args.flip_x_opt = lua_toboolean(L, -1);
|
|
||||||
args.flip_x_opt_set = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "flip_y");
|
|
||||||
if (lua_isboolean(L, -1)) {
|
|
||||||
args.flip_y_opt = lua_toboolean(L, -1);
|
|
||||||
args.flip_y_opt_set = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "stretch");
|
|
||||||
if (lua_isboolean(L, -1)) {
|
|
||||||
args.stretch_opt = lua_toboolean(L, -1);
|
|
||||||
args.stretch_opt_set = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_sprite_args(args);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* rectangle(data [table]) */
|
|
||||||
/* data should contain the following fields: */
|
|
||||||
/*
|
|
||||||
* rect [table]
|
|
||||||
* color [table], optional, defaults to white
|
|
||||||
*/
|
|
||||||
static int b_rectangle(lua_State *L) {
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "rect");
|
|
||||||
if (!lua_istable(L, -1))
|
|
||||||
luaL_error(L, "bad field 'rect' in 'data' (table expected, got %s)", luaL_typename(L, -1));
|
|
||||||
Rect rect = table_to_rect(L, -1, "data.rect");
|
|
||||||
|
|
||||||
Color color = { 255, 255, 255, 255 };
|
|
||||||
lua_getfield(L, 1, "color");
|
|
||||||
if (lua_istable(L, -1))
|
|
||||||
color = table_to_color(L, -1);
|
|
||||||
|
|
||||||
draw_rectangle(rect, color);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* text(data [table]) */
|
|
||||||
/* data should contain the following fields: */
|
|
||||||
/*
|
|
||||||
* string [string]
|
|
||||||
* position [table]
|
|
||||||
* height_px [number], optional, defaults to 22
|
|
||||||
* color [table], optional, defaults to black
|
|
||||||
* font [string]
|
|
||||||
*/
|
|
||||||
static int b_text(lua_State *L) {
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "string");
|
|
||||||
const char *string = lua_tostring(L, -1);
|
|
||||||
if (string == NULL)
|
|
||||||
luaL_error(L, "bad field 'string' in 'data' (string expected, got %s)", luaL_typename(L, -1));
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "position");
|
|
||||||
if (!lua_istable(L, -1))
|
|
||||||
luaL_error(L, "bad field 'position' in 'data' (table expected, got %s)", luaL_typename(L, -1));
|
|
||||||
lua_getfield(L, -1, "x");
|
|
||||||
float x = (float)lua_tonumber(L, -1);
|
|
||||||
lua_getfield(L, -2, "y");
|
|
||||||
float y = (float)lua_tonumber(L, -1);
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "height_px");
|
|
||||||
int is_num;
|
|
||||||
int height_px = (int)lua_tointegerx(L, -1, &is_num);
|
|
||||||
if (!is_num)
|
|
||||||
height_px = 22;
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "color");
|
|
||||||
Color color = { 0, 0, 0, 255 };
|
|
||||||
if (lua_istable(L, -1))
|
|
||||||
color = table_to_color(L, -1);
|
|
||||||
|
|
||||||
lua_getfield(L, 1, "font");
|
|
||||||
const char *font = lua_tostring(L, -1);
|
|
||||||
if (font == NULL)
|
|
||||||
luaL_error(L, "bad field 'font' in 'data' (string expected, got %s)", luaL_typename(L, -1));
|
|
||||||
|
|
||||||
draw_text(string, (Vec2) { x, y }, height_px, color, font);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void game_tick(void) {
|
void game_tick(void) {
|
||||||
if (ctx.initialization_needed) {
|
if (ctx.initialization_needed) {
|
||||||
if (!ctx.udata)
|
if (!ctx.udata)
|
||||||
ctx.udata = ccalloc(1, sizeof (State));
|
ctx.udata = SDL_calloc(1, sizeof (State));
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
/* let's init lua */
|
/* let's init lua */
|
||||||
/* state existed already */
|
lua_State *new_state = lua_newstate(custom_alloc, NULL);
|
||||||
|
lua_setallocf(new_state, custom_alloc, NULL);
|
||||||
|
|
||||||
|
/* state existed already, copy its udata over */
|
||||||
if (state->L != NULL) {
|
if (state->L != NULL) {
|
||||||
|
lua_getglobal(state->L, "ctx");
|
||||||
|
lua_getfield(state->L, -1, "udata");
|
||||||
|
SDL_assert(!lua_isnoneornil(state->L, -1));
|
||||||
|
SDL_assert(!lua_isnoneornil(state->L, -2));
|
||||||
|
if (!lua_isnoneornil(state->L, -1)) {
|
||||||
|
lua_newtable(new_state);
|
||||||
|
exchange_lua_states(state->L, new_state, 0, -1);
|
||||||
|
lua_setfield(new_state, -2, "udata");
|
||||||
|
lua_setglobal(new_state, "ctx");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bye :) */
|
||||||
lua_close(state->L);
|
lua_close(state->L);
|
||||||
}
|
}
|
||||||
state->L = luaL_newstate();
|
|
||||||
|
|
||||||
/* fakey version of luaL_openlibs() that excludes file i/o */
|
state->L = new_state;
|
||||||
|
|
||||||
|
/* fakey version of luaL_openlibs() that excludes file i/o and os stuff */
|
||||||
{
|
{
|
||||||
static const luaL_Reg loaded_libs[] = {
|
static const luaL_Reg loaded_libs[] = {
|
||||||
{ LUA_GNAME, luaopen_base },
|
{ LUA_GNAME, luaopen_base },
|
||||||
{ LUA_LOADLIBNAME, luaopen_package },
|
{ LUA_LOADLIBNAME, luaopen_package },
|
||||||
{ LUA_COLIBNAME, luaopen_coroutine },
|
{ LUA_COLIBNAME, luaopen_coroutine },
|
||||||
{ LUA_TABLIBNAME, luaopen_table },
|
{ LUA_TABLIBNAME, luaopen_table },
|
||||||
{ LUA_OSLIBNAME, luaopen_os },
|
|
||||||
{ LUA_STRLIBNAME, luaopen_string },
|
{ LUA_STRLIBNAME, luaopen_string },
|
||||||
{ LUA_MATHLIBNAME, luaopen_math },
|
{ LUA_MATHLIBNAME, luaopen_math },
|
||||||
{ LUA_UTF8LIBNAME, luaopen_utf8 },
|
{ LUA_UTF8LIBNAME, luaopen_utf8 },
|
||||||
@ -269,7 +201,7 @@ void game_tick(void) {
|
|||||||
|
|
||||||
/* package.searchers = { physfs_loader } */
|
/* package.searchers = { physfs_loader } */
|
||||||
lua_getglobal(state->L, "package");
|
lua_getglobal(state->L, "package");
|
||||||
lua_newtable(state->L);
|
lua_createtable(state->L, 0, 1);
|
||||||
lua_setfield(state->L, -2, "searchers");
|
lua_setfield(state->L, -2, "searchers");
|
||||||
|
|
||||||
lua_getfield(state->L, -1, "searchers");
|
lua_getfield(state->L, -1, "searchers");
|
||||||
@ -280,35 +212,54 @@ void game_tick(void) {
|
|||||||
lua_pop(state->L, 2);
|
lua_pop(state->L, 2);
|
||||||
|
|
||||||
/* binding */
|
/* binding */
|
||||||
lua_register(state->L, "sprite", b_sprite);
|
bindgen_load_twn(state->L);
|
||||||
lua_register(state->L, "rectangle", b_rectangle);
|
|
||||||
lua_register(state->L, "text", b_text);
|
|
||||||
|
|
||||||
/* now finally get to running the code */
|
/* now finally get to running the code */
|
||||||
unsigned char *game_buf = NULL;
|
String file = file_read("/scripts/game.lua", ":binary");
|
||||||
size_t game_buf_size = file_to_bytes("/scripts/game.lua", &game_buf);
|
/* TODO: use reader interface for streaming instead */
|
||||||
if (luaL_loadbuffer(state->L, (char *)game_buf, game_buf_size, "game.lua") == LUA_OK) {
|
if (luaL_loadbuffer(state->L, file.data, (size_t)file.length, "game.lua") == LUA_OK) {
|
||||||
if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) {
|
if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) {
|
||||||
log_critical("%s", lua_tostring(state->L, -1));
|
log_string(luaL_tolstring(state->L, -1, NULL), "Error executing /scripts/game.lua entry");
|
||||||
lua_pop(state->L, 1);
|
lua_pop(state->L, 1);
|
||||||
}
|
} else
|
||||||
|
state->loaded_successfully = true;
|
||||||
|
} else {
|
||||||
|
/* got some sort of error, it should be pushed on top of the stack */
|
||||||
|
SDL_assert(lua_isstring(state->L, -1));
|
||||||
|
log_string(luaL_tolstring(state->L, -1, NULL), "Error loading /scripts/game.lua entry");
|
||||||
|
lua_pop(state->L, 1);
|
||||||
}
|
}
|
||||||
free(game_buf);
|
|
||||||
/* from this point we have access to everything defined in lua */
|
/* from this point we have access to everything defined in lua */
|
||||||
}
|
}
|
||||||
|
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
|
||||||
lua_getglobal(state->L, "game_tick");
|
if (state->loaded_successfully) {
|
||||||
if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) {
|
bindgen_build_context(state->L);
|
||||||
log_critical("%s", lua_tostring(state->L, -1));
|
lua_getglobal(state->L, "ctx");
|
||||||
|
if (!lua_isnoneornil(state->L, -1)) {
|
||||||
|
lua_getfield(state->L, -1, "udata");
|
||||||
|
lua_setfield(state->L, -3, "udata");
|
||||||
|
}
|
||||||
lua_pop(state->L, 1);
|
lua_pop(state->L, 1);
|
||||||
|
lua_setglobal(state->L, "ctx");
|
||||||
|
|
||||||
|
lua_getglobal(state->L, "game_tick");
|
||||||
|
if (lua_pcall(state->L, 0, 0, 0) != LUA_OK) {
|
||||||
|
log_string(luaL_tolstring(state->L, -1, NULL), "Error executing game_tick()");
|
||||||
|
lua_pop(state->L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getglobal(state->L, "ctx");
|
||||||
|
bindgen_upload_context(state->L);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void game_end(void) {
|
void game_end(void) {
|
||||||
State *state = ctx.udata;
|
State *state = ctx.udata;
|
||||||
|
bindgen_unload_twn(state->L);
|
||||||
lua_close(state->L);
|
lua_close(state->L);
|
||||||
free(state);
|
SDL_free(state);
|
||||||
}
|
}
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
# Makefile for installing Lua
|
|
||||||
# See doc/readme.html for installation and customization instructions.
|
|
||||||
|
|
||||||
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
|
|
||||||
|
|
||||||
# Your platform. See PLATS for possible values.
|
|
||||||
PLAT= guess
|
|
||||||
|
|
||||||
# Where to install. The installation starts in the src and doc directories,
|
|
||||||
# so take care if INSTALL_TOP is not an absolute path. See the local target.
|
|
||||||
# You may want to make INSTALL_LMOD and INSTALL_CMOD consistent with
|
|
||||||
# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h.
|
|
||||||
INSTALL_TOP= /usr/local
|
|
||||||
INSTALL_BIN= $(INSTALL_TOP)/bin
|
|
||||||
INSTALL_INC= $(INSTALL_TOP)/include
|
|
||||||
INSTALL_LIB= $(INSTALL_TOP)/lib
|
|
||||||
INSTALL_MAN= $(INSTALL_TOP)/man/man1
|
|
||||||
INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V
|
|
||||||
INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V
|
|
||||||
|
|
||||||
# How to install. If your install program does not support "-p", then
|
|
||||||
# you may have to run ranlib on the installed liblua.a.
|
|
||||||
INSTALL= install -p
|
|
||||||
INSTALL_EXEC= $(INSTALL) -m 0755
|
|
||||||
INSTALL_DATA= $(INSTALL) -m 0644
|
|
||||||
#
|
|
||||||
# If you don't have "install" you can use "cp" instead.
|
|
||||||
# INSTALL= cp -p
|
|
||||||
# INSTALL_EXEC= $(INSTALL)
|
|
||||||
# INSTALL_DATA= $(INSTALL)
|
|
||||||
|
|
||||||
# Other utilities.
|
|
||||||
MKDIR= mkdir -p
|
|
||||||
RM= rm -f
|
|
||||||
|
|
||||||
# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======
|
|
||||||
|
|
||||||
# Convenience platforms targets.
|
|
||||||
PLATS= guess aix bsd c89 freebsd generic ios linux linux-readline macosx mingw posix solaris
|
|
||||||
|
|
||||||
# What to install.
|
|
||||||
TO_BIN= lua luac
|
|
||||||
TO_INC= lua.h luaconf.h lualib.h lauxlib.h lua.hpp
|
|
||||||
TO_LIB= liblua.a
|
|
||||||
TO_MAN= lua.1 luac.1
|
|
||||||
|
|
||||||
# Lua version and release.
|
|
||||||
V= 5.4
|
|
||||||
R= $V.7
|
|
||||||
|
|
||||||
# Targets start here.
|
|
||||||
all: $(PLAT)
|
|
||||||
|
|
||||||
$(PLATS) help test clean:
|
|
||||||
@cd src && $(MAKE) $@
|
|
||||||
|
|
||||||
install: dummy
|
|
||||||
cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)
|
|
||||||
cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)
|
|
||||||
cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)
|
|
||||||
cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)
|
|
||||||
cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)
|
|
||||||
|
|
||||||
uninstall:
|
|
||||||
cd src && cd $(INSTALL_BIN) && $(RM) $(TO_BIN)
|
|
||||||
cd src && cd $(INSTALL_INC) && $(RM) $(TO_INC)
|
|
||||||
cd src && cd $(INSTALL_LIB) && $(RM) $(TO_LIB)
|
|
||||||
cd doc && cd $(INSTALL_MAN) && $(RM) $(TO_MAN)
|
|
||||||
|
|
||||||
local:
|
|
||||||
$(MAKE) install INSTALL_TOP=../install
|
|
||||||
|
|
||||||
# make may get confused with install/ if it does not support .PHONY.
|
|
||||||
dummy:
|
|
||||||
|
|
||||||
# Echo config parameters.
|
|
||||||
echo:
|
|
||||||
@cd src && $(MAKE) -s echo
|
|
||||||
@echo "PLAT= $(PLAT)"
|
|
||||||
@echo "V= $V"
|
|
||||||
@echo "R= $R"
|
|
||||||
@echo "TO_BIN= $(TO_BIN)"
|
|
||||||
@echo "TO_INC= $(TO_INC)"
|
|
||||||
@echo "TO_LIB= $(TO_LIB)"
|
|
||||||
@echo "TO_MAN= $(TO_MAN)"
|
|
||||||
@echo "INSTALL_TOP= $(INSTALL_TOP)"
|
|
||||||
@echo "INSTALL_BIN= $(INSTALL_BIN)"
|
|
||||||
@echo "INSTALL_INC= $(INSTALL_INC)"
|
|
||||||
@echo "INSTALL_LIB= $(INSTALL_LIB)"
|
|
||||||
@echo "INSTALL_MAN= $(INSTALL_MAN)"
|
|
||||||
@echo "INSTALL_LMOD= $(INSTALL_LMOD)"
|
|
||||||
@echo "INSTALL_CMOD= $(INSTALL_CMOD)"
|
|
||||||
@echo "INSTALL_EXEC= $(INSTALL_EXEC)"
|
|
||||||
@echo "INSTALL_DATA= $(INSTALL_DATA)"
|
|
||||||
|
|
||||||
# Echo pkg-config data.
|
|
||||||
pc:
|
|
||||||
@echo "version=$R"
|
|
||||||
@echo "prefix=$(INSTALL_TOP)"
|
|
||||||
@echo "libdir=$(INSTALL_LIB)"
|
|
||||||
@echo "includedir=$(INSTALL_INC)"
|
|
||||||
|
|
||||||
# Targets that do not create files (not all makes understand .PHONY).
|
|
||||||
.PHONY: all $(PLATS) help test clean install uninstall local dummy echo pc
|
|
||||||
|
|
||||||
# (end of Makefile)
|
|
@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:0f6cc3aaa68597034efda8bb6bfa214fc2fde99bd1e67d0bb32d7fe75a5dc777
|
|
||||||
size 12127
|
|
@ -1,21 +0,0 @@
|
|||||||
ul {
|
|
||||||
list-style-type: none ;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.contents {
|
|
||||||
padding: 0 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border: none ;
|
|
||||||
border-spacing: 0 ;
|
|
||||||
border-collapse: collapse ;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
vertical-align: top ;
|
|
||||||
padding: 0 ;
|
|
||||||
text-align: left ;
|
|
||||||
line-height: 1.25 ;
|
|
||||||
width: 15% ;
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 9.7 KiB |
@ -1,155 +0,0 @@
|
|||||||
.\" $Id: lua.man,v 1.14 2024/05/08 18:48:27 lhf Exp $
|
|
||||||
.TH LUA 1 "$Date: 2024/05/08 18:48:27 $"
|
|
||||||
.SH NAME
|
|
||||||
lua \- Lua interpreter
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.B lua
|
|
||||||
[
|
|
||||||
.I options
|
|
||||||
]
|
|
||||||
[
|
|
||||||
.I script
|
|
||||||
[
|
|
||||||
.I args
|
|
||||||
]
|
|
||||||
]
|
|
||||||
.SH DESCRIPTION
|
|
||||||
.B lua
|
|
||||||
is the standalone Lua interpreter.
|
|
||||||
It loads and executes Lua programs,
|
|
||||||
either in textual source form or
|
|
||||||
in precompiled binary form.
|
|
||||||
(Precompiled binaries are output by
|
|
||||||
.BR luac ,
|
|
||||||
the Lua compiler.)
|
|
||||||
.B lua
|
|
||||||
can be used as a batch interpreter and also interactively.
|
|
||||||
.LP
|
|
||||||
After handling the
|
|
||||||
.IR options ,
|
|
||||||
the Lua program in file
|
|
||||||
.I script
|
|
||||||
is loaded and executed.
|
|
||||||
The
|
|
||||||
.I args
|
|
||||||
are available to
|
|
||||||
.I script
|
|
||||||
as strings in a global table named
|
|
||||||
.B arg
|
|
||||||
and also as arguments to its main function.
|
|
||||||
When called without arguments,
|
|
||||||
.B lua
|
|
||||||
behaves as
|
|
||||||
.B "lua \-v \-i"
|
|
||||||
if the standard input is a terminal,
|
|
||||||
and as
|
|
||||||
.B "lua \-"
|
|
||||||
otherwise.
|
|
||||||
.LP
|
|
||||||
In interactive mode,
|
|
||||||
.B lua
|
|
||||||
prompts the user,
|
|
||||||
reads lines from the standard input,
|
|
||||||
and executes them as they are read.
|
|
||||||
If the line contains an expression,
|
|
||||||
then the line is evaluated and the result is printed.
|
|
||||||
If a line does not contain a complete statement,
|
|
||||||
then a secondary prompt is displayed and
|
|
||||||
lines are read until a complete statement is formed or
|
|
||||||
a syntax error is found.
|
|
||||||
.LP
|
|
||||||
Before handling command line options and scripts,
|
|
||||||
.B lua
|
|
||||||
checks the contents of the environment variables
|
|
||||||
.B LUA_INIT_5_4
|
|
||||||
and
|
|
||||||
.BR LUA_INIT ,
|
|
||||||
in that order.
|
|
||||||
If the contents are of the form
|
|
||||||
.RI '@ filename ',
|
|
||||||
then
|
|
||||||
.I filename
|
|
||||||
is executed.
|
|
||||||
Otherwise, the contents are assumed to be a Lua statement and is executed.
|
|
||||||
When
|
|
||||||
.B LUA_INIT_5_4
|
|
||||||
is defined,
|
|
||||||
.B LUA_INIT
|
|
||||||
is ignored.
|
|
||||||
.SH OPTIONS
|
|
||||||
.TP
|
|
||||||
.BI \-e " stat"
|
|
||||||
execute statement
|
|
||||||
.IR stat .
|
|
||||||
.TP
|
|
||||||
.B \-i
|
|
||||||
enter interactive mode after executing
|
|
||||||
.IR script .
|
|
||||||
.TP
|
|
||||||
.BI \-l " mod"
|
|
||||||
require library
|
|
||||||
.I mod
|
|
||||||
into global
|
|
||||||
.IR mod .
|
|
||||||
.TP
|
|
||||||
.BI \-l " g=mod"
|
|
||||||
require library
|
|
||||||
.I mod
|
|
||||||
into global
|
|
||||||
.IR g .
|
|
||||||
.TP
|
|
||||||
.B \-v
|
|
||||||
show version information.
|
|
||||||
.TP
|
|
||||||
.B \-E
|
|
||||||
ignore environment variables.
|
|
||||||
.TP
|
|
||||||
.B \-W
|
|
||||||
turn warnings on.
|
|
||||||
.TP
|
|
||||||
.B \-\-
|
|
||||||
stop handling options.
|
|
||||||
.TP
|
|
||||||
.B \-
|
|
||||||
stop handling options and execute the standard input as a file.
|
|
||||||
.SH ENVIRONMENT VARIABLES
|
|
||||||
The following environment variables affect the execution of
|
|
||||||
.BR lua .
|
|
||||||
When defined,
|
|
||||||
the version-specific variants take priority
|
|
||||||
and the version-neutral variants are ignored.
|
|
||||||
.TP
|
|
||||||
.B LUA_INIT, LUA_INIT_5_4
|
|
||||||
Code to be executed before command line options and scripts.
|
|
||||||
.TP
|
|
||||||
.B LUA_PATH, LUA_PATH_5_4
|
|
||||||
Initial value of package.path,
|
|
||||||
the path used by require to search for Lua loaders.
|
|
||||||
.TP
|
|
||||||
.B LUA_CPATH, LUA_CPATH_5_4
|
|
||||||
Initial value of package.cpath,
|
|
||||||
the path used by require to search for C loaders.
|
|
||||||
.SH EXIT STATUS
|
|
||||||
If a script calls os.exit,
|
|
||||||
then
|
|
||||||
.B lua
|
|
||||||
exits with the given exit status.
|
|
||||||
Otherwise,
|
|
||||||
.B lua
|
|
||||||
exits
|
|
||||||
with EXIT_SUCCESS (0 on POSIX systems) if there were no errors
|
|
||||||
and
|
|
||||||
with EXIT_FAILURE (1 on POSIX systems) if there were errors.
|
|
||||||
Errors raised in interactive mode do not cause exits.
|
|
||||||
.SH DIAGNOSTICS
|
|
||||||
Error messages should be self explanatory.
|
|
||||||
.SH "SEE ALSO"
|
|
||||||
.BR luac (1)
|
|
||||||
.br
|
|
||||||
The documentation at lua.org,
|
|
||||||
especially section 7 of the reference manual.
|
|
||||||
.SH AUTHORS
|
|
||||||
R. Ierusalimschy,
|
|
||||||
L. H. de Figueiredo,
|
|
||||||
W. Celes
|
|
||||||
.\" EOF
|
|
@ -1,162 +0,0 @@
|
|||||||
html {
|
|
||||||
background-color: #F8F8F8 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background-color: #FFFFFF ;
|
|
||||||
color: #000000 ;
|
|
||||||
font-family: Helvetica, Arial, sans-serif ;
|
|
||||||
text-align: justify ;
|
|
||||||
line-height: 1.25 ;
|
|
||||||
margin: 16px auto ;
|
|
||||||
padding: 32px ;
|
|
||||||
border: solid #ccc 1px ;
|
|
||||||
border-radius: 20px ;
|
|
||||||
max-width: 70em ;
|
|
||||||
width: 90% ;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4 {
|
|
||||||
color: #000080 ;
|
|
||||||
font-family: Verdana, Geneva, sans-serif ;
|
|
||||||
font-weight: normal ;
|
|
||||||
font-style: normal ;
|
|
||||||
text-align: left ;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 28pt ;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 img {
|
|
||||||
vertical-align: text-bottom ;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2:before {
|
|
||||||
content: "\2756" ;
|
|
||||||
padding-right: 0.5em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none ;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link {
|
|
||||||
color: #000080 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link:hover, a:visited:hover {
|
|
||||||
background-color: #D0D0FF ;
|
|
||||||
color: #000080 ;
|
|
||||||
border-radius: 4px ;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link:active, a:visited:active {
|
|
||||||
color: #FF0000 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.menubar {
|
|
||||||
padding-bottom: 0.5em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.menubar {
|
|
||||||
margin-left: 2.5em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menubar a:hover {
|
|
||||||
margin: -3px -3px -3px -3px ;
|
|
||||||
padding: 3px 3px 3px 3px ;
|
|
||||||
border-radius: 4px ;
|
|
||||||
}
|
|
||||||
|
|
||||||
:target {
|
|
||||||
background-color: #F0F0F0 ;
|
|
||||||
margin: -8px ;
|
|
||||||
padding: 8px ;
|
|
||||||
border-radius: 8px ;
|
|
||||||
outline: none ;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
display: none ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table hr {
|
|
||||||
background-color: #a0a0a0 ;
|
|
||||||
color: #a0a0a0 ;
|
|
||||||
border: 0 ;
|
|
||||||
height: 1px ;
|
|
||||||
display: block ;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
color: gray ;
|
|
||||||
font-size: x-small ;
|
|
||||||
text-transform: lowercase ;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=text] {
|
|
||||||
border: solid #a0a0a0 2px ;
|
|
||||||
border-radius: 2em ;
|
|
||||||
background-image: url('images/search.png') ;
|
|
||||||
background-repeat: no-repeat ;
|
|
||||||
background-position: 4px center ;
|
|
||||||
padding-left: 20px ;
|
|
||||||
height: 2em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre.session {
|
|
||||||
background-color: #F8F8F8 ;
|
|
||||||
padding: 1em ;
|
|
||||||
border-radius: 8px ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border: none ;
|
|
||||||
border-spacing: 0 ;
|
|
||||||
border-collapse: collapse ;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding: 0 ;
|
|
||||||
margin: 0 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.gutter {
|
|
||||||
width: 4% ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.columns td {
|
|
||||||
vertical-align: top ;
|
|
||||||
padding-bottom: 1em ;
|
|
||||||
text-align: justify ;
|
|
||||||
line-height: 1.25 ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.book td {
|
|
||||||
vertical-align: top ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.book td.cover {
|
|
||||||
padding-right: 1em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.book img {
|
|
||||||
border: solid #000080 1px ;
|
|
||||||
border-radius: 2px ;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.book span {
|
|
||||||
font-size: small ;
|
|
||||||
text-align: left ;
|
|
||||||
display: block ;
|
|
||||||
margin-top: 0.25em ;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.logos a:link:hover, p.logos a:visited:hover {
|
|
||||||
background-color: inherit ;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
background-color: white ;
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
.\" $Id: luac.man,v 1.29 2011/11/16 13:53:40 lhf Exp $
|
|
||||||
.TH LUAC 1 "$Date: 2011/11/16 13:53:40 $"
|
|
||||||
.SH NAME
|
|
||||||
luac \- Lua compiler
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.B luac
|
|
||||||
[
|
|
||||||
.I options
|
|
||||||
] [
|
|
||||||
.I filenames
|
|
||||||
]
|
|
||||||
.SH DESCRIPTION
|
|
||||||
.B luac
|
|
||||||
is the Lua compiler.
|
|
||||||
It translates programs written in the Lua programming language
|
|
||||||
into binary files containing precompiled chunks
|
|
||||||
that can be later loaded and executed.
|
|
||||||
.LP
|
|
||||||
The main advantages of precompiling chunks are:
|
|
||||||
faster loading,
|
|
||||||
protecting source code from accidental user changes,
|
|
||||||
and
|
|
||||||
off-line syntax checking.
|
|
||||||
Precompiling does not imply faster execution
|
|
||||||
because in Lua chunks are always compiled into bytecodes before being executed.
|
|
||||||
.B luac
|
|
||||||
simply allows those bytecodes to be saved in a file for later execution.
|
|
||||||
Precompiled chunks are not necessarily smaller than the corresponding source.
|
|
||||||
The main goal in precompiling is faster loading.
|
|
||||||
.LP
|
|
||||||
In the command line,
|
|
||||||
you can mix
|
|
||||||
text files containing Lua source and
|
|
||||||
binary files containing precompiled chunks.
|
|
||||||
.B luac
|
|
||||||
produces a single output file containing the combined bytecodes
|
|
||||||
for all files given.
|
|
||||||
Executing the combined file is equivalent to executing the given files.
|
|
||||||
By default,
|
|
||||||
the output file is named
|
|
||||||
.BR luac.out ,
|
|
||||||
but you can change this with the
|
|
||||||
.B \-o
|
|
||||||
option.
|
|
||||||
.LP
|
|
||||||
Precompiled chunks are
|
|
||||||
.I not
|
|
||||||
portable across different architectures.
|
|
||||||
Moreover,
|
|
||||||
the internal format of precompiled chunks
|
|
||||||
is likely to change when a new version of Lua is released.
|
|
||||||
Make sure you save the source files of all Lua programs that you precompile.
|
|
||||||
.LP
|
|
||||||
.SH OPTIONS
|
|
||||||
.TP
|
|
||||||
.B \-l
|
|
||||||
produce a listing of the compiled bytecode for Lua's virtual machine.
|
|
||||||
Listing bytecodes is useful to learn about Lua's virtual machine.
|
|
||||||
If no files are given, then
|
|
||||||
.B luac
|
|
||||||
loads
|
|
||||||
.B luac.out
|
|
||||||
and lists its contents.
|
|
||||||
Use
|
|
||||||
.B \-l \-l
|
|
||||||
for a full listing.
|
|
||||||
.TP
|
|
||||||
.BI \-o " file"
|
|
||||||
output to
|
|
||||||
.IR file ,
|
|
||||||
instead of the default
|
|
||||||
.BR luac.out .
|
|
||||||
(You can use
|
|
||||||
.B "'\-'"
|
|
||||||
for standard output,
|
|
||||||
but not on platforms that open standard output in text mode.)
|
|
||||||
The output file may be one of the given files because
|
|
||||||
all files are loaded before the output file is written.
|
|
||||||
Be careful not to overwrite precious files.
|
|
||||||
.TP
|
|
||||||
.B \-p
|
|
||||||
load files but do not generate any output file.
|
|
||||||
Used mainly for syntax checking and for testing precompiled chunks:
|
|
||||||
corrupted files will probably generate errors when loaded.
|
|
||||||
If no files are given, then
|
|
||||||
.B luac
|
|
||||||
loads
|
|
||||||
.B luac.out
|
|
||||||
and tests its contents.
|
|
||||||
No messages are displayed if the file loads without errors.
|
|
||||||
.TP
|
|
||||||
.B \-s
|
|
||||||
strip debug information before writing the output file.
|
|
||||||
This saves some space in very large chunks,
|
|
||||||
but if errors occur when running a stripped chunk,
|
|
||||||
then the error messages may not contain the full information they usually do.
|
|
||||||
In particular,
|
|
||||||
line numbers and names of local variables are lost.
|
|
||||||
.TP
|
|
||||||
.B \-v
|
|
||||||
show version information.
|
|
||||||
.TP
|
|
||||||
.B \-\-
|
|
||||||
stop handling options.
|
|
||||||
.TP
|
|
||||||
.B \-
|
|
||||||
stop handling options and process standard input.
|
|
||||||
.SH "SEE ALSO"
|
|
||||||
.BR lua (1)
|
|
||||||
.br
|
|
||||||
The documentation at lua.org.
|
|
||||||
.SH DIAGNOSTICS
|
|
||||||
Error messages should be self explanatory.
|
|
||||||
.SH AUTHORS
|
|
||||||
R. Ierusalimschy,
|
|
||||||
L. H. de Figueiredo,
|
|
||||||
W. Celes
|
|
||||||
.\" EOF
|
|
@ -1,21 +0,0 @@
|
|||||||
h3 code {
|
|
||||||
font-family: inherit ;
|
|
||||||
font-size: inherit ;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre, code {
|
|
||||||
font-size: 12pt ;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.apii {
|
|
||||||
color: gray ;
|
|
||||||
float: right ;
|
|
||||||
font-family: inherit ;
|
|
||||||
font-style: normal ;
|
|
||||||
font-size: small ;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2:before {
|
|
||||||
content: "" ;
|
|
||||||
padding-right: 0em ;
|
|
||||||
}
|
|
@ -1,225 +0,0 @@
|
|||||||
# Makefile for building Lua
|
|
||||||
# See ../doc/readme.html for installation and customization instructions.
|
|
||||||
|
|
||||||
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
|
|
||||||
|
|
||||||
# Your platform. See PLATS for possible values.
|
|
||||||
PLAT= guess
|
|
||||||
|
|
||||||
CC= gcc -std=gnu99
|
|
||||||
CFLAGS= -O2 -Wall -Wextra -DLUA_COMPAT_5_3 $(SYSCFLAGS) $(MYCFLAGS)
|
|
||||||
LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS)
|
|
||||||
LIBS= -lm $(SYSLIBS) $(MYLIBS)
|
|
||||||
|
|
||||||
AR= ar rcu
|
|
||||||
RANLIB= ranlib
|
|
||||||
RM= rm -f
|
|
||||||
UNAME= uname
|
|
||||||
|
|
||||||
SYSCFLAGS=
|
|
||||||
SYSLDFLAGS=
|
|
||||||
SYSLIBS=
|
|
||||||
|
|
||||||
MYCFLAGS=
|
|
||||||
MYLDFLAGS=
|
|
||||||
MYLIBS=
|
|
||||||
MYOBJS=
|
|
||||||
|
|
||||||
# Special flags for compiler modules; -Os reduces code size.
|
|
||||||
CMCFLAGS=
|
|
||||||
|
|
||||||
# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE =======
|
|
||||||
|
|
||||||
PLATS= guess aix bsd c89 freebsd generic ios linux linux-readline macosx mingw posix solaris
|
|
||||||
|
|
||||||
LUA_A= liblua.a
|
|
||||||
CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o
|
|
||||||
LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
|
|
||||||
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
|
|
||||||
|
|
||||||
LUA_T= lua
|
|
||||||
LUA_O= lua.o
|
|
||||||
|
|
||||||
LUAC_T= luac
|
|
||||||
LUAC_O= luac.o
|
|
||||||
|
|
||||||
ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O)
|
|
||||||
ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)
|
|
||||||
ALL_A= $(LUA_A)
|
|
||||||
|
|
||||||
# Targets start here.
|
|
||||||
default: $(PLAT)
|
|
||||||
|
|
||||||
all: $(ALL_T)
|
|
||||||
|
|
||||||
o: $(ALL_O)
|
|
||||||
|
|
||||||
a: $(ALL_A)
|
|
||||||
|
|
||||||
$(LUA_A): $(BASE_O)
|
|
||||||
$(AR) $@ $(BASE_O)
|
|
||||||
$(RANLIB) $@
|
|
||||||
|
|
||||||
$(LUA_T): $(LUA_O) $(LUA_A)
|
|
||||||
$(CC) -o $@ $(LDFLAGS) $(LUA_O) $(LUA_A) $(LIBS)
|
|
||||||
|
|
||||||
$(LUAC_T): $(LUAC_O) $(LUA_A)
|
|
||||||
$(CC) -o $@ $(LDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)
|
|
||||||
|
|
||||||
test:
|
|
||||||
./$(LUA_T) -v
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(RM) $(ALL_T) $(ALL_O)
|
|
||||||
|
|
||||||
depend:
|
|
||||||
@$(CC) $(CFLAGS) -MM l*.c
|
|
||||||
|
|
||||||
echo:
|
|
||||||
@echo "PLAT= $(PLAT)"
|
|
||||||
@echo "CC= $(CC)"
|
|
||||||
@echo "CFLAGS= $(CFLAGS)"
|
|
||||||
@echo "LDFLAGS= $(LDFLAGS)"
|
|
||||||
@echo "LIBS= $(LIBS)"
|
|
||||||
@echo "AR= $(AR)"
|
|
||||||
@echo "RANLIB= $(RANLIB)"
|
|
||||||
@echo "RM= $(RM)"
|
|
||||||
@echo "UNAME= $(UNAME)"
|
|
||||||
|
|
||||||
# Convenience targets for popular platforms.
|
|
||||||
ALL= all
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo "Do 'make PLATFORM' where PLATFORM is one of these:"
|
|
||||||
@echo " $(PLATS)"
|
|
||||||
@echo "See doc/readme.html for complete instructions."
|
|
||||||
|
|
||||||
guess:
|
|
||||||
@echo Guessing `$(UNAME)`
|
|
||||||
@$(MAKE) `$(UNAME)`
|
|
||||||
|
|
||||||
AIX aix:
|
|
||||||
$(MAKE) $(ALL) CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-ldl" SYSLDFLAGS="-brtl -bexpall"
|
|
||||||
|
|
||||||
bsd:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-Wl,-E"
|
|
||||||
|
|
||||||
c89:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_C89" CC="gcc -std=c89"
|
|
||||||
@echo ''
|
|
||||||
@echo '*** C89 does not guarantee 64-bit integers for Lua.'
|
|
||||||
@echo '*** Make sure to compile all external Lua libraries'
|
|
||||||
@echo '*** with LUA_USE_C89 to ensure consistency'
|
|
||||||
@echo ''
|
|
||||||
|
|
||||||
FreeBSD NetBSD OpenBSD freebsd:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc"
|
|
||||||
|
|
||||||
generic: $(ALL)
|
|
||||||
|
|
||||||
ios:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_IOS"
|
|
||||||
|
|
||||||
Linux linux: linux-noreadline
|
|
||||||
|
|
||||||
linux-noreadline:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl"
|
|
||||||
|
|
||||||
linux-readline:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE" SYSLIBS="-Wl,-E -ldl -lreadline"
|
|
||||||
|
|
||||||
Darwin macos macosx:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX -DLUA_USE_READLINE" SYSLIBS="-lreadline"
|
|
||||||
|
|
||||||
mingw:
|
|
||||||
$(MAKE) "LUA_A=lua54.dll" "LUA_T=lua.exe" \
|
|
||||||
"AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
|
|
||||||
"SYSCFLAGS=-DLUA_BUILD_AS_DLL" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe
|
|
||||||
$(MAKE) "LUAC_T=luac.exe" luac.exe
|
|
||||||
|
|
||||||
posix:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX"
|
|
||||||
|
|
||||||
SunOS solaris:
|
|
||||||
$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN -D_REENTRANT" SYSLIBS="-ldl"
|
|
||||||
|
|
||||||
# Targets that do not create files (not all makes understand .PHONY).
|
|
||||||
.PHONY: all $(PLATS) help test clean default o a depend echo
|
|
||||||
|
|
||||||
# Compiler modules may use special flags.
|
|
||||||
llex.o:
|
|
||||||
$(CC) $(CFLAGS) $(CMCFLAGS) -c llex.c
|
|
||||||
|
|
||||||
lparser.o:
|
|
||||||
$(CC) $(CFLAGS) $(CMCFLAGS) -c lparser.c
|
|
||||||
|
|
||||||
lcode.o:
|
|
||||||
$(CC) $(CFLAGS) $(CMCFLAGS) -c lcode.c
|
|
||||||
|
|
||||||
# DO NOT DELETE
|
|
||||||
|
|
||||||
lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
|
||||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \
|
|
||||||
ltable.h lundump.h lvm.h
|
|
||||||
lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h
|
|
||||||
lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
|
|
||||||
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
|
|
||||||
ldo.h lgc.h lstring.h ltable.h lvm.h
|
|
||||||
lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h
|
|
||||||
ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
|
||||||
lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \
|
|
||||||
ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h
|
|
||||||
ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
|
||||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \
|
|
||||||
lparser.h lstring.h ltable.h lundump.h lvm.h
|
|
||||||
ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \
|
|
||||||
ltm.h lzio.h lmem.h lundump.h
|
|
||||||
lfunc.o: lfunc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h
|
|
||||||
lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
|
|
||||||
linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h
|
|
||||||
liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \
|
|
||||||
lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \
|
|
||||||
lstring.h ltable.h
|
|
||||||
lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lmem.o: lmem.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h
|
|
||||||
loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \
|
|
||||||
ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \
|
|
||||||
lvm.h
|
|
||||||
lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h
|
|
||||||
loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
|
|
||||||
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
|
|
||||||
ldo.h lfunc.h lstring.h lgc.h ltable.h
|
|
||||||
lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
|
|
||||||
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \
|
|
||||||
lstring.h ltable.h
|
|
||||||
lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
|
|
||||||
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h
|
|
||||||
lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
|
|
||||||
ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
|
|
||||||
lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h ldebug.h lstate.h \
|
|
||||||
lobject.h llimits.h ltm.h lzio.h lmem.h lopcodes.h lopnames.h lundump.h
|
|
||||||
lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
|
|
||||||
lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \
|
|
||||||
lundump.h
|
|
||||||
lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
|
|
||||||
lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
|
|
||||||
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \
|
|
||||||
ltable.h lvm.h ljumptab.h
|
|
||||||
lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \
|
|
||||||
lobject.h ltm.h lzio.h
|
|
||||||
|
|
||||||
# (end of Makefile)
|
|
File diff suppressed because it is too large
Load Diff
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: lapi.h $
|
|
||||||
** Auxiliary functions from Lua API
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef lapi_h
|
|
||||||
#define lapi_h
|
|
||||||
|
|
||||||
|
|
||||||
#include "llimits.h"
|
|
||||||
#include "lstate.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Increments 'L->top.p', checking for stack overflows */
|
|
||||||
#define api_incr_top(L) {L->top.p++; \
|
|
||||||
api_check(L, L->top.p <= L->ci->top.p, \
|
|
||||||
"stack overflow");}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** If a call returns too many multiple returns, the callee may not have
|
|
||||||
** stack space to accommodate all results. In this case, this macro
|
|
||||||
** increases its stack space ('L->ci->top.p').
|
|
||||||
*/
|
|
||||||
#define adjustresults(L,nres) \
|
|
||||||
{ if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \
|
|
||||||
L->ci->top.p = L->top.p; }
|
|
||||||
|
|
||||||
|
|
||||||
/* Ensure the stack has at least 'n' elements */
|
|
||||||
#define api_checknelems(L,n) \
|
|
||||||
api_check(L, (n) < (L->top.p - L->ci->func.p), \
|
|
||||||
"not enough elements in the stack")
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** To reduce the overhead of returning from C functions, the presence of
|
|
||||||
** to-be-closed variables in these functions is coded in the CallInfo's
|
|
||||||
** field 'nresults', in a way that functions with no to-be-closed variables
|
|
||||||
** with zero, one, or "all" wanted results have no overhead. Functions
|
|
||||||
** with other number of wanted results, as well as functions with
|
|
||||||
** variables to be closed, have an extra check.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
|
|
||||||
|
|
||||||
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
|
|
||||||
#define codeNresults(n) (-(n) - 3)
|
|
||||||
#define decodeNresults(n) (-(n) - 3)
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
@ -1,301 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: lauxlib.h $
|
|
||||||
** Auxiliary functions for building Lua libraries
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef lauxlib_h
|
|
||||||
#define lauxlib_h
|
|
||||||
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "luaconf.h"
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* global table */
|
|
||||||
#define LUA_GNAME "_G"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct luaL_Buffer luaL_Buffer;
|
|
||||||
|
|
||||||
|
|
||||||
/* extra error code for 'luaL_loadfilex' */
|
|
||||||
#define LUA_ERRFILE (LUA_ERRERR+1)
|
|
||||||
|
|
||||||
|
|
||||||
/* key, in the registry, for table of loaded modules */
|
|
||||||
#define LUA_LOADED_TABLE "_LOADED"
|
|
||||||
|
|
||||||
|
|
||||||
/* key, in the registry, for table of preloaded loaders */
|
|
||||||
#define LUA_PRELOAD_TABLE "_PRELOAD"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct luaL_Reg {
|
|
||||||
const char *name;
|
|
||||||
lua_CFunction func;
|
|
||||||
} luaL_Reg;
|
|
||||||
|
|
||||||
|
|
||||||
#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
|
|
||||||
#define luaL_checkversion(L) \
|
|
||||||
luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
|
|
||||||
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
|
|
||||||
LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
|
|
||||||
LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
|
|
||||||
LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname);
|
|
||||||
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
|
|
||||||
size_t *l);
|
|
||||||
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
|
|
||||||
const char *def, size_t *l);
|
|
||||||
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
|
|
||||||
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
|
|
||||||
|
|
||||||
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
|
|
||||||
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
|
|
||||||
lua_Integer def);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
|
|
||||||
LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
|
|
||||||
LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
|
|
||||||
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
|
|
||||||
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
|
|
||||||
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
|
|
||||||
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
|
|
||||||
const char *const lst[]);
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
|
|
||||||
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
|
|
||||||
|
|
||||||
|
|
||||||
/* predefined references */
|
|
||||||
#define LUA_NOREF (-2)
|
|
||||||
#define LUA_REFNIL (-1)
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_ref) (lua_State *L, int t);
|
|
||||||
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
|
|
||||||
const char *mode);
|
|
||||||
|
|
||||||
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
|
|
||||||
const char *name, const char *mode);
|
|
||||||
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
|
|
||||||
|
|
||||||
LUALIB_API lua_State *(luaL_newstate) (void);
|
|
||||||
|
|
||||||
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,
|
|
||||||
const char *p, const char *r);
|
|
||||||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
|
|
||||||
const char *p, const char *r);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
|
|
||||||
|
|
||||||
LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
|
|
||||||
const char *msg, int level);
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
|
|
||||||
lua_CFunction openf, int glb);
|
|
||||||
|
|
||||||
/*
|
|
||||||
** ===============================================================
|
|
||||||
** some useful macros
|
|
||||||
** ===============================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#define luaL_newlibtable(L,l) \
|
|
||||||
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
|
|
||||||
|
|
||||||
#define luaL_newlib(L,l) \
|
|
||||||
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
|
|
||||||
|
|
||||||
#define luaL_argcheck(L, cond,arg,extramsg) \
|
|
||||||
((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
|
|
||||||
|
|
||||||
#define luaL_argexpected(L,cond,arg,tname) \
|
|
||||||
((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
|
|
||||||
|
|
||||||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
|
|
||||||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
|
|
||||||
|
|
||||||
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
|
|
||||||
|
|
||||||
#define luaL_dofile(L, fn) \
|
|
||||||
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
|
||||||
|
|
||||||
#define luaL_dostring(L, s) \
|
|
||||||
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
|
|
||||||
|
|
||||||
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
|
||||||
|
|
||||||
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
|
|
||||||
|
|
||||||
#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Perform arithmetic operations on lua_Integer values with wrap-around
|
|
||||||
** semantics, as the Lua core does.
|
|
||||||
*/
|
|
||||||
#define luaL_intop(op,v1,v2) \
|
|
||||||
((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))
|
|
||||||
|
|
||||||
|
|
||||||
/* push the value used to represent failure/error */
|
|
||||||
#define luaL_pushfail(L) lua_pushnil(L)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Internal assertions for in-house debugging
|
|
||||||
*/
|
|
||||||
#if !defined(lua_assert)
|
|
||||||
|
|
||||||
#if defined LUAI_ASSERT
|
|
||||||
#include <assert.h>
|
|
||||||
#define lua_assert(c) assert(c)
|
|
||||||
#else
|
|
||||||
#define lua_assert(c) ((void)0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {======================================================
|
|
||||||
** Generic Buffer manipulation
|
|
||||||
** =======================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct luaL_Buffer {
|
|
||||||
char *b; /* buffer address */
|
|
||||||
size_t size; /* buffer size */
|
|
||||||
size_t n; /* number of characters in buffer */
|
|
||||||
lua_State *L;
|
|
||||||
union {
|
|
||||||
LUAI_MAXALIGN; /* ensure maximum alignment for buffer */
|
|
||||||
char b[LUAL_BUFFERSIZE]; /* initial buffer */
|
|
||||||
} init;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#define luaL_bufflen(bf) ((bf)->n)
|
|
||||||
#define luaL_buffaddr(bf) ((bf)->b)
|
|
||||||
|
|
||||||
|
|
||||||
#define luaL_addchar(B,c) \
|
|
||||||
((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
|
|
||||||
((B)->b[(B)->n++] = (c)))
|
|
||||||
|
|
||||||
#define luaL_addsize(B,s) ((B)->n += (s))
|
|
||||||
|
|
||||||
#define luaL_buffsub(B,s) ((B)->n -= (s))
|
|
||||||
|
|
||||||
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
|
|
||||||
LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
|
|
||||||
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
|
|
||||||
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
|
|
||||||
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
|
|
||||||
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
|
|
||||||
LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
|
|
||||||
LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
|
|
||||||
|
|
||||||
#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
|
|
||||||
|
|
||||||
/* }====================================================== */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {======================================================
|
|
||||||
** File handles for IO library
|
|
||||||
** =======================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
|
|
||||||
** initial structure 'luaL_Stream' (it may contain other fields
|
|
||||||
** after that initial structure).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LUA_FILEHANDLE "FILE*"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct luaL_Stream {
|
|
||||||
FILE *f; /* stream (NULL for incompletely created streams) */
|
|
||||||
lua_CFunction closef; /* to close stream (NULL for closed streams) */
|
|
||||||
} luaL_Stream;
|
|
||||||
|
|
||||||
/* }====================================================== */
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {==================================================================
|
|
||||||
** "Abstraction Layer" for basic report of messages and errors
|
|
||||||
** ===================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* print a string */
|
|
||||||
#if !defined(lua_writestring)
|
|
||||||
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* print a newline and flush the output */
|
|
||||||
#if !defined(lua_writeline)
|
|
||||||
#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* print an error message */
|
|
||||||
#if !defined(lua_writestringerror)
|
|
||||||
#define lua_writestringerror(s,p) \
|
|
||||||
(fprintf(stderr, (s), (p)), fflush(stderr))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* }================================================================== */
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {============================================================
|
|
||||||
** Compatibility with deprecated conversions
|
|
||||||
** =============================================================
|
|
||||||
*/
|
|
||||||
#if defined(LUA_COMPAT_APIINTCASTS)
|
|
||||||
|
|
||||||
#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
|
|
||||||
#define luaL_optunsigned(L,a,d) \
|
|
||||||
((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
|
|
||||||
|
|
||||||
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
|
|
||||||
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
|
|
||||||
|
|
||||||
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
|
|
||||||
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
|
|
||||||
|
|
||||||
#endif
|
|
||||||
/* }============================================================ */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
@ -1,549 +0,0 @@
|
|||||||
/*
|
|
||||||
** $Id: lbaselib.c $
|
|
||||||
** Basic library
|
|
||||||
** See Copyright Notice in lua.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define lbaselib_c
|
|
||||||
#define LUA_LIB
|
|
||||||
|
|
||||||
#include "lprefix.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "lua.h"
|
|
||||||
|
|
||||||
#include "lauxlib.h"
|
|
||||||
#include "lualib.h"
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_print (lua_State *L) {
|
|
||||||
int n = lua_gettop(L); /* number of arguments */
|
|
||||||
int i;
|
|
||||||
for (i = 1; i <= n; i++) { /* for each argument */
|
|
||||||
size_t l;
|
|
||||||
const char *s = luaL_tolstring(L, i, &l); /* convert it to string */
|
|
||||||
if (i > 1) /* not the first element? */
|
|
||||||
lua_writestring("\t", 1); /* add a tab before it */
|
|
||||||
lua_writestring(s, l); /* print it */
|
|
||||||
lua_pop(L, 1); /* pop result */
|
|
||||||
}
|
|
||||||
lua_writeline();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Creates a warning with all given arguments.
|
|
||||||
** Check first for errors; otherwise an error may interrupt
|
|
||||||
** the composition of a warning, leaving it unfinished.
|
|
||||||
*/
|
|
||||||
static int luaB_warn (lua_State *L) {
|
|
||||||
int n = lua_gettop(L); /* number of arguments */
|
|
||||||
int i;
|
|
||||||
luaL_checkstring(L, 1); /* at least one argument */
|
|
||||||
for (i = 2; i <= n; i++)
|
|
||||||
luaL_checkstring(L, i); /* make sure all arguments are strings */
|
|
||||||
for (i = 1; i < n; i++) /* compose warning */
|
|
||||||
lua_warning(L, lua_tostring(L, i), 1);
|
|
||||||
lua_warning(L, lua_tostring(L, n), 0); /* close warning */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#define SPACECHARS " \f\n\r\t\v"
|
|
||||||
|
|
||||||
static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
|
|
||||||
lua_Unsigned n = 0;
|
|
||||||
int neg = 0;
|
|
||||||
s += strspn(s, SPACECHARS); /* skip initial spaces */
|
|
||||||
if (*s == '-') { s++; neg = 1; } /* handle sign */
|
|
||||||
else if (*s == '+') s++;
|
|
||||||
if (!isalnum((unsigned char)*s)) /* no digit? */
|
|
||||||
return NULL;
|
|
||||||
do {
|
|
||||||
int digit = (isdigit((unsigned char)*s)) ? *s - '0'
|
|
||||||
: (toupper((unsigned char)*s) - 'A') + 10;
|
|
||||||
if (digit >= base) return NULL; /* invalid numeral */
|
|
||||||
n = n * base + digit;
|
|
||||||
s++;
|
|
||||||
} while (isalnum((unsigned char)*s));
|
|
||||||
s += strspn(s, SPACECHARS); /* skip trailing spaces */
|
|
||||||
*pn = (lua_Integer)((neg) ? (0u - n) : n);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_tonumber (lua_State *L) {
|
|
||||||
if (lua_isnoneornil(L, 2)) { /* standard conversion? */
|
|
||||||
if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */
|
|
||||||
lua_settop(L, 1); /* yes; return it */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
size_t l;
|
|
||||||
const char *s = lua_tolstring(L, 1, &l);
|
|
||||||
if (s != NULL && lua_stringtonumber(L, s) == l + 1)
|
|
||||||
return 1; /* successful conversion to number */
|
|
||||||
/* else not a number */
|
|
||||||
luaL_checkany(L, 1); /* (but there must be some parameter) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
size_t l;
|
|
||||||
const char *s;
|
|
||||||
lua_Integer n = 0; /* to avoid warnings */
|
|
||||||
lua_Integer base = luaL_checkinteger(L, 2);
|
|
||||||
luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */
|
|
||||||
s = lua_tolstring(L, 1, &l);
|
|
||||||
luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
|
|
||||||
if (b_str2int(s, (int)base, &n) == s + l) {
|
|
||||||
lua_pushinteger(L, n);
|
|
||||||
return 1;
|
|
||||||
} /* else not a number */
|
|
||||||
} /* else not a number */
|
|
||||||
luaL_pushfail(L); /* not a number */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_error (lua_State *L) {
|
|
||||||
int level = (int)luaL_optinteger(L, 2, 1);
|
|
||||||
lua_settop(L, 1);
|
|
||||||
if (lua_type(L, 1) == LUA_TSTRING && level > 0) {
|
|
||||||
luaL_where(L, level); /* add extra information */
|
|
||||||
lua_pushvalue(L, 1);
|
|
||||||
lua_concat(L, 2);
|
|
||||||
}
|
|
||||||
return lua_error(L);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_getmetatable (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
if (!lua_getmetatable(L, 1)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1; /* no metatable */
|
|
||||||
}
|
|
||||||
luaL_getmetafield(L, 1, "__metatable");
|
|
||||||
return 1; /* returns either __metatable field (if present) or metatable */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_setmetatable (lua_State *L) {
|
|
||||||
int t = lua_type(L, 2);
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
|
|
||||||
if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL))
|
|
||||||
return luaL_error(L, "cannot change a protected metatable");
|
|
||||||
lua_settop(L, 2);
|
|
||||||
lua_setmetatable(L, 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_rawequal (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
luaL_checkany(L, 2);
|
|
||||||
lua_pushboolean(L, lua_rawequal(L, 1, 2));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_rawlen (lua_State *L) {
|
|
||||||
int t = lua_type(L, 1);
|
|
||||||
luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
|
|
||||||
"table or string");
|
|
||||||
lua_pushinteger(L, lua_rawlen(L, 1));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_rawget (lua_State *L) {
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
luaL_checkany(L, 2);
|
|
||||||
lua_settop(L, 2);
|
|
||||||
lua_rawget(L, 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int luaB_rawset (lua_State *L) {
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
luaL_checkany(L, 2);
|
|
||||||
luaL_checkany(L, 3);
|
|
||||||
lua_settop(L, 3);
|
|
||||||
lua_rawset(L, 1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int pushmode (lua_State *L, int oldmode) {
|
|
||||||
if (oldmode == -1)
|
|
||||||
luaL_pushfail(L); /* invalid call to 'lua_gc' */
|
|
||||||
else
|
|
||||||
lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
|
|
||||||
: "generational");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** check whether call to 'lua_gc' was valid (not inside a finalizer)
|
|
||||||
*/
|
|
||||||
#define checkvalres(res) { if (res == -1) break; }
|
|
||||||
|
|
||||||
static int luaB_collectgarbage (lua_State *L) {
|
|
||||||
static const char *const opts[] = {"stop", "restart", "collect",
|
|
||||||
"count", "step", "setpause", "setstepmul",
|
|
||||||
"isrunning", "generational", "incremental", NULL};
|
|
||||||
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
|
|
||||||
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
|
|
||||||
LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC};
|
|
||||||
int o = optsnum[luaL_checkoption(L, 1, "collect", opts)];
|
|
||||||
switch (o) {
|
|
||||||
case LUA_GCCOUNT: {
|
|
||||||
int k = lua_gc(L, o);
|
|
||||||
int b = lua_gc(L, LUA_GCCOUNTB);
|
|
||||||
checkvalres(k);
|
|
||||||
lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case LUA_GCSTEP: {
|
|
||||||
int step = (int)luaL_optinteger(L, 2, 0);
|
|
||||||
int res = lua_gc(L, o, step);
|
|
||||||
checkvalres(res);
|
|
||||||
lua_pushboolean(L, res);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case LUA_GCSETPAUSE:
|
|
||||||
case LUA_GCSETSTEPMUL: {
|
|
||||||
int p = (int)luaL_optinteger(L, 2, 0);
|
|
||||||
int previous = lua_gc(L, o, p);
|
|
||||||
checkvalres(previous);
|
|
||||||
lua_pushinteger(L, previous);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case LUA_GCISRUNNING: {
|
|
||||||
int res = lua_gc(L, o);
|
|
||||||
checkvalres(res);
|
|
||||||
lua_pushboolean(L, res);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
case LUA_GCGEN: {
|
|
||||||
int minormul = (int)luaL_optinteger(L, 2, 0);
|
|
||||||
int majormul = (int)luaL_optinteger(L, 3, 0);
|
|
||||||
return pushmode(L, lua_gc(L, o, minormul, majormul));
|
|
||||||
}
|
|
||||||
case LUA_GCINC: {
|
|
||||||
int pause = (int)luaL_optinteger(L, 2, 0);
|
|
||||||
int stepmul = (int)luaL_optinteger(L, 3, 0);
|
|
||||||
int stepsize = (int)luaL_optinteger(L, 4, 0);
|
|
||||||
return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize));
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
int res = lua_gc(L, o);
|
|
||||||
checkvalres(res);
|
|
||||||
lua_pushinteger(L, res);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
luaL_pushfail(L); /* invalid call (inside a finalizer) */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_type (lua_State *L) {
|
|
||||||
int t = lua_type(L, 1);
|
|
||||||
luaL_argcheck(L, t != LUA_TNONE, 1, "value expected");
|
|
||||||
lua_pushstring(L, lua_typename(L, t));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_next (lua_State *L) {
|
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
|
||||||
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
|
|
||||||
if (lua_next(L, 1))
|
|
||||||
return 2;
|
|
||||||
else {
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int pairscont (lua_State *L, int status, lua_KContext k) {
|
|
||||||
(void)L; (void)status; (void)k; /* unused */
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int luaB_pairs (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
|
|
||||||
lua_pushcfunction(L, luaB_next); /* will return generator, */
|
|
||||||
lua_pushvalue(L, 1); /* state, */
|
|
||||||
lua_pushnil(L); /* and initial value */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
|
|
||||||
lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
|
|
||||||
}
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Traversal function for 'ipairs'
|
|
||||||
*/
|
|
||||||
static int ipairsaux (lua_State *L) {
|
|
||||||
lua_Integer i = luaL_checkinteger(L, 2);
|
|
||||||
i = luaL_intop(+, i, 1);
|
|
||||||
lua_pushinteger(L, i);
|
|
||||||
return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** 'ipairs' function. Returns 'ipairsaux', given "table", 0.
|
|
||||||
** (The given "table" may not be a table.)
|
|
||||||
*/
|
|
||||||
static int luaB_ipairs (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
lua_pushcfunction(L, ipairsaux); /* iteration function */
|
|
||||||
lua_pushvalue(L, 1); /* state */
|
|
||||||
lua_pushinteger(L, 0); /* initial value */
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int load_aux (lua_State *L, int status, int envidx) {
|
|
||||||
if (l_likely(status == LUA_OK)) {
|
|
||||||
if (envidx != 0) { /* 'env' parameter? */
|
|
||||||
lua_pushvalue(L, envidx); /* environment for loaded function */
|
|
||||||
if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
|
|
||||||
lua_pop(L, 1); /* remove 'env' if not used by previous call */
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else { /* error (message is on top of the stack) */
|
|
||||||
luaL_pushfail(L);
|
|
||||||
lua_insert(L, -2); /* put before error message */
|
|
||||||
return 2; /* return fail plus error message */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_loadfile (lua_State *L) {
|
|
||||||
const char *fname = luaL_optstring(L, 1, NULL);
|
|
||||||
const char *mode = luaL_optstring(L, 2, NULL);
|
|
||||||
int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */
|
|
||||||
int status = luaL_loadfilex(L, fname, mode);
|
|
||||||
return load_aux(L, status, env);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** {======================================================
|
|
||||||
** Generic Read function
|
|
||||||
** =======================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** reserved slot, above all arguments, to hold a copy of the returned
|
|
||||||
** string to avoid it being collected while parsed. 'load' has four
|
|
||||||
** optional arguments (chunk, source name, mode, and environment).
|
|
||||||
*/
|
|
||||||
#define RESERVEDSLOT 5
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Reader for generic 'load' function: 'lua_load' uses the
|
|
||||||
** stack for internal stuff, so the reader cannot change the
|
|
||||||
** stack top. Instead, it keeps its resulting string in a
|
|
||||||
** reserved slot inside the stack.
|
|
||||||
*/
|
|
||||||
static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
|
|
||||||
(void)(ud); /* not used */
|
|
||||||
luaL_checkstack(L, 2, "too many nested functions");
|
|
||||||
lua_pushvalue(L, 1); /* get function */
|
|
||||||
lua_call(L, 0, 1); /* call it */
|
|
||||||
if (lua_isnil(L, -1)) {
|
|
||||||
lua_pop(L, 1); /* pop result */
|
|
||||||
*size = 0;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else if (l_unlikely(!lua_isstring(L, -1)))
|
|
||||||
luaL_error(L, "reader function must return a string");
|
|
||||||
lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
|
|
||||||
return lua_tolstring(L, RESERVEDSLOT, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_load (lua_State *L) {
|
|
||||||
int status;
|
|
||||||
size_t l;
|
|
||||||
const char *s = lua_tolstring(L, 1, &l);
|
|
||||||
const char *mode = luaL_optstring(L, 3, "bt");
|
|
||||||
int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */
|
|
||||||
if (s != NULL) { /* loading a string? */
|
|
||||||
const char *chunkname = luaL_optstring(L, 2, s);
|
|
||||||
status = luaL_loadbufferx(L, s, l, chunkname, mode);
|
|
||||||
}
|
|
||||||
else { /* loading from a reader function */
|
|
||||||
const char *chunkname = luaL_optstring(L, 2, "=(load)");
|
|
||||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
|
||||||
lua_settop(L, RESERVEDSLOT); /* create reserved slot */
|
|
||||||
status = lua_load(L, generic_reader, NULL, chunkname, mode);
|
|
||||||
}
|
|
||||||
return load_aux(L, status, env);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* }====================================================== */
|
|
||||||
|
|
||||||
|
|
||||||
static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
|
|
||||||
(void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */
|
|
||||||
return lua_gettop(L) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_dofile (lua_State *L) {
|
|
||||||
const char *fname = luaL_optstring(L, 1, NULL);
|
|
||||||
lua_settop(L, 1);
|
|
||||||
if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
|
|
||||||
return lua_error(L);
|
|
||||||
lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
|
|
||||||
return dofilecont(L, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_assert (lua_State *L) {
|
|
||||||
if (l_likely(lua_toboolean(L, 1))) /* condition is true? */
|
|
||||||
return lua_gettop(L); /* return all arguments */
|
|
||||||
else { /* error */
|
|
||||||
luaL_checkany(L, 1); /* there must be a condition */
|
|
||||||
lua_remove(L, 1); /* remove it */
|
|
||||||
lua_pushliteral(L, "assertion failed!"); /* default message */
|
|
||||||
lua_settop(L, 1); /* leave only message (default if no other one) */
|
|
||||||
return luaB_error(L); /* call 'error' */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_select (lua_State *L) {
|
|
||||||
int n = lua_gettop(L);
|
|
||||||
if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
|
|
||||||
lua_pushinteger(L, n-1);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lua_Integer i = luaL_checkinteger(L, 1);
|
|
||||||
if (i < 0) i = n + i;
|
|
||||||
else if (i > n) i = n;
|
|
||||||
luaL_argcheck(L, 1 <= i, 1, "index out of range");
|
|
||||||
return n - (int)i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Continuation function for 'pcall' and 'xpcall'. Both functions
|
|
||||||
** already pushed a 'true' before doing the call, so in case of success
|
|
||||||
** 'finishpcall' only has to return everything in the stack minus
|
|
||||||
** 'extra' values (where 'extra' is exactly the number of items to be
|
|
||||||
** ignored).
|
|
||||||
*/
|
|
||||||
static int finishpcall (lua_State *L, int status, lua_KContext extra) {
|
|
||||||
if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */
|
|
||||||
lua_pushboolean(L, 0); /* first result (false) */
|
|
||||||
lua_pushvalue(L, -2); /* error message */
|
|
||||||
return 2; /* return false, msg */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return lua_gettop(L) - (int)extra; /* return all results */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_pcall (lua_State *L) {
|
|
||||||
int status;
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
lua_pushboolean(L, 1); /* first result if no errors */
|
|
||||||
lua_insert(L, 1); /* put it in place */
|
|
||||||
status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall);
|
|
||||||
return finishpcall(L, status, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Do a protected call with error handling. After 'lua_rotate', the
|
|
||||||
** stack will have <f, err, true, f, [args...]>; so, the function passes
|
|
||||||
** 2 to 'finishpcall' to skip the 2 first values when returning results.
|
|
||||||
*/
|
|
||||||
static int luaB_xpcall (lua_State *L) {
|
|
||||||
int status;
|
|
||||||
int n = lua_gettop(L);
|
|
||||||
luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */
|
|
||||||
lua_pushboolean(L, 1); /* first result */
|
|
||||||
lua_pushvalue(L, 1); /* function */
|
|
||||||
lua_rotate(L, 3, 2); /* move them below function's arguments */
|
|
||||||
status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall);
|
|
||||||
return finishpcall(L, status, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int luaB_tostring (lua_State *L) {
|
|
||||||
luaL_checkany(L, 1);
|
|
||||||
luaL_tolstring(L, 1, NULL);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const luaL_Reg base_funcs[] = {
|
|
||||||
{"assert", luaB_assert},
|
|
||||||
{"collectgarbage", luaB_collectgarbage},
|
|
||||||
{"dofile", luaB_dofile},
|
|
||||||
{"error", luaB_error},
|
|
||||||
{"getmetatable", luaB_getmetatable},
|
|
||||||
{"ipairs", luaB_ipairs},
|
|
||||||
{"loadfile", luaB_loadfile},
|
|
||||||
{"load", luaB_load},
|
|
||||||
{"next", luaB_next},
|
|
||||||
{"pairs", luaB_pairs},
|
|
||||||
{"pcall", luaB_pcall},
|
|
||||||
{"print", luaB_print},
|
|
||||||
{"warn", luaB_warn},
|
|
||||||
{"rawequal", luaB_rawequal},
|
|
||||||
{"rawlen", luaB_rawlen},
|
|
||||||
{"rawget", luaB_rawget},
|
|
||||||
{"rawset", luaB_rawset},
|
|
||||||
{"select", luaB_select},
|
|
||||||
{"setmetatable", luaB_setmetatable},
|
|
||||||
{"tonumber", luaB_tonumber},
|
|
||||||
{"tostring", luaB_tostring},
|
|
||||||
{"type", luaB_type},
|
|
||||||
{"xpcall", luaB_xpcall},
|
|
||||||
/* placeholders */
|
|
||||||
{LUA_GNAME, NULL},
|
|
||||||
{"_VERSION", NULL},
|
|
||||||
{NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
LUAMOD_API int luaopen_base (lua_State *L) {
|
|
||||||
/* open lib into global table */
|
|
||||||
lua_pushglobaltable(L);
|
|
||||||
luaL_setfuncs(L, base_funcs, 0);
|
|
||||||
/* set global _G */
|
|
||||||
lua_pushvalue(L, -1);
|
|
||||||
lua_setfield(L, -2, LUA_GNAME);
|
|
||||||
/* set global _VERSION */
|
|
||||||
lua_pushliteral(L, LUA_VERSION);
|
|
||||||
lua_setfield(L, -2, "_VERSION");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user