mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
adding loadorder to savegame
This commit is contained in:
@@ -65,4 +65,6 @@ module Terrarum {
|
||||
exports net.torvald.terrarum.modulebasegame.gameworld;
|
||||
exports net.torvald.terrarum.modulebasegame.ui;
|
||||
exports net.torvald.terrarum.modulebasegame.worldgenerator;
|
||||
|
||||
exports net.torvald.terrarum.debuggerapp;
|
||||
}
|
||||
@@ -147,13 +147,12 @@ class SavegameCracker(
|
||||
@Command("Lists contents of the disk")
|
||||
fun ls(args: List<String>) {
|
||||
letdisk {
|
||||
it.entries.forEach { i, entry ->
|
||||
if (i != 0L)
|
||||
println(
|
||||
ccNoun + i.toString(10).padStart(11, ' ') + " " +
|
||||
ccNoun2 + (diskIDtoReadableFilename(entry.entryID) + cc0).padEnd(24) { if (it == 0) ' ' else '.' } +
|
||||
ccConst + " " + entry.contents.getSizePure() + " bytes"
|
||||
)
|
||||
it.entries.toSortedMap().forEach { (i, entry) ->
|
||||
if (i != 0L) println(
|
||||
ccNoun + i.toString(10).padStart(11, ' ') + " " +
|
||||
ccNoun2 + (diskIDtoReadableFilename(entry.entryID) + cc0).padEnd(24) { if (it == 0) ' ' else '.' } +
|
||||
ccConst + " " + entry.contents.getSizePure() + " bytes"
|
||||
)
|
||||
}
|
||||
val entryCount = it.entries.size - 1
|
||||
println("${cc0}$entryCount entries, total ${it.usedBytes} bytes")
|
||||
|
||||
@@ -236,18 +236,22 @@ class VirtualDisk(
|
||||
fun diskIDtoReadableFilename(id: EntryID): String = when (id) {
|
||||
0L -> "root"
|
||||
-1L -> "savegameinfo.json"
|
||||
-2L -> "thumbnail.tga.gz"
|
||||
-16L -> "blockcodex.json.gz"
|
||||
-17L -> "itemcodex.json.gz"
|
||||
-18L -> "wirecodex.json.gz"
|
||||
-19L -> "materialcodex.json.gz"
|
||||
-20L -> "factioncodex.json.gz"
|
||||
-1024L -> "apocryphas.json.gz"
|
||||
in 1..65535 -> "worldinfo-$id.json"
|
||||
in 1048576..2147483647 -> "actor-$id.json"
|
||||
-2L -> "thumbnail.tga.gz (world)/spritedef (player)"
|
||||
-3L -> "spritedef-glow (player)"
|
||||
-4L -> "loadOrder.txt"
|
||||
// -16L -> "blockcodex.json.gz"
|
||||
// -17L -> "itemcodex.json.gz"
|
||||
// -18L -> "wirecodex.json.gz"
|
||||
// -19L -> "materialcodex.json.gz"
|
||||
// -20L -> "factioncodex.json.gz"
|
||||
// -1024L -> "apocryphas.json.gz"
|
||||
-1025L -> "bodypart-to-entry.map"
|
||||
-1026L -> "bodypartglow-to-entry.map"
|
||||
in 1..65535 -> "bodypart #$id.tga.gz (player)"
|
||||
in 1048576..2147483647 -> "actor #$id.json"
|
||||
in 0x0000_0001_0000_0000L..0x0000_FFFF_FFFF_FFFFL ->
|
||||
"World${id.ushr(32)}-L${id.and(0xFF00_0000).ushr(24)}-C${id.and(0xFFFFFF)}.gz"
|
||||
else -> "file-$id"
|
||||
else -> "file #$id"
|
||||
}
|
||||
|
||||
class DiskEntry(
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.torvald.terrarum.serialise
|
||||
import net.torvald.gdx.graphics.PixmapIO2
|
||||
import net.torvald.terrarum.App.printdbg
|
||||
import net.torvald.terrarum.ItemCodex
|
||||
import net.torvald.terrarum.ModMgr
|
||||
import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM
|
||||
import net.torvald.terrarum.gameitems.ItemID
|
||||
import net.torvald.terrarum.itemproperties.ItemRemapTable
|
||||
@@ -172,6 +173,16 @@ class WorldSavingThread(
|
||||
}
|
||||
|
||||
|
||||
// write loadorder //
|
||||
val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET)
|
||||
loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n"))
|
||||
loadOrderBa64Writer.flush(); loadOrderBa64Writer.close()
|
||||
val loadOrderText = loadOrderBa64Writer.toByteArray64()
|
||||
val loadOrderContents = EntryFile(loadOrderText)
|
||||
addFile(disk, DiskEntry(-4L, 0L, creation_t, time_t, loadOrderContents))
|
||||
|
||||
|
||||
|
||||
// Echo("Writing file to disk...")
|
||||
|
||||
disk.entries[0]!!.modificationDate = time_t
|
||||
|
||||
@@ -2,19 +2,15 @@ package net.torvald.terrarum.serialise
|
||||
|
||||
import net.torvald.spriteanimation.AssembledSpriteAnimation
|
||||
import net.torvald.spriteanimation.HasAssembledSprite
|
||||
import net.torvald.spriteanimation.SpriteAnimation
|
||||
import net.torvald.terrarum.spriteassembler.ADProperties
|
||||
import net.torvald.terrarum.ItemCodex
|
||||
import net.torvald.terrarum.ModMgr
|
||||
import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM
|
||||
import net.torvald.terrarum.gameactors.Actor
|
||||
import net.torvald.terrarum.gameactors.ActorWithBody
|
||||
import net.torvald.terrarum.gameitems.GameItem
|
||||
import net.torvald.terrarum.gameitems.ItemID
|
||||
import net.torvald.terrarum.itemproperties.ItemRemapTable
|
||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
|
||||
import net.torvald.terrarum.printStackTrace
|
||||
import net.torvald.terrarum.savegame.*
|
||||
import net.torvald.terrarum.spriteassembler.ADProperties
|
||||
import java.io.Reader
|
||||
import java.util.*
|
||||
|
||||
@@ -111,7 +107,13 @@ object WritePlayer {
|
||||
addFile(playerDisk, DiskEntry(-3L, 0L, adlGlowCreationDate, time_t, adlGlowContents))
|
||||
}
|
||||
|
||||
|
||||
// write loadorder //
|
||||
val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET)
|
||||
loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n"))
|
||||
loadOrderBa64Writer.flush(); loadOrderBa64Writer.close()
|
||||
val loadOrderText = loadOrderBa64Writer.toByteArray64()
|
||||
val loadOrderContents = EntryFile(loadOrderText)
|
||||
addFile(playerDisk, DiskEntry(-4L, 0L, jsonCreationDate, time_t, loadOrderContents))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,10 +10,11 @@ The main game directory is composed of following directories:
|
||||
- "${PlayerName}-${UUID}", TVDA {
|
||||
[-1] player JSON,
|
||||
[-2] spritedef,
|
||||
[-3] optional spritedef-glow,
|
||||
[-1025] sprite-bodypart-name-to-entry-number-map.properties,
|
||||
[-1026] spriteglow-bodypart-name-to-entry-number-map.properties,
|
||||
[1+] optional bodyparts tga.gz
|
||||
[-3] !optional! spritedef-glow,
|
||||
[-4] loadOrder.txt
|
||||
[-1025] !optional! sprite-bodypart-name-to-entry-number-map.properties,
|
||||
[-1026] !optional! spriteglow-bodypart-name-to-entry-number-map.properties,
|
||||
[1+] !optional! bodyparts tga.gz
|
||||
}
|
||||
*if file -1025 is not there, read bodyparts from assets directory
|
||||
*optionally encrypt the files other than -1
|
||||
@@ -27,6 +28,7 @@ The main game directory is composed of following directories:
|
||||
[actorID] actors (mainly fixtures) JSON,
|
||||
[0x1_0000_0000L or (layerNumber shl 24) or chunkNumber] chunk data,
|
||||
[-2] screenshot.tga.gz taken by the last player
|
||||
[-4] loadOrder.txt
|
||||
}
|
||||
*disk name is world's name encoded in UTF-8
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
A savegame consists of a Playable Character Information, Savegame Metadata, and other files.
|
||||
A savegame is a single file in the format of TerranVirtualDisk.
|
||||
|
||||
Files contained the TerranVirtualDisk is as follows:
|
||||
|
||||
(root)
|
||||
worldinfo0 -- Savegame Metadata (TESV)
|
||||
Has fixed Entry ID of 32766
|
||||
worldinfo1 -- (TODO Copy of blocks.csv OR BlockCodex in JSON) -- will use this from the next load
|
||||
Has fixed Entry ID of 32765
|
||||
worldinfo2 -- (TODO Copy of items.csv OR ItemCodex in JSON, static only) -- will use this from the next load
|
||||
Has fixed Entry ID of 32764
|
||||
worldinfo3 -- (TODO Copy of materials.csv OR MaterialCodex in JSON) -- will use this from the next load
|
||||
Has fixed Entry ID of 32763
|
||||
world[n] -- Layer Data (TEMD); [n] is a serial number of the world (starts at 1)
|
||||
Has fixed Entry ID of [n]
|
||||
(any random number in Hex ACTORID_MIN..FFFFFFFF) -- Serialised Entity Information (including Player), Entry ID is random
|
||||
(PLAYER_REF_ID in Hex -- 91A7E2) -- Player Character Information (Serialised--JSON'd--Entity Information), Entry ID is random
|
||||
(51621D) -- The Debug Player (Serialised Entity Information), Entry ID is random
|
||||
load_order.txt -- LoadOrder.csv (NOT zipped)
|
||||
Has fixed Entry ID of 32767
|
||||
|
||||
// TODO select one of following:
|
||||
(any random number in Hex 32768..ACTORID_MIN - 1) -- Serialised Dynamic Item?
|
||||
worldinfo4 -- dynamic item codex in JSON, has fixed Entry ID of 32762
|
||||
|
||||
Remarks: world history is created at the load time by scanning all the actors' corresponding ActorValue
|
||||
@@ -1,45 +0,0 @@
|
||||
Savegame metadata
|
||||
|
||||
* Endianness: LITTLE
|
||||
* Filename: 'worldinfo0'
|
||||
|
||||
Ord Hex Description
|
||||
00 54 T
|
||||
01 45 E
|
||||
02 4D S
|
||||
03 44 V
|
||||
|
||||
04 01 Descriptor version number
|
||||
|
||||
05 03 Number of hashes
|
||||
|
||||
06 Name of the world in UTF-8 (arbitrary length, must not contain NULL)
|
||||
n-1 00 String terminator
|
||||
|
||||
(Ord is now offset from n)
|
||||
|
||||
00 Terrain seed (8 bytes)
|
||||
08 Randomiser s0 (8 bytes)
|
||||
10 Randomiser s1 (8 bytes)
|
||||
18 Weather s0 (8 bytes)
|
||||
20 Weather s1 (8 bytes)
|
||||
|
||||
28 ReferenceID of the player (4 bytes, a fixed value of 91A7E2)
|
||||
2C Current world's time_t (the ingame time, 8 bytes)
|
||||
|
||||
34 Creation time in time_t (6 bytes)
|
||||
3A Last play time in time_t (6 bytes)
|
||||
40 Total playtime in time_t (4 bytes) // will record 136.1 years of playtime
|
||||
|
||||
44 SHA-256 hash of worldinfo1 (32 bytes)
|
||||
72 SHA-256 hash of worldinfo2 (32 bytes)
|
||||
A4 SHA-256 hash of worldinfo3 (32 bytes)
|
||||
|
||||
D6 Uncompressed size (2 bytes)
|
||||
D8 Deflated thumbnail image in TGA format
|
||||
p-2 (it's deflated so that it saves faster, so no Lzma)
|
||||
p-2 0xFF
|
||||
p-1 0xFE
|
||||
|
||||
Note: if you're going to add more footer beyond this point, DON'T;
|
||||
instead pack the thumbnail.tga and other footers in TEVD container.
|
||||
@@ -1,94 +0,0 @@
|
||||
Terrarum Game Map Format
|
||||
|
||||
* Endianness: LITTLE
|
||||
|
||||
Ord Hex Description
|
||||
00 54 T
|
||||
01 45 E
|
||||
02 4D M
|
||||
03 7A z # 'z' because it's compressed
|
||||
|
||||
04 03 Version revision number of this format (unreleased numbers also count)
|
||||
|
||||
05 03 Number of layers, NOT the number of payload
|
||||
|
||||
06 05 Number of payloads
|
||||
|
||||
07 01 Compression algorithm, 0 for none, 1 for DEFLATE, 2 for LZMA, otherwise undefined (maybe LZMA2 for the future?)
|
||||
Value of 01 (DEFLATE) is recommended for its faster compression
|
||||
|
||||
08 World generator version. If the generator adds new feature (e.g. new ores, new buildings)
|
||||
09 this number must be incremented by one.
|
||||
|
||||
0A World width
|
||||
0B World width
|
||||
0C World width
|
||||
0D World width
|
||||
|
||||
0E World height
|
||||
0F World height
|
||||
10 World height
|
||||
11 World height
|
||||
|
||||
12 Default spawn coord in Absolute Tile Number
|
||||
13 Default spawn coord in Absolute Tile Number
|
||||
14 Default spawn coord in Absolute Tile Number
|
||||
15 Default spawn coord in Absolute Tile Number
|
||||
16 Default spawn coord in Absolute Tile Number
|
||||
17 Default spawn coord in Absolute Tile Number
|
||||
|
||||
# Payload
|
||||
#
|
||||
# Each layer and other information are stored as a "payload"
|
||||
# A payload is consisted as follows:
|
||||
#
|
||||
# Literal Description
|
||||
# "\0pLd" Payload header [00, 70, 4C, 64]
|
||||
# [4] Identifier. 4 lettres ASCII string
|
||||
# [6] Uncompressed size of DEFLATEd binary (max size 256 TB)
|
||||
# [6] Length of the actual payload (max size 256 TB)
|
||||
# [..] DEFLATEd binary (begins with one of these: 0x789C, 0x78DA, 0x7801)
|
||||
|
||||
|
||||
Payload "TERR" -- world terrain data in Uint16
|
||||
Uncompressed size will be 2x of (width * height)
|
||||
|
||||
Payload "WALL" -- world walls data in Uint16
|
||||
Uncompressed size will be 2x of (width * height)
|
||||
|
||||
Payload "TdMG" -- world terrain damage data, array of: (Int48 tileAddress, Float32 damage)
|
||||
Uncompressed size will be arbitrary (multiple of tens)
|
||||
|
||||
Payload "WdMG" -- world walls damage data, array of: (Int48 tileAddress, Float32 damage)
|
||||
Uncompressed size will be arbitrary (multiple of tens)
|
||||
|
||||
Payload "FlTP" -- world fluid types, array of: (Int48 tileAddress, Signed Int16 type)
|
||||
Uncompressed size will be arbitrary (multiple of eights)
|
||||
|
||||
Payload "FlFL" -- world fluid fills, array of: (Int48 tileAddress, Float32 amount)
|
||||
Uncompressed size will be arbitrary (multiple of tens)
|
||||
If the 'amount' < 0.0001f (WorldSimulator.FLUID_MIN_MASS), the entry must be discarded
|
||||
|
||||
Payload "WiNt" -- wiring nodes, in JSON format
|
||||
|
||||
Payload "TMaP" -- tile number to name map, array of: (Int32, tileNumber, String itemID)
|
||||
String is null-terminated byte array
|
||||
|
||||
TODO need a format that can store arbitrary number of conduits, not just limited to 32
|
||||
|
||||
/*Payload "CtYP" -- conduit types, array of: (Int48 tileAddress, Uint32 bitarray)
|
||||
can hold 32 different wires simultaneously
|
||||
|
||||
Payload "CfL0" -- conduit fills, aka size of liquid/gas packet, array of: (Int48 tileAddress, Float32 fill)
|
||||
CfL0..CfL9, CfLa..CfLf are available to store values for 16 different things.*/
|
||||
|
||||
|
||||
EOF 45 E
|
||||
EOF 6E n
|
||||
EOF 64 d
|
||||
EOF 54 T
|
||||
EOF 45 E
|
||||
EOF 4D M
|
||||
EOF FF Byte order mark
|
||||
EOF FE Byte order mark
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
## Savegame Structure
|
||||
|
||||
- The Savegame is a TerranVirtualDisk archive that stores multiple files in the disk's root directory
|
||||
- Savegame stores metadata, Worlds and Actors in the game
|
||||
- A player gets one unique Savegame
|
||||
- A player can have Multiple worlds
|
||||
- Worlds are identified using integer ranged 1 through 32767 (inclusive)
|
||||
- Actor ID is unique within the scope of the Savegame
|
||||
- A World stores list of Actor IDs that resides in the world
|
||||
|
||||
|
||||
### File Structure
|
||||
|
||||
Each file on the Savegame has following convention:
|
||||
|
||||
|Type|Filename|ID|
|
||||
|---|---|---|
|
||||
|Metadata|savegame|-1|
|
||||
|Blocks Properties|blocks|-16|
|
||||
|Items Properties|items|-17|
|
||||
|Wires Properties|wires|-18|
|
||||
|Materials Properties|materials|-19|
|
||||
|Factions Properties|factions|-20|
|
||||
|Other Properties used by modules|modprops|-1024|
|
||||
|Worlds|world$n ($n is a world index)|$n|
|
||||
|Actors|actor$n ($n is an Actor ID)|$n|
|
||||
|
||||
User formats can have ID of -2147483648..-65536
|
||||
|
||||
### Solving Problems
|
||||
|
||||
#### How do I determine which world to read in?
|
||||
|
||||
Load the player (always has the entry ID of 9545698) and the property "worldCurrentlyPlaying" should
|
||||
contain an integer that is a world index. Only the actors that are instance of IngamePlayer will have
|
||||
the property.
|
||||
|
||||
|
||||
### Save File Examples
|
||||
|
||||
Following code is an example Savegame JSON files.
|
||||
|
||||
#### savegame.json
|
||||
```
|
||||
{
|
||||
savename: "Test World 1",
|
||||
genver: 0x00030001, /* generator version in integer; always use TerrarumAppConfiguration.VERSION_RAW */
|
||||
terrseed: "84088805e145b555",
|
||||
randseed: "19b25856e1c150ca834cffc8b59b23ad",
|
||||
weatseed: "e5e72beb4e3c6926d3dc9e3e2ef7833b",
|
||||
playerid: 9545698,
|
||||
creation_t: <creation time in real-world unix time>,
|
||||
lastplay_t: <last play time in real-world unix time>,
|
||||
playtime_t: <total play time in real-world unix time>,
|
||||
thumb: <Ascii85-encoded gzipped thumbnail image in TGA>,
|
||||
loadorder: <LoadOrder serialised>,
|
||||
worlds: [1,2,6,7]
|
||||
}
|
||||
```
|
||||
|
||||
#### world1.json
|
||||
|
||||
File is named as `"world"+world_index+".json"`.
|
||||
The fields are auto-generated by GDX's JSON serialiser.
|
||||
|
||||
```
|
||||
{
|
||||
worldName: "New World",
|
||||
worldIndex: 1,
|
||||
width: 9000,
|
||||
height: 2250,
|
||||
spawnX: 4500,
|
||||
spawnY: 248,
|
||||
creationTime: 1629857065,
|
||||
lastPlayTime: 1629857065,
|
||||
totalPlayTime: 0,
|
||||
layerTerrain: {
|
||||
h: <SHA-256 hash of 'b'>,
|
||||
b: <Ascii85-encoded gzipped terrain layerdata>,
|
||||
x: 9000,
|
||||
y: 2250
|
||||
},
|
||||
layerWall: {
|
||||
h: <SHA-256 hash of 'b'>,
|
||||
b: <Ascii85-encoded gzipped wall layerdata>,
|
||||
x: 9000,
|
||||
y: 2250
|
||||
},
|
||||
wallDamages:{},
|
||||
terrainDamages: {},
|
||||
fluidTypes: {}
|
||||
fluidFills: {},
|
||||
wirings: {},
|
||||
wiringGraph: {},
|
||||
gravitation: {y:9.8}
|
||||
globalLight: {
|
||||
r:0.8826928,
|
||||
g:0.8901961,
|
||||
b:0.9055425,
|
||||
a:0.93691504
|
||||
},
|
||||
averageTemperature: 288,
|
||||
generatorSeed: 0,
|
||||
worldTime: 27874,
|
||||
tileNumberToNameMap: {},
|
||||
extraFields: {},
|
||||
genver: 4
|
||||
comp: 1
|
||||
}
|
||||
```
|
||||
|
||||
#### actors.json
|
||||
|
||||
The fields are auto-generated by GDX's JSON serialiser.
|
||||
|
||||
```
|
||||
[
|
||||
{ /* actor serialised in JSON *
|
||||
class: "net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer", /* depends on the actor */
|
||||
referenceID: 1342111743,
|
||||
actorValue: { /* actorValue serialised in JSON */ },
|
||||
hitbox: ...,
|
||||
...
|
||||
},
|
||||
...
|
||||
]
|
||||
```
|
||||
47
work_files/DataFormats/terrarum_advanced_lang_file.txt
Normal file
47
work_files/DataFormats/terrarum_advanced_lang_file.txt
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
# example lang file
|
||||
|
||||
meta language:fiFI
|
||||
meta nounclasslabel:nom,par,accnom,accgen,gen,iness,ela,illa,adess,abla,alla,ess,trans,inst,abess,comit,pnom,ppar,paccnom,paccgen,pgen,piness,pela,pilla,padess,pabla,palla,pess,ptrans,pinst,pabess,pcomit
|
||||
meta nounseries:follow-nounclasslabel # basically tells the parser that 'nounclasslabel' has everything you need
|
||||
CONTERT_HOUSE_NOUN:talo,taloa,talo,talon,talon,talossa,talosta,taloon,talolla,talolta,talolle,talona,taloksi,,talotta,,talot,taloja,talot,talot,talojen,taloissa,taloista,taloihin,taloilla,taloilta,taloille,taloina,taloiksi,taloin,taloitta,taloineen
|
||||
CONTEXT_GO_TO_VERB:mennä <1:illa> # with CONTERT_HOUSE_NOUN: "mennä taloon"
|
||||
|
||||
|
||||
meta language:koKR
|
||||
meta nounclasslabel:use korean # built-in automation for korean
|
||||
meta nounseries:undefined # the grammar of this language does not take noun's count into account
|
||||
CONTEXT_HOUSE_NOUN:집
|
||||
CONTEXT_TOWARDS_VERB:<1>{1:로,으로,로} 가기 # when 'korean' is used for nounclasslabel, the character code of the hangul letter is taken into account. GIVEN_WORD_SET.get(i), where i = ((char - 44032) % 28 == 0) ? 0 : ((char - 44032) % 28 == 8) 2 : 1
|
||||
|
||||
|
||||
meta language:enUS
|
||||
meta nounseries:singular-plural # tells the parser that first element is singular, and the second is plural
|
||||
CONTEXT_HOUSE_NOUN:House,Houses
|
||||
CONTEXT_TOWARDS_VERB:Go to <1>
|
||||
|
||||
|
||||
meta language:frFR
|
||||
meta nounclasslabel:m,f,pm,pf,vm,vf
|
||||
meta nounseries:singular-plural # tells the parser that first element is singular, and the second is plural
|
||||
CONTEXT_COVID_NOUN:f:Covid,Covid
|
||||
CONTEXT_INTERNATIONALE_NOUN:vf:Internationale,Internationale
|
||||
CONTEXT_THE_STH:{1:Le ,La ,L’,L’}<1> # with CONTEXT_COVID_NOUN: "La Covid"; with CONTEXT_INTERNATIONALE_NOUN: "L’Internationale"
|
||||
|
||||
|
||||
meta language:la
|
||||
meta nounclasslabel:m,f,n
|
||||
meta nounseries:singular-plural # tells the parser that first element is singular, and the second is plural
|
||||
CONTEXT_CHICKEN_NOUN:m:Gallus,Galli
|
||||
CONTEXT_VERITAS_NOUN:f:Veritas,Veritates
|
||||
CONTEXT_X_IS_MY_LIGHT:<1:plural> lux {1:meus,mea,meum} # 'plural' is pre-defined name that comes with 'singular-plural' nounseries
|
||||
|
||||
|
||||
## the preamble
|
||||
|
||||
meta nounclasslabel:m,f,n,mp,fp,np # for german: masculine singular, feminine singular, neuter singular, masculine plural, feminine plural, neuter plural
|
||||
|
||||
meta nounclasslabel:m,f,n,md,fd,nd,mp,fp,np # for sanskrit: masc./fem./neu. singular, dual and plural
|
||||
|
||||
meta nounclasslabel:use korean # a pragma to use built-in automation labeled 'korean'
|
||||
|
||||
Reference in New Issue
Block a user