Compare commits

...

149 Commits

Author SHA1 Message Date
minjaesong
b547914865 long overdue JVM tuning 2026-04-27 17:57:00 +09:00
minjaesong
c3c6f70e38 wtf? 2026-04-25 15:50:16 +09:00
minjaesong
8824bad9bf Graal update 2026-04-20 17:34:07 +09:00
minjaesong
9edf3a6573 better splash 2026-04-06 11:11:36 +09:00
minjaesong
f3fb8a96f0 more interesting splash screen wip 2026-04-06 02:05:05 +09:00
minjaesong
281146dd92 title screen fade 2026-04-06 01:40:06 +09:00
minjaesong
8eff89a7cb fix: inputstrober not working 2026-04-04 22:59:30 +09:00
minjaesong
73c7c8942e title screen fade-in transition 2026-04-04 22:33:10 +09:00
minjaesong
6118f30d5f async loading (hopefully?) 2026-04-04 17:05:00 +09:00
minjaesong
981418d0c7 async loading (hopefully) 2026-04-04 01:56:40 +09:00
CuriousTorvald
3cbfb5c10f Update CodeQL workflow to exclude certain languages 2026-03-29 00:22:48 +09:00
minjaesong
1a0b754cde font update, video sprite (TAV) 2026-03-15 13:26:05 +09:00
CuriousTorvald
db3a1f9a39 Modify funding configuration in FUNDING.yml
Updated funding options to include GitHub Sponsors and a PayPal link.
2026-03-13 19:20:45 +09:00
minjaesong
247fd6195f wiki update (asset archiving, building instructions) 2026-02-28 10:53:51 +09:00
minjaesong
97d22913bf custom asset archive loading 2026-02-22 00:09:48 +09:00
minjaesong
2dea82bb9c font update 2026-02-20 11:10:38 +09:00
minjaesong
7c8baa151f asset archiving wip 2026-02-20 10:39:53 +09:00
minjaesong
04b49b8a5c asset archiving wip, font update 2026-02-19 09:57:59 +09:00
minjaesong
661d516800 buildapp update 2026-02-17 05:04:27 +09:00
minjaesong
9efb800d47 alpha blending diagnostic shaders 2026-02-13 21:03:01 +09:00
minjaesong
93c5822c7b finally fixing contradictory key press handling (left+right, up+down) 2026-02-13 19:52:14 +09:00
minjaesong
4d9252dd80 world control hint 2026-02-10 20:17:43 +09:00
minjaesong
232bc0297d copyright year update 2026-02-10 16:00:41 +09:00
minjaesong
612871eb2a fix: moving platform is dragging player into the terrain again 2026-02-09 09:46:35 +09:00
minjaesong
8bbe79acfe pushing down on moving platform will dismount 2026-02-09 03:15:11 +09:00
minjaesong
54715e41b0 fix: sinking platform taking actors with it 2026-02-09 03:06:28 +09:00
minjaesong
e91b7ceb0d player can exit from sinking platform by jumping 2026-02-09 02:54:56 +09:00
minjaesong
855e0ea12a moving platforms can 'steal' actors 2026-02-09 02:34:49 +09:00
minjaesong
5394f17686 moving platform fix: double speed gain 2026-02-09 02:25:52 +09:00
minjaesong
72aae3cffd moving platform kinda working 2026-02-08 23:54:07 +09:00
minjaesong
b5b9e22091 platform wip3 2026-02-08 20:02:39 +09:00
minjaesong
5b1d0ca049 platform surface friction 2026-02-08 03:53:10 +09:00
minjaesong
623ee14d93 platform wip2 2026-02-08 03:43:37 +09:00
minjaesong
b328b609cc watchface: 'Y' is more distinguished 2026-02-08 03:12:49 +09:00
minjaesong
ff9f02322b moving platform wip 2026-02-08 02:06:29 +09:00
minjaesong
4997353f83 wtf 2026-02-07 16:39:33 +09:00
minjaesong
068d0bf1b2 control preset to be stored into separate file 2026-02-07 16:19:42 +09:00
minjaesong
e838991826 new: #if(n)def preprocessor for Terrarum-formatted CSVs 2026-01-19 17:50:44 +09:00
minjaesong
63566a507b new: world update ahchoring 2026-01-19 17:08:31 +09:00
minjaesong
104481a7d5 fix: fixture pickup mess with quickslots and needed empty hands 2026-01-17 20:50:26 +09:00
minjaesong
a4df761359 wire sim incremental graph update (partial) 2026-01-08 23:20:24 +09:00
minjaesong
aa22fe69ff wire sim refactor 2026-01-08 19:06:20 +09:00
minjaesong
e14e689dce fix: bogoflops counter being overestimated on M4 Mac due to Math.random() reliance 2025-11-27 10:41:13 +09:00
minjaesong
767aa09a17 faster block tiling hash 2025-10-13 20:08:22 +09:00
minjaesong
99bd56317d bump AirCompressor to 2.0.2 2025-09-01 17:51:52 +09:00
minjaesong
3f6f599865 1 meter is now 25 pixels 2025-08-08 01:01:45 +09:00
minjaesong
c803ddfae8 why the hell I thought circular buffering would be better, when it makes FOUR memcpy insead of TWO 2025-07-04 06:13:04 +09:00
minjaesong
f11ea557be reverting FFT changes 2025-06-29 19:29:02 +09:00
minjaesong
3f787108b2 more small optimisation of FFT 2025-06-26 19:14:39 +09:00
minjaesong
6bdf585b49 realFFT is somehow faster while everything else stays the same? 2025-06-26 19:08:16 +09:00
minjaesong
fa56fc14bf deploying new wood textures 2025-05-18 19:33:03 +09:00
minjaesong
d922aa5d67 finished wooden texture 48 2025-05-18 15:21:14 +09:00
minjaesong
0ca5e6435c better wooden plank texture wip 2025-05-18 14:54:33 +09:00
minjaesong
3b755f52da testing new wooden plank texture 2025-05-18 13:28:56 +09:00
minjaesong
b167f20935 randomised music generator for test item 2025-05-09 20:02:20 +09:00
minjaesong
41c47a0777 minor readme update 2025-05-09 19:58:04 +09:00
minjaesong
5495e017d5 more canisters 2025-04-29 22:59:26 +09:00
minjaesong
488a214a19 TODO new fluid container system 2025-04-29 21:11:36 +09:00
minjaesong
946824f861 copyright date 2024 to 2025 2025-04-07 22:54:09 +09:00
minjaesong
dabcfe03ed clipboard wip 2025-04-06 22:21:35 +09:00
minjaesong
06a8fd38d1 more computer stuffs 2025-04-01 21:03:09 +09:00
minjaesong
a4b6ad1d5f more minor codes 2025-04-01 20:50:57 +09:00
minjaesong
1c0162ec25 wire ports for computers first draught 2025-03-31 22:21:13 +09:00
minjaesong
b6ee5ee0f7 computer wires 2025-03-30 20:00:02 +09:00
minjaesong
51da0612d2 sprites 2025-03-30 18:07:02 +09:00
minjaesong
0c35faa548 external classloader from modmgr 2025-03-30 17:00:39 +09:00
minjaesong
852c0b7dcc omfg is it actually working now? 2025-03-25 17:50:10 +09:00
minjaesong
759f8a4c4e bottom parts on conveyor now works 2025-03-25 16:58:55 +09:00
minjaesong
506ca7a7b1 curved parts on conveyor now works 2025-03-25 14:15:34 +09:00
minjaesong
42eee2bba7 small steps man :p 2025-03-24 22:02:07 +09:00
minjaesong
d77e1f38dc finally code's working as intended 2025-03-23 22:12:33 +09:00
minjaesong
e5f8e3b76d curved parts are finally appearing where they should 2025-03-23 21:11:52 +09:00
minjaesong
f5744550a4 straight part of the belt now use proper maths 2025-03-23 20:34:52 +09:00
minjaesong
bbd0bc7eb2 some mac-specific codes 2025-03-22 23:40:58 +09:00
minjaesong
7188fddc7e more conveyor aesthetics 2025-03-22 23:29:23 +09:00
minjaesong
5c2d201151 more versatile weather CLUT defs 2025-03-22 19:39:35 +09:00
minjaesong
c54cafae0f more conveyor visuals 2025-03-21 15:04:43 +09:00
minjaesong
3c8f272d4a conveyor wip 2025-03-21 14:30:32 +09:00
minjaesong
bbe28903d8 more conveyor aesthetics 2025-03-17 20:58:34 +09:00
minjaesong
b19b7c7e79 btex.xml 2025-03-17 19:40:45 +09:00
minjaesong
73bf7f8793 more belt graphics 2025-03-16 21:57:04 +09:00
minjaesong
5631b8e807 more belt graphics 2025-03-16 20:17:02 +09:00
minjaesong
dba941d4de rubber coloured belts 2025-03-16 18:16:28 +09:00
minjaesong
27b4137455 belts using their own shaperenderer 2025-03-16 16:36:26 +09:00
minjaesong
9af3f3b883 fixed rendering of belts 2025-03-15 22:08:09 +09:00
minjaesong
b577df0155 partially working conveyor 2025-03-15 21:40:14 +09:00
minjaesong
18feeb1826 wip conveyor drawfun 2025-03-14 20:51:24 +09:00
minjaesong
d598ef497d wippieeee 2025-03-13 21:30:04 +09:00
minjaesong
e652ace37b conveyer belt wip 2025-03-10 21:33:47 +09:00
minjaesong
bdd00676e6 no-auto-pickup variant of droppeditem 2025-03-09 12:56:26 +09:00
minjaesong
19bc779ae1 more token ring stuff 2025-03-08 22:17:36 +09:00
minjaesong
16cac2f1ab fixing minor bugs 2025-03-04 20:56:12 +09:00
minjaesong
a362ade9af some refactoring on FixtureRingBusCore 2025-03-03 22:01:22 +09:00
minjaesong
fea4e34733 ring bus debugger ui wip 2025-03-03 21:27:35 +09:00
minjaesong
f861a2727d now with watchdogs 2025-03-02 20:42:03 +09:00
minjaesong
2bb1c8400e more token ring stuff 2025-03-02 19:47:52 +09:00
minjaesong
ef4a5d6eb9 token ring stuff and renaming things to avoid confusion 2025-03-02 15:04:53 +09:00
minjaesong
f74b7218b0 token ring stuff wip 2025-03-01 23:03:56 +09:00
minjaesong
f154b2348b token ring stuff wip 2025-02-28 22:44:57 +09:00
minjaesong
e889b397d0 token ring stuff wip 2025-02-27 20:58:14 +09:00
minjaesong
65f771e9de replacing N'gasta with random Lorem texts to avoid possible copyright issues 2025-02-24 21:48:17 +09:00
minjaesong
8a26f328c7 more postal codes 2025-02-20 19:41:41 +09:00
minjaesong
37d6d3004b fix: targeted voucher code not working 2025-02-15 23:56:53 +09:00
minjaesong
ad68e04b83 working redeem code generator 2025-02-15 23:25:30 +09:00
minjaesong
4aae0bf733 redeem code gen wip 2025-02-15 20:05:17 +09:00
minjaesong
c2d0803ee3 redeem code gen wip 2025-02-15 16:18:30 +09:00
minjaesong
3d5672bc4d redeem code gen wip 2025-02-13 21:06:13 +09:00
minjaesong
22786f9a28 better wire branching control 2025-02-09 21:56:46 +09:00
minjaesong
34d8f1504f actorvalue blob clone wip 2025-02-09 20:55:42 +09:00
minjaesong
497782b428 integration of blob and actorvalue 2025-02-09 14:58:06 +09:00
minjaesong
fd0732cd39 actorvalue: new format 'blob' 2025-02-08 22:47:01 +09:00
minjaesong
d4e81c8c06 instance info 2025-02-01 21:04:12 +09:00
minjaesong
69ebdbc542 fix: some NaNs that can be caused by scroll bars 2025-02-01 20:51:40 +09:00
minjaesong
a2006b0354 debugging thing update 2025-02-01 19:31:07 +09:00
minjaesong
150b4e6b6b 'contrast strip' for volume control 2025-02-01 19:11:59 +09:00
minjaesong
b3b86b0965 a debug item 2025-02-01 14:53:14 +09:00
minjaesong
ff2a022394 item and sprite for 'document' type items 2025-01-31 22:44:08 +09:00
minjaesong
bb1da3b1ec working ItemFileRef instantiation and serialisation 2025-01-30 11:53:21 +09:00
minjaesong
29034e2104 mediumidentifier to loosely follow MIME format 2025-01-28 23:08:39 +09:00
minjaesong
daa6c09b52 more on itemfileref 2025-01-28 21:34:11 +09:00
minjaesong
879cc79bbf code input wip 2025-01-27 23:23:27 +09:00
minjaesong
6b4b7917d9 mysterious wip 2025-01-22 21:10:21 +09:00
minjaesong
ed9d8cffd6 more codes 2025-01-19 19:54:40 +09:00
minjaesong
3d34b9162b fix: AZERTY layout was missing < key 2025-01-17 17:40:45 +09:00
minjaesong
1b69d74291 minor fixes 2025-01-15 20:39:44 +09:00
minjaesong
15e3276f46 redeem code input wip 2025-01-15 16:07:04 +09:00
minjaesong
51dd99fd80 better post definition 2025-01-15 13:23:28 +09:00
minjaesong
c14720d649 better code font 2025-01-15 10:57:07 +09:00
minjaesong
f4fb7cccfb audio compressor option is now available 2025-01-11 23:46:54 +09:00
minjaesong
c10823ddce postal system wip 2024-12-29 19:49:27 +09:00
minjaesong
c1a5624167 locales for comp 2024-12-26 12:35:20 +09:00
minjaesong
3eee0dcd62 a working compressor this time 2024-12-26 12:28:12 +09:00
minjaesong
cc3e59796f i don't know if it's even working lol 2024-12-25 15:18:07 +09:00
minjaesong
9dc2dffef9 bandpass dsp 2024-12-22 15:56:35 +09:00
minjaesong
fe4318877a shader preloading 2024-12-16 19:36:58 +09:00
minjaesong
3322878074 both outline and a shadow for 'behind' actors 2024-12-05 21:47:02 +09:00
minjaesong
869b751488 prebuild update 2024-11-30 22:49:01 +09:00
minjaesong
450129f650 better shadow for wall-stickers 2024-11-30 20:52:52 +09:00
minjaesong
bf626e35da buttons for crafting/techtree transition 2024-11-26 22:29:48 +09:00
minjaesong
e581099aec minor numbers changes 2024-11-26 08:15:55 +09:00
minjaesong
95af4e8f53 tech tree view wip 2024-11-25 23:50:28 +09:00
minjaesong
661c4cdc4d more prominent shadow around wall-sticking tiles 2024-11-24 22:41:44 +09:00
minjaesong
a24eab209b shallow shadow improvements 2024-11-24 19:13:12 +09:00
minjaesong
6ec5ba5603 no shadows against skybox 2024-11-24 18:52:44 +09:00
minjaesong
8cf4b5d9a9 shadows around actors as well as terrain 2024-11-24 17:02:00 +09:00
minjaesong
5bf60cfa82 now terrain also lives on its own framebuffer 2024-11-24 13:19:20 +09:00
minjaesong
5cc7db8ecc putting some actors into their own framebuffer for shadowing 2024-11-24 13:10:08 +09:00
minjaesong
89b12aabb4 new debug cmd ExportFBO 2024-11-24 12:53:44 +09:00
minjaesong
f5846d9bae screen blur helper class 2024-11-23 21:17:16 +09:00
539 changed files with 78930 additions and 1817 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: [curioustorvald]
custom: ["https://paypal.me/curioustorvald"]

