diff --git a/doc/en/0.Home.md b/doc/en/0.Home.md new file mode 100644 index 00000000..70302da8 --- /dev/null +++ b/doc/en/0.Home.md @@ -0,0 +1,11 @@ +# Sections + +- [Engine usage recommendations](1.Engine-usage-recommendations.md) +- [Content-packs](2.Content-packs.md) +- [Block properties](3.Block-properties.md) +- [Item properties](4.Item-properties.md) +- [XML UI building](5.XML-UI-building.md) +- [Assets preloading](6.Assets-preloading.md) +- [Audio](7.Audio.md) +- [Scripting](8.Scripting.md) +- [Block modoels](9.Block-models.md) diff --git a/doc/en/1.Engine-usage-recommendations.md b/doc/en/1.Engine-usage-recommendations.md new file mode 100644 index 00000000..f5b13478 --- /dev/null +++ b/doc/en/1.Engine-usage-recommendations.md @@ -0,0 +1,31 @@ +# Engine usage recommendations + +## Content naming + +### Content packs ID + +Content pack identifier requirements: +- name can consist of Capital letters A-Z, lowercase letters a-z digits 0-9, and underscore '\_' signs. +- the first character must not be a digit. +- name length must be in range \[2, 24\] + +### Blocks and items + +- blocks and items identifiers follow the same requirements as content-pack ID. +- `.item` suffix added only to replace auto-generated block item. Example: `base:stone.item` - an item generated for stone block. +- **caption** field specifying name displayed in inventory UI should not be Capitalized. The engine does it automatically depending on display context. + +## Storage + +### Content packs data + +Settings and other state that supposed to be saved with a world, must be stored in `world:data/pack_id/`. The path should be retrieved by calling a function: +```lua +local path = pack.data_file(PACK_ID, "file_name") +file.write(path, some_data) +-- writes data to file world:data/PACK_ID/file_name +``` +PACK_ID is an existing variable containing current content-pack name. + +Directory `world:data/PACK_ID` will be created on call `pack.data_file(...)`. + diff --git a/doc/en/2.Content-packs.md b/doc/en/2.Content-packs.md new file mode 100644 index 00000000..eb5b26c3 --- /dev/null +++ b/doc/en/2.Content-packs.md @@ -0,0 +1,40 @@ +# Content-packs + +Every content pack must have an ID following requirements: +- name can consist of Capital letters A-Z, lowercase letters a-z digits 0-9, and underscore '\_' signs. +- the first character must not be a digit. +- name length must be in range \[2, 24\] + +Content-pack folder having name same as ID may be created in *res/content*. +Content-pack folder must contain file **package.json** with following contents: + +```json +{ + "id": "pack_id", + "title": "pack name will be displayed in the content menu", + "version": "content-pack version - major.minor", + "creator": "content-pack creator", + "description": "short description", + "dependencies": [ + "pack", + "dependencies" + ] +} +``` + +Example: +```json +{ + "id": "doors", + "title": "DOORS", + "creator": "MihailRis", + "version": "1.0", + "description": "doors test" +} +``` + +Content pack picture should be added as *icon.png* file. Recommended size: 128x128 + +See *res/content/base* as an example of content pack structure. + + diff --git a/doc/en/3.Block-properties.md b/doc/en/3.Block-properties.md new file mode 100644 index 00000000..6f3cee7e --- /dev/null +++ b/doc/en/3.Block-properties.md @@ -0,0 +1,130 @@ +# Block properties +## Visual + +### *texture* + +Block texture name (name of the file in `textures/blocks/` with no path and extension included, just name) + +Texture file must be a **png** image + +### *texture-faces* + +> [!IMPORTANT] +> Can't be used if `texture` already specified + +An array of 6 texture names for block sides. + +Example: +```json +"texture-faces": [ + "grass_side", + "grass_side", + "dirt", + "grass_top", + "grass_side", + "grass_side" +] +``` + +### *model* + +Block model type from list: +- "block" - default block model +- "none" - invisible block (air) +- "X" - grass model (two crossed sprites) +- "aabb" - model based of block hitbox (complex hitbox will be combined into one). Examples: pipes, bulbs, panels. + +### *draw-group* + +Integer specifying number of block draw group (render order). Used for semi-transparent blocks. + +### *rotation* + +Rotation profile (set of available block rotations and behaviour of placing block rotation) from list: + +- "none" - no rotation available (default profile) +- "pipe" - wood logs, pipes, pillars +- "pane" - panels, doors, signs + +## Lighting + +### *emission* + +An array of 3 integers - R, G, B of light in range \[0, 15\] + +Examples: + +- *\[15, 15, 15\]* - white with maximal intensity +- *\[7, 0, 0\]* - dim red light +- *\[0, 0, 0\]* - no emission (default value) + +### *light-passing* + +Light ignores block if **true** + +### *sky-light-passing* + +Vertical sky light ray ignores block if **true**. (used for water) + +## Physics + +### *obstacle* + +Block is not a physical obstacle if **false** + +### *hitbox* + +An array of 6 numbers describing an offset an size of a block hitbox. + +Array *\[0.25, 0.0, 0.5, 0.75, 0.4, 0.3\]* describes hitbox width: +- 0.75m width (from east to west) +- 0.4m height +- 0.3m length (from south to north) +- offset 0.25m east +- offset 0.0m up +- offset 0.5m north + +### *grounded* + +Is block may only be set on a solid block and destructs on below block destruction. + +### *selectable* + +Cursor ray will ignore block if **false**. + +### *replaceable* + +Is block replaceable. Examples: air, water, grass, flower. + +### *breakable* + +Is block breakable by mouse click. + +## Inventory + +### *hidden* + +If **true** an item will not be generated for block. **picking-item** must be specified + +### *picking-item* + +Item will be chosen on MMB click on the block. + +Example: block `door:door_open` is hidden, so you need to specify `picking-item: "door:door.item"` to bind it to not hidden `door:door` block item. + +### *script-name* + +Used to specify block script name (to reuse one script to multiple blocks). Name must not contain `packid:scripts/` and extension. Just name. + +### *ui-layout* + +Block UI XML layout name. Default: string block id. + +Examples for block `containermod:container`: +- default: `containermod:container` (*containermod/layouts/container.xml*) +- if `containermod:randombox` specified: (*containermod/layouts/randombox.xml*) + +### *inventory-size* + +Number of block inventory slots. Default - 0 (no inventory). + diff --git a/doc/en/4.Item-properties.md b/doc/en/4.Item-properties.md new file mode 100644 index 00000000..bab9769c --- /dev/null +++ b/doc/en/4.Item-properties.md @@ -0,0 +1,39 @@ +# Visual + +## *icon-type* and *icon* itself + +Icon type defines a source of an item image displayed in inventory. +- **none** - invisible type, used for *core:empty* only (empty item, like the air block). May be removed in future updates. +- **sprite** - default type. 2D image. Requires *icon* set to *atlas_name:texture_name*. Example: *blocks:notfound*. + There's two atlases available: + - **blocks** (generated from *png* files in *res/textures/blocks/*) + - **items** (generated from *png* files in *res/textures/items/*) +- **block** - block preview. Block ID must be specified in **icon** property. Example: *base:wood*. + +# Behaviour + +## *placing-block* + +Specifies what block will be placed on RMB click. Automatically specified in generated items. + +Example: an items that places bazalt blocks: + +```json +"placing-block": "base:bazalt" +``` + +## *emission* + +Light emitted when player holds the item in hand. + +An array of 3 integers - R, G, B of light in range \[0, 15\] + +Examples: + +- *\[15, 15, 15\]* - white with maximal intensity +- *\[7, 0, 0\]* - dim red light +- *\[0, 0, 0\]* - no emission (default value) + +## *stack-size* + +Maximal number of an item units in one slot. Default - 64. diff --git a/doc/en/5.XML-UI-building.md b/doc/en/5.XML-UI-building.md new file mode 100644 index 00000000..5fe0efdb --- /dev/null +++ b/doc/en/5.XML-UI-building.md @@ -0,0 +1,102 @@ +# XML UI Building + +# Specific types + +**2D vector** - pair of numbers separated with comma. +Examples: +- "500,200" +- "0.4,53.01" +- "0,0" + +**3D vector** - three numbers separated with comma. +Examples: +- "60,30,53" +- "0.4,0.1,0.753" + +**4D vector** - four numbers separated with comma. +- "10,5,10,3" +- "0.1,0.5,0.0,0.0" + +**RGBA color** - only HEX notation available +Examples: +- "#FF8000" - opaque orange +- "#FFFFFF80" - semi-transparent white +- "#000000FF" - opaque black + +# Common element attributes + +- **id** - element identifier. Type: string. +- **pos** - element position. Type: 2D vector. +- **size** - element size. Type: 2D vector. +- **color** - element color. Type: RGBA color. +- **margin** - element margin. Type: 4D vector + *left, top, right, bottom* +- **visible** - element visibility. Type: boolean (true/false) +- **position-func** - position supplier for an element (two numbers), called on every parent container size update or on element adding on a container. May be called before *on_hud_open* +# Common *container* attributes + +Buttons and panels are also containers. + +- **padding** - element padding. Type: 4D vector. + *left, top, right, bottom* + **scrollable** - element scrollability. Works on panels only. Type: boolean + +# Common *panel* attributes + +Buttons are also panels. + +- **max-length** - maximal length of panel stretching before scrolling (if scrollable = true). Type: number +# Common elements + +## *button* + +Inner text is a button text. + +- **text-align** - inner text alignment (*left/center/right*). Type: string. +- **onclick** - Lua function called on button press. + +## *image* + +- **src** - name of an image stored in textures folder. Extension is not specified. Type: string. + Example: *gui/error* + +## *trackbar* + +- **min** - minimal value. Type: number. Default: 0 +- **max** - maximal value. Type: number. Default: 1 +- **value** - initial value. Type: number. Default: 0 +- **step** - track step size. Type: number: Default: 1 +- **track-width** track pointer width (in steps). Type: number. Default: 1 +- **consumer** - Lua function - new value consumer +- **supplier** - Lua function - value supplier + +# Inventory elements + +## *inventory* + +Element is a container. Does not have specific attributes. + +> [!WARNING] +> Inventories position is controlled by the engine and can not be changed by attributes *pos* and *margin* + +## *slot* + +Element must be in direct sub-element of *inventory*. +- **index** - inventory slot index (starting from 0). Type: integer +- **item-source** - content access panel behaviour (infinite source of an item). Type: boolean +- **sharefunc** - Lua event called on LMB + Shift. Inventory id and slot index passed as arguments. +- **updatefunc** - Lua event called on slot content update.Inventory id and slot index passed as arguments. +- **onrightclick** - Lua event called on RMB click. Inventory id and slot index passed as arguments. + +## *slots-grid* + +- **start-index** - inventory slot index of the first slot. Type: integer +- **rows** - number of grid rows (unnecessary if *cols* and *count* specified). Type: integer +- **cols** - number of grid columns (unnecessary if *rows* and *count* specified). Type: integer +- **count** - total number of slots in grid (unnecessary if *rows* and *cols* specified). Type: integer +- **interval** - visual slots interval. Type: number +- **padding** - grid padding (not slots interval). Type: number. (*deprecated*) +- **sharefunc** - Lua event called on LMB + Shift. Inventory id and slot index passed as arguments. +- **updatefunc** - Lua event called on slot content update.Inventory id and slot index passed as arguments. +- **onrightclick** - Lua event called on RMB click. Inventory id and slot index passed as arguments. + diff --git a/doc/en/6.Assets-preloading.md b/doc/en/6.Assets-preloading.md new file mode 100644 index 00000000..0d3fed0b --- /dev/null +++ b/doc/en/6.Assets-preloading.md @@ -0,0 +1,55 @@ +# Assets preloading (*preload.json* file) + +`preload.json` in content-pack folder is used for specifying additional assets should be loaded, like sounds. + +The file contains following categories available: +- fonts +- shaders +- textures +- sounds + +> [!NOTE] +> Sound loading with all variations following template: +> (sound: *sound_name*) -> *sound_name.ogg, sound_name_1.ogg, sound_name_2.ogg, ...* +> or *sound_name_0.ogg, sound_name_1.ogg, sound_name_2.ogg, ...* + +Adding sound `packid:sounds/events/explosion.ogg` with all variants example: +```json +{ + "sounds": [ + "events/explosion" + ] +} +``` + +Sound will be available as "events/explosion" + +Additional load settings example: +```json +{ + "sounds": [ + { + "name": "events/explosion", + "keep-pcm": true + } + ] +} +``` + +*preload.json* example from `core:` package (`res/preload.json`): +```json +{ + "shaders": [ + "ui3d", + "screen", + "background", + "skybox_gen" + ], + "textures": [ + "misc/moon", + "misc/sun", + "gui/crosshair" + ] +} +``` + diff --git a/doc/en/7.Audio.md b/doc/en/7.Audio.md new file mode 100644 index 00000000..5b810a34 --- /dev/null +++ b/doc/en/7.Audio.md @@ -0,0 +1,195 @@ +# Audio + +## Definitions + +### Backend + +Internal audio system implementation controlling audio output. +- NoAudio - dummy audio used on OpenAL initialize fail or if audio is disabled by the *settings.toml*: *\[audio\] enabled=false* +- ALAudio - OpenAL audio used + +### Channel + +Defines a sound sources category for group volume control, effects and pause. + +Now the engine has following channels: +- *master* - controls other channels volume. Should not be used as a target channel when playing an audio. +- *ui* - ui elements sounds (button clicks and other) +- *regular* - world sounds, that will be paused with the game. +- *ambient* - same as *regular* but added for background and ambient sounds/streams like weather. +- *music* - music channel. + +Channels are controlled by the engine and currently are not available in scripts. + +### Speaker + +One-time use playing audio controller for sound or stream. Speaker is destroying after stop (**stop** method call or audio track end if not looped) + +> [!NOTE] +> Speaker access is performed using 64 bit integer identifiers that **will not be reused** after speaker destruction. You should avoid storing direct references and pointers instead of ID. + +Speaker ID starts with 1, so 0 means audio play failure. + +### Sound + +Audio data loaded in memory to play multiple simultaneous instances from multiple sources. Can give access to loaded PCM data. + +### PCMStream (PCM data source) + +Stream used by an audio stream as an audio data source. Implementation depends on audio file format, not a backend. This interface may be used to implement network audio stream. + +### Stream + +Streaming audio. Not fully loading to the memory. Cannot be played via multiple speakers simultaneously. + +## Formats support + +- WAV: 8 and 16 bit supported (24 bit is not supported by OpenAL) +- OGG: implemented with libvorbis + +## Additional information + +> [!WARNING] +> **Stereo** audio played with OpenAL will ignore 3D position relative to the listener. Sounds that supposed to be played at specific world position must be **mono** + +## Scripting Audio API + +### Playing audio + +Library **audio** contains available Audio API in Lua scripts. + +```lua +audio.play_stream( + -- audio file location + name: string, + -- audio source world position + x: number, y: number, z: number, + -- audio gain (0.0 - 1.0) + volume: number + -- audio playback speed (positive number) + pitch: number, + -- [optional] channel name: regular/ambient/music/ui (default - regular) + channel: string, + -- [optional] loop stream (default - false) + loop: bool +) -> int +``` + +Plays streaming audio from the specified file at the specified world position. Returns speaker ID. + +```lua +audio.play_stream_2d( + -- audio file location + name: string, + -- audio gain (0.0 - 1.0) + volume: number + -- audio playback speed (positive number) + pitch: number, + -- [optional] channel name: regular/ambient/music/ui (default - regular) + channel: string, + -- [optional] loop stream (default - false) + loop: bool +) -> int + +``` + +Plays streaming audio from the specified file. Returns speaker ID. + +```lua +audio.play_sound( + -- name of a loaded sound without pack prefix, "sounds/", variant number and extension + -- example: "steps/stone" to play sound loaded from "sounds/steps/stone.ogg" or any of its variant + -- variant will be randomly chosen + name: string, + -- audio source world position + x: number, y: number, z: number, + -- audio gain (0.0 - 1.0) + volume: number + -- audio playback speed (positive number) + pitch: number, + -- [optional] channel name: regular/ambient/music/ui (default - regular) + channel: string, + -- [optional] loop sound (default - false) + loop: bool +) -> int +``` + +Plays the specified sound on the specified position in world. Returns speaker ID. + +```lua +audio.play_sound_2d( + -- name of a loaded sound without pack prefix, "sounds/", variant number and extension + -- example: "steps/stone" to play sound loaded from "sounds/steps/stone.ogg" or any of its variant + -- variant will be randomly chosen + name: string, + -- audio gain (0.0 - 1.0) + volume: number + -- audio playback speed (positive number) + pitch: number, + -- [optional] channel name: regular/ambient/music/ui (default - regular) + channel: string, + -- [optional] loop sound (default - false) + loop: bool +) -> int +``` + +Plays the specified sound. Returns speaker ID. + +### Speaker interaction + +Interaction with a non-existing or destroyed speaker will be ignored. + + +```lua +-- stop audio playback and destroy the speaker +audio.stop(speakerid: integer) + +-- pause speaker +audio.pause(speakerid: integer) + +-- unpause speaker +audio.resume(speakerid: integer) + +-- set audio loop +audio.set_loop(speakerid: integer, state: bool) + +-- check if audio is in loop (false if does not exists) +audio.is_loop(speakerid: integer) -> bool + +-- get audio gain value (0.0 if does not exists) +audio.get_volume(speakerid: integer) -> number + +-- set audio gain value +audio.set_volume(speakerid: integer, volume: number) + +-- get audio playback speed (1.0 if does not exists) +audio.get_pitch(speakerid: integer) -> number + +-- set audio playback speed +audio.set_pitch(speakerid: integer, pitch: number) + +-- get current audio playback time in seconds (0.0 if does not exists) +audio.get_time(speakerid: integer) -> number + +-- set audio playback time position in seconds +audio.set_time(speakerid: integer, time: number) + +-- get audio source world position (nil if does not exists) +audio.get_position(speakerid: integer) -> number, number, number + +-- set audio source world position +audio.set_position(speakerid: integer, x: number, y: number, z: number) + +-- get audio source movement speed in world (nil if does not exists) +-- (OpenAL uses it for Doppler effect simulation) +audio.get_velocity(speakerid: integer) -> number, number, number + +-- set audio source movement speed in world +-- (OpenAL uses it for Doppler effect simulation) +audio.set_velocity(speakerid: integer, x: number, y: number, z: number) + +-- get audio duration +-- returns 0, if does not exists +-- also returns 0, if duration is unknown (example: radio) +audio.get_duration(speakerid: integer) -> number +``` diff --git a/doc/en/8.Scripting.md b/doc/en/8.Scripting.md new file mode 100644 index 00000000..80126126 --- /dev/null +++ b/doc/en/8.Scripting.md @@ -0,0 +1,523 @@ +# Scripting + +Project uses LuaJIT as a scripting language. + +## Core functions + +```lua +require "packid:module_name" -- load Lua module from pack-folder/modules/ +-- no extension included, just name + +-- deprecated functions +load_script("packid:scripts/script_name.lua") -- load Lua script if not loaded yet +load_script("packid:scripts/script_name.lua", true) -- load Lua script anyway +``` + +## *player* library + + +```python +player.get_pos(playerid: int) -> number, number, number +``` +Returns x, y, z coordinates of the player + +```python +player.set_pos(playerid: int, x: number, y: number, z: number) +``` + +Set player position + +```python +player.get_rot(playerid: int) -> number, number +``` + +Returns x, y of camera rotation (radians) + +```python +player.set_rot(playerid: int, x: number, y: number, z: number) +``` + +Set camera rotation (radians) + +```python +player.get_inventory(playerid: int) -> int, int +``` + +Returns player inventory ID and selected slot index (0-9) + +## *world* library + +```python +world.get_day_time() -> number +``` + +Returns current day time in range \[0.0-1.0\] where 0.0 and 1.0 - midnight, 0.5 - noon. + +```python +world.set_day_time(time: number) +``` + +Set day time value. + +```python +world.get_total_time() -> number +``` + +Returns total time passed in the world + +```python +world.get_seed() -> int +``` + +Returns world seed. + +## *gui* library + +Library contains ui elements access functions. Library should not be directly used, because script *layouts/layout_name.xml.lua* already has a generated variable **document** (instance of **Document**) + +Example: + +```lua +print(document.some_button.text) -- where 'some_button' is an element id +document.some_button.text = "new text" +``` + +## **inventory** library + +Library for inventories interaction. + +```python +inventory.get(invid: int, slot: int) -> int, int +``` + +Requires an inventory ID and slot index. Returns item ID and count. ID = 0 (core:empty) means that slot is empty. + +```python +inventory.set(invid: int, slot: int, itemid: int, count: int) +``` + +Set slot content. + +```python +inventory.size(invid: int) -> int +``` + +Returns inventory size (slots number). Throws an exception if there's no inventory having specified ID. + +```python +inventory.add(invid: int, itemid: int, count: int) -> int +``` + +Add an item to the specified inventory. Returns remaining count if could not to add fully. + +```python +inventory.get_block(x: int, y: int, z: int) -> int +``` + +Returns block inventory ID or 0. + +```python +inventory.bind_block(invid: int, x: int, y: int, z: int) +``` + +Bind inventory to the specified block. + +```python +inventory.unbind_block(x: int, y: int, z: int) +``` + +Unbind inventory from the specified block. + +> [!WARNING] +> Unbound inventories will be deleted on world close. + +```python +inventory.clone(invid: int) -> int +``` + +Create inventory copy. Returns the created copy ID. + +## *block* library + +```python +block.name(blockid: int) -> str +``` + +Returns block string ID (name) by index + +```python +block.index(name: str) -> int +``` + +Returns block integer ID (index) by name + +```python +block.get(x: int, y: int, z: int) -> int +``` + +Returns integer ID by block position + +```python +block.get_states(x: int, y: int, z: int) -> int +``` + +Returns block state (rotation + additional information) as an integer. + +```python +block.set(x: int, y: int, z: int, id: int, states: int) +``` + +Set block with specified integer ID and state (default - 0) at specified position. + +> [!WARNING] +> `block.set` does not trigger on_placed. + +```python +block.is_solid_at(x: int, y: int, z: int) -> bool +``` + +Check if block at the specified position is solid. + +```python +block.is_replaceable_at(x: int, y: int, z: int) -> bool +``` +Check if block may be placed at specified position. (Examples: air, water, grass, flower) + +```python +block.defs_count() -> int +``` + +Returns count of available block IDs. + +Following three functions return direction vectors based on block rotation. + + +```python +block.get_X(x: int, y: int, z: int) -> int, int, int +``` + +Returns X: integer direction vector of the block at specified coordinates. +Example: no rotation: 1, 0, 0 + +```python +block.get_Y(x: int, y: int, z: int) -> int, int, int +``` + +Returns Y: integer direction vector of the block at specified coordinates. +Example: no rotation: 0, 1, 0 + +```python +block.get_Z(x: int, y: int, z: int) -> int, int, int +``` + +Returns Z: integer direction vector of the block at specified coordinates. +Example: no rotation: 0, 0, 1 + +### User bits + +Part of a voxel data used for scripting. Size: 8 bit. + +```python +block.get_user_bits(x: int, y: int, z: int, offset: int, bits: int) -> int +``` + +Get specified bits as an unsigned integer. + +```python +block.set_user_bits(x: int, y: int, z: int, offset: int, bits: int, value: int) -> int +``` +Set specified bits. + +## *item* library + + +```python +item.name(itemid: int) -> str +``` + +Returns item string ID (name) by index + +```python +item.index(name: str) -> int +``` + +Returns item integer ID (index) by name + +```python +item.stack_size(itemid: int) -> int +``` + +Returns max stack size for the item + +```python +item.defs_count() -> int +``` + +Returns count of available item IDs. + +## *hud* library + + +```python +hud.open_inventory() +``` + +Open player inventory + +```python +hud.close_inventory() +``` + +Close inventory + +```python +hud.open_block(x: int, y: int, z: int) -> int, str +``` + +Open block UI and inventory. Throws an exception if block has no UI layout. + +Returns block inventory ID (if *"inventory-size"=0* a virtual inventory will be created), and UI layout ID. + +> [!NOTE] +> Only one block may be open at same time + +```python +hud.open_permanent(layoutid: str) +``` + +Add element to the screen. The element will be removed on world close only. +**inventory** element will be bound to the player inventory. + +```python +hud.close(layoutid: str) +``` + +Remove an element from the screen + +## Block events + +```lua +function on_placed(x, y, z, playerid) +``` + +Called on block placed by player + +```lua +function on_broken(x, y, z, playerid) +``` + +Called on block broken by player + +```lua +function on_interact(x, y, z, playerid) -> bool +``` + +Called on block RMB click interaction. Prevents block placing if **true** returned. + +```lua +function on_update(x, y, z) +``` + +Called on block update (near block changed) + +```lua +function on_random_update(x, y, z) +``` + +Called on random block update (grass growth) + +```lua +function on_blocks_tick(tps: int) +``` + +Called tps (20) times per second. + +## Item events + +```lua +function on_use(playerid: int) +``` + +Called on RMB click out of a block. + +```lua +function on_use_on_block(x: int, y: int, z: int, playerid: int) +``` + +Called on block RMB click. Prevents block **placing-block** placing if returns **true** + +```lua +function on_block_break_by(x: int, y: int, z: int, playerid: int) +``` + +Called on block LMB click (unbreakable blocks included). Prevents block destruction if returns **true**. + +## World events + +Script *scripts/world.lua* events. + +```lua +function on_world_open() +``` + +Called on world open. + +```lua +function on_world_save() +``` + +Called before world save. + +```lua +function on_world_tick() +``` + +Called 20 times per second + +```lua +function on_world_quit() +``` + +Called on world close (after saving) + +## Layout events + +Script *layouts/layout_name.xml.lua* events. + +```lua +function on_open(invid: int, x: int, y: int, z: int) +``` + +Called on element added to the screen. +invid=0 if no inventory bound +x,y,z=0 if no block bound + +```lua +function on_close(invid: int) +``` + +Called on element removed from the screen. + +## HUD events + +Script *scripts/hud.lua* events. + + +```lua +function on_hud_open(playerid: int) +``` + +Called after world open. + +```lua +function on_hud_close(playerid: int) +``` + +Called on world close (before saving) + +## Engine libraries + +### file + +Filesystem interaction library. + +```python +file.resolve(path: str) -> str +``` + +Function turns *entry_point:path* (example *user:worlds/house1*) to a regular path. (example *C://Users/user/.voxeng/worlds/house1*) + +> [!NOTE] +> The function should be used for debug only. *entry_point:path* notation is required in all **file** functions. + +Resulting path is not canonical and may be relative. + +```python +file.read(path: str) -> str +``` + +Read whole text file. + +```python +file.read_bytes(path: str) -> array of integers +``` + +Read file into bytes array. + +```python +file.write(path: str, text: str) -> nil +``` + +Overwrite text file. + +```python +file.write_bytes(path: str, data: array of integers) +``` + +Overwrite binary file with bytes array. + +```python +file.length(path: str) -> int +``` + +Get file length (bytes) or 0. + +```python +file.exists(path: str) -> bool +``` + +Check if file or directory exist. + +```python +file.isfile(path: str) -> bool +``` + +Check if the path points to a file. + +```python +file.isdir(path: str) -> bool +``` + +Check if the path points to a directory. + +```python +file.mkdir(path: str) -> bool +``` + +Create directory. Returns true if new directory created + +```python +file.mkdirs(path: str) -> bool +``` + +Create directories chain. Returns true if new directory created + +### time + +```python +time.uptime() -> float +``` + +Returns time elapsed since the engine started. + +## Available modules + +### TOML serialization/deserialization + +```lua +local toml = require "core:toml" + +local t = {a=53, b=42, s="test", sub={x=1, y=6}} +local s = toml.serialize(t) +print(s) +local t2 = toml.deserialize(s) +``` +output: +```toml +b = 42 +s = "test" +a = 53 +[sub] +y = 6 +x = 1 +``` diff --git a/doc/en/9.Block-models.md b/doc/en/9.Block-models.md new file mode 100644 index 00000000..c6fe7f19 --- /dev/null +++ b/doc/en/9.Block-models.md @@ -0,0 +1,20 @@ +# Block Models + +Block model may be created with following properties: + +```js +"model": "custom", +"model-primitives": { + "aabbs": [ + // list of AABB primitives + ], + // ... other primitives +} +``` + +**AABB** primitive is an array of values: +``` +[x, y, z, width, height, depth, texture names for all 6 sides] +``` + +**tetragon** primitive (more like parallelogram) an array of three vectors, describing primitive position, X vector \* width, Y vector \* height. diff --git a/doc/ru/0.Главная.md b/doc/ru/0.Главная.md new file mode 100644 index 00000000..ae961c9e --- /dev/null +++ b/doc/ru/0.Главная.md @@ -0,0 +1,13 @@ +# Разделы + +- [Рекомендации по использованию движка](1.Рекомендации-по-использованию-движка.md) +- [Контент‐паки](2.Контент‐паки.md) +- [Свойства блоков](3.Свойства-блоков.md) +- [Свойства предметов](4.Свойства-предметов.md) +- [XML разметка интерфейса](5.XML-разметка-интерфейса.md) +- [Предзагрузка ассетов](6.Предзагрузка-ассетов.md) +- [Аудио](7.Аудио.md) +- [Скриптинг](8.Скриптинг.md) + - [Модуль core:bit_converter](8.1.Модуль-Lua-core_bit_converter.md) + - [Модуль core:data_buffer](8.2.Модуль-Lua-core_data_buffer.md) +- [Модели блоков](9.Модели-блоков.md) diff --git a/doc/ru/1.Рекомендации-по-использованию-движка.md b/doc/ru/1.Рекомендации-по-использованию-движка.md new file mode 100644 index 00000000..92737140 --- /dev/null +++ b/doc/ru/1.Рекомендации-по-использованию-движка.md @@ -0,0 +1,30 @@ +# Рекомендации по использованию движка + +## Наименование контента + +### ID контент-паков + +Идентификатор контент-пака должен следовать следующим требованиям: +- название может состоять только из букв латиницы, цифр и символа подчёркивания '\_' +- название не может начинаться с цифры +- длина названия должна находиться в пределах от 2 до 24 включительно + +### Блоки и предметы + +- id блоков и предметов следуют тем же требованиям, что и ID контент-пака. +- окончание `.item` добавляется только для замены сгенерированного для блока предмета. Пример: `base:stone.item` - предмет сгенерированный для блока камня. +- поле **caption**, предназначенное для отображения названия в инвентаре, не указывается с заглавной буквы, без необходимости. Движок автоматически повышает регистр при отображении в интерфейсе. + +## Хранение файлов + +### Данные контент-паков + +Настройки, состояние, которое нужно сохранять в мире, должны находиться в `world:data/id_пака/`. Путь следует получать через специальную функцию: +```lua +local path = pack.data_file(PACK_ID, "имя_файла") +file.write(path, данные) +-- запишет данные в файл world:data/PACK_ID/имя_файла +``` +Здесь PACK_ID является доступной константой, т.е не нужно вписывать имя пака самостоятельно. + +Папка `world:data/PACK_ID` будет создана при вызове `pack.data_file`. \ No newline at end of file diff --git a/doc/ru/2.Контент‐паки.md b/doc/ru/2.Контент‐паки.md new file mode 100644 index 00000000..f89c1f39 --- /dev/null +++ b/doc/ru/2.Контент‐паки.md @@ -0,0 +1,39 @@ +# Контент-паки + +Для создания контент-пака сначала нужно придумать ему название (id) соответствующее следующим требованиям: +- название может состоять только из букв латиницы, цифр и символа подчёркивания '\_' +- название не может начинаться с цифры +- длина названия должна находиться в пределах от 2 до 24 включительно + +Далее в *res/content* создаётся папка с выбранным названием контент-пака. + +В созданной папке создаётся файл **package.json** с следующим содержимым: +```json +{ + "id": "выбранное_имя_пака", + "title": "имя контент-пака для отображения в меню контента", + "version": "версия контент-пака в формате major.minor", + "creator": "создатель контент-пака", + "description": "краткое описание", + "dependencies": [ + "зависимости", + "пакета" + ] +} +``` + +Пример: +```json +{ + "id": "doors", + "title": "DOORS", + "creator": "MihailRis", + "version": "1.0", + "description": "doors test" +} +``` + +Изображение контент-пака добавляется в виде файла *icon.png* в папку пака (не в textures). Рекомендованный размер изображения: 128x128 + +Новые блоки добавляются в под-папку **blocks**, предметы в **items**, текстуры в **textures** +С примером файловой структуры лучше ознакомиться через базовый пакет (*res/content/base*) diff --git a/doc/ru/3.Свойства-блоков.md b/doc/ru/3.Свойства-блоков.md new file mode 100644 index 00000000..c1cc2bda --- /dev/null +++ b/doc/ru/3.Свойства-блоков.md @@ -0,0 +1,131 @@ +# Вид + +## Текстура - `texture` + +Название текстуры блока (указывается только имя, без расширения или пути к файлу) + +Файл текстуры должен находиться в `res/textures/blocks/` и иметь формат **png** + +## Текстурирование сторон - `texture-faces` + +>[!IMPORTANT] +> Не может использоваться одновременно с `texture` + +Массив из 6 названий текстур, позволяющих указать их для каждой из сторон отдельно. + +Пример: +```json +"texture-faces": [ + "grass_side", + "grass_side", + "dirt", + "grass_top", + "grass_side", + "grass_side" +] +``` + +## Модель - `model` + +Модель блока из списка: +- "block" - используется по-умолчанию для всех обычных блоков +- "none" - невидимый блок (пример: воздух) +- "X" - модель травы (крест из двух спрайтов) +- "aabb" - модель, соответствующая хитбоксу блока (составной хитбокс будет объединен в один). Примеры: трубы, лампочки, панели. + +## Группа отрисовки - `draw-group` + +Целое число определяющее номер группы отрисовки данного блока. +Актуально для полупрозрачных блоков - решает проблемы невидимых сторон блоков за этим блоком. + +## Вращение - `rotation` + +Профиль вращения (набор положений, в которые можно установить блок) из списка: + +- "none" - вращение блока отключено (по-умолчанию) +- "pipe" - профиль "труба". Примеры блоков: бревно, труба, лампочка +- "pane" - профиль "панель". Примеры блоков: панель, дверь, табличка + +# Освещение + +## Излучение - `emission` + +Массив из трех целых чисел - R, G, B освещения от 0 до 15. + +Примеры: + +- `[15, 15, 15]` - самый яркий белый свет +- `[7, 0, 0]` - слабый красный свет +- `[0, 0, 0]` - блок не излучает свет (по-умолчанию) + + +## Светопроводимость - `light-passing` + +При значении `true` блок проводит свет от излучающих блоков. + +## Солнечная светопроводимость - `sky-light-passing` + +При значении `true` блок не препятствует прохождению вертикального луча солнечного света. + +# Физика + +## Препятствие - `obstacle`: + +Значение false отключает хитбокс у блока (позволяет игроку проходить сквозь блок) + +## Хитбокс - `hitbox`: + +Массив из 6 чисел описывающих смещение и размер хитбокса блока. + +Числа указываются в диапазоне [0.0, 1.0] - т.е в пределах блока. + +Массив `[0.25, 0.0, 0.5, 0.75, 0.4, 0.3]` описывает хитбокс: +- шириной (с востока на запад) 0.75 м +- высотой 0.4 м +- длиной (с юга на север) 0.3 м +- смещен на 0.25 м на запад +- смещен на 0.0 м вверх +- смещен на 0.5 м на север + +## Приземленность - `grounded` + +Блок может быть установлен только на полный блок. +Разрушается при разрушении блока под ним. + +## Выделяемость - `selectable` + +При значении в `false` курсор будет игнорировать блок, выделяя тот, что находится за ним. + +## Заменяемость - `replaceable` + +При значении в `true` на месте блока можно установить любой другой блок. Пример: вода, трава, цветок. + +## Разрушаемость - `breakable` + +При значении в `false` блок нельзя сломать. + +# Инвентарь + +## Скрытый блок - `hidden` + +При значении в `true` блок не появляется в инвентаре и для него не генерируется предмет, поэтому c 0.17 требуется указать свойство `picking-item` + +## Подбираемый предмет - `picking-item` + +Предмет, который будет выбран при при нажатии средней кнопкой мыши на блок. + +Пример: блок `door:door_open` скрыт (hidden) поэтому указывается `picking-item: "door:door.item"` + +## Имя скрипта - `script-name` + +Позволяет указать название скрипта блока. Свойство обеспечивает возможность использования одного скрипта для нескольких блоков. +Название указывается без `пак:scripts/` и расширения. + +## Имя макета UI - `ui-layout` + +Позволяет указать id XML-макета интерфейса блока. По-умолчанию используется строковый id блока. + +## Размер инвентаря - `inventory-size` + +Число слотов инвентаря блока. По-умолчанию - 0 (инвентарь отсутствует) + diff --git a/doc/ru/4.Свойства-предметов.md b/doc/ru/4.Свойства-предметов.md new file mode 100644 index 00000000..180aef4a --- /dev/null +++ b/doc/ru/4.Свойства-предметов.md @@ -0,0 +1,38 @@ +# Вид + +## Тип иконки - `icon-type` и сама иконка - `icon` + +В последней версии движка существуют следующие типы иконок предметов, определяющих то, как предмет будет отображаться в инвентаре: +- `none` - невидимый тип, используется только для `core:empty` (пустой предмет). Не влияет на появление предмета на панель доступа к контенту. Тип может быть удалён в будущем +- `sprite` - 2D изображение. Требуется указание свойства icon, состоящее из имени атласа и имени текстуры в этом атласе, разделённые `:`. Пример: `blocks:notfound`. На данный момент в движке существует два текстурных атласа: + - blocks (генерируется из png файлов в `res/textures/blocks/`) + - items (генерируется из png файлов в `res/textures/items/`) +- `block` - отображает предпросмотр блока. В icon указывается строковый id блока который нужно отображать. Пример `base:wood` + +# Поведение + +## Устанавливаемый блок - `placing-block` + +При указании строкового id блока предмет устанавливает его при нажатии ПКМ. Именно это свойство используется у всех сгенерированных для блоков предметов. + +Пример: предмет ставит блоки базальта: + +```json +"placing-block": "base:bazalt" +``` + +## Излучение - `emission` + +Влияет на свет излучаемый предметом, когда он находится в руке игрока. + +Массив из трех целых чисел - R, G, B освещения от 0 до 15. + +Примеры: + +- `[15, 15, 15]` - самый яркий белый свет +- `[7, 0, 0]` - слабый красный свет +- `[0, 0, 0]` - предмет не излучает свет (по-умолчанию) + +## Размер стопки (стека) - `stack-size` + +Определяет максимальное количество предмета в одном слоте. Значение по-умолчанию - 64. diff --git a/doc/ru/5.XML-разметка-интерфейса.md b/doc/ru/5.XML-разметка-интерфейса.md new file mode 100644 index 00000000..27d07be1 --- /dev/null +++ b/doc/ru/5.XML-разметка-интерфейса.md @@ -0,0 +1,113 @@ +# XML разметка интерфейса + +# Специфические типы + +**2D вектор** - пара чисел, разделенная запятой. +Примеры: +- `"500,200"` +- `"0.4,53.01"` +- `"0,0"` + +**3D вектор** - три числа, разделенная запятой. +Примеры: +- `"60,30,53"` +- `"0.4,0.1,0.753"` + +**4D вектор** - четыре числа, разделенная запятой. +Примеры: +- `"10,5,10,3"` +- `"0.1,0.5,0.0,0.0"` + +**RGBA цвет** - на данный момент доступна только HEX запись. +Примеры: +- `"#FF8000"` - оранжевый непрозрачный +- `"#FFFFFF80"` - белый полупрозрачный +- `"#000000FF"` - черный непрозрачный + +# Общие атрибуты элементов + +- `id` - идентификатор элемента. Тип: строка. +- `pos` - позиция элемента. Тип: 2D вектор. +- `size` - размер элемента. Тип: 2D вектор. +- `color` - цвет элемента. Тип: RGBA цвет. +- `margin` - внешний отступ элемента. Тип: 4D вектор. + Порядок: `"left,top,right,bottom"` +- `visible` - видимость элемента. Тип: логический ("true"/"false"). +- `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open. + +# Общие атрибуты контейнеров + +В число контейнеров также входят панели и кнопки. +- `padding` - внутренний отступ элемента. Тип: 4D вектор. + Порядок: `"left,top,right,bottom"` +- `scrollable` - возможность скроллинга. Работает только у Panel. Тип: логический. + +# Общие атрибуты панелей + +В число панелей также входят кнопки. +- `max-length` - максимальная длина, на которую растягивается панель до начала скроллинга (если scrollable = true). Тип: число + +# Основные элементы + +## Кнопка `button` + +Внутренний текст - текст кнопки. + +- `text-align` - выравнивание текста ("left", "center" или "right"). Тип: строка. +- `onclick` - lua функция вызываемая при нажатии на кнопку. + +## Изображение `image` + +- `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error` + +## Изображение `image` + +- `src` - имя изображения в папке textures без указания расширения. Тип: строка. Например `gui/error` + +# Текстовое поле `textbox` + +Внутренний текст - изначально введенный текст + +- `placeholder` - текст подстановки (используется текстовое поле пусто) +- `consumer` - lua функция-приемник введенного текста. Вызывается только при завершении ввода + +## Ползунок `trackbar` + +- `min` - минимальное значение. Тип: число. По-умолчанию: 0 +- `max` - максимальное значение. Тип: число. По-умолчанию: 1 +- `value` - изначальное значение. Тип: число. По-умолчанию: 0 +- `step` - размер деления ползунка. Тип: число. По-умолчанию: 1 +- `track-width` - ширина указателя (в делениях). Тип: число. По-умолчанию: 1 +- `consumer` - lua функция-приемник установленного значения +- `supplier` - lua функция-поставщик значения + +# Элементы инвентаря + +## Инвентарь `inventory` + +Элемент является контейнером. На данный момент не имеет специфических атрибутов. + +> [!WARNING] +> Расположение инвентарей управляется движком и не может быть изменено свойствами pos, margin и т.д. + +## Одиночный слот `slot` + +Элемент должен находиться внутри `inventory` элемента, без посредников. +- `index` - индекс слота инвентаря. (Нумерация с 0) +- `item-source` - включает поведение подобное панели контента. Тип: логический +- `sharefunc` - lua событие вызываемое при использовании ЛКМ + Shift. Передается id инвентаря и индекс слота +- `updatefunc` - lua событие вызываемое при изменении содержимого слота +- `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота + +## Решетка слотов `slots-grid` + +Элемент должен находиться внутри `inventory` элемента, без посредников. +- `start-index` - индекс первого слота +- `rows` - число рядов (не указывается, если указано cols). +- `cols` - число столбцов (не указывается, если указано rows). +- `count` - общее число слотов (не указывается, если указаны rows и cols). +- `interval` - интервал между слотами. Тип: число. +- `padding` - отступ вокруг решетки слотов. Тип: число. (*атрибут будет удален*) +- `sharefunc` - lua событие вызываемое при использовании ЛКМ + Shift. Передается id инвентаря и индекс слота +- `updatefunc` - lua событие вызываемое при изменении содержимого слота +- `onrightclick` - lua событие вызываемое при использовании ПКМ. Передается id инвентаря и индекс слота \ No newline at end of file diff --git a/doc/ru/6.Предзагрузка-ассетов.md b/doc/ru/6.Предзагрузка-ассетов.md new file mode 100644 index 00000000..5b56757a --- /dev/null +++ b/doc/ru/6.Предзагрузка-ассетов.md @@ -0,0 +1,54 @@ +# Предзагрузка ассетов (файл *preload.json*) + +Для загрузки ассетов, не загружаемых автоматически, такие как звуки, дополнительные текстуры, используется файл `preload.json`, создающийся в папке контент-пака. + +Ассеты в файле разделяются на категории: +- fonts - шрифты +- shaders - шейдеры +- textures - текстуры +- sounds - звуки + +> [!NOTE] +> При загрузке звука подгружаются все его вариации, по шаблону: +> (звук: sound_name) -> *sound_name.ogg, sound_name_1.ogg, sound_name_2.ogg, ...* +> или *sound_name_0.ogg, sound_name_1.ogg, sound_name_2.ogg, ...* + +Добавление звука `пак:sounds/events/explosion.ogg` со всеми его вариантами: +```json +{ + "sounds": [ + "events/explosion" + ] +} +``` +Будет доступен под именем: "events/explosion" + +В случае, если нужно будет работать с PCM данными звука (сейчас не доступно из скриптинга), требуется указать параметр `keep-pcm`: +```json +{ + "sounds": [ + { + "name": "events/explosion", + "keep-pcm": true + } + ] +} +``` + + +Пример файла из пакета `core:` (`res/preload.json`): +```json +{ + "shaders": [ + "ui3d", + "screen", + "background", + "skybox_gen" + ], + "textures": [ + "misc/moon", + "misc/sun", + "gui/crosshair" + ] +} +``` \ No newline at end of file diff --git a/doc/ru/7.Аудио.md b/doc/ru/7.Аудио.md new file mode 100644 index 00000000..66dfb311 --- /dev/null +++ b/doc/ru/7.Аудио.md @@ -0,0 +1,196 @@ +# Аудио + +## Основные понятия + +### Бекенд (Backend) + +Вариант внутренней реализации звуковой подсистемы, управляющий выводом звука. +На данный момент в движке существует два: +- NoAudio - заглушка, используемая при невозможности инициализации OpenAL, либо, при отключенной через файл настроек, аудиосистеме: `[audio] enabled=false`. Данный бекенд загружает PCM данные только по требованию, не создает спикеров при попытке воспроизведения аудио. +- ALAudio - основной вариант. Вывод звука через OpenAL. + +### Канал (Channel) + +Определяет категорию источников аудио для регулирования громкости, наложения эффектов, паузы. +На данный момент существует следующий набор каналов: +- master - управляет громкостью остальных каналов. Не следует указывать как целевой канал при воспроизведении аудио. +- ui - звуки интерфейса +- regular - звуки игрового мира, ставятся на паузу вместе с игрой. +- ambient - то же, что и regular, но предназначается для фоновых звуков: погода и иной эмбиент. +- music - канал для воспроизведения музыки. Как правило, потокового аудио. + +Каналы управляются самим движком. +### Спикер (Speaker) + +Одноразовый контроллер проигрываемого аудио: звука или потока. Спикер уничтожается после остановки через вызов метода **stop** или при окончании аудио (поток также не удерживает спикер от уничтожения). +Контроллер продолжает жить при паузе. + +> [!NOTE] +Доступ к спикерам производится по целочисленным id, которые не повторяются за время работы движка, следует избегать хранения прямых указателей на объекты класса. + +Нумерация ID спикеров начинается с 1. ID 0 означает невозможность воспроизведения, по какой-либо причине. +### Звук (Sound) + +Звуковые данные загруженные в память для возможности одновременного воспроизведения из нескольких источников. Может предоставлять доступ к PCM данным. + +### Источник PCM (PCMStream) + +Поток, используемый потоком как источник PCM-данных. Реализация зависит не от бекенда звуковой системы, а от формата файла. Реализация потокового аудио из сетевого соединения делается через реализацию данного интерфейса. + +### Поток (Stream) + +Потоковое аудио. Не загружается полностью в память, поэтому не требует предзагрузки через `preload.json`. Не может воспроизводиться через несколько спикеров одновременно. + +## Поддержка форматов + +На данный момент реализована поддержка двух форматов. +- WAV: поддерживаются 8 и 16 bit (24 bit не поддерживается OpenAL) +- OGG: реализовано через библиотеку libvorbis + + +## Дополнительно + +> [!WARNING] +> При воспроизведении через OpenAL стерео звуки не будут учитывать расположение источников относительно игрока. Звуки, которые должны учитывать расположение, должны быть в моно. + +## API аудио в скриптинге + +### Воспроизведение аудио + +Работа с аудио производится с библиотекой `audio`. + +```lua +audio.play_stream( + -- путь к аудио-файлу + name: string, + -- позиция источника аудио в мире + x: number, y: number, z: number, + -- громкость аудио (от 0.0 до 1.0) + volume: number + -- скорость воспроизведения (положительное число) + pitch: number, + -- [опционально] имя канала: regular/ambient/music/ui (по-умолчанию - regular) + channel: string, + -- [опционально] зацикливание потока (по-умолчанию - false) + loop: bool +) -> int +``` + +Воспроизводит потоковое аудио из указанного файла, на указанной позиции в мире. Возвращает id спикера. + +```lua +audio.play_stream_2d( + -- путь к аудио-файлу + name: string, + -- громкость аудио (от 0.0 до 1.0) + volume: number + -- скорость воспроизведения (положительное число) + pitch: number, + -- [опционально] имя канала: regular/ambient/music/ui (по-умолчанию - regular) + channel: string, + -- [опционально] зацикливание потока (по-умолчанию - false) + loop: bool +) -> int +``` + +Воспроизводит потоковое аудио из указанного файла. Возвращает id спикера. + + +```lua +audio.play_sound( + -- название загруженного звука без префикса пака, "sounds/", номера варианта и расширения + -- пример "steps/stone" для проигрывания звука, загруженного из "sounds/steps/stone.ogg" или любого из его вариантов + -- вариант звука выбирается случайно + name: string, + -- позиция источника аудио в мире + x: number, y: number, z: number, + -- громкость аудио (от 0.0 до 1.0) + volume: number + -- скорость воспроизведения (положительное число) + pitch: number, + -- [опционально] имя канала: regular/ambient/music/ui (по-умолчанию - regular) + channel: string, + -- [опционально] зацикливание потока (по-умолчанию - false) + loop: bool +) -> int +``` + +Воспроизводит звук на указанной позиции в мире. Возвращает id спикера. + +```lua +audio.play_sound_2d( + -- название загруженного звука без префикса пака, "sounds/", номера варианта и расширения + -- пример "steps/stone" для проигрывания звука, загруженного из "sounds/steps/stone.ogg" или любого из его вариантов + -- вариант звука выбирается случайно + name: string, + -- громкость аудио (от 0.0 до 1.0) + volume: number + -- скорость воспроизведения (положительное число) + pitch: number, + -- [опционально] имя канала: regular/ambient/music/ui (по-умолчанию - regular) + channel: string, + -- [опционально] зацикливание потока (по-умолчанию - false) + loop: bool +) -> int +``` + +Воспроизводит звук. Возвращает id спикера. + +### Взаимодействие со спикером. + +При обращении к несуществующим спикером ничего происходить не будет. + +```lua +-- остановить воспроизведение спикера +audio.stop(speakerid: integer) + +-- поставить спикер на паузу +audio.pause(speakerid: integer) + +-- снять спикер с паузы +audio.resume(speakerid: integer) + +-- установить зацикливание аудио +audio.set_loop(speakerid: integer, state: bool) + +-- проверить, зациклено ли аудио (false если не существует) +audio.is_loop(speakerid: integer) -> bool + +-- получить громкость спикера (0.0 если не существует) +audio.get_volume(speakerid: integer) -> number + +-- установить громкость спикера +audio.set_volume(speakerid: integer, volume: number) + +-- получить скорость воспроизведения (1.0 если не существует) +audio.get_pitch(speakerid: integer) -> number + +-- установить скорость воспроизведения +audio.set_pitch(speakerid: integer, pitch: number) + +-- получить временную позицию аудио в секундах (0.0 если не существует) +audio.get_time(speakerid: integer) -> number + +-- установить временную позицию аудио в секундах +audio.set_time(speakerid: integer, time: number) + +-- получить позицию источника звука в мире (nil если не существует) +audio.get_position(speakerid: integer) -> number, number, number + +-- установить позицию источника звука в мире +audio.set_position(speakerid: integer, x: number, y: number, z: number) + +-- получить скорость движения источника звука в мире (nil если не существует) +-- (используется OpenAL для имитации эффекта Доплера) +audio.get_velocity(speakerid: integer) -> number, number, number + +-- установить скорость движения источника звука в мире +-- (используется OpenAL для имитации эффекта Доплера) +audio.set_velocity(speakerid: integer, x: number, y: number, z: number) + +-- получить длительность аудио в секуднах, проигрываемого источником +-- возвращает 0, если не спикер не существует +-- так же возвращает 0, если длительность неизвестна (пример: радио) +audio.get_duration(speakerid: integer) -> number +``` + diff --git a/doc/ru/8.1.Модуль-Lua-core_bit_converter.md b/doc/ru/8.1.Модуль-Lua-core_bit_converter.md new file mode 100644 index 00000000..cf2126c6 --- /dev/null +++ b/doc/ru/8.1.Модуль-Lua-core_bit_converter.md @@ -0,0 +1,91 @@ +## Конвертация значений в байты и обратно + +```lua +function bit_converter.string_to_bytes(string: str) -> table +``` +Конвертирует строку в байты + +```lua +function bit_converter.bool_to_byte(boolean: bool) -> integer +``` +Конвертирует логический булев в байт + +```lua +function bit_converter.single_to_bytes(number: single) -> table +``` +Конвертирует плавающее значение одинарной точности в байты + +```lua +function bit_converter.double_to_bytes(number: double) -> table +``` +Конвертирует плавающее значение двойной точности в байты + +```lua +function bit_converter.uint16_to_bytes(integer: int) -> table +``` +Конвертирует беззнаковое 2-х битное целое число в байты + +```lua +function bit_converter.uint32_to_bytes(integer: int) -> table +``` +Конвертирует беззнаковое 4-х битное целое число в байты + +```lua +function bit_converter.int16_to_bytes(integer: int) -> table +``` +Конвертирует знаковое 2-х битное целое число в байты + +```lua +function bit_converter.int32_to_bytes(integer: int) -> table +``` +Конвертирует знаковое 4-х битное целое число в байты + +```lua +function bit_converter.int64_to_bytes(integer: int) -> table +``` +Конвертирует знаковое 8-и битное целое число в байты + +```lua +function bit_converter.bytes_to_string(table: bytes) -> string +``` +Конвертирует массив байтов в строку + +```lua +function bit_converter.byte_to_bool(integer: byte) -> boolean +``` +Конвертирует байт в логическое булевое значение + +```lua +function bit_converter.bytes_to_single(table: bytes) -> number№ +``` +Конвертирует массив байтов в плавающее число одинарной точности + +```lua +function bit_converter.bytes_to_double(table: bytes) -> number +``` +Конвертирует массив байтов в плавающее число двойной точности + +```lua +function bit_converter.bytes_to_uint16(table: bytes) -> integer +``` +Конвертирует массив байтов в 2-х битное беззнаковое число + +```lua +function bit_converter.bytes_to_uint32(table: bytes) -> integer +``` +Конвертирует массив байтов в 4-х битное беззнаковое число + +```lua +function bit_converter.bytes_to_int16(table: bytes) -> integer +``` +Конвертирует массив байтов в 2-х битное знаковое число + +```lua +function bit_converter.bytes_to_int32(table: bytes) -> integer +``` +Конвертирует массив байтов в 4-х битное знаковое число + +```lua +function bit_converter.bytes_to_int64(table: bytes) -> integer +``` +Конвертирует массив байтов в 8-х битное знаковое число \ No newline at end of file diff --git a/doc/ru/8.2.Модуль-Lua-core_data_buffer.md b/doc/ru/8.2.Модуль-Lua-core_data_buffer.md new file mode 100644 index 00000000..4bbbacf6 --- /dev/null +++ b/doc/ru/8.2.Модуль-Lua-core_data_buffer.md @@ -0,0 +1,153 @@ +## Буффер данных +### Хранит в себе массив байтов и позволяет легко получать или добавлять разные значения + +```lua +function data_buffer(bytes) +``` +Создаёт новый экземпляр data_buffer (параметр bytes необязательный) + +```lua +function data_buffer:put_byte(integer: byte) +``` +Записывает байт в буффер + +```lua +function data_buffer:put_bytes(table: bytes) +``` +Записывает байты в буффер + +```lua +function data_buffer:put_string(string: str) +``` +Конвертирует строку в байты и записывает их в буффер + +```lua +function data_buffer:put_bool(boolean: bool) +``` +Конвертирует булевое значение в байт и записывает его в буффер + +```lua +function data_buffer:put_single(number: single) +``` +Конвертирует плавающее число одинарной точности в байты и записывает их в буффер + +```lua +function data_buffer:put_double(number: double) +``` +Конвертирует плавающее число двойной точности в байты и записывает их в буффер + +```lua +function data_buffer:put_uint16(integer: int) +``` +Конвертирует беззнаковое 2-х битное число в байты и записывает их в буффер + +```lua +function data_buffer:put_uint32(integer: int) +``` +Конвертирует беззнаковое 4-х битное число в байты и записывает их в буффер + +```lua +function data_buffer:put_int16(integer: int) +``` +Конвертирует знаковое 2-х битное число в байты и записывает их в буффер + +```lua +function data_buffer:put_int32(integer: int) +``` +Конвертирует знаковое 4-х битное число в байты и записывает их в буффер + +```lua +function data_buffer:put_int64(integer: int) +``` +Конвертирует знаковое 8-и битное число в байты и записывает их в буффер + +```lua +function data_buffer:put_number(number: num) +``` +Конвертирует любое число в байты и записывает их в буффер; + +Первый байт это тип значения: +```lua +zero = 0 +uint16 = 1 +uint32 = 2 +int16 = 3 +int32 = 4 +int64 = 5 +double = 6 +``` + +```lua +function data_buffer:get_byte() -> integer +``` +Возвращает следующий байт из буффера + +```lua +function data_buffer:get_bytes(n) -> table +``` +Возвращает n следующих байтов, если n равен nil или не указан, то возвращается массив всех байтов + +```lua +function data_buffer:get_string() -> string +``` +Читает следующую строку из буффера + +```lua +function data_buffer:get_bool() -> boolean +``` +Читает следующий логический булев из буффера + +```lua +function data_buffer:get_single() -> number +``` +Читает следующее плавающее число одинарной точности из буффера + +```lua +function data_buffer:get_double() -> number +``` +Читает следующее плавающее число двойной точности из буффера + +```lua +function data_buffer:get_uint16() -> integer +``` +Читает следующее 2-х битное беззнаковое целое число из буффера + +```lua +function data_buffer:get_uint32() -> integer +``` +Читает следующее 4-х битное беззнаковое целое число из буффера + +```lua +function data_buffer:get_int16() -> integer +``` +Читает следующее 2-х битное знаковое целое число из буффера + +```lua +function data_buffer:get_int32() -> integer +``` +Читает следующее 4-х битное знаковое целое число из буффера + +```lua +function data_buffer:get_int64() -> integer +``` +Читает следующее 8-х битное знаковое целое число из буффера + +```lua +function data_buffer:get_number() -> number +``` +Читает следующее число (см. data_buffer:put_number) + +```lua +function data_buffer:size() -> integer +``` +Возвращает размер буффера + +```lua +function data_buffer:set_position(integer: pos) +``` +Устанавливает текущую позицию в буффере + +```lua +function data_buffer:set_bytes(table: bytes) +``` +Устанавливает байты в буффер \ No newline at end of file diff --git a/doc/ru/8.Скриптинг.md b/doc/ru/8.Скриптинг.md new file mode 100644 index 00000000..9f88954f --- /dev/null +++ b/doc/ru/8.Скриптинг.md @@ -0,0 +1,513 @@ +# Скриптинг + +В качестве языка сценариев используется LuaJIT + +## Функции, доступные в скриптах + +```lua +load_script("контентпак:scripts/имя_скрипта.lua") -- загружает скрипт, если ещё не загружен +load_script("контентпак:scripts/имя_скрипта.lua", true) -- перезагружает скрипт +require "контентпак:имя_модуля" -- загружает lua модуль из папки modules (расширение не указывается) +``` + +## Библиотека player +```python +player.get_pos(playerid: int) -> number, number, number +``` +Возвращает x, y, z координаты игрока + +```python +player.set_pos(playerid: int, x: number, y: number, z: number) +``` + +Устанавливает x, y, z координаты игрока + +```python +player.get_rot(playerid: int) -> number, number +``` + +Возвращает x, y вращения камеры (в радианах) + +```python +player.set_rot(playerid: int, x: number, y: number, z: number) +``` + +Устанавливает x, y вращения камеры (в радианах) + +```python +player.get_inventory(playerid: int) -> int, int +``` + +Возвращает id инвентаря игрока и индекс выбранного слота (от 0 до 9) + +## Библиотека world + +```python +world.get_day_time() -> number +``` + +Возвращает текущее игровое время от 0.0 до 1.0, где 0.0 и 1.0 - полночь, 0.5 - полдень. + +```python +world.set_day_time(time: number) +``` + +Устанавливает указанное игровое время. + +```python +world.get_total_time() -> number +``` + +Возвращает общее суммарное время, прошедшее в мире + +```python +world.get_seed() -> int +``` + +Возвращает зерно мира. + +## Библиотека gui + +Библиотека содержит функции для доступа к свойствам UI элементов. Вместо gui следует использовать объектную обертку, предоставляющую доступ к свойствам через мета-методы __index, __newindex: +```lua +local inventory_doc = Document.new("id-макета") +print(inventory_doc.some_button.text) +indentory_doc.some_button.text = "new text" +``` + +В скрипте макета `layouts/файл_макета.xml` - `layouts/файл_макета.xml.lua` уже доступна переменная **document** содержащая объект класса Document + +## Библиотека inventory + +Библиотека функций для работы с инвентарем. + +```python +inventory.get(invid: int, slot: int) -> int, int +``` + +Принимает id инвентаря и индекс слота. Возвращает id предмета и его количество. id = 0 (core:empty) обозначает, что слот пуст. + +```python +inventory.set(invid: int, slot: int, itemid: int, count: int) +``` + +Устанавливает содержимое слота. + +```python +inventory.size(invid: int) -> int +``` + +Возращает размер инвентаря (число слотов). Если указанного инвентаря не существует, бросает исключение. + +```python +inventory.add(invid: int, itemid: int, count: int) -> int +``` + +Добавляет предмет в инвентарь. Если не удалось вместить все количество, возвращает остаток. + +```python +inventory.get_block(x: int, y: int, z: int) -> int +``` + +Функция возвращает id инвентаря указанного блока. Если блок не может иметь инвентарь - возвращает 0. + +```python +inventory.bind_block(invid: int, x: int, y: int, z: int) +``` + +Привязывает указанный инвентарь к блоку. + +```python +inventory.unbind_block(x: int, y: int, z: int) +``` + +Отвязывает инвентарь от блока. + +> [!WARNING] +> Инвентари, не привязанные ни к одному из блоков, удаляются при выходе из мира. + +```python +inventory.clone(invid: int) -> int +``` + +Создает копию инвентаря и возвращает id копии. Если копируемого инвентаря не существует, возвращает 0. + +## Библиотека block + +```python +block.name(blockid: int) -> str +``` + +Возвращает строковый id блока по его числовому id + +```python +block.index(name: str) -> int +``` + +Возвращает числовой id блока, принимая в качестве агрумента строковый + +```python +block.get(x: int, y: int, z: int) -> int +``` + +Возвращает числовой id блока на указанных координатах. Если чанк на указанных координатах не загружен, возвращает -1. + +```python +block.get_states(x: int, y: int, z: int) -> int +``` + +Возвращает состояние (поворот + доп. информация) в виде целого числа + +```python +block.set(x: int, y: int, z: int, id: int, states: int) +``` + +Устанавливает блок с заданным числовым id и состоянием (0 - по-умолчанию) на заданных координатах. + +> [!WARNING] +> `block.set` не вызывает событие on_placed. + +```python +block.is_solid_at(x: int, y: int, z: int) -> bool +``` + +Проверяет, является ли блок на указанных координатах полным + +```python +block.is_replaceable_at(x: int, y: int, z: int) -> bool +``` +Проверяет, можно ли на заданных координатах поставить блок (примеры: воздух, трава, цветы, вода) + +```python +block.defs_count() -> int +``` + +Возвращает количество id доступных в движке блоков + +Следующие три функции используется для учёта вращения блока при обращении к соседним блокам или других целей, где направление блока имеет решающее значение. + + +```python +block.get_X(x: int, y: int, z: int) -> int, int, int +``` + +Возвращает целочисленный единичный вектор X блока на указанных координатах с учётом его вращения (три целых числа). +Если поворот отсутствует, возвращает 1, 0, 0 + +```python +block.get_Y(x: int, y: int, z: int) -> int, int, int +``` + +Возвращает целочисленный единичный вектор Y блока на указанных координатах с учётом его вращения (три целых числа). +Если поворот отсутствует, возвращает 0, 1, 0 + +```python +block.get_Z(x: int, y: int, z: int) -> int, int, int +``` + +Возвращает целочисленный единичный вектор Z блока на указанных координатах с учётом его вращения (три целых числа). +Если поворот отсутствует, возвращает 0, 0, 1 + +### Пользовательские биты + +Выделенная под использования в скриптах часть поля `voxel.states` хранящего доп-информацию о вокселе, такую как вращение блока. На данный момент выделенная часть составляет 8 бит. + +```python +block.get_user_bits(x: int, y: int, z: int, offset: int, bits: int) -> int +``` + +Возвращает выбранное число бит с указанного смещения в виде целого беззнакового числа + +```python +block.set_user_bits(x: int, y: int, z: int, offset: int, bits: int, value: int) -> int +``` +Записывает указанное число бит значения value в user bits по выбранному смещению + +## Библиотека item + +```python +item.name(itemid: int) -> str +``` + +Возвращает строковый id предмета по его числовому id (как block.name) + +```python +item.index(name: str) -> int +``` + +Возвращает числовой id предмета по строковому id (как block_index) + +```python +item.stack_size(itemid: int) -> int +``` + +Возвращает максимальный размер стопки для предмета. + +```python +item.defs_count() -> int +``` + +Возвращает общее число доступных предметов (включая сгенерированные) + +## Библиотека hud + +```python +hud.open_inventory() +``` + +Открывает инвентарь + +```python +hud.close_inventory() +``` + +Закрывает инвентарь + +```python +hud.open_block(x: int, y: int, z: int) -> int, str +``` + +Открывает инвентарь и UI блока. Если блок не имеет макета UI - бросается исключение. + +Возвращает id инвентаря блока (при *"inventory-size"=0* создаётся виртуальный инвентарь, который удаляется после закрытия), и id макета UI. + +> [!NOTE] +> Одновременно может быть открыт только один блок + +```python +hud.open_permanent(layoutid: str) +``` + +Добавляет постоянный элемент на экран. Элемент не удаляется при закрытии инвентаря. Чтобы не перекрывать затенение в режиме инвентаря нужно установить z-index элемента меньшим чем -1. В случае тега inventory, произойдет привязка слотов к инвентарю игрока. + +```python +hud.close(layoutid: str) +``` + +Удаляет элемент с экрана +## События блоков + +```lua +function on_placed(x, y, z, playerid) +``` + +Вызывается после установки блока игроком + +```lua +function on_broken(x, y, z, playerid) +``` + +Вызывается после разрушения блока игроком + +```lua +function on_interact(x, y, z, playerid) -> bool +``` + +Вызывается при нажатии на блок ПКМ. Предотвращает установку блоков, если возвращает `true` + +```lua +function on_update(x, y, z) +``` + +Вызывается при обновлении блока (если изменился соседний блок) + +```lua +function on_random_update(x, y, z) +``` + +Вызывается в случайные моменты времени (рост травы на блоках земли) + +```lua +function on_blocks_tick(tps: int) +``` + +Вызывается tps (20) раз в секунду + +## События предметов + +```lua +function on_use(playerid: int) +``` + +Вызывается при нажатии ПКМ не на блок. + +```lua +function on_use_on_block(x: int, y: int, z: int, playerid: int) +``` + +Вызывается при нажатии ПКМ на блок. Предотвращает установку блока, прописанного в `placing-block` если возвращает `true` + +```lua +function on_block_break_by(x: int, y: int, z: int, playerid: int) +``` + +Вызывается при нажатии ЛКМ на блок (в т.ч неразрушимый). Предотвращает разрушение блока, если возвращает `true` + +## События мира + +События мира для контент-пака прописываются в `scripts/world.lua` + +```lua +function on_world_open() +``` + +Вызывается при загрузке мира + +```lua +function on_world_save() +``` + +Вызывается перед сохранением мира + +```lua +function on_world_tick() +``` + +Вызывается 20 раз в секунду + +```lua +function on_world_quit() +``` + +Вызывается при выходе из мира (после сохранения) + +## События макета + +События прописываются в файле `layouts/имя_макета.xml.lua`. + +```lua +function on_open(invid: int, x: int, y: int, z: int) +``` + +Вызывается при добавлении элемента на экран. +При отсутствии привязки к инвентарю invid будет равен 0. +При отсутствии привязки к блоку x, y, z так же будут равны 0. + +```lua +function on_close(invid: int) +``` + +Вызывается при удалении элемента с экрана. + +## События HUD + +События связанные с игровым интерфейсом прописываются в файле `scripts/hud.lua` + +```lua +function on_hud_open(playerid: int) +``` + +Вызывается после входа в мир, когда становится доступна библиотека hud. Здесь на экран добавляются постоянные элементы. + +```lua +function on_hud_close(playerid: int) +``` + +Вызывается при выходе из мира, перед его сохранением. + +## Библиотеки движка + +### file + +Библиотека функций для работы с файлами + +```python +file.resolve(путь: str) -> str +``` + +Функция приводит запись `точка_входа:путь` (например `user:worlds/house1`) к обычному пути. (например `C://Users/user/.voxeng/worlds/house1`) + +> [!NOTE] +> Функцию не нужно использовать в сочетании с другими функциями из библиотеки, так как они делают это автоматически + +Возвращаемый путь не является каноническим и может быть как абсолютным, так и относительным. + +```python +file.read(путь: str) -> str +``` + +Читает весь текстовый файл и возвращает в виде строки + +```python +file.read_bytes(путь: str) -> array of integers +``` + +Читает файл в массив байт. + +```python +file.write(путь: str, текст: str) -> nil +``` + +Записывает текст в файл (с перезаписью) + +```python +file.write_bytes(путь: str, data: array of integers) +``` + +Записывает массив байт в файл (с перезаписью) + +```python +file.length(путь: str) -> int +``` + +Возвращает размер файла в байтах, либо -1, если файл не найден + +```python +file.exists(путь: str) -> bool +``` + +Проверяет, существует ли по данному пути файл или директория + +```python +file.isfile(путь: str) -> bool +``` + +Проверяет, существует ли по данному пути файл + +```python +file.isdir(путь: str) -> bool +``` + +Проверяет, существует ли по данному пути директория + +```python +file.mkdir(путь: str) -> bool +``` + +Создает директорию. Возвращает true если была создана новая директория + +```python +file.mkdirs(путь: str) -> bool +``` + +Создает всю цепочку директорий. Возвращает true если были созданы директории. + +### time + +```python +time.uptime() -> float +``` + +Возвращает время с момента запуска движка в секундах + +## Доступные модули + +### TOML сериализация/десериализация + +```lua +local toml = require "core:toml" + +local t = {a=53, b=42, s="test", sub={x=1, y=6}} +local s = toml.serialize(t) +print(s) +local t2 = toml.deserialize(s) +``` +вывод: +```toml +b = 42 +s = "test" +a = 53 +[sub] +y = 6 +x = 1 +``` diff --git a/doc/ru/9.Модели-блоков.md b/doc/ru/9.Модели-блоков.md new file mode 100644 index 00000000..660f5d37 --- /dev/null +++ b/doc/ru/9.Модели-блоков.md @@ -0,0 +1,19 @@ +# Модели блоков + +Создание собственной модели блока может быть реализовано при указании у блока свойств: +```js +"model": "custom", +"model-primitives": { + "aabbs": [ + // список описаний AABB примитивов + ], + // ... другие примитивы +} +``` + +**AABB** примитив - массив состоящий из значений: +``` +[x, y, z, width, height, depth, имёна текстур для каждой стороны] +``` + +**tetragon** примитив (по смыслу скорее parallelogram) - массив из трёх векторов, описывающих позицию примитива, вектор X\*ширина, Y\*высота.