99
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,99 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Advanced"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: '41 4 * * 0'
jobs:
analyze:
name: Analyze (${{ matrix.language }})
# Runner size impacts CodeQL analysis time. To learn more, please see:
# - https://gh.io/recommended-hardware-resources-for-running-codeql
# - https://gh.io/supported-runners-and-hardware-resources
# - https://gh.io/using-larger-runners (GitHub.com only)
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
permissions:
# required for all workflows
security-events: write
# required to fetch internal or private CodeQL packs
packages: read
# only required for workflows in private repositories
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
- language: java-kotlin
build-mode: none
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
# Use `c-cpp` to analyze code written in C, C++ or both
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Add any setup steps before running the `github/codeql-action/init` action.
# This includes steps like installing compilers or runtimes (`actions/setup-node`
# or others). This is typically only required for manual builds.
# - name: Setup runtime (example)
# uses: actions/setup-example@v1
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# If the analyze step fails for one of the languages you are analyzing with
# "We were unable to automatically build your code", modify the matrix above
# to set the build mode to "manual" for that language. Then modify this step
# to build your code.
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
- name: Run manual build steps
if: matrix.build-mode == 'manual'
shell: bash
run: |
echo 'If you are using a "manual" build mode for one or more of the' \
'languages you are analyzing, replace this with the commands to build' \
'your code, for example:'
echo ' make bootstrap'
echo ' make release'
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{matrix.language}}"

View File

@@ -25,12 +25,20 @@
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk8.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-stdlib-jdk7.jar" path-in-jar="/" />
<element id="extracted-dir" path="$KOTLIN_BUNDLED$/lib/kotlin-reflect.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/graal-sdk-22.3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/icu4j-71.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-22.3.1-edit.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-scriptengine-22.3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/regex-22.3.1-edit.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/truffle-api-22.3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/polyglot-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-language-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-scriptengine-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/regex-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/truffle-api-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/truffle-runtime-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/truffle-compiler-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/compiler-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/compiler-management-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/icu4j-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/collections-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/word-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/nativeimage-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jniutils-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-1.12.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-backend-lwjgl3-1.12.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/lwjgl-3.3.3.jar" path-in-jar="/" />

View File

@@ -18,16 +18,23 @@
<element id="extracted-dir" path="$PROJECT_DIR$/lib/Terrarum_Joise.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/TerrarumSansBitmap.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-jnigen-loader-2.3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-scriptengine-22.3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/truffle-api-22.3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/icu4j-71.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/regex-22.3.1-edit.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-22.3.1-edit.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/graal-sdk-22.3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/polyglot-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-language-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-scriptengine-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/regex-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/truffle-api-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/truffle-runtime-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/truffle-compiler-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/compiler-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/compiler-management-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/icu4j-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/collections-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/word-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/nativeimage-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/jniutils-23.1.10.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/commons-math3-3.6.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/JTransforms-3.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/JLargeArrays-1.5.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/aircompressor-0.25.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-1.12.1.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/gdx-backend-lwjgl3-1.12.1.jar" path-in-jar="/" />
@@ -87,6 +94,7 @@
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.0.0/kotlin-stdlib-2.0.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.0.0/kotlin-stdlib-jdk7-2.0.0.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/GetBatteryStatus.jar" path-in-jar="/" />
<element id="extracted-dir" path="$PROJECT_DIR$/lib/aircompressor-2.0.2.jar" path-in-jar="/" />
</root>
</artifact>
</component>

View File

@@ -1,5 +1,6 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="999" />
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>

View File

@@ -1,28 +1,52 @@
<component name="libraryTable">
<library name="graalvm-js 22.3.1">
<library name="graalvm-js 23.1.10">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/graal-sdk-22.3.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/icu4j-71.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-22.3.1-edit.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-scriptengine-22.3.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/regex-22.3.1-edit.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-api-22.3.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/polyglot-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/icu4j-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-language-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-scriptengine-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/regex-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-api-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-runtime-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-compiler-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/compiler-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/compiler-management-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/collections-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/word-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/nativeimage-23.1.10.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/jniutils-23.1.10.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$PROJECT_DIR$/lib/graal-sdk-22.3.1-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/icu4j-71.1-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-22.3.1-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-scriptengine-22.3.1-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/regex-22.3.1-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-api-22.3.1-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/polyglot-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/icu4j-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-language-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-scriptengine-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/regex-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-api-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-runtime-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-compiler-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/compiler-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/compiler-management-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/collections-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/word-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/nativeimage-23.1.10-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/jniutils-23.1.10-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$PROJECT_DIR$/lib/graal-sdk-22.3.1-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/icu4j-71.1-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-22.3.1-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-scriptengine-22.3.1-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/regex-22.3.1-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-api-22.3.1-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/polyglot-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/icu4j-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-language-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/js-scriptengine-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/regex-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-api-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-runtime-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/truffle-compiler-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/compiler-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/compiler-management-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/collections-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/word-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/nativeimage-23.1.10-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/jniutils-23.1.10-sources.jar!/" />
</SOURCES>
</library>
</component>
</component>

View File

@@ -1,14 +1,14 @@
<component name="libraryTable">
<library name="io.airlift.aircompressor" type="repository">
<properties maven-id="io.airlift:aircompressor:0.25" />
<properties maven-id="io.airlift:aircompressor:2.0.2" />
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/aircompressor-0.25.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/aircompressor-2.0.2.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$PROJECT_DIR$/lib/aircompressor-0.25-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/aircompressor-2.0.2-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$PROJECT_DIR$/lib/aircompressor-0.25-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/aircompressor-2.0.2-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="SpriteAssemblerApp" type="JarApplication">
<option name="JAR_PATH" value="$PROJECT_DIR$/out/SpriteAssemblerApp.jar" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="VM_PARAMETERS" value="-ea --upgrade-module-path=lib/compiler-23.1.10.jar:lib/compiler-management-23.1.10.jar:lib/truffle-compiler-23.1.10.jar:lib/truffle-api-23.1.10.jar:lib/truffle-runtime-23.1.10.jar:lib/polyglot-23.1.10.jar:lib/collections-23.1.10.jar:lib/word-23.1.10.jar:lib/nativeimage-23.1.10.jar:lib/jniutils-23.1.10.jar -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --add-exports=java.base/jdk.internal.misc=jdk.internal.vm.compiler" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH" value="17" />
<module name="TerrarumBuild" />

View File

@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Terrarum" type="JarApplication">
<option name="JAR_PATH" value="$PROJECT_DIR$/out/TerrarumBuild.jar" />
<option name="VM_PARAMETERS" value="-ea -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd" />
<option name="VM_PARAMETERS" value="-ea --upgrade-module-path=lib/compiler-23.1.10.jar:lib/compiler-management-23.1.10.jar:lib/truffle-compiler-23.1.10.jar:lib/truffle-api-23.1.10.jar:lib/truffle-runtime-23.1.10.jar:lib/polyglot-23.1.10.jar:lib/collections-23.1.10.jar:lib/word-23.1.10.jar:lib/nativeimage-23.1.10.jar:lib/jniutils-23.1.10.jar -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --add-exports=java.base/jdk.internal.misc=jdk.internal.vm.compiler -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH" value="17" />
<module name="TerrarumBuild" />

View File

@@ -1,7 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Terrarum (no DEV MODE)" type="JarApplication">
<option name="JAR_PATH" value="$PROJECT_DIR$/out/TerrarumBuild.jar" />
<option name="VM_PARAMETERS" value="-Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd" />
<option name="VM_PARAMETERS" value="--upgrade-module-path=lib/compiler-23.1.10.jar:lib/compiler-management-23.1.10.jar:lib/truffle-compiler-23.1.10.jar:lib/truffle-api-23.1.10.jar:lib/truffle-runtime-23.1.10.jar:lib/polyglot-23.1.10.jar:lib/collections-23.1.10.jar:lib/word-23.1.10.jar:lib/nativeimage-23.1.10.jar:lib/jniutils-23.1.10.jar -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --add-exports=java.base/jdk.internal.misc=jdk.internal.vm.compiler -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH" value="17" />
<module name="TerrarumBuild" />

View File

@@ -0,0 +1,16 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Terrarum (no prebuild, release-mode assets)" type="JarApplication">
<option name="JAR_PATH" value="$PROJECT_DIR$/out/TerrarumBuild.jar" />
<option name="VM_PARAMETERS" value="-ea --upgrade-module-path=lib/compiler-23.1.10.jar:lib/compiler-management-23.1.10.jar:lib/truffle-compiler-23.1.10.jar:lib/truffle-api-23.1.10.jar:lib/truffle-runtime-23.1.10.jar:lib/polyglot-23.1.10.jar:lib/collections-23.1.10.jar:lib/word-23.1.10.jar:lib/nativeimage-23.1.10.jar:lib/jniutils-23.1.10.jar -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --add-exports=java.base/jdk.internal.misc=jdk.internal.vm.compiler -Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcd" />
<option name="PROGRAM_PARAMETERS" value="--assets buildapp/out/assets.tevd" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="ALTERNATIVE_JRE_PATH" value="17" />
<module name="TerrarumBuild" />
<method v="2">
<option name="BuildArtifacts" enabled="true">
<artifact name="TerrarumBuild" />
</option>
<option name="RunConfigurationTask" enabled="false" run_configuration_name="QuickDirtyLint" run_configuration_type="Application" />
</method>
</configuration>
</component>

1
.idea/vcs.xml generated
View File

@@ -2,5 +2,6 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/Terrarum.wiki" vcs="Git" />
</component>
</project>

176
CLAUDE.md Normal file
View File

@@ -0,0 +1,176 @@
# Terrarum
A modular 2D side-scrolling tilemap platformer engine and game, built on LibGDX with Kotlin/Java. GPL-3.0.
## Build & Run
- **IDE**: IntelliJ IDEA (project files: `Terrarum_renewed.iml`, `TerrarumBuild.iml`)
- **JDK**: 21
- **Language**: Kotlin + Java mixed; Kotlin is primary, `App.java` and `FrameBufferManager.java` are Java
- **Dependencies**: All libraries are in `lib/` (fat-jar approach, no Maven/Gradle). Includes LibGDX, GraalVM JS (modified), dyn4j (Vector2 only), custom bitmap font lib
- **Entry point**: `net.torvald.terrarum.Principii.main()` (Java) which launches `App` (the LibGDX `ApplicationListener`)
- **Distribution**: `buildapp/` scripts produce per-platform bundles with jlink'd runtimes
- **Assets**: `assets/` for development, `assets_release/` for distribution. Custom `.tevd` archive format for release assets (`AssetCache.kt`)
## Project Structure
```
src/net/torvald/terrarum/
App.java -- Main application class (LibGDX ApplicationListener). GL thread, render loop, splash screen
CommonResourcePool.kt -- Thread-safe GL resource loading with dispatch queues
ModMgr.kt -- Module manager. Scans/loads game modules, provides getGdxFile/getJavaClass
IngameInstance.kt -- Base class for game screens (show/hide/render/resize lifecycle)
Terrarum.kt -- Game-level singleton (ingame instance management, extension functions)
GameUpdateGovernor.kt -- Update/render tick governors (ConsistentUpdateRate, Anarchy)
MusicService.kt -- Music playback management
modulebasegame/
EntryPoint.kt -- Basegame module entry point (registers items, fixtures, blocks, weather)
TitleScreen.kt -- Title screen (demo world rendering, async loading)
TerrarumIngame.kt -- Main gameplay screen
IngameRenderer.kt -- World rendering pipeline (FBO composition, lightmap, blur, shadows)
BuildingMaker.kt -- Building editor screen
worlddrawer/
WorldCamera.kt -- Camera position tracking (follows player, interpolated movement)
LightmapRenderer.kt -- Per-tile RGB+UV light calculation and rasterisation
BlocksDrawer.kt -- Tile rendering
FeaturesDrawer.kt -- Wire/feature overlay rendering
gamecontroller/
IME.kt -- Input Method Engine (GraalVM JS keyboard layouts, loaded on daemon thread)
weather/
WeatherMixer.kt -- Weather state machine, skybox rendering
SkyboxModelHosek.kt -- Physically-based sky model
```
## Architecture: Threading & GL Dispatch
All OpenGL calls (Texture, ShaderProgram, FrameBuffer creation) **must** happen on the GL thread. The codebase uses a dispatch mechanism to safely create GL resources from background threads.
### CommonResourcePool (the dispatch hub)
```
Background Thread GL Thread (App.render)
| |
|-- addToLoadingList("name", { Texture() })|
|-- loadAll() |
| | |
| +-- if on GL thread: run directly |
| +-- if not: enqueue to |
| glDispatchQueue + latch.await() |
| CommonResourcePool.update()
| |-- poll glDispatchQueue
| | run loadfun, latch.countDown()
| |-- poll glRunnableQueue
| | run block, latch.countDown()
| |-- poll slowLoadingQueue (one per tick)
| |
+-- latch released, continues <------------+
```
Key methods:
- `loadAll()` -- batch load; blocks background thread until GL thread processes
- `loadAllSlowly()` -- timesliced; one resource per tick via `update()`
- `runOnGLThread { }` -- run arbitrary block on GL thread, blocking caller
- `update()` -- called every tick from `App.render()`, processes all queues
- `getOrPut(name, loadfun, killfun)` -- thread-safe get-or-create with GL dispatch
### App.java Boot Sequence
```
create()
-> postInit() [GL thread, synchronous]
|-- load shaders, fonts, audio device, IME
|-- CommonResourcePool.setGLThread(currentThread)
|-- spawn Terrarum-PostInitLoader thread:
| ModMgr.invoke() // module entry points (dispatched to GL via runOnGLThread)
| CommonResourcePool.loadAllSlowly() // timesliced resource loading
| loadingThreadDone = true
+-- return (non-blocking)
render() loop [GL thread]
while currentScreen == null:
|-- drawSplash()
|-- CommonResourcePool.update() // process GL dispatch queues
|-- when loadingThreadDone && loaded:
| postLoadInit() // tile atlas, audio mixer, Terrarum.initialise()
| setScreen(titleScreen) // transitions to title screen
```
### TitleScreen Async Loading
`TitleScreen.show()` returns immediately after starting a background thread. The splash screen continues displaying until all loading completes.
```
show()
|-- quick GL setup (viewport, input processor, FBO, savegame list)
|-- spawn Terrarum-TitleScreenLoader thread:
| load demo world, compute camera nodes, bogoflops, audio reset
| backgroundLoadDone = true
+-- return
renderImpl() [GL thread, every frame]
if !loadDone:
|-- App.drawSplash()
|-- processGLLoadStep() // state machine, one step per frame:
| 0: wait for backgroundLoadDone
| 1: SkyboxModelHosek.loadlut()
| 2: IngameRenderer.setRenderedWorld + WeatherMixer init
| 3: load halfgrad texture
| 4: UIFakeGradOverlay
| 5: UIFakeBlurOverlay
| 6: UIRemoCon
| 7: MusicService.enterScene, gameUpdateGovernor.reset(), loadDone=true
else:
normal title screen rendering (world + UI via IngameRenderer)
```
### IME Loading
`IME.kt` object `init {}` spawns a `Terrarum-IMELoader` daemon thread for GraalVM JS context binding and key layout/IME file scanning. Icon texture loading remains synchronous (requires GL thread).
### ModMgr Class Initialisation
`ModMgr.kt` object `init {}` only does metadata loading and class instantiation (fast). The heavy `invoke()` method runs entry points wrapped in `CommonResourcePool.runOnGLThread { }` to avoid GL calls on the wrong thread. This separation prevents `<clinit>` lock deadlocks where a background thread holding the class init lock blocks on a GL dispatch latch while the GL thread tries to access the same class.
## Architecture: Rendering Pipeline
### IngameRenderer
Singleton object. Lazily initialised on first `invoke()` call via `invokeInit()`.
Render path (per frame):
1. `LightmapRenderer.recalculate()` -- every 3 frames, computes per-tile RGBA light values
2. `prepLightmapRGBA()` -- Kawase blur on lightmap into `lightmapFbo`
3. `BlocksDrawer.renderData()` -- prepare tile draw data
4. `drawToRGB()` -- render world tiles + actors into `fboRGB` (with shadow FBOs)
5. `drawToA()` -- render alpha/glow channel
6. Composite: sky -> world -> light multiply -> emissive blend -> vibrancy -> UI
7. Output to `App.renderFBO`, then `TerrarumPostProcessor.draw()` applies final effects
**Critical ordering in `resize()`**: `BlocksDrawer.resize()` and `LightmapRenderer.resize()` must be called **before** `lightmapFbo` creation, because `lightmapFbo` dimensions derive from `LightmapRenderer.lightBuffer` size.
### WorldCamera
Follows an `ActorWithBody` (player or camera actor). Position interpolated per frame. `WorldCamera.x/y` = top-left corner of visible area. `gdxCamX/Y` = centre of visible area. `moveCameraToWorldCoord()` positions the IngameRenderer camera for world-space drawing; `setCameraPosition(0,0)` positions it for screen-space drawing.
### FBO Extension Functions
- `FrameBuffer.inAction(camera, batch) { }` -- bind FBO, set camera to FBO dimensions (Y-down), run block, restore camera to screen dimensions
- `FrameBuffer.inActionF(camera, batch) { }` -- same but Y-up (for flipped FBO content)
Both save/restore `camera.position` and call `setToOrtho` with `App.scr.wf/hf` on exit.
## Key Conventions
- **GL thread safety**: Any code creating `Texture`, `TextureRegion`, `TextureRegionPack`, `ShaderProgram`, `FrameBuffer`, or `Pixmap` must run on the GL thread. Use `CommonResourcePool.runOnGLThread { }` when on a background thread.
- **Kotlin `object` singletons**: First access triggers `<clinit>`. Keep `init {}` blocks fast (no blocking, no GL dispatch). Defer heavy work to explicit `invoke()` methods.
- **`ConcurrentHashMap` cannot store null values**. Use `HashMap` for maps that need nullable values (e.g. `poolKillFun`).
- **`@Volatile`** for cross-thread boolean flags (`loadDone`, `backgroundLoadDone`, etc.)
- **`lateinit var` guards**: Use `::prop.isInitialized` checks in `resize()` and `dispose()` for properties set during async loading steps.
- **`ConsistentUpdateRate`**: Accumulates delta time; may call `updateFunction` multiple times before `renderFunction`. Call `.reset()` before transitioning to active rendering to avoid catch-up storms.
- **Textures**: Use TGA format. Images with semitransparency must be TGA; opaque images may be PNG.
- **Coordinate system**: Y-down (camera `setToOrtho(true, ...)`). `setCameraPosition(0, 0)` places origin at screen top-left, which is not LibGDX nor OpenGL works natively, hence the existence of `FlippingSpriteBatch`. If the user reports renders are flipped vertically, try draw()/drawFlipped() accordingly.
- **ROUNDWORLD**: World wraps horizontally. `WorldCamera.x` uses `fmod worldWidth`. Lightmap and tile drawing account for wrapping.

View File

@@ -9,7 +9,7 @@
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="ModuleComputersLib" level="project" />
<orderEntry type="module" module-name="TerrarumBuild" scope="PROVIDED" />
<orderEntry type="library" scope="PROVIDED" name="graalvm-js 22.3.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="graalvm-js 23.1.10" level="project" />
<orderEntry type="library" scope="PROVIDED" name="TerrarumSansBitmap" level="project" />
<orderEntry type="library" scope="PROVIDED" name="badlogicgames.gdx" level="project" />
<orderEntry type="library" scope="PROVIDED" name="badlogicgames.gdx.backend.lwjgl3" level="project" />

View File

@@ -1,5 +1,7 @@
package net.torvald.terrarum.modulecomputers
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.ItemSheet
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.ModuleEntryPoint
@@ -11,7 +13,17 @@ class EntryPoint : ModuleEntryPoint() {
private val moduleName = "dwarventech"
override fun invoke() {
// load common resources to the AssetsManager
CommonResourcePool.addToLoadingList("$moduleName.items") {
ItemSheet(ModMgr.getGdxFile(moduleName, "items/items.tga"))
}
CommonResourcePool.loadAll()
ModMgr.GameItemLoader.invoke(moduleName)
ModMgr.GameBlockLoader.invoke(moduleName)
ModMgr.GameWatchdogLoader.register(moduleName, NetFrameWatchdog())
println("[${moduleName[0].toUpperCase()}${moduleName.substring(1)}] Dirtboard(tm) go drrrrr")
}

View File

@@ -0,0 +1,17 @@
package net.torvald.terrarum.modulecomputers
import net.torvald.terrarum.App
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.TerrarumWorldWatchdog
import net.torvald.terrarum.modulebasegame.gameworld.NetRunner
/**
* Created by minjaesong on 2025-03-02.
*/
class NetFrameWatchdog : TerrarumWorldWatchdog(App.TICK_SPEED * 60) {
override fun invoke(world: GameWorld) {
(world.extraFields["tokenring"] as? NetRunner)?.let {
it.purgeDeadFrames()
}
}
}

View File

@@ -0,0 +1,135 @@
package net.torvald.terrarum.modulecomputers.gameactors
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameactors.BlockBox
import net.torvald.terrarum.modulebasegame.gameactors.Electric
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2025-03-30.
*/
class FixtureComputerConsole : Electric {
@Transient override val spawnNeedsStableFloor = true
@Transient override val spawnNeedsWall = false
constructor() : super(
BlockBox(BlockBox.ALLOW_MOVE_DOWN, 2, 2),
nameFun = { Lang["ITEM_COMPUTER_CONSOLE"] }
)
init {
val itemImage = FixtureItemBase.getItemImageFromSingleImage("dwarventech", "sprites/fixtures/computer_operator_terminal.tga")
makeNewSprite(TextureRegionPack(itemImage.texture, 2*TILE_SIZE, 2*TILE_SIZE)).let {
it.setRowsAndFrames(1,1)
}
setWireSinkAt(0, 1, "io_bus")
setWireSinkAt(1, 1, "power_low")
}
}
/**
* Created by minjaesong on 2025-03-30.
*/
class FixtureComputerProcessor : Electric {
@Transient override val spawnNeedsStableFloor = true
@Transient override val spawnNeedsWall = false
constructor() : super(
BlockBox(BlockBox.ALLOW_MOVE_DOWN, 2, 3),
nameFun = { Lang["ITEM_COMPUTER_PROCESSOR"] }
)
init {
val itemImage = FixtureItemBase.getItemImageFromSingleImage("dwarventech", "sprites/fixtures/computer_cpu.tga")
makeNewSprite(TextureRegionPack(itemImage.texture, 2*TILE_SIZE, 3*TILE_SIZE)).let {
it.setRowsAndFrames(1,1)
}
setWireSinkAt(0, 0, "memory_bus")
setWireSinkAt(0, 1, "serial")
setWireSinkAt(1, 1, "serial")
setWireSinkAt(0, 2, "io_bus")
setWireSinkAt(1, 2, "power_low")
}
}
/**
* Created by minjaesong on 2025-04-01.
*/
class FixtureNetworkInterface : Electric {
@Transient override val spawnNeedsStableFloor = true
@Transient override val spawnNeedsWall = false
constructor() : super(
BlockBox(BlockBox.ALLOW_MOVE_DOWN, 2, 3),
nameFun = { Lang["ITEM_NETWORK_INTERFACE"] }
)
init {
val itemImage = FixtureItemBase.getItemImageFromSingleImage("dwarventech", "sprites/fixtures/computer_network_interface.tga")
makeNewSprite(TextureRegionPack(itemImage.texture, 2*TILE_SIZE, 3*TILE_SIZE)).let {
it.setRowsAndFrames(1,1)
}
setWireSinkAt(0, 0, "token_ring")
setWireSinkAt(1, 0, "token_ring")
setWireSinkAt(0, 2, "serial")
setWireSinkAt(1, 2, "power_low")
}
}
/**
* Created by minjaesong on 2025-04-01.
*/
class FixtureNetworkBridge : Electric {
@Transient override val spawnNeedsStableFloor = true
@Transient override val spawnNeedsWall = false
constructor() : super(
BlockBox(BlockBox.ALLOW_MOVE_DOWN, 2, 3),
nameFun = { Lang["ITEM_NETWORK_BRIDGE"] }
)
init {
val itemImage = FixtureItemBase.getItemImageFromSingleImage("dwarventech", "sprites/fixtures/computer_network_bridge.tga")
makeNewSprite(TextureRegionPack(itemImage.texture, 2*TILE_SIZE, 3*TILE_SIZE)).let {
it.setRowsAndFrames(1,1)
}
setWireSinkAt(0, 0, "token_ring")
setWireSinkAt(1, 0, "token_ring")
setWireSinkAt(0, 1, "token_ring")
setWireSinkAt(1, 1, "token_ring")
setWireSinkAt(1, 2, "power_low")
}
}
/**
* Created by minjaesong on 2025-04-01.
*/
class FixtureMemoryCabinet : Electric {
@Transient override val spawnNeedsStableFloor = true
@Transient override val spawnNeedsWall = false
constructor() : super(
BlockBox(BlockBox.ALLOW_MOVE_DOWN, 2, 3),
nameFun = { Lang["ITEM_MEMORY_CABINET"] }
)
init {
val itemImage = FixtureItemBase.getItemImageFromSingleImage("dwarventech", "sprites/fixtures/computer_memory_stack_1.tga")
makeNewSprite(TextureRegionPack(itemImage.texture, 1*TILE_SIZE, 3*TILE_SIZE)).let {
it.setRowsAndFrames(1,1)
}
setWireSinkAt(0, 0, "memory_bus")
}
}

View File

@@ -0,0 +1,289 @@
package net.torvald.terrarum.modulecomputers.gameactors
import com.badlogic.gdx.utils.Queue
import net.torvald.random.HQRNG
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.Point2i
import net.torvald.terrarum.modulebasegame.gameactors.BlockBox
import net.torvald.terrarum.modulebasegame.gameactors.Electric
import net.torvald.terrarum.modulebasegame.gameworld.NetFrame
import net.torvald.terrarum.modulebasegame.gameworld.NetRunner
import net.torvald.terrarum.ui.UICanvas
import org.dyn4j.geometry.Vector2
import kotlin.math.sign
/**
* Created by minjaesong on 2025-03-01.
*/
open class FixtureRingBusCore : Electric {
@Transient private val rng = HQRNG()
val mac = rng.nextInt()
companion object {
const val INITIAL_LISTENING_TIMEOUT = 300
const val SIGNAL_TOO_WEAK_THRESHOLD = 1.0 / 16.0
}
private constructor() : super()
private var portEmit = Point2i(0, 0)
private var portSink = Point2i(1, 0)
constructor(portEmit: Point2i, portSink: Point2i, blockBox: BlockBox, nameFun: () -> String) : super(
blockBox0 = blockBox,
nameFun = nameFun,
) {
this.portEmit = portEmit
this.portSink = portSink
}
private fun setEmitterAndSink() {
clearStatus()
setWireEmitterAt(portEmit.x, portEmit.y, "10base2")
setWireSinkAt(portSink.x, portSink.y, "10base2")
}
init {
setEmitterAndSink()
if (!INGAME.world.extraFields.containsKey("tokenring")) {
INGAME.world.extraFields["tokenring"] = NetRunner()
}
}
override fun reload() {
super.reload()
setEmitterAndSink()
if (!INGAME.world.extraFields.containsKey("tokenring")) {
INGAME.world.extraFields["tokenring"] = NetRunner()
}
}
internal val msgQueue = Queue<Pair<Int, ByteArray>>()
internal val msgLog = Queue<Pair<Int, NetFrame>>()
private var statusAbort = false
private var activeMonitorStatus = 0 // 0: unknown, 1: known and not me, 2: known and it's me
private var lastAccessTime = -1L
open protected val ringBusFirmware = RingBusFirmware(0)
private enum class RingBusState {
NORMAL,
ABORT, // will "eat away" any receiving frames unless the frame is a ballot frame
ELECTING,
IVE_GOT_ELECTED
}
private var currentState = RingBusState.NORMAL
override fun updateSignal() {
val time_t = INGAME.world.worldTime.TIME_T
if (lastAccessTime == -1L) lastAccessTime = time_t
// monitor the input port
val inn = getWireStateAt(1, 0, "10base2")
// if a signal is there
if (inn.x >= SIGNAL_TOO_WEAK_THRESHOLD) {
lastAccessTime = time_t
val frameNumber = (inn.y + (0.5 * inn.y.sign)).toInt()
if (frameNumber != 0) { // frame number must be non-zero
processFrameBasedOnState(frameNumber)
} else {
setWireEmissionAt(portEmit.x, portEmit.y, Vector2())
}
} else {
handleNoSignal(time_t)
}
}
private fun processFrameBasedOnState(frameNumber: Int) {
try {
val frame = getFrameByNumber(frameNumber)
when (currentState) {
RingBusState.NORMAL -> handleNormalState(frame, frameNumber)
RingBusState.ABORT -> handleAbortState(frame, frameNumber)
RingBusState.ELECTING -> handleElectingState(frame, frameNumber)
RingBusState.IVE_GOT_ELECTED -> handleIveGotElectedState(frame, frameNumber)
}
} catch (e: NullPointerException) {
handleFrameLoss()
}
}
private fun handleNormalState(frame: NetFrame, frameNumber: Int) {
if (msgQueue.notEmpty() || frame.shouldIintercept(mac)) {
val newFrame = doSomethingWithFrame(frame) ?: frameNumber
setWireEmissionAt(portEmit.x, portEmit.y, Vector2(1.0, newFrame.toDouble()))
if (newFrame != frameNumber) {
frame.discardFrame()
}
} else {
setWireEmissionAt(portEmit.x, portEmit.y, Vector2(1.0, frameNumber.toDouble()))
}
}
private fun handleAbortState(frame: NetFrame, frameNumber: Int) {
if (frame.getFrameType() == "token") {
currentState = RingBusState.NORMAL
setWireEmissionAt(portEmit.x, portEmit.y, Vector2(1.0, frameNumber.toDouble()))
} else if (frame.getFrameType() == "abort") {
setWireEmissionAt(portEmit.x, portEmit.y, Vector2(1.0, frameNumber.toDouble()))
} else {
setWireEmissionAt(portEmit.x, portEmit.y, Vector2())
}
}
private fun handleElectingState(frame: NetFrame, frameNumber: Int) {
if (frame.getFrameType() == "ballot") {
if (frame.getBallot() < mac) {
frame.setBallot(mac)
}
if (frame.getSender() == mac && frame.getBallot() == mac) {
currentState = RingBusState.IVE_GOT_ELECTED
// elected Active Monitor sends out the first token
val newFrame = emitNewFrame(NetFrame.makeToken(mac))
setWireEmissionAt(portEmit.x, portEmit.y, Vector2(1.0, newFrame.toDouble()))
} else {
setWireEmissionAt(portEmit.x, portEmit.y, Vector2(1.0, frameNumber.toDouble()))
}
} else {
setWireEmissionAt(portEmit.x, portEmit.y, Vector2(1.0, frameNumber.toDouble()))
}
}
private fun handleIveGotElectedState(frame: NetFrame, frameNumber: Int) {
if (frame.getFrameType() == "abort") {
// immediately return to normal state
val newFrame = emitNewFrame(NetFrame.makeToken(mac))
currentState = RingBusState.NORMAL
setWireEmissionAt(portEmit.x, portEmit.y, Vector2(1.0, newFrame.toDouble()))
} else {
// make sure to skip a turn after the election, even if the NIC has something to send out
currentState = RingBusState.NORMAL
setWireEmissionAt(portEmit.x, portEmit.y, Vector2())
}
}
private fun handleFrameLoss() {
emitNewFrame(NetFrame.makeAbort(mac))
currentState = RingBusState.ABORT
}
private fun handleNoSignal(time_t: Long) {
setWireEmissionAt(portEmit.x, portEmit.y, Vector2())
if (time_t - lastAccessTime > INITIAL_LISTENING_TIMEOUT) {
currentState = RingBusState.ELECTING
emitNewFrame(NetFrame.makeBallot(mac))
lastAccessTime = time_t
}
}
protected fun doSomethingWithFrame(incomingFrame: NetFrame): Int? {
return when (incomingFrame.getFrameType()) {
"token" -> doSomethingWithToken(incomingFrame)
"data" -> doSomethingWithData(incomingFrame)
"ack" -> doSomethingWithAck(incomingFrame)
"ballot" -> doSomethingWithBallot(incomingFrame)
"abort" -> 0
else -> null /* returns the frame untouched */
}
}
private fun getFrameByNumber(number: Int) = (INGAME.world.extraFields["tokenring"] as NetRunner)[number]
private fun emitNewFrame(frame: NetFrame): Int {
return (INGAME.world.extraFields["tokenring"] as NetRunner).addFrame(frame).also {
setWireEmissionAt(portEmit.x, portEmit.y, Vector2(1.0, it.toDouble()))
}
}
protected fun doSomethingWithToken(incomingFrame: NetFrame): Int? {
if (msgQueue.isEmpty) return null
val (recipient, msgByte) = msgQueue.removeFirst()
return emitNewFrame(NetFrame.makeData(mac, recipient, msgByte))
}
protected fun doSomethingWithData(incomingFrame: NetFrame): Int? {
val rec = incomingFrame.getDataRecipient()
// if the message is for me, put incoming message into queue, then send out ack
if (rec == mac) {
msgLog.addLast(rec to incomingFrame)
val datagramme = incomingFrame.getDataContents()
val (ret, nextMsg) = if (datagramme == null) (-1 to null) else ringBusFirmware.workWithDataFrame(mac, datagramme)
// make ack
return emitNewFrame(NetFrame.makeAck(mac, incomingFrame.getSender(), ret))
}
else return null
}
protected fun doSomethingWithAck(incomingFrame: NetFrame): Int? {
if (msgQueue.isEmpty) return null
val topMsg = msgQueue.first()
// if the ACK is sent to me...
if (incomingFrame.getDataRecipient() == mac && incomingFrame.getSender() == topMsg.first) {
// ack or nak?
val successful = (incomingFrame.getAckStatus() == 0)
// if successful, remove the message from the queue, then send out empty token
// if failed, keep the message, then send out empty token anyway
if (successful) {
msgQueue.removeFirst()
}
// make an empty token
return emitNewFrame(NetFrame.makeToken(mac))
}
else return null
}
protected fun doSomethingWithBallot(incomingFrame: NetFrame): Int? {
val ballotStatus = incomingFrame.getFrameNumber()
// frame is in election phase
if (ballotStatus == 0) {
// if i'm also in the election phase, participate
if (activeMonitorStatus == 0) {
if (incomingFrame.getBallot() < mac) {
incomingFrame.setBallot(mac)
}
// check if the election must be finished
if (incomingFrame.getSender() == mac && incomingFrame.getBallot() == mac) {
activeMonitorStatus = 2
// send out first empty token
return emitNewFrame(NetFrame.makeToken(mac))
}
}
// if i'm in the winner announcement phase, kill the frame
else {
incomingFrame.discardFrame()
return 0
}
}
// frame is in winner announcement phase
else if (ballotStatus == 1) {
activeMonitorStatus = 1
}
return null
}
}

View File

@@ -0,0 +1,62 @@
package net.torvald.terrarum.modulecomputers.gameactors
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.App
import net.torvald.terrarum.Point2i
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameactors.BlockBox
import net.torvald.terrarum.modulebasegame.gameworld.NetFrame.Companion.toMAC
import net.torvald.terrarum.modulecomputers.ui.UIRingBusAnalyser
import net.torvald.terrarum.modulecomputers.ui.UIRingBusExerciser
/**
* Created by minjaesong on 2025-03-03.
*/
class FixtureRingBusExerciser : FixtureRingBusCore {
constructor() : super(
portEmit = Point2i(0, 0),
portSink = Point2i(1, 0),
blockBox = BlockBox(BlockBox.NO_COLLISION, 2, 2),
nameFun = { Lang["ITEM_DEBUG_RING_BUS_EXERCISER"] }
) {
this.mainUI = UIRingBusExerciser(this)
}
override fun drawBody(frameDelta: Float, batch: SpriteBatch) {
super.drawBody(frameDelta, batch)
// draw its own MAC address
drawUsingDrawFunInGoodPosition(frameDelta) { x, y ->
App.fontSmallNumbers.draw(batch, super.mac.toMAC(), x, y + 2*TILE_SIZE - 12)
}
}
}
/**
* Created by minjaesong on 2025-03-03.
*/
class FixtureRingBusAnalyser : FixtureRingBusCore {
constructor() : super(
portEmit = Point2i(0, 0),
portSink = Point2i(1, 0),
blockBox = BlockBox(BlockBox.NO_COLLISION, 2, 1),
nameFun = { Lang["ITEM_DEBUG_RING_BUS_ANALYSER"] },
) {
this.mainUI = UIRingBusAnalyser(this)
}
override fun drawBody(frameDelta: Float, batch: SpriteBatch) {
super.drawBody(frameDelta, batch)
// draw its own MAC address
drawUsingDrawFunInGoodPosition(frameDelta) { x, y ->
App.fontSmallNumbers.draw(batch, super.mac.toMAC(), x, y + 1*TILE_SIZE - 12)
}
}
}

View File

@@ -0,0 +1,60 @@
package net.torvald.terrarum.modulecomputers.gameactors
import net.torvald.terrarum.serialise.*
open class RingBusFirmware(val devtype: Int) {
fun workWithDataFrame(hostMAC: Int, datagramme: ByteArray): Pair<Int, ByteArray?> {
if (datagramme.size < 12) return -1 to null
val protocol = datagramme[0].toUint()
val mode = datagramme[1].toUint()
val arg1 = datagramme.toBigInt16(2)
val recipient = datagramme.toBigInt32(4)
val sender = datagramme.toBigInt32(8)
val body = datagramme.sliceArray(12 until datagramme.size)
return invoke(hostMAC, protocol, mode, arg1, recipient, sender, body, datagramme)
}
open fun invoke(hostMAC: Int, protocol: Int, mode: Int, arg1: Int, recipient: Int, sender: Int, body: ByteArray, datagramme: ByteArray): Pair<Int, ByteArray?> {
return when (protocol) {
1 -> 0 to echo(recipient, sender, body)
2 -> 0 to blockTransfer(arg1, recipient, sender, body)
4 -> 0 to deviceDiscovery(hostMAC, datagramme)
else -> -1 to null
}
}
open fun echo(recipient: Int, sender: Int, body: ByteArray): ByteArray {
return ByteArray(8 + body.size).makeEmptyPacket(1, 1, 0, recipient, sender).also {
it.writeBigInt48(System.currentTimeMillis(), 12)
System.arraycopy(body, 6, this, 6, body.size - 6)
}
}
open fun blockTransfer(sequence: Int, recipient: Int, sender: Int, body: ByteArray): ByteArray {
return ByteArray(12).makeEmptyPacket(2, 1, sequence, recipient, sender) // always ACK
}
open fun deviceDiscovery(host: Int, wholeMessage: ByteArray): ByteArray {
return ByteArray(wholeMessage.size + 6).also {
System.arraycopy(wholeMessage, 0, it, 0, wholeMessage.size)
it[it.size - 5] = devtype.toByte()
it.writeBigInt32(host, it.size - 4)
}
}
private fun ByteArray.makeEmptyPacket(protocol: Int, mode: Int, arg1: Int, recipient: Int, sender: Int): ByteArray {
this[0] = protocol.toByte()
this[1] = mode.toByte()
this.writeBigInt16(arg1, 2)
this.writeBigInt32(recipient, 4)
this.writeBigInt32(sender, 8)
return this
}
}

View File

@@ -0,0 +1,89 @@
package net.torvald.terrarum.modulecomputers.gameitems
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
/**
* Created by minjaesong on 2025-03-30.
*/
class ItemComputerConsole(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulecomputers.gameactors.FixtureComputerConsole") {
override var dynamicID: ItemID = originalID
override var baseMass = 80.0
override val canBeDynamic = false
override val materialId = "STAL"
init {
itemImage = FixtureItemBase.getItemImageFromSingleImage("dwarventech", "sprites/fixtures/computer_operator_terminal.tga")
}
override var baseToolSize: Double? = baseMass
override var originalName = "ITEM_COMPUTER_CONSOLE"
}
/**
* Created by minjaesong on 2025-03-30.
*/
class ItemComputerProcessor(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulecomputers.gameactors.FixtureComputerProcessor") {
override var dynamicID: ItemID = originalID
override var baseMass = 200.0
override val canBeDynamic = false
override val materialId = "STAL"
init {
itemImage = FixtureItemBase.getItemImageFromSingleImage("dwarventech", "sprites/fixtures/computer_cpu.tga")
}
override var baseToolSize: Double? = baseMass
override var originalName = "ITEM_COMPUTER_PROCESSOR"
}
/**
* Created by minjaesong on 2025-04-01.
*/
class ItemNetworkInterface(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulecomputers.gameactors.FixtureNetworkInterface") {
override var dynamicID: ItemID = originalID
override var baseMass = 200.0
override val canBeDynamic = false
override val materialId = "STAL"
init {
itemImage = FixtureItemBase.getItemImageFromSingleImage("dwarventech", "sprites/fixtures/computer_network_interface.tga")
}
override var baseToolSize: Double? = baseMass
override var originalName = "ITEM_NETWORK_INTERFACE"
}
/**
* Created by minjaesong on 2025-04-01.
*/
class ItemNetworkBridge(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulecomputers.gameactors.FixtureNetworkBridge") {
override var dynamicID: ItemID = originalID
override var baseMass = 200.0
override val canBeDynamic = false
override val materialId = "STAL"
init {
itemImage = FixtureItemBase.getItemImageFromSingleImage("dwarventech", "sprites/fixtures/computer_network_bridge.tga")
}
override var baseToolSize: Double? = baseMass
override var originalName = "ITEM_NETWORK_BRIDGE"
}
/**
* Created by minjaesong on 2025-04-01.
*/
class ItemMemoryCabinet(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulecomputers.gameactors.FixtureMemoryCabinet") {
override var dynamicID: ItemID = originalID
override var baseMass = 80.0
override val canBeDynamic = false
override val materialId = "STAL"
init {
itemImage = FixtureItemBase.getItemImageFromSingleImage("dwarventech", "sprites/fixtures/computer_memory_stack_1.tga")
}
override var baseToolSize: Double? = baseMass
override var originalName = "ITEM_MEMORY_CABINET"
}

View File

@@ -0,0 +1,79 @@
package net.torvald.terrarum.modulecomputers.gameitems
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.WireCodex
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameitems.FixtureInteractionBlocked
import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.modulebasegame.gameitems.BlockBase
/**
* Created by minjaesong on 2025-03-30.
*/
class WirePieceNetworkBus(originalID: ItemID, private val atlasID: String, private val sheetX: Int, private val sheetY: Int)
: GameItem(originalID), FixtureInteractionBlocked {
override var dynamicID: ItemID = originalID
override var baseMass = 0.001
override var baseToolSize: Double? = null
override var inventoryCategory = Category.WIRE
override val canBeDynamic = false
override val materialId = ""
init {
itemImage = CommonResourcePool.getAsItemSheet(atlasID).get(sheetX, sheetY)
}
init {
equipPosition = GameItem.EquipPosition.HAND_GRIP
originalName = "ITEM_NETWORK_BUS_WIRE"
tags.addAll(WireCodex[originalID].tags)
}
override fun startPrimaryUse(actor: ActorWithBody, delta: Float): Long {
return BlockBase.wireStartPrimaryUse(actor,this, delta)
}
override fun effectWhileEquipped(actor: ActorWithBody, delta: Float) {
BlockBase.wireEffectWhenEquipped(this, delta)
}
override fun effectOnUnequip(actor: ActorWithBody) {
BlockBase.wireEffectWhenUnequipped(this)
}
}
/**
* Created by minjaesong on 2025-03-30.
*/
class WirePieceIOBus(originalID: ItemID, private val atlasID: String, private val sheetX: Int, private val sheetY: Int)
: GameItem(originalID), FixtureInteractionBlocked {
override var dynamicID: ItemID = originalID
override var baseMass = 0.001
override var baseToolSize: Double? = null
override var inventoryCategory = Category.WIRE
override val canBeDynamic = false
override val materialId = ""
init {
itemImage = CommonResourcePool.getAsItemSheet(atlasID).get(sheetX, sheetY)
}
init {
equipPosition = GameItem.EquipPosition.HAND_GRIP
originalName = "ITEM_IO_BUS_WIRE"
tags.addAll(WireCodex[originalID].tags)
}
override fun startPrimaryUse(actor: ActorWithBody, delta: Float): Long {
return BlockBase.wireStartPrimaryUse(actor,this, delta)
}
override fun effectWhileEquipped(actor: ActorWithBody, delta: Float) {
BlockBase.wireEffectWhenEquipped(this, delta)
}
override fun effectOnUnequip(actor: ActorWithBody) {
BlockBase.wireEffectWhenUnequipped(this)
}
}

View File

@@ -0,0 +1,101 @@
package net.torvald.terrarum.modulecomputers.ui
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.App
import net.torvald.terrarum.ifNaN
import net.torvald.terrarum.modulecomputers.gameactors.FixtureRingBusCore
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.ui.UIItemVertSlider
import net.torvald.terrarum.ui.Toolkit
import kotlin.math.roundToInt
class UIRingBusAnalyser(val host: FixtureRingBusCore) : UICanvas() {
override var width = Toolkit.drawWidth
override var height = App.scr.height
private val analyserPosX = 10
private val analyserPosY = 10
private val analyserWidth = width - 20
private val analyserHeight = height - 20
private val TEXT_LINE_HEIGHT = 24
private var analysisTextBuffer = ArrayList<String>()
private val analyserScroll = UIItemVertSlider(this,
analyserPosX - 18,
analyserPosY + 1,
0.0, 0.0, 1.0, analyserHeight - 2, analyserHeight - 2
)
init {
addUIitem(analyserScroll)
refreshAnalysis()
}
private fun refreshAnalysis() {
analysisTextBuffer.clear()
host.msgLog.forEach { frame ->
analysisTextBuffer.add(frame.toString())
}
// update scrollbar
analyserScroll.handleHeight = if (analysisTextBuffer.isEmpty())
analyserHeight
else
(analyserHeight.toFloat() / analysisTextBuffer.size.times(TEXT_LINE_HEIGHT))
.times(analyserHeight)
.roundToInt()
.coerceIn(12, analyserHeight)
}
private fun drawAnalysis(batch: SpriteBatch) {
val scroll = (analyserScroll.value * analysisTextBuffer.size.times(TEXT_LINE_HEIGHT)
.minus(analyserHeight - 3))
.ifNaN(0.0)
.roundToInt()
.coerceAtLeast(0)
analysisTextBuffer.forEachIndexed { index, s ->
App.fontGame.draw(batch, s,
analyserPosX + 6f,
analyserPosY + 3f + index * TEXT_LINE_HEIGHT - scroll
)
}
}
override fun updateImpl(delta: Float) {
refreshAnalysis()
uiItems.forEach { it.update(delta) }
}
override fun renderImpl(frameDelta: Float, batch: SpriteBatch, camera: OrthographicCamera) {
// Draw background box
batch.color = Color(0x7F)
Toolkit.fillArea(batch, analyserPosX, analyserPosY, analyserWidth, analyserHeight)
batch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(batch, analyserPosX, analyserPosY, analyserWidth, analyserHeight)
// Draw text content
batch.color = Color.WHITE
drawAnalysis(batch)
// Draw UI elements
uiItems.forEach { it.render(frameDelta, batch, camera) }
}
override fun doOpening(delta: Float) {
refreshAnalysis()
}
override fun doClosing(delta: Float) {
// nothing needed
}
override fun dispose() {
}
}

View File

@@ -0,0 +1,101 @@
package net.torvald.terrarum.modulecomputers.ui
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.App
import net.torvald.terrarum.ifNaN
import net.torvald.terrarum.modulecomputers.gameactors.FixtureRingBusCore
import net.torvald.terrarum.ui.UICanvas
import net.torvald.terrarum.ui.UIItemVertSlider
import net.torvald.terrarum.ui.Toolkit
import kotlin.math.roundToInt
class UIRingBusExerciser(val host: FixtureRingBusCore) : UICanvas() {
override var width = Toolkit.drawWidth
override var height = App.scr.height
private val analyserPosX = 10
private val analyserPosY = 10
private val analyserWidth = width - 20
private val analyserHeight = height - 20
private val TEXT_LINE_HEIGHT = 24
private var analysisTextBuffer = ArrayList<String>()
private val analyserScroll = UIItemVertSlider(this,
analyserPosX - 18,
analyserPosY + 1,
0.0, 0.0, 1.0, analyserHeight - 2, analyserHeight - 2
)
init {
addUIitem(analyserScroll)
refreshAnalysis()
}
private fun refreshAnalysis() {
analysisTextBuffer.clear()
host.msgLog.forEach { frame ->
analysisTextBuffer.add(frame.toString())
}
// update scrollbar
analyserScroll.handleHeight = if (analysisTextBuffer.isEmpty())
analyserHeight
else
(analyserHeight.toFloat() / analysisTextBuffer.size.times(TEXT_LINE_HEIGHT))
.times(analyserHeight)
.roundToInt()
.coerceIn(12, analyserHeight)
}
private fun drawAnalysis(batch: SpriteBatch) {
val scroll = (analyserScroll.value * analysisTextBuffer.size.times(TEXT_LINE_HEIGHT)
.minus(analyserHeight - 3))
.ifNaN(0.0)
.roundToInt()
.coerceAtLeast(0)
analysisTextBuffer.forEachIndexed { index, s ->
App.fontGame.draw(batch, s,
analyserPosX + 6f,
analyserPosY + 3f + index * TEXT_LINE_HEIGHT - scroll
)
}
}
override fun updateImpl(delta: Float) {
refreshAnalysis()
uiItems.forEach { it.update(delta) }
}
override fun renderImpl(frameDelta: Float, batch: SpriteBatch, camera: OrthographicCamera) {
// Draw background box
batch.color = Color(0x7F)
Toolkit.fillArea(batch, analyserPosX, analyserPosY, analyserWidth, analyserHeight)
batch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(batch, analyserPosX, analyserPosY, analyserWidth, analyserHeight)
// Draw text content
batch.color = Color.WHITE
drawAnalysis(batch)
// Draw UI elements
uiItems.forEach { it.render(frameDelta, batch, camera) }
}
override fun doOpening(delta: Float) {
refreshAnalysis()
}
override fun doClosing(delta: Float) {
// nothing needed
}
override fun dispose() {
}
}

View File

@@ -1,6 +1,6 @@
## Aperçu ##
This project is to create a modular game engine that accommodates a 2D side-scrolling tilemap platformer, and a game that runs on top of it.
The goal of this project is to create a modular game engine that accommodates a 2D side-scrolling tilemap platformer, and a game that runs on top of it.
The project is divided into two parts: **Terrarum the Game Engine** and **Terrarum the actual game**.
@@ -43,7 +43,7 @@ Requires 64 bit processor and operation system.
Kotlin runtimes must be downloaded using the IntelliJ IDEA. All other libraries are included in the repository.
The project includes modified version of the GraalVM-JS, in which the only difference is the `regex-22.3.1-edit.jar` is a modification of `regex-22.3.1.jar` where the only difference is its `MANIFEST.MD`
The project uses GraalVM 23.1.10 for JavaScript execution (with the Graal JIT compiler enabled via `--upgrade-module-path`).
## Copyright ##

View File

@@ -17,7 +17,7 @@
<orderEntry type="library" name="gdx-controllers-core-2.2.1" level="project" />
<orderEntry type="library" name="gdx-controllers-desktop-2.2.1" level="project" />
<orderEntry type="library" name="jxinput-1.0.0" level="project" />
<orderEntry type="library" name="graalvm-js 22.3.1" level="project" />
<orderEntry type="library" name="graalvm-js 23.1.10" level="project" />
<orderEntry type="library" name="badlogicgames.gdx" level="project" />
<orderEntry type="library" name="badlogicgames.gdx.backend.lwjgl3" level="project" />
<orderEntry type="library" name="io.github.classgraph" level="project" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
{"n":"FR Azerty","capslock":"shift","t":[[""],[undefined],
{"n":"FR Azerty","capslock":"shift","l":"iso","t":[[""],[undefined],
[undefined],
["<HOME>"],
[undefined],
@@ -237,7 +237,7 @@
[undefined],
[undefined],
[undefined],
[undefined],
["<",">","≤","≥"],
[undefined],
[undefined],
[undefined],

View File

@@ -254,7 +254,7 @@ let states = {"keylayouts":[[""],[undefined],
[undefined],
[undefined]
],
"dict":IMEProvider.requestDictionary("cj5-sc.han"),
"dict":IMEProvider.requestDictionary("cangjie5_dict/cj5-sc.han"),
"code":0, //0: not composing, 1: composing (has candidates), 2: composing (no candidates)
"buf":"",
"candidates":""/*comma-separated values*/}
@@ -316,4 +316,4 @@ return Object.freeze({"n":"五仓简体 Qwerty","v":"many","c":"CuriousTo\uA75Bv
},
"reset":()=>{ reset() },
"composing":()=>(states.code!=0)
})
})

View File

@@ -254,7 +254,7 @@ let states = {"keylayouts":[[""],[undefined],
[undefined],
[undefined]
],
"dict":IMEProvider.requestDictionary("cj5-tc.han"),
"dict":IMEProvider.requestDictionary("cangjie5_dict/cj5-tc.han"),
"code":0, //0: not composing, 1: composing (has candidates), 2: composing (no candidates)
"buf":"",
"candidates":""/*comma-separated values*/}
@@ -316,4 +316,4 @@ return Object.freeze({"n":"五倉正體 Qwerty","v":"many","c":"CuriousTo\uA75Bv
},
"reset":()=>{ reset() },
"composing":()=>(states.code!=0)
})
})

View File

@@ -7,7 +7,9 @@
"CONTEXT_TIME_SECOND_PLURAL": "Seconds",
"COPYRIGHT_ALL_RIGHTS_RESERVED": "All rights reserved",
"COPYRIGHT_GNU_GPL_3": "Distributed under GNU GPL 3",
"GAME_ACTION_CHANGE_COLOR" : "Change color",
"GAME_ACTION_MOVE_VERB" : "Move",
"GAME_ACTION_PUT_DOWN" : "Put down",
"GAME_ACTION_ZOOM" : "Zoom",
"GAME_ACTION_ZOOM_OUT" : "Zoom out",
"MENU_IO_AUTOSAVE": "Autosave",
@@ -54,7 +56,7 @@
"MENU_OPTIONS_SPEAKER_HEADPHONE": "Headphone",
"MENU_OPTIONS_SPEAKER_SETUP": "Speaker Setup",
"MENU_OPTIONS_SPEAKER_STEREO": "Stereo",
"MENU_OPTIONS_STREAMERS_LAYOUT": "Chat Overlay",
"MENU_OPTIONS_STREAMERS_LAYOUT": "Space for Chat Overlay",
"MENU_CREDIT_GPL_DNT" : "GPL",
"MENU_LABEL_JVM_DNT" : "JVM",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -52,7 +52,7 @@
<btex/> abstracts away the meticulous styling and typesetting configurations, so you can focus on
actually writing your texts than debugging the <latex/> macros. This does come with a downside of
not being able to change the given style.</p>
<p><btex/> document is divided up to five parts: the <a href="btexdoc">Style Declaration</a>, the
<p><btex/> document is divided up to the five parts: the <a href="btexdoc">Style Declaration</a>, the
<a href="cover">Cover</a>, the <a href="table of contents">Table of Contents</a>, the
<a href="manuscript">Manuscript</a>, and the <a href="index page">Index Page</a>, of which the
Style Declaration and the Manuscript are the mandatory parts.</p>
@@ -87,7 +87,7 @@
</callout>
<p>Only the <code>title</code> tag is mandatory. Cover texts will be printed using a special font that has wider
gaps between characters. The title text will be printed in a double-size.</p>
<p>The cover can have different colour with the <code>hue</code> attribute, which takes a number between 0 and 360.</p>
<p>The cover can have a different colour with the <code>hue</code> attribute, which takes a number between 0 and 360.</p>
<chapter>The Table of Contents</chapter>
@@ -97,7 +97,7 @@
<callout align="left" class="code"><index id="tocpage (tag)"/>&lt;tocpage title="Custom page name if necessary"&gt;&zwsp;&lt;tableofcontents/&gt;&zwsp;&lt;/tocpage&gt;
</callout>
<p>The optional <code>title</code> attribute allows a custom name can be given to this page.
<p>The optional <code>title</code> attribute allows custom names to a page.
If unspecified, the default name is “Table of Contents”.</p>
<p>The tag <code>&lt;tableofcontents/&gt;</code> is an internal tag used by the typesetter.</p>
@@ -173,8 +173,8 @@
</callout>
<p>The <code>height</code> attribute specifies the height of the image <emph>in the number of lines</emph>,
rather than pixels, the width is calculated automatically; image width wider than the text width
will cause an error. The tag optionally takes caption attribute which prints a text below the image.</p>
rather than the pixels, the width is calculated automatically; image width wider than the text width
will cause an error. The tag optionally takes <code>caption</code> attribute which prints a text below the image.</p>
<p>Supported image formats: JPEG, PNG, BMP or TGA</p>
@@ -201,7 +201,7 @@
<chapter>Conclusion</chapter>
<p>The finished book description using <btex/> can be sent to a publisher, and if there are no errors
<p>The finished book description using <btex/> can be sent to the publisher, and if there are no errors
on your submission, the printed books of specified number of copies will be delivered to your
location within a reasonable amount of business days. Happy writing!</p>
@@ -287,14 +287,14 @@
In other words, <code>veun</code> and <code>vneun</code> are identical.</p>
<p>These tags can be used in the following situation: suppose you have a <btex/> document,</p>
<callout align="left" class="code">[en] Send your &lt;v fromgame="GAME_ITEM_HOLOTAPE"/&gt; that contains the manuscript to the publisher via mail to have your books printed.<br/><!--
<callout align="left" class="code">[en] Send your &lt;v fromgame="GAME_ITEM_HOLOTAPE"/&gt; containing the manuscript to the publisher via post to have your books printed.<br/><!--
-->[koKR] 원고가 담긴 &lt;veul fromgame="GAME_ITEM_HOLOTAPE"/&gt; 출판사로 우편을 통해 보내야 책이 인쇄됩니다.</callout>
<p>The variables will be resolved based on the current ingame language, then the necessary
linguistic processing is performed, which would result in a following paragraph
(text in blue denotes the text is inserted using the tags):</p>
<callout>[en] Send your <itemname>Holotape</itemname> that contains the manuscript to the publisher via mail to have your books printed.<br/><!--
<callout>[en] Send your <itemname>Holotape</itemname> that contains the manuscript to the publisher via post to have your books printed.<br/><!--
-->[koKR] 원고가 담긴 <itemname>홀로테이프를</itemname> 출판사로 우편을 통해 보내야 책이 인쇄됩니다.</callout>
@@ -309,12 +309,12 @@
encounter the freezing every time the book is being printed, and this would be a huge minus towards
the gameplay experience.</p>
<p>If the process exits without any error, the mailing system will be
notified and will send the mail containing finished books to the player; if the process exits
with errors, the mail containing details of the errors will be sent instead.</p>
<p>If the process exits without any error, the post system will be
notified and will send a post containing the finished books to the player; if the process exits
with errors, the post containing the details of the errors will be sent instead.</p>
<p>For this reason, the “printing press” is not exposed to the player, they only get to interact with it
indirectly through the “publisher” via mail.</p>
indirectly through the “publisher” via post.</p>

View File

@@ -0,0 +1,3 @@
id;itemid;tags
1;item@basegame:59;FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
2;item@basegame:60;FLUIDSTORAGE,OPENSTORAGE
1 id itemid tags
2 1 item@basegame:59 FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
3 2 item@basegame:60 FLUIDSTORAGE,OPENSTORAGE

View File

@@ -3,6 +3,7 @@ CheatWarnTest
CodexEdictis
ExportAtlas
ExportCodices
ExportFBO
ExportMap
ExportMap2
ExportWorld
@@ -31,6 +32,7 @@ SetSol
SetTurb
SetTime
SetTimeDelta
SpawnMovingPlatform
SpawnPhysTestBall
Teleport
ToggleNoClip
1 CatStdout
3 CodexEdictis
4 ExportAtlas
5 ExportCodices
6 ExportFBO
7 ExportMap
8 ExportMap2
9 ExportWorld
32 SetTurb
33 SetTime
34 SetTimeDelta
35 SpawnMovingPlatform
36 SpawnPhysTestBall
37 Teleport
38 ToggleNoClip

View File

@@ -32,37 +32,37 @@
"wire@basegame:8192": { /* signal wire red */
"workbench": "wirerollingmill",
"ingredients": [
[10, 1, "item@basegame:112"] /* 1 copper */
[25, 1, "item@basegame:112"] /* 1 copper */
]
},
"wire@basegame:8193": { /* signal wire green */
"workbench": "wirerollingmill",
"ingredients": [
[10, 1, "item@basegame:112"] /* 1 copper */
[25, 1, "item@basegame:112"] /* 1 copper */
] },
"wire@basegame:8194": { /* signal wire blue */
"workbench": "wirerollingmill",
"ingredients": [
[10, 1, "item@basegame:112"] /* 1 copper */
[25, 1, "item@basegame:112"] /* 1 copper */
]
},
"wire@basegame:8195": { /* signal wire yellow */
"workbench": "wirerollingmill",
"ingredients": [
[10, 1, "item@basegame:112"] /* 1 copper */
[25, 1, "item@basegame:112"] /* 1 copper */
]
},
"wire@basegame:8196": { /* signal wire purple */
"workbench": "wirerollingmill",
"ingredients": [
[10, 1, "item@basegame:112"] /* 1 copper */
[25, 1, "item@basegame:112"] /* 1 copper */
]
},
"item@basegame:50": { /* soldering wire */
"workbench": "wirerollingmill",
"ingredients": [
[10, 1, "item@basegame:181"] /* 1 solder bar */
[25, 1, "item@basegame:181"] /* 1 solder bar */
]
},

View File

@@ -58,6 +58,8 @@ id;classname;tags
57;net.torvald.terrarum.modulebasegame.gameitems.ItemLogicSignalPushbutton;FIXTURE,SIGNAL
58;net.torvald.terrarum.modulebasegame.gameitems.ItemLogicSignalPressurePlate;FIXTURE,SIGNAL
59;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden;CONTAINER
60;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron;CONTAINER
# ingots
112;net.torvald.terrarum.modulebasegame.gameitems.IngotCopper;INGOT
@@ -132,11 +134,12 @@ id;classname;tags
320;net.torvald.terrarum.modulebasegame.gameitems.ItemWorldPortal;FIXTURE,STATION
# industrial
#2048;net.torvald.terrarum.modulebasegame.gameitems.ItemInductionMotor;FIXTURE,POWER,KINETIC
#2049;net.torvald.terrarum.modulebasegame.gameitems.ItemGearbox;FIXTURE,POWER,KINETIC
2048;net.torvald.terrarum.modulebasegame.gameitems.ItemInductionMotor;FIXTURE,POWER,KINETIC
2049;net.torvald.terrarum.modulebasegame.gameitems.ItemGearbox;FIXTURE,POWER,KINETIC
2050;net.torvald.terrarum.modulebasegame.gameitems.ItemConveyorBelt;FIXTURE,KINETIC
# data storage (discs; 256)
# 32768 is a reserved number for a blank disc
# data storage (pre-composed discs; 256)
# 32768 is a reserved number for a BASEOBJECT disc (players can't produce own recordings... yet)
32769;net.torvald.terrarum.modulebasegame.gameitems.MusicDisc01;MUSIC,PHONO
32770;net.torvald.terrarum.modulebasegame.gameitems.MusicDisc02;MUSIC,PHONO
32771;net.torvald.terrarum.modulebasegame.gameitems.MusicDisc03;MUSIC,PHONO
@@ -147,19 +150,39 @@ id;classname;tags
32776;net.torvald.terrarum.modulebasegame.gameitems.MusicDisc08;MUSIC,PHONO
32777;net.torvald.terrarum.modulebasegame.gameitems.MusicDisc09;MUSIC,PHONO
# data storage (tapestries; 256)
#33024;net.torvald.terrarum.modulebasegame.gameitems.ItemTapestry;FIXTURE,BASEOBJECT
# data storage (pre-composed tapestries; 256)
33024;net.torvald.terrarum.modulebasegame.gameitems.ItemTapestry;FIXTURE,BASEOBJECT
# data storage (text signs; 256)
# data storage (pre-composed text signs; 256)
33280;net.torvald.terrarum.modulebasegame.gameitems.ItemTextSignCopper;FIXTURE,BASEOBJECT
# data storage (pre-composed typewritten leaflets; 256)
33536;net.torvald.terrarum.modulebasegame.gameitems.ItemPlainDocument;READINGS,BASEOBJECT
# data storage (pre-composed unsealed letters; 256)
33792;net.torvald.terrarum.modulebasegame.gameitems.ItemUnsealedLetter;POST,BASEOBJECT
# data storage (pre-composed sealed letters; 256)
34048;net.torvald.terrarum.modulebasegame.gameitems.ItemSealedLetter;POST,BASEOBJECT
# data storage (pre-composed delivery packets; 256)
34304;net.torvald.terrarum.modulebasegame.gameitems.ItemPostParcel;POST,PARCEL,BASEOBJECT
# give it 65536 or greater ID so it would not be able to be redeemed by a code
65536;net.torvald.terrarum.modulebasegame.gameitems.ItemFileRef;BASEOBJECT
# fluids on storage
# preferably autogenerated
# FUTURE QUEST: autogenerate them using CANISTERS CODEX
# with new ItemID scheme:
# basegame_2<fluid@basegame:1
# stands for a wooden bucket (CANISTERS basegame_2) holding a block of water (fluid@basegame:1)
#
# FLUIDSTORAGE: required tag for buckets/canisters
# OPENSTORAGE: cannot hold gas. Canisters need LIDDEDSTORAGE/SEALEDSTORAGE
# NOEXTREMETHERM: cannot hold cryogenic/molten fluids (can only hold fluid with therm 0 or 1)
#
# BELOW ARE COMPELETY IRRELEVANT AND PENDING FOR REMOVAL
# 100000h..1000FFh : container type 0 x Fluid type 0..255
# 100100h..1001FFh : container type 1 x Fluid type 0..255
# 100200h..1002FFh : container type 2 x Fluid type 0..255
@@ -168,16 +191,20 @@ id;classname;tags
# 100500h..1005FFh : container type 5 x Fluid type 0..255
# ...
# 10FF00h..10FFFFh : container type 255 x Fluid type 0..255
1048576;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden00;FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM,FLUIDSTORAGEEMPTY
1048577;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden01;FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
#1048578;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden02;FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
1048579;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden03;FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
1048832;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron00;FLUIDSTORAGE,OPENSTORAGE,FLUIDSTORAGEEMPTY
1048833;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron01;FLUIDSTORAGE,OPENSTORAGE
1048834;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron02;FLUIDSTORAGE,OPENSTORAGE
1048835;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron03;FLUIDSTORAGE,OPENSTORAGE
# reserved for debug items
#16777216;net.torvald.terrarum.modulebasegame.gameitems.ItemBottomlessWaterBucket;DEBUG,TOOL
#16777217;net.torvald.terrarum.modulebasegame.gameitems.ItemBottomlessLavaBucket;DEBUG,TOOL
#1048576;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden00;FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM,FLUIDSTORAGEEMPTY
#1048577;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden01;FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
##1048578;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden02;FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
#1048579;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden03;FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
#
#1048832;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron00;FLUIDSTORAGE,OPENSTORAGE,FLUIDSTORAGEEMPTY
#1048833;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron01;FLUIDSTORAGE,OPENSTORAGE
#1048834;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron02;FLUIDSTORAGE,OPENSTORAGE
#1048835;net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron03;FLUIDSTORAGE,OPENSTORAGE
#
## reserved for debug items
#ifdef App.IS_DEVELOPMENT_BUILD
16777216;net.torvald.terrarum.modulebasegame.gameitems.ItemBottomlessWaterBucket;DEBUG,TOOL
16777217;net.torvald.terrarum.modulebasegame.gameitems.ItemBottomlessLavaBucket;DEBUG,TOOL
16777472;net.torvald.terrarum.modulebasegame.gameitems.ItemMysteriousATM;DEBUG
16777473;net.torvald.terrarum.modulebasegame.gameitems.ItemDebugInventron;DEBUG
#endif
1 id classname tags
58 58 net.torvald.terrarum.modulebasegame.gameitems.ItemLogicSignalPressurePlate FIXTURE,SIGNAL
59 # ingots 59 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden CONTAINER
60 112 60 net.torvald.terrarum.modulebasegame.gameitems.IngotCopper net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron INGOT CONTAINER
61 # ingots
62 112 net.torvald.terrarum.modulebasegame.gameitems.IngotCopper INGOT
63 113 net.torvald.terrarum.modulebasegame.gameitems.IngotIron INGOT
64 114 net.torvald.terrarum.modulebasegame.gameitems.ItemCoalCoke COMBUSTIBLE
65 115 net.torvald.terrarum.modulebasegame.gameitems.IngotZinc INGOT
134 32774 32773 net.torvald.terrarum.modulebasegame.gameitems.MusicDisc06 net.torvald.terrarum.modulebasegame.gameitems.MusicDisc05 MUSIC,PHONO
135 32775 32774 net.torvald.terrarum.modulebasegame.gameitems.MusicDisc07 net.torvald.terrarum.modulebasegame.gameitems.MusicDisc06 MUSIC,PHONO
136 32776 32775 net.torvald.terrarum.modulebasegame.gameitems.MusicDisc08 net.torvald.terrarum.modulebasegame.gameitems.MusicDisc07 MUSIC,PHONO
137 32777 32776 net.torvald.terrarum.modulebasegame.gameitems.MusicDisc09 net.torvald.terrarum.modulebasegame.gameitems.MusicDisc08 MUSIC,PHONO
138 # data storage (tapestries 32777 256) net.torvald.terrarum.modulebasegame.gameitems.MusicDisc09 MUSIC,PHONO
139 # data storage (pre-composed tapestries 256)
140 #33024 33024 net.torvald.terrarum.modulebasegame.gameitems.ItemTapestry FIXTURE,BASEOBJECT
141 # data storage (text signs # data storage (pre-composed text signs 256)
142 33280 net.torvald.terrarum.modulebasegame.gameitems.ItemTextSignCopper FIXTURE,BASEOBJECT
143 # fluids on storage # data storage (pre-composed typewritten leaflets 256)
144 # preferably autogenerated 33536 net.torvald.terrarum.modulebasegame.gameitems.ItemPlainDocument READINGS,BASEOBJECT
145 # FLUIDSTORAGE: required tag for buckets/canisters # data storage (pre-composed unsealed letters 256)
150 # 100200h..1002FFh : container type 2 x Fluid type 0..255 34304 net.torvald.terrarum.modulebasegame.gameitems.ItemPostParcel POST,PARCEL,BASEOBJECT
151 # 100300h..1003FFh : container type 3 x Fluid type 0..255 # give it 65536 or greater ID so it would not be able to be redeemed by a code
152 # 100400h..1004FFh : container type 4 x Fluid type 0..255 65536 net.torvald.terrarum.modulebasegame.gameitems.ItemFileRef BASEOBJECT
153 # 100500h..1005FFh : container type 5 x Fluid type 0..255 # fluids on storage
154 # ... # FUTURE QUEST: autogenerate them using CANISTERS CODEX
155 # 10FF00h..10FFFFh : container type 255 x Fluid type 0..255 # with new ItemID scheme:
156 1048576 # basegame_2<fluid@basegame:1
157 1048577 # stands for a wooden bucket (CANISTERS basegame_2) holding a block of water (fluid@basegame:1)
158 #1048578 #
159 # FLUIDSTORAGE: required tag for buckets/canisters
160 # OPENSTORAGE: cannot hold gas. Canisters need LIDDEDSTORAGE/SEALEDSTORAGE
161 1048579 # NOEXTREMETHERM: cannot hold cryogenic/molten fluids (can only hold fluid with therm 0 or 1)
162 #
163 # BELOW ARE COMPELETY IRRELEVANT AND PENDING FOR REMOVAL
164 # 100000h..1000FFh : container type 0 x Fluid type 0..255
165 # 100100h..1001FFh : container type 1 x Fluid type 0..255
166 # 100200h..1002FFh : container type 2 x Fluid type 0..255
167 # 100300h..1003FFh : container type 3 x Fluid type 0..255
168 # 100400h..1004FFh : container type 4 x Fluid type 0..255
169 # 100500h..1005FFh : container type 5 x Fluid type 0..255
170 # ...
171 # 10FF00h..10FFFFh : container type 255 x Fluid type 0..255
172 #1048576 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden00 FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM,FLUIDSTORAGEEMPTY
173 #1048577 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden01 FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
174 1048832 ##1048578 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron00 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden02 FLUIDSTORAGE,OPENSTORAGE,FLUIDSTORAGEEMPTY FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
175 1048833 #1048579 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron01 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketWooden03 FLUIDSTORAGE,OPENSTORAGE FLUIDSTORAGE,OPENSTORAGE,NOEXTREMETHERM
176 1048834 #
177 #1048832 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron00 FLUIDSTORAGE,OPENSTORAGE,FLUIDSTORAGEEMPTY
178 #1048833 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron01 FLUIDSTORAGE,OPENSTORAGE
179 #1048834 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron02 FLUIDSTORAGE,OPENSTORAGE
180 #1048835 net.torvald.terrarum.modulebasegame.gameitems.ItemBucketIron03 FLUIDSTORAGE,OPENSTORAGE
181 1048835 #
182 # reserved for debug items ## reserved for debug items
183 #16777216 #ifdef App.IS_DEVELOPMENT_BUILD
184 16777216 net.torvald.terrarum.modulebasegame.gameitems.ItemBottomlessWaterBucket DEBUG,TOOL
185 16777217 net.torvald.terrarum.modulebasegame.gameitems.ItemBottomlessLavaBucket DEBUG,TOOL
186 #16777217 16777472 net.torvald.terrarum.modulebasegame.gameitems.ItemBottomlessLavaBucket net.torvald.terrarum.modulebasegame.gameitems.ItemMysteriousATM DEBUG,TOOL DEBUG
187 16777473 net.torvald.terrarum.modulebasegame.gameitems.ItemDebugInventron DEBUG
188 #endif
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210

Binary file not shown.

View File

@@ -1,6 +1,10 @@
{
"MENU_MONITOR_CALI_TITLE": "Check Monitor",
"MENU_OPTIONS_AUDIO_COMP": "Enhance Quiet Sounds",
"MENU_OPTIONS_DISABLE": "Disable",
"MENU_OPTIONS_LIGHT": "Light",
"MENU_OPTIONS_SHOW_ORES_TOOLTIP_WHEN_MINING": "Show Ore Tooltip When Mining",
"MENU_OPTIONS_STRONG": "Strong",
"MENU_OPTIONS_MAX_CRAFTING": "Max Number of Items for Crafting",
"MENU_OPTIONS_UNIT_BLOCKS": "Blocks",
"MENU_OPTIONS_UNIT_FEET": "Imperial",

View File

@@ -1,6 +1,10 @@
{
"MENU_MONITOR_CALI_TITLE": "모니터 확인",
"MENU_OPTIONS_AUDIO_COMP": "조용한 소리 증폭",
"MENU_OPTIONS_DISABLE": "비활성화",
"MENU_OPTIONS_LIGHT": "약하게",
"MENU_OPTIONS_SHOW_ORES_TOOLTIP_WHEN_MINING": "채굴 시 광석 툴팁 보이기",
"MENU_OPTIONS_STRONG": "강하게",
"MENU_OPTIONS_MAX_CRAFTING": "한번에 제작할 최대 아이템 수",
"MENU_OPTIONS_UNIT_BLOCKS": "블록",
"MENU_OPTIONS_UNIT_FEET": "피트",

View File

@@ -1,3 +1,4 @@
## this file is auto-generated using Prebuild.kt ##
# The name that will be displayed in-game
propername=Terrarum
@@ -44,10 +45,10 @@ package=net.torvald.terrarum.modulebasegame
entrypoint=net.torvald.terrarum.modulebasegame.EntryPoint
# Release date in YYYY-MM-DD
releasedate=2024-03-28
releasedate=2025-01-19
# The version, must follow Semver 2.0.0 scheme (https://semver.org/)
version=0.4.2
version=0.5.2
# External JAR that the module is compiled. If your module requires yet another library, the JAR must be compiled as a "Fatjar";
# Due to security reasons, loading an arbitrary JAR is not allowed.

Binary file not shown.

View File

@@ -1,8 +1,8 @@
{
"identifier": "clear01",
"skyboxGradColourMap": "generic_skybox.tga",
"daylightClut": "clut_daylight.tga",
"tags": "generic,clear",
"skyboxGradColourMap": "model:hosek",
"daylightClut": "lut:clut_daylight.tga",
"tags": "terrestrial,generic,clear",
"cloudChance": 12,
"cloudGamma": [1.2, 2.4],
"cloudGammaVariance": [0.111, 0.0],

View File

@@ -1,8 +1,8 @@
{
"identifier": "generic01",
"skyboxGradColourMap": "generic_skybox.tga",
"daylightClut": "clut_daylight.tga",
"tags": "generic",
"skyboxGradColourMap": "model:hosek",
"daylightClut": "lut:clut_daylight.tga",
"tags": "terrestrial,generic",
"cloudChance": 60,
"cloudGamma": [0.9, 2.4],
"cloudGammaVariance": [0.111, 0.0],

View File

@@ -1,8 +1,8 @@
{
"identifier": "generic02",
"skyboxGradColourMap": "generic_skybox.tga",
"daylightClut": "clut_daylight.tga",
"tags": "generic2",
"skyboxGradColourMap": "model:hosek",
"daylightClut": "lut:clut_daylight.tga",
"tags": "terrestrial,generic",
"cloudChance": 150,
"cloudGamma": [0.9, 2.4],
"cloudGammaVariance": [0.111, 0.0],

View File

@@ -1,8 +1,8 @@
{
"identifier": "overcast01",
"skyboxGradColourMap": "generic_skybox.tga",
"daylightClut": "clut_daylight.tga",
"tags": "overcast",
"skyboxGradColourMap": "model:hosek",
"daylightClut": "lut:clut_daylight.tga",
"tags": "terrestrial,overcast",
"cloudChance": 200,
"cloudGamma": [1.45, 1.0],
"cloudGammaVariance": [0.0, 0.0],

View File

@@ -0,0 +1,15 @@
{
"identifier": "space",
"skyboxGradColourMap": "static:#020202,#060604",
"daylightClut": "static:#FFF7",
"tags": "space",
"cloudChance": 0,
"cloudGamma": [1.0, 1.0],
"cloudGammaVariance": [0.0, 0.0],
"windSpeed": 0.0,
"windSpeedVariance": 0.0,
"windSpeedDamping": 0.0,
"clouds": {},
"atmoTurbidity": 1.0,
"shaderVibrancy": [1.0, 1.0]
}

View File

@@ -1,21 +1,21 @@
id;drop;name;renderclass;accept;inputcount;inputtype;outputtype;javaclass;inventoryimg;branching;tags
8192;8192;WIRE_RED;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,0,4;1;"SIGNALWIRE"
8193;8193;WIRE_GREEN;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,1,4;1;"SIGNALWIRE"
8194;8194;WIRE_BLUE;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,2,4;1;"SIGNALWIRE"
8195;8195;WIRE_YELLOW;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,3,4;1;"SIGNALWIRE"
8196;8196;WIRE_PURPLE;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,4,4;1;"SIGNALWIRE"
8192;8192;WIRE_RED;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,0,4;3;"SIGNALWIRE"
8193;8193;WIRE_GREEN;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,1,4;3;"SIGNALWIRE"
8194;8194;WIRE_BLUE;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,2,4;3;"SIGNALWIRE"
8195;8195;WIRE_YELLOW;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,3,4;3;"SIGNALWIRE"
8196;8196;WIRE_PURPLE;signal;digital_bit;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,4,4;3;"SIGNALWIRE"
1;1;WIRE_POWER_LOW;power;power_low;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,5,4;1;"POWERWIRE_LOW"
2;2;WIRE_POWER_HIGH;power;power_high;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,6,4;1;"POWERWIRE_HIGH"
16;16;WIRE_ETHERNET;network;10base2;3;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire;basegame.items,7,4;1;"ETHERNETWIRE"
#256;256;AXLE;axle;axle;1;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceAxle;basegame.items,1,5;0;"AXLE"
256;256;AXLE;axle;axle;1;N/A;N/A;net.torvald.terrarum.modulebasegame.gameitems.WirePieceAxle;basegame.items,1,5;0;"AXLE"
# accept: which wiretype (defined elsewhere) the wires acceps. Use comma to separate multiple. N/A for electronic components (aka not wires)
# inputcount: how many sides are input (outputcount is deduced from the inputcount). N/A for wires
# inputtype: which wiretype it accepts. N/A for wires
# outputtype: which wiretype it emits. N/A for wires
# branching: if this wire can have branches. Something like a thicknet can't have branches
# branching: if this wire can have branches. 0: unable, 1: tee-only, 2: cross-only, 3: tee and cross.
# Something like a thicknet can't have branches
#
# comments
# digital_3bits must come right after three wires it bundles
1 id drop name renderclass accept inputcount inputtype outputtype javaclass inventoryimg branching tags
2 8192 8192 WIRE_RED signal digital_bit 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items,0,4 1 3 SIGNALWIRE
3 8193 8193 WIRE_GREEN signal digital_bit 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items,1,4 1 3 SIGNALWIRE
4 8194 8194 WIRE_BLUE signal digital_bit 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items,2,4 1 3 SIGNALWIRE
5 8195 8195 WIRE_YELLOW signal digital_bit 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items,3,4 1 3 SIGNALWIRE
6 8196 8196 WIRE_PURPLE signal digital_bit 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items,4,4 1 3 SIGNALWIRE
7 1 1 WIRE_POWER_LOW power power_low 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items,5,4 1 POWERWIRE_LOW
8 2 2 WIRE_POWER_HIGH power power_high 3 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire basegame.items,6,4 1 POWERWIRE_HIGH
9 16 256 16 256 WIRE_ETHERNET AXLE network axle 10base2 axle 3 1 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceSignalWire net.torvald.terrarum.modulebasegame.gameitems.WirePieceAxle basegame.items,7,4 basegame.items,1,5 1 0 ETHERNETWIRE AXLE
#256 256 AXLE axle axle 1 N/A N/A net.torvald.terrarum.modulebasegame.gameitems.WirePieceAxle basegame.items,1,5 0 AXLE
10 # accept: which wiretype (defined elsewhere) the wires acceps. Use comma to separate multiple. N/A for electronic components (aka not wires)
11 # inputcount: how many sides are input (outputcount is deduced from the inputcount). N/A for wires
12 # inputtype: which wiretype it accepts. N/A for wires
13 # outputtype: which wiretype it emits. N/A for wires
14 # branching: if this wire can have branches. Something like a thicknet can't have branches # branching: if this wire can have branches. 0: unable, 1: tee-only, 2: cross-only, 3: tee and cross.
15 # # Something like a thicknet can't have branches
16 # comments #
17 # digital_3bits must come right after three wires it bundles # comments
18 # digital_3bits must come right after three wires it bundles
19 # what's the point of WIRE_BUNDLE when you can overlap as many wires as you want? -- Torvald, 2021-08-09
20
21

Some files were not shown because too many files have changed in this diff Show More