mirror of
https://github.com/Theaninova/mhlib.git
synced 2026-01-04 15:52:52 +00:00
initial commit
This commit is contained in:
9
README.md
Normal file
9
README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# MHLIB
|
||||
|
||||
A project to make the old classic Moorhuhn Games playable
|
||||
on modern systems via an engine reimplementation, written
|
||||
in Rust and Godot.
|
||||
|
||||
Most of the Moorhuhn Games are structured quite similarly,
|
||||
which makes supporting them relatively easy.
|
||||
|
||||
2
godot/.gitattributes
vendored
Normal file
2
godot/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
||||
2
godot/.gitignore
vendored
Normal file
2
godot/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
BIN
godot/icon.png
Normal file
BIN
godot/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
34
godot/icon.png.import
Normal file
34
godot/icon.png.import
Normal file
@@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c1vqk21x7qa8t"
|
||||
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.png"
|
||||
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
12
godot/mhjnr.gdextension
Normal file
12
godot/mhjnr.gdextension
Normal file
@@ -0,0 +1,12 @@
|
||||
[configuration]
|
||||
entry_symbol = "gdext_rust_init"
|
||||
|
||||
[libraries]
|
||||
linux.debug.x86_64 = "res://../rust/target/debug/libmhjnr.so"
|
||||
linux.release.x86_64 = "res://../rust/target/release/libmhjnr.so"
|
||||
windows.debug.x86_64 = "res://../rust/target/debug/mhjnr.dll"
|
||||
windows.release.x86_64 = "res://../rust/target/release/mhjnr.dll"
|
||||
macos.debug = "res://../rust/target/debug/mhjnr.dylib"
|
||||
macos.release = "res://../rust/target/release/mhjnr.dylib"
|
||||
macos.debug.arm64 = "res://../rust/target/aarch64-apple-darwin/debug/mhjnr.dylib"
|
||||
macos.release.arm64 = "res://../rust/target/aarch64-apple-darwin/release/mhjnr.dylib"
|
||||
60
godot/mhjnr/Camera2D.gd
Normal file
60
godot/mhjnr/Camera2D.gd
Normal file
@@ -0,0 +1,60 @@
|
||||
extends CharacterBody2D
|
||||
|
||||
@export var max_jumps: int = 2
|
||||
@export_range(0, 4000, 1, "suffix:px/s") var speed: float = 400
|
||||
@export_range(0, 4000, 1, "suffix:px/s") var terminal_velocity: float = 2000
|
||||
@export_range(0, 4000, 1, "suffix:px/s") var jump_speed: float = 900
|
||||
@export_range(0, 4000, 1, "suffix:px/s²") var acceleration: float = 800
|
||||
@export_range(0, 4000, 1, "suffix:px/s²") var deceleration: float = 1000
|
||||
|
||||
@onready var state_machine: AnimationNodeStateMachinePlayback = %AnimationTree["parameters/playback"]
|
||||
@onready var jumps: int = max_jumps
|
||||
@onready var gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")
|
||||
|
||||
func is_running():
|
||||
return Input.is_action_pressed("Move Left") or Input.is_action_pressed("Move Right")
|
||||
|
||||
func is_on_ledge():
|
||||
return is_on_floor() and not is_running() and not %Slip.is_colliding()
|
||||
|
||||
func did_jump():
|
||||
return Input.is_action_just_pressed("Move Up") and jumps > 0
|
||||
|
||||
func _ready() -> void:
|
||||
apply_floor_snap()
|
||||
|
||||
func clamp_dir(value: float, dir: float):
|
||||
return clampf(value, min(dir, 0.0), max(dir, 0.0))
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
velocity.y += gravity * delta
|
||||
velocity.y = minf(velocity.y, terminal_velocity)
|
||||
|
||||
var vertical: float = Input.get_axis("Move Down", "Move Up")
|
||||
var horizontal: float = Input.get_axis("Move Left", "Move Right")
|
||||
|
||||
velocity
|
||||
if is_on_floor():
|
||||
jumps = max_jumps
|
||||
if did_jump():
|
||||
if jumps != max_jumps:
|
||||
state_machine.start(state_machine.get_current_node(), true)
|
||||
jumps -= 1
|
||||
velocity.y = -jump_speed
|
||||
|
||||
var max_speed: float = speed * horizontal
|
||||
|
||||
velocity.x += acceleration * horizontal * delta
|
||||
if is_running():
|
||||
velocity.x = clamp_dir(velocity.x, max_speed)
|
||||
%animations.flip_h = horizontal > 0.0
|
||||
else:
|
||||
velocity.x -= clamp_dir(deceleration * velocity.x * delta, velocity.x)
|
||||
|
||||
if is_on_ledge():
|
||||
jumps = 0
|
||||
var direction: float = 1.0 if %SlipTestFront.is_colliding() else -1.0
|
||||
velocity.x += 10_000 * delta * direction
|
||||
|
||||
if move_and_slide() and is_on_wall():
|
||||
velocity.x = 0.0
|
||||
648
godot/mhjnr/Moorhuhn.tscn
Normal file
648
godot/mhjnr/Moorhuhn.tscn
Normal file
@@ -0,0 +1,648 @@
|
||||
[gd_scene load_steps=61 format=3 uid="uid://ctoj2a102rs6f"]
|
||||
|
||||
[ext_resource type="Script" path="res://mhjnr/Camera2D.gd" id="1_nngds"]
|
||||
[ext_resource type="SpriteFrames" path="datafile://data/player/sprites.txt" id="2_valkm"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_mob27"]
|
||||
size = Vector2(96, 85)
|
||||
|
||||
[sub_resource type="Animation" id="Animation_7sfno"]
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"mh_runW"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [0]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_v01jo"]
|
||||
resource_name = "crawl"
|
||||
length = 1.00001
|
||||
loop_mode = 1
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"MH_crawlWCycle"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333, 0.4, 0.466667, 0.533333, 0.6, 0.666667, 0.733333, 0.8, 0.866667, 0.933333, 1),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_jmosv"]
|
||||
resource_name = "crawl_end"
|
||||
length = 0.400007
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"MH_crawlWEnd"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333, 0.4),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4, 5, 6]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_8i52u"]
|
||||
resource_name = "crawl_start"
|
||||
length = 0.266673
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"MH_crawlWStart"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_f4p3b"]
|
||||
resource_name = "idle"
|
||||
length = 4.00001
|
||||
loop_mode = 1
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"MH_stayW_idle"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 1, 1.06667, 1.13333, 1.2, 1.26667, 1.33333, 2.26667, 2.33333, 2.4, 2.46667, 2.53333, 2.6, 2.66667, 3.6, 3.66667, 3.73333, 3.8, 3.86667, 3.93333),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_kugay"]
|
||||
resource_name = "jump"
|
||||
length = 0.266673
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"MH_stayW_jumpCycle"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_kbk86"]
|
||||
resource_name = "jump_end"
|
||||
length = 0.53334
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"MH_stayW_jumpEnd+particle"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333, 0.4, 0.466667, 0.533333),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4, 5, 6, 7, 8]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_0uslx"]
|
||||
resource_name = "jump_start"
|
||||
length = 0.33334
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"MH_stayW_jumpStart"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4, 5]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_bifhq"]
|
||||
resource_name = "run"
|
||||
length = 0.600007
|
||||
loop_mode = 1
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"mh_runW"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333, 0.4, 0.466667, 0.533333, 0.6),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_517vc"]
|
||||
resource_name = "run_end"
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"MH_runW_stop+particle"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333, 0.4, 0.466667, 0.533333, 0.6, 0.666667, 0.733333, 0.8, 0.866667, 0.933333, 1),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_1mxgl"]
|
||||
resource_name = "run_end_fall"
|
||||
length = 1.13334
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"mh_fall_down"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333, 0.4, 0.466667, 0.533333, 0.6, 0.666667, 0.733333, 0.8, 0.866667, 0.933333, 1, 1.06667, 1.13333),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_fmxky"]
|
||||
resource_name = "run_jump"
|
||||
length = 0.466673
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"MH_runW_jump"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333, 0.4, 0.466667),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4, 5, 6, 7]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_l4g2q"]
|
||||
resource_name = "run_smash_wall"
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:animation")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"mh_hit_smashedWall"]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:frame")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333, 0.4, 0.466667, 0.533333, 0.6, 0.666667, 0.733333, 0.8, 0.866667, 0.933333),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_khe4g"]
|
||||
resource_name = "run_start"
|
||||
length = 0.53334
|
||||
step = 0.0666667
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:frame")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333, 0.4, 0.466667, 0.533333),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 1,
|
||||
"values": [0, 1, 2, 3, 4, 5, 6, 7, 8]
|
||||
}
|
||||
tracks/1/type = "value"
|
||||
tracks/1/imported = false
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath(".:animation")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 1,
|
||||
"values": [&"MH_runW_1.stepStart"]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_jvkhi"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_7sfno"),
|
||||
"crawl": SubResource("Animation_v01jo"),
|
||||
"crawl_end": SubResource("Animation_jmosv"),
|
||||
"crawl_start": SubResource("Animation_8i52u"),
|
||||
"idle": SubResource("Animation_f4p3b"),
|
||||
"jump": SubResource("Animation_kugay"),
|
||||
"jump_end": SubResource("Animation_kbk86"),
|
||||
"jump_start": SubResource("Animation_0uslx"),
|
||||
"run": SubResource("Animation_bifhq"),
|
||||
"run_end": SubResource("Animation_517vc"),
|
||||
"run_end_fall": SubResource("Animation_1mxgl"),
|
||||
"run_jump": SubResource("Animation_fmxky"),
|
||||
"run_smash_wall": SubResource("Animation_l4g2q"),
|
||||
"run_start": SubResource("Animation_khe4g")
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_u60at"]
|
||||
animation = &"RESET"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_qt2l3"]
|
||||
animation = &"run"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_ohkqh"]
|
||||
animation = &"run_end"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_s2hko"]
|
||||
animation = &"run_smash_wall"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_t7lts"]
|
||||
animation = &"run_start"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_xgmeb"]
|
||||
advance_mode = 2
|
||||
advance_expression = "not is_running()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_x0rjg"]
|
||||
advance_mode = 2
|
||||
advance_expression = "is_on_wall()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_orvkk"]
|
||||
switch_mode = 2
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_mtp52"]
|
||||
switch_mode = 2
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_yfk7a"]
|
||||
switch_mode = 2
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_p4gp5"]
|
||||
advance_mode = 2
|
||||
advance_expression = "not is_running() or is_on_wall()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_suygg"]
|
||||
advance_mode = 2
|
||||
advance_expression = "is_running()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_750mf"]
|
||||
advance_mode = 2
|
||||
advance_expression = "velocity.x != 0"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_0ymww"]
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_gi4av"]
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_h8tv8"]
|
||||
states/End/position = Vector2(668, 134)
|
||||
states/RESET/node = SubResource("AnimationNodeAnimation_u60at")
|
||||
states/RESET/position = Vector2(367, -131)
|
||||
states/Start/position = Vector2(258, -131)
|
||||
states/run/node = SubResource("AnimationNodeAnimation_qt2l3")
|
||||
states/run/position = Vector2(668, -56)
|
||||
states/run_end/node = SubResource("AnimationNodeAnimation_ohkqh")
|
||||
states/run_end/position = Vector2(668, 32)
|
||||
states/run_smash_wall/node = SubResource("AnimationNodeAnimation_s2hko")
|
||||
states/run_smash_wall/position = Vector2(865, -131)
|
||||
states/run_start/node = SubResource("AnimationNodeAnimation_t7lts")
|
||||
states/run_start/position = Vector2(495, -131)
|
||||
transitions = ["run", "run_end", SubResource("AnimationNodeStateMachineTransition_xgmeb"), "run", "run_smash_wall", SubResource("AnimationNodeStateMachineTransition_x0rjg"), "run_smash_wall", "End", SubResource("AnimationNodeStateMachineTransition_orvkk"), "run_end", "End", SubResource("AnimationNodeStateMachineTransition_mtp52"), "run_start", "run", SubResource("AnimationNodeStateMachineTransition_yfk7a"), "run_start", "End", SubResource("AnimationNodeStateMachineTransition_p4gp5"), "run_end", "run_start", SubResource("AnimationNodeStateMachineTransition_suygg"), "run_smash_wall", "run_start", SubResource("AnimationNodeStateMachineTransition_750mf"), "Start", "RESET", SubResource("AnimationNodeStateMachineTransition_0ymww"), "RESET", "run_start", SubResource("AnimationNodeStateMachineTransition_gi4av")]
|
||||
graph_offset = Vector2(77, -267)
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_wubix"]
|
||||
animation = &"jump"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_1p554"]
|
||||
animation = &"idle"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_q376h"]
|
||||
animation = &"jump_start"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_kxajv"]
|
||||
animation = &"jump_end"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_io5l5"]
|
||||
animation = &"run_jump"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_mgepe"]
|
||||
animation = &"run_end_fall"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_khcbs"]
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_6878b"]
|
||||
advance_mode = 2
|
||||
advance_expression = "velocity.y > 0.0"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_1lxrg"]
|
||||
advance_mode = 2
|
||||
advance_expression = "is_on_floor()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_5me5o"]
|
||||
switch_mode = 2
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_nrhuk"]
|
||||
switch_mode = 2
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_og56a"]
|
||||
advance_mode = 2
|
||||
advance_expression = "is_running() and velocity.x != 0.0"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_do5xa"]
|
||||
advance_mode = 2
|
||||
advance_expression = "is_on_ledge()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_okk45"]
|
||||
switch_mode = 2
|
||||
advance_mode = 2
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_r33lm"]
|
||||
advance_mode = 2
|
||||
advance_expression = "is_on_floor() and not is_on_ledge()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_mqs3h"]
|
||||
advance_mode = 2
|
||||
advance_expression = "did_jump()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_jd8pa"]
|
||||
advance_mode = 2
|
||||
advance_expression = "velocity.y > 0.0"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_c8u27"]
|
||||
advance_mode = 2
|
||||
advance_expression = "did_jump()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_3lran"]
|
||||
advance_mode = 2
|
||||
advance_expression = "not is_running()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_8e8p2"]
|
||||
advance_mode = 2
|
||||
advance_expression = "is_on_floor()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_aivqa"]
|
||||
advance_mode = 2
|
||||
advance_expression = "is_running()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_pp0po"]
|
||||
advance_mode = 2
|
||||
advance_expression = "is_on_ledge()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_rh1ge"]
|
||||
advance_mode = 2
|
||||
advance_expression = "did_jump()"
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_lv3mp"]
|
||||
states/End/position = Vector2(893, 119)
|
||||
states/Run/node = SubResource("AnimationNodeStateMachine_h8tv8")
|
||||
states/Run/position = Vector2(592, 119)
|
||||
states/Start/position = Vector2(73, 119)
|
||||
states/fall/node = SubResource("AnimationNodeAnimation_wubix")
|
||||
states/fall/position = Vector2(430, -168)
|
||||
states/idle/node = SubResource("AnimationNodeAnimation_1p554")
|
||||
states/idle/position = Vector2(235, 119)
|
||||
states/jump/node = SubResource("AnimationNodeAnimation_q376h")
|
||||
states/jump/position = Vector2(144, -19)
|
||||
states/jump_end/node = SubResource("AnimationNodeAnimation_kxajv")
|
||||
states/jump_end/position = Vector2(235, -168)
|
||||
states/run_jump/node = SubResource("AnimationNodeAnimation_io5l5")
|
||||
states/run_jump/position = Vector2(592, -40)
|
||||
states/slip/node = SubResource("AnimationNodeAnimation_mgepe")
|
||||
states/slip/position = Vector2(421, 58)
|
||||
transitions = ["Start", "idle", SubResource("AnimationNodeStateMachineTransition_khcbs"), "jump", "fall", SubResource("AnimationNodeStateMachineTransition_6878b"), "fall", "jump_end", SubResource("AnimationNodeStateMachineTransition_1lxrg"), "jump_end", "idle", SubResource("AnimationNodeStateMachineTransition_5me5o"), "Run", "idle", SubResource("AnimationNodeStateMachineTransition_nrhuk"), "idle", "Run", SubResource("AnimationNodeStateMachineTransition_og56a"), "idle", "slip", SubResource("AnimationNodeStateMachineTransition_do5xa"), "slip", "fall", SubResource("AnimationNodeStateMachineTransition_okk45"), "slip", "jump_end", SubResource("AnimationNodeStateMachineTransition_r33lm"), "idle", "jump", SubResource("AnimationNodeStateMachineTransition_mqs3h"), "idle", "fall", SubResource("AnimationNodeStateMachineTransition_jd8pa"), "Run", "run_jump", SubResource("AnimationNodeStateMachineTransition_c8u27"), "run_jump", "fall", SubResource("AnimationNodeStateMachineTransition_3lran"), "run_jump", "Run", SubResource("AnimationNodeStateMachineTransition_8e8p2"), "jump_end", "Run", SubResource("AnimationNodeStateMachineTransition_aivqa"), "jump_end", "slip", SubResource("AnimationNodeStateMachineTransition_pp0po"), "jump_end", "jump", SubResource("AnimationNodeStateMachineTransition_rh1ge")]
|
||||
graph_offset = Vector2(-106, -229)
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachinePlayback" id="AnimationNodeStateMachinePlayback_udssx"]
|
||||
|
||||
[sub_resource type="AnimationNodeStateMachinePlayback" id="AnimationNodeStateMachinePlayback_n5wvc"]
|
||||
|
||||
[node name="Moorhuhn" type="CharacterBody2D"]
|
||||
script = ExtResource("1_nngds")
|
||||
jump_speed = 610.0
|
||||
|
||||
[node name="Slip" type="RayCast2D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
target_position = Vector2(0, 100)
|
||||
|
||||
[node name="SlipTestFront" type="RayCast2D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
position = Vector2(-55, 0)
|
||||
target_position = Vector2(0, 100)
|
||||
|
||||
[node name="Collision" type="CollisionShape2D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
position = Vector2(0, 40)
|
||||
shape = SubResource("RectangleShape2D_mob27")
|
||||
|
||||
[node name="animations" type="AnimatedSprite2D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
sprite_frames = ExtResource("2_valkm")
|
||||
animation = &"mh_runW"
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
root_node = NodePath("../animations")
|
||||
playback_process_mode = 0
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_jvkhi")
|
||||
}
|
||||
|
||||
[node name="AnimationTree" type="AnimationTree" parent="."]
|
||||
unique_name_in_owner = true
|
||||
tree_root = SubResource("AnimationNodeStateMachine_lv3mp")
|
||||
anim_player = NodePath("../AnimationPlayer")
|
||||
advance_expression_base_node = NodePath("..")
|
||||
active = true
|
||||
process_callback = 0
|
||||
parameters/playback = SubResource("AnimationNodeStateMachinePlayback_udssx")
|
||||
parameters/Run/playback = SubResource("AnimationNodeStateMachinePlayback_n5wvc")
|
||||
7
godot/mhjnr/camera.gd
Normal file
7
godot/mhjnr/camera.gd
Normal file
@@ -0,0 +1,7 @@
|
||||
extends Camera2D
|
||||
|
||||
var player: CharacterBody2D
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
position = player.position
|
||||
pass
|
||||
61
godot/mhjnr/level.gd
Normal file
61
godot/mhjnr/level.gd
Normal file
@@ -0,0 +1,61 @@
|
||||
@tool
|
||||
extends Node2D
|
||||
|
||||
@export var level_id: int = 1
|
||||
|
||||
func _ready() -> void:
|
||||
var player = preload("res://mhjnr/Moorhuhn.tscn").instantiate()
|
||||
add_child(player)
|
||||
%Camera.player = player
|
||||
%HudLevel.text = "Level %d" % level_id
|
||||
player.position = Vector2(200, 10)
|
||||
var camera_rect: Rect2i
|
||||
|
||||
var level: ObjectScript = load("datafile://data/level%02d/settings/level.txt" % level_id)
|
||||
for data in level.static_objects:
|
||||
match data.resource_type:
|
||||
"LevelSettings":
|
||||
%LevelTimer.start(data.props["levelTime"])
|
||||
"TiledLayer":
|
||||
var scene = load("datafile://data/level%02d/layers/%s.dat" % [level_id, data.name.to_lower()])
|
||||
var tiles: TileMap = scene.instantiate()
|
||||
tiles.name = data.name
|
||||
tiles.visible = data.props["is visible"] == 1
|
||||
|
||||
var used = tiles.get_used_rect()
|
||||
used.position *= tiles.cell_quadrant_size
|
||||
used.size *= tiles.cell_quadrant_size
|
||||
camera_rect = used if camera_rect == null else camera_rect.merge(used)
|
||||
|
||||
var scroll_speed: Vector2 = data.props["scroll speed"]
|
||||
if scroll_speed.is_equal_approx(Vector2(1, 1)):
|
||||
add_child(tiles)
|
||||
else:
|
||||
var parallax = ParallaxLayer.new()
|
||||
parallax.name = data.name
|
||||
parallax.visible = data.props["is visible"] == 1
|
||||
parallax.motion_scale = data.props["scroll speed"]
|
||||
%parallax.add_child(parallax)
|
||||
parallax.add_child(tiles)
|
||||
|
||||
%Camera.limit_left = camera_rect.position.x
|
||||
%Camera.limit_top = camera_rect.position.y
|
||||
%Camera.limit_right = camera_rect.position.x + camera_rect.size.x
|
||||
%Camera.limit_bottom = camera_rect.position.y + camera_rect.size.y
|
||||
|
||||
%WorldBoundLeft.position.x = camera_rect.position.x
|
||||
%WorldBoundTop.position.y = camera_rect.position.y
|
||||
%WorldBoundRight.position.x = camera_rect.position.x + camera_rect.size.x
|
||||
%WorldBoundBottom.position.y = camera_rect.position.y + camera_rect.size.y
|
||||
|
||||
#var enemies: ObjectScript = load("datafile://data/level%02d/layers/%s.dat" % [level_id, name.to_lower()])
|
||||
#for object in enemies.dynamic_objects:
|
||||
# match object.props["subType"]:
|
||||
## 1: create_movable(object)
|
||||
# 0: create_enemy(object)
|
||||
|
||||
func create_enemy(data: ObjectData):
|
||||
pass
|
||||
|
||||
func create_movable(data: ObjectData):
|
||||
pass
|
||||
142
godot/mhjnr/level.tscn
Normal file
142
godot/mhjnr/level.tscn
Normal file
@@ -0,0 +1,142 @@
|
||||
[gd_scene load_steps=14 format=3 uid="uid://bgb4avgjexp4t"]
|
||||
|
||||
[ext_resource type="Script" path="res://mhjnr/level.gd" id="1_dfqgf"]
|
||||
[ext_resource type="Theme" uid="uid://ks2uyxqg6u4k" path="res://mhjnr/theme.tres" id="3_a2fmg"]
|
||||
[ext_resource type="Script" path="res://mhjnr/camera.gd" id="3_e6xoo"]
|
||||
[ext_resource type="Texture2D" path="datafile://data/set1/sprites/hud_live.bmp" id="4_4bu8b"]
|
||||
[ext_resource type="Texture2D" path="datafile://data/set1/sprites/hud_shield.bmp" id="5_6bu8b"]
|
||||
[ext_resource type="Texture2D" path="datafile://data/set1/sprites/hud_bullet.bmp" id="5_rxbck"]
|
||||
|
||||
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_j78b3"]
|
||||
normal = Vector2(0, 1)
|
||||
|
||||
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_3mxdl"]
|
||||
normal = Vector2(-1, 0)
|
||||
|
||||
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_p4tp1"]
|
||||
normal = Vector2(1, 0)
|
||||
|
||||
[sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_ixcvh"]
|
||||
|
||||
[sub_resource type="GDScript" id="GDScript_nmpfh"]
|
||||
resource_name = "TimeLabel"
|
||||
script/source = "extends Label
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
var time_left = %LevelTimer.time_left
|
||||
text = \"%02d:%02d\" % [int(time_left / 60), int(time_left) % 60]
|
||||
"
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_glgkj"]
|
||||
atlas = ExtResource("5_6bu8b")
|
||||
region = Rect2(0, 0, 136, 42)
|
||||
filter_clip = true
|
||||
|
||||
[sub_resource type="AtlasTexture" id="AtlasTexture_2nrpq"]
|
||||
atlas = ExtResource("4_4bu8b")
|
||||
region = Rect2(0, 0, 36, 32)
|
||||
filter_clip = true
|
||||
|
||||
[node name="level" type="Node2D"]
|
||||
script = ExtResource("1_dfqgf")
|
||||
level_id = 2
|
||||
|
||||
[node name="Camera" type="Camera2D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
process_callback = 0
|
||||
limit_smoothed = true
|
||||
position_smoothing_enabled = true
|
||||
position_smoothing_speed = 10.0
|
||||
drag_horizontal_enabled = true
|
||||
drag_vertical_enabled = true
|
||||
script = ExtResource("3_e6xoo")
|
||||
|
||||
[node name="parallax" type="ParallaxBackground" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="WorldBounds" type="StaticBody2D" parent="."]
|
||||
|
||||
[node name="WorldBoundTop" type="CollisionShape2D" parent="WorldBounds"]
|
||||
unique_name_in_owner = true
|
||||
shape = SubResource("WorldBoundaryShape2D_j78b3")
|
||||
|
||||
[node name="WorldBoundRight" type="CollisionShape2D" parent="WorldBounds"]
|
||||
unique_name_in_owner = true
|
||||
shape = SubResource("WorldBoundaryShape2D_3mxdl")
|
||||
|
||||
[node name="WorldBoundLeft" type="CollisionShape2D" parent="WorldBounds"]
|
||||
unique_name_in_owner = true
|
||||
shape = SubResource("WorldBoundaryShape2D_p4tp1")
|
||||
|
||||
[node name="KillFloor" type="Area2D" parent="."]
|
||||
|
||||
[node name="WorldBoundBottom" type="CollisionShape2D" parent="KillFloor"]
|
||||
unique_name_in_owner = true
|
||||
shape = SubResource("WorldBoundaryShape2D_ixcvh")
|
||||
|
||||
[node name="LevelTimer" type="Timer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="HUD" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="HUD"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 32
|
||||
theme_override_constants/margin_top = 32
|
||||
theme_override_constants/margin_right = 32
|
||||
theme_override_constants/margin_bottom = 32
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="HUD/MarginContainer"]
|
||||
layout_mode = 2
|
||||
columns = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="HUD/MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Score" type="Label" parent="HUD/MarginContainer/GridContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "0035570"
|
||||
|
||||
[node name="Time" type="Label" parent="HUD/MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 6
|
||||
theme = ExtResource("3_a2fmg")
|
||||
text = "04:36"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
script = SubResource("GDScript_nmpfh")
|
||||
|
||||
[node name="HudLevel" type="Label" parent="HUD/MarginContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Level 4"
|
||||
|
||||
[node name="shield" type="TextureRect" parent="HUD/MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 10
|
||||
texture = SubResource("AtlasTexture_glgkj")
|
||||
|
||||
[node name="Lives" type="HBoxContainer" parent="HUD/MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 6
|
||||
size_flags_vertical = 8
|
||||
|
||||
[node name="Label" type="Label" parent="HUD/MarginContainer/GridContainer/Lives"]
|
||||
layout_mode = 2
|
||||
text = "4"
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="HUD/MarginContainer/GridContainer/Lives"]
|
||||
layout_mode = 2
|
||||
texture = SubResource("AtlasTexture_2nrpq")
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="HUD/MarginContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 8
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="HUD/MarginContainer/GridContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
texture = ExtResource("5_rxbck")
|
||||
6
godot/mhjnr/theme.tres
Normal file
6
godot/mhjnr/theme.tres
Normal file
@@ -0,0 +1,6 @@
|
||||
[gd_resource type="Theme" format=3 uid="uid://ks2uyxqg6u4k"]
|
||||
|
||||
[ext_resource type="FontFile" path="datafile://data/fonts/menufont.bmp" id="9_6bx8b"]
|
||||
|
||||
[resource]
|
||||
/fonts/menufont = ExtResource("9_6bx8b")
|
||||
50
godot/project.godot
Normal file
50
godot/project.godot
Normal file
@@ -0,0 +1,50 @@
|
||||
; Engine configuration file.
|
||||
; It's best edited using the editor UI and not directly,
|
||||
; since the parameters that go here are not all obvious.
|
||||
;
|
||||
; Format:
|
||||
; [section] ; section goes between []
|
||||
; param=value ; assign values to parameters
|
||||
|
||||
config_version=5
|
||||
|
||||
[application]
|
||||
|
||||
config/name="MHJNR"
|
||||
run/main_scene="res://mhjnr/level.tscn"
|
||||
config/features=PackedStringArray("4.0", "GL Compatibility")
|
||||
boot_splash/bg_color=Color(0, 0, 0, 1)
|
||||
boot_splash/image="res://icon.png"
|
||||
boot_splash/fullsize=false
|
||||
|
||||
[input]
|
||||
|
||||
"Move Up"={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
"Move Down"={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
"Move Right"={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
"Move Left"={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[physics]
|
||||
|
||||
2d/default_gravity=2000.0
|
||||
|
||||
[rendering]
|
||||
|
||||
renderer/rendering_method="gl_compatibility"
|
||||
renderer/rendering_method.mobile="gl_compatibility"
|
||||
BIN
icon.blend
Normal file
BIN
icon.blend
Normal file
Binary file not shown.
3
rust/.gitignore
vendored
Normal file
3
rust/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/target
|
||||
.idea
|
||||
mhjnr.iml
|
||||
832
rust/Cargo.lock
generated
Normal file
832
rust/Cargo.lock
generated
Normal file
@@ -0,0 +1,832 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "array-init"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
|
||||
[[package]]
|
||||
name = "binrw"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "272caaf6e0bfb7d508c0606e541e2c68f85c0d6352b62d0b299924eed59fe384"
|
||||
dependencies = [
|
||||
"array-init",
|
||||
"binrw_derive",
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binrw_derive"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb4b28c1e534d96213c8966bb9240095757aa0909128985f97d16afd2e7257a8"
|
||||
dependencies = [
|
||||
"either",
|
||||
"owo-colors",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"flume",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide 0.6.2",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.10.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"pin-project",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c"
|
||||
|
||||
[[package]]
|
||||
name = "godot"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#5d5132cffecc584f9c9ed1b55306dcbb60ec3972"
|
||||
dependencies = [
|
||||
"godot-core",
|
||||
"godot-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-bindings"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#5d5132cffecc584f9c9ed1b55306dcbb60ec3972"
|
||||
dependencies = [
|
||||
"godot4-prebuilt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-codegen"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#5d5132cffecc584f9c9ed1b55306dcbb60ec3972"
|
||||
dependencies = [
|
||||
"godot-bindings",
|
||||
"heck",
|
||||
"nanoserde",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-core"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#5d5132cffecc584f9c9ed1b55306dcbb60ec3972"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"godot-codegen",
|
||||
"godot-ffi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-ffi"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#5d5132cffecc584f9c9ed1b55306dcbb60ec3972"
|
||||
dependencies = [
|
||||
"godot-bindings",
|
||||
"godot-codegen",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot-macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/godot-rust/gdext?branch=master#5d5132cffecc584f9c9ed1b55306dcbb60ec3972"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"venial",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "godot4-prebuilt"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/godot-rust/godot4-prebuilt?branch=4.0.1#f9e8cfec0ec565201801b02160ef836800746617"
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"png",
|
||||
"qoi",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mhjnr"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"binrw",
|
||||
"godot",
|
||||
"image",
|
||||
"itertools",
|
||||
"serde",
|
||||
"serde-xml-rs",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanorand"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanoserde"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "755e7965536bc54d7c9fba2df5ada5bf835b0443fd613f0a53fa199a301839d3"
|
||||
dependencies = [
|
||||
"nanoserde-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nanoserde-derive"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed7a94da6c6181c35d043fc61c43ac96d3a5d739e7b8027f77650ba41504d6ab"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.160"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-xml-rs"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"xml-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.160"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
|
||||
[[package]]
|
||||
name = "venial"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61584a325b16f97b5b25fcc852eb9550843a251057a5e3e5992d2376f3df4bb2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
22
rust/Cargo.toml
Normal file
22
rust/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "mhjnr"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "mhjnr-viewer"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lib]
|
||||
name = "mhjnr"
|
||||
crate-type = ["cdylib", "lib"]
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.10.5"
|
||||
unicode-segmentation = "1.10.1"
|
||||
binrw = "0.11.1"
|
||||
serde = {version = "1.0.160", features = ["derive"]}
|
||||
serde-xml-rs = "0.6.0"
|
||||
image = "0.24.6"
|
||||
base64 = "0.21.0"
|
||||
godot = { git = "https://github.com/godot-rust/gdext", branch = "master" }
|
||||
46
rust/src/formats/datafile.rs
Normal file
46
rust/src/formats/datafile.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use binrw::{binread, NullString};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[binread]
|
||||
#[br(little, magic = b"MHJNR")]
|
||||
#[derive(Debug)]
|
||||
pub struct Datafile {
|
||||
#[br(align_after = 0x20)]
|
||||
pub edition: Edition,
|
||||
|
||||
#[br(temp)]
|
||||
pub count: u32,
|
||||
#[br(align_after = 0x20)]
|
||||
pub unk1: u32,
|
||||
|
||||
#[br(count = count)]
|
||||
pub files: Vec<FileEntry>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[derive(Debug)]
|
||||
pub enum Edition {
|
||||
#[br(magic = b"-XS")]
|
||||
Xs,
|
||||
#[br(magic = b"-XXL")]
|
||||
Xxl,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[binread]
|
||||
pub struct FileEntry {
|
||||
#[br(pad_size_to = 0x68)]
|
||||
pub name: NullString,
|
||||
pub pos: u32,
|
||||
#[br(pad_after = 0x10)]
|
||||
pub len: u32,
|
||||
}
|
||||
|
||||
impl Datafile {
|
||||
pub fn into_index(self) -> HashMap<String, FileEntry> {
|
||||
self.files
|
||||
.into_iter()
|
||||
.map(|entry| (entry.name.to_string(), entry))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
46
rust/src/formats/level.rs
Normal file
46
rust/src/formats/level.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use binrw::prelude::*;
|
||||
use binrw::{BinRead, Error};
|
||||
use image;
|
||||
use image::error::{DecodingError, ImageFormatHint};
|
||||
use image::{ImageError, ImageResult, Rgb, RgbImage};
|
||||
use std::io::Cursor;
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
pub struct LevelTile {
|
||||
pub index: u8,
|
||||
pub id: u8,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
pub struct LevelLayer {
|
||||
pub tile_count: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub unknown_2: u32,
|
||||
#[br(count = width * height)]
|
||||
pub tiles: Vec<LevelTile>,
|
||||
}
|
||||
|
||||
pub fn level_tile_data_to_image(tile_data: &[u8]) -> ImageResult<RgbImage> {
|
||||
let mut cursor = Cursor::new(tile_data);
|
||||
let layer = LevelLayer::read(&mut cursor).map_err(to_decoding_err)?;
|
||||
|
||||
let mut image = RgbImage::new(layer.width, layer.height);
|
||||
for y in 0..layer.height {
|
||||
for x in 0..layer.width {
|
||||
let tile = LevelTile::read(&mut cursor).map_err(to_decoding_err)?;
|
||||
image.put_pixel(x, y, Rgb([tile.id, tile.index, 0]));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(image)
|
||||
}
|
||||
|
||||
fn to_decoding_err(err: Error) -> ImageError {
|
||||
ImageError::Decoding(DecodingError::new(
|
||||
ImageFormatHint::Name(String::from("mhjnr_layer")),
|
||||
err,
|
||||
))
|
||||
}
|
||||
115
rust/src/formats/mod.rs
Normal file
115
rust/src/formats/mod.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
use crate::formats::datafile::FileEntry;
|
||||
use crate::formats::level::LevelLayer;
|
||||
use crate::formats::rle::RleImage;
|
||||
use crate::formats::sprites::Sprites;
|
||||
use crate::formats::txt::{decrypt_txt, DecryptError};
|
||||
use crate::formats::ui_xml::UiTag;
|
||||
use binrw::BinRead;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Debug;
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom};
|
||||
use std::path::Path;
|
||||
|
||||
pub mod datafile;
|
||||
pub mod level;
|
||||
pub mod rle;
|
||||
pub mod sprites;
|
||||
pub mod txt;
|
||||
pub mod ui_xml;
|
||||
|
||||
pub enum DatafileFile {
|
||||
Txt(String),
|
||||
Level(LevelLayer),
|
||||
Sprites(Vec<Sprites>),
|
||||
RleSprite(Box<RleImage>),
|
||||
Bitmap(Vec<u8>),
|
||||
Vorbis(Vec<u8>),
|
||||
TileCollision(String),
|
||||
Ui(UiTag),
|
||||
}
|
||||
|
||||
pub enum Error {
|
||||
Deserialization,
|
||||
UnknownFormat(String),
|
||||
UnknownError,
|
||||
Custom(String),
|
||||
DecryptError(DecryptError),
|
||||
}
|
||||
|
||||
fn custom_err<T>(e: T) -> Error
|
||||
where
|
||||
T: Debug,
|
||||
{
|
||||
Error::Custom(format!("{:#?}", e))
|
||||
}
|
||||
|
||||
pub fn load_data<R>(entry: &FileEntry, reader: &mut R) -> Result<DatafileFile, Error>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
reader
|
||||
.seek(SeekFrom::Start(entry.pos as u64))
|
||||
.map_err(custom_err)?;
|
||||
let mut data = vec![0u8; entry.len as usize];
|
||||
reader.read_exact(&mut data).map_err(custom_err)?;
|
||||
|
||||
let name = entry.name.to_string();
|
||||
let path = Path::new(&name);
|
||||
|
||||
match path
|
||||
.extension()
|
||||
.and_then(OsStr::to_str)
|
||||
.ok_or(Error::Custom("No extension".to_string()))?
|
||||
{
|
||||
"dat" => Ok(DatafileFile::Level(
|
||||
LevelLayer::read(&mut Cursor::new(data)).map_err(custom_err)?,
|
||||
)),
|
||||
"rle" => Ok(DatafileFile::RleSprite(Box::new(
|
||||
RleImage::read(&mut Cursor::new(data)).map_err(custom_err)?,
|
||||
))),
|
||||
"bmp" => Ok(DatafileFile::Bitmap(data)),
|
||||
"ogg" => Ok(DatafileFile::Vorbis(data)),
|
||||
"xml" => {
|
||||
serde_xml_rs::from_str::<UiTag>(String::from_utf8(data).map_err(custom_err)?.as_str())
|
||||
.map_err(custom_err)
|
||||
.map(DatafileFile::Ui)
|
||||
}
|
||||
"txt" => {
|
||||
let stem = path
|
||||
.file_stem()
|
||||
.and_then(OsStr::to_str)
|
||||
.ok_or(Error::Custom("Stem".to_string()))?;
|
||||
let decr = decrypt_txt(data.into_iter()).map_err(|e| Error::DecryptError(e))?;
|
||||
if stem.starts_with("tile_collision") {
|
||||
Ok(DatafileFile::TileCollision(decr))
|
||||
} else if stem == "sprites" {
|
||||
Ok(DatafileFile::Sprites(
|
||||
Sprites::parse(decr.as_str()).map_err(custom_err)?,
|
||||
))
|
||||
} else {
|
||||
Ok(DatafileFile::Txt(decr))
|
||||
}
|
||||
}
|
||||
/*Some("rle") => {
|
||||
let image: RleImage = RleImage::read(&mut Cursor::new(data)).unwrap();
|
||||
let path = Path::new(dat_path).with_file_name("res.gif");
|
||||
println!("{:?}", path);
|
||||
let mut encoder = GifEncoder::new(
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.unwrap(),
|
||||
);
|
||||
encoder.set_repeat(Repeat::Infinite).unwrap();
|
||||
encoder.try_encode_frames(image.into_frames()).unwrap();
|
||||
}
|
||||
Some("dat") => {
|
||||
let image = level_tile_data_to_image(&data).unwrap();
|
||||
let path = Path::new(dat_path).with_file_name("res.png");
|
||||
println!("{:?}", path);
|
||||
image.save_with_format(path, ImageFormat::Png).unwrap();
|
||||
}*/
|
||||
ext => Err(Error::UnknownFormat(ext.to_string())),
|
||||
}
|
||||
}
|
||||
131
rust/src/formats/rle.rs
Normal file
131
rust/src/formats/rle.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
use binrw::prelude::*;
|
||||
use binrw::Endian;
|
||||
use image::error::{DecodingError, ImageFormatHint};
|
||||
use image::{AnimationDecoder, Delay, Frame, Frames, ImageBuffer, ImageError};
|
||||
use std::io::{Read, Seek};
|
||||
use std::time::Duration;
|
||||
|
||||
#[binread]
|
||||
#[br(little, magic = 0x67u32)]
|
||||
pub struct RleImage {
|
||||
pub hash: u64,
|
||||
pub color_table: [[u8; 4]; 512],
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub numerator: u32,
|
||||
pub denominator: u32,
|
||||
#[br(temp)]
|
||||
pub frame_count: u32,
|
||||
#[br(count = frame_count)]
|
||||
pub frames: Vec<RleLayer>,
|
||||
}
|
||||
|
||||
#[binread]
|
||||
#[br(little)]
|
||||
pub struct RleLayer {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub left: u32,
|
||||
pub top: u32,
|
||||
pub numerator: u32,
|
||||
pub denominator: u32,
|
||||
pub data_size: u32,
|
||||
pub unknown3: u32,
|
||||
#[br(args(width * height), parse_with = parse_rle)]
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn parse_rle<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
endian: Endian,
|
||||
(size,): (u32,),
|
||||
) -> BinResult<Vec<u8>> {
|
||||
let mut data = Vec::with_capacity(size as usize);
|
||||
|
||||
while data.len() != size as usize {
|
||||
let count: i8 = reader.read_type(endian)?;
|
||||
if count > 0 {
|
||||
let value: u8 = reader.read_type(endian)?;
|
||||
for _ in 0..count {
|
||||
data.push(value);
|
||||
}
|
||||
} else {
|
||||
for _ in 0..-count {
|
||||
data.push(reader.read_type(endian)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
impl RleImage {
|
||||
pub fn get_image_data(&self, layer: &RleLayer) -> Vec<u8> {
|
||||
let mut data = Vec::<u8>::with_capacity(self.width as usize * self.height as usize * 4);
|
||||
let mut i = 0;
|
||||
for y in 0..self.height {
|
||||
for x in 0..self.width {
|
||||
if y < layer.top
|
||||
|| y >= layer.top + layer.height
|
||||
|| x < layer.left
|
||||
|| x >= layer.left + layer.width
|
||||
{
|
||||
data.push(0);
|
||||
data.push(0);
|
||||
data.push(0);
|
||||
data.push(0);
|
||||
} else {
|
||||
let color = self.color_table[layer.data[i] as usize];
|
||||
i += 1;
|
||||
data.push(color[2]);
|
||||
data.push(color[1]);
|
||||
data.push(color[0]);
|
||||
data.push(if color[2] == 0 && color[1] == 0 && color[0] == 0 {
|
||||
0
|
||||
} else {
|
||||
255
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AnimationDecoder<'a> for RleImage {
|
||||
fn into_frames(self) -> Frames<'a> {
|
||||
Frames::new(Box::new(self.frames.into_iter().map(move |frame| {
|
||||
let buffer = ImageBuffer::from_raw(
|
||||
frame.width,
|
||||
frame.height,
|
||||
frame
|
||||
.data
|
||||
.into_iter()
|
||||
.flat_map(|it| bgra_to_rgba(self.color_table[it as usize]))
|
||||
.collect(),
|
||||
)
|
||||
.ok_or(to_rle_image_err(std::fmt::Error::default()))?;
|
||||
Ok(Frame::from_parts(
|
||||
buffer,
|
||||
frame.left,
|
||||
frame.top,
|
||||
Delay::from_saturating_duration(Duration::from_millis(80)),
|
||||
))
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bgra_to_rgba(pixel: [u8; 4]) -> [u8; 4] {
|
||||
[pixel[2], pixel[1], pixel[0], pixel[3]]
|
||||
}
|
||||
|
||||
fn to_rle_image_err<T>(err: T) -> ImageError
|
||||
where
|
||||
T: Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||
{
|
||||
ImageError::Decoding(DecodingError::new(
|
||||
ImageFormatHint::Name(String::from("mhjnr_rle")),
|
||||
err,
|
||||
))
|
||||
}
|
||||
75
rust/src/formats/sprites.rs
Normal file
75
rust/src/formats/sprites.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
#[derive(Debug)]
|
||||
pub struct Sprites {
|
||||
pub name: String,
|
||||
pub sprite_type: SpriteType,
|
||||
pub file_name: String,
|
||||
pub render_mode: RenderMode,
|
||||
pub frames: Option<CropMode>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
InvalidData,
|
||||
UnknownEnum(String),
|
||||
}
|
||||
|
||||
impl Sprites {
|
||||
pub fn parse(string: &str) -> Result<Vec<Self>, Error> {
|
||||
string
|
||||
.split('\n')
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(Sprites::parse_single)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn parse_single(string: &str) -> Result<Self, Error> {
|
||||
let mut components = string.split_whitespace();
|
||||
|
||||
Ok(Sprites {
|
||||
file_name: components.next().ok_or(Error::InvalidData)?.to_string(),
|
||||
sprite_type: match components.next().ok_or(Error::InvalidData)? {
|
||||
"anim_rle" => SpriteType::AnimRle,
|
||||
"anim" => SpriteType::Anim,
|
||||
"static" => SpriteType::Static,
|
||||
e => return Err(Error::UnknownEnum(e.to_string())),
|
||||
},
|
||||
name: components.next().ok_or(Error::InvalidData)?.to_string(),
|
||||
render_mode: match components.next().ok_or(Error::InvalidData)? {
|
||||
"normx" => RenderMode::NormX,
|
||||
"flipx" => RenderMode::FlipX,
|
||||
e => return Err(Error::UnknownEnum(e.to_string())),
|
||||
},
|
||||
frames: if let Some(c) = components.next() {
|
||||
Some(match c {
|
||||
"nocrop" => CropMode::NoCrop,
|
||||
x => x
|
||||
.parse::<i32>()
|
||||
.map(CropMode::FrameCount)
|
||||
.map_err(|e| Error::UnknownEnum(e.to_string()))?,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CropMode {
|
||||
FrameCount(i32),
|
||||
NoCrop,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RenderMode {
|
||||
NormX,
|
||||
FlipX,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SpriteType {
|
||||
Static,
|
||||
Anim,
|
||||
AnimRle,
|
||||
}
|
||||
68
rust/src/formats/txt.rs
Normal file
68
rust/src/formats/txt.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use std::num::ParseIntError;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DecryptError {
|
||||
FromUtf8Error(FromUtf8Error),
|
||||
ParseIntError(ParseIntError),
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for DecryptError {
|
||||
fn from(e: FromUtf8Error) -> DecryptError {
|
||||
DecryptError::FromUtf8Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseIntError> for DecryptError {
|
||||
fn from(e: ParseIntError) -> DecryptError {
|
||||
DecryptError::ParseIntError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Decrypts txt files contained inside the dat file
|
||||
pub fn decrypt_txt<I>(buffer: I) -> Result<String, DecryptError>
|
||||
where
|
||||
I: Iterator<Item = u8>,
|
||||
{
|
||||
let mut key = 0x1234u16;
|
||||
|
||||
String::from_utf8(
|
||||
buffer
|
||||
.map(|char| {
|
||||
let decr = char ^ key as u8;
|
||||
key = key.wrapping_mul(3).wrapping_add(2);
|
||||
decr
|
||||
})
|
||||
.map(|char| (((char >> 1) ^ (char << 1)) & 0x55) ^ (char << 1))
|
||||
.collect(),
|
||||
)
|
||||
.map_err(DecryptError::from)
|
||||
}
|
||||
|
||||
/// Parses a hex string to a Vec<u8>
|
||||
fn from_hex(line: &str) -> Result<Vec<u8>, ParseIntError> {
|
||||
(0..line.len())
|
||||
.step_by(2)
|
||||
.map(|i| u8::from_str_radix(line.get(i..=i + 1).unwrap_or(""), 16))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// This function is applied to *exposed* txt files,
|
||||
/// such as the player profile or high scores
|
||||
///
|
||||
/// If the file is contained in the datafile, it has
|
||||
/// to first be decrypted normally and then again
|
||||
/// with this function.
|
||||
pub fn decrypt_exposed_txt(contents: String) -> Result<String, DecryptError> {
|
||||
contents
|
||||
.split_terminator("\r\n")
|
||||
.map(|line| line.trim())
|
||||
.filter(|line| !line.is_empty())
|
||||
.map(from_hex)
|
||||
.map(|line| decrypt_txt(line.map_err(DecryptError::from)?.into_iter()))
|
||||
.collect::<Result<Vec<String>, _>>()
|
||||
.map(|l| l.join("\r\n"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
77
rust/src/formats/ui_xml.rs
Normal file
77
rust/src/formats/ui_xml.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
use serde::de::Error;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub enum UiTag {
|
||||
Menu(UiMenu),
|
||||
Image(UiImage),
|
||||
TextButton(UiTextButton),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct UiMenu {
|
||||
pub selected: String,
|
||||
#[serde(rename = "OnBack")]
|
||||
pub on_back: String,
|
||||
#[serde(rename = "$value")]
|
||||
pub children: Vec<UiTag>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct UiImage {
|
||||
pub texture: String,
|
||||
#[serde(deserialize_with = "deserialize_vec2")]
|
||||
pub position: [i32; 2],
|
||||
#[serde(deserialize_with = "deserialize_vec2")]
|
||||
pub size: [i32; 2],
|
||||
#[serde(rename = "fademode")]
|
||||
pub fade_mode: FadeMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct UiTextButton {
|
||||
pub name: Option<String>,
|
||||
pub text: String,
|
||||
#[serde(deserialize_with = "deserialize_vec2")]
|
||||
pub position: [i32; 2],
|
||||
#[serde(rename = "halign")]
|
||||
pub horizontal_align: HorizontalAlign,
|
||||
#[serde(rename = "fademode")]
|
||||
pub fade_mode: FadeMode,
|
||||
#[serde(rename = "OnSelect")]
|
||||
pub on_select: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum HorizontalAlign {
|
||||
Center,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum FadeMode {
|
||||
None,
|
||||
}
|
||||
|
||||
fn deserialize_vec2<'de, D>(deserializer: D) -> Result<[i32; 2], D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let buf = String::deserialize(deserializer)?;
|
||||
let mut values: Vec<Result<i32, D::Error>> = buf
|
||||
.split(',')
|
||||
.into_iter()
|
||||
.map(|value| {
|
||||
// there's some typos so we have to cover that...
|
||||
value.split_ascii_whitespace().collect::<Vec<&str>>()[0]
|
||||
.trim()
|
||||
.parse::<i32>()
|
||||
.map_err(|err| Error::custom(err.to_string()))
|
||||
})
|
||||
.collect();
|
||||
let y = values.pop().ok_or(Error::custom("InvalidField"))??;
|
||||
let x = values.pop().ok_or(Error::custom("InvalidField"))??;
|
||||
|
||||
Ok([x, y])
|
||||
}
|
||||
272
rust/src/godot/datafile.rs
Normal file
272
rust/src/godot/datafile.rs
Normal file
@@ -0,0 +1,272 @@
|
||||
use crate::formats;
|
||||
use crate::formats::datafile::{Datafile, FileEntry};
|
||||
use crate::formats::sprites::{CropMode, RenderMode, SpriteType};
|
||||
use crate::formats::{load_data, DatafileFile};
|
||||
use crate::godot::font::load_bitmap_font;
|
||||
use crate::godot::game_object::parse_game_object;
|
||||
use crate::godot::image::{load_bmp_as_image_texture, load_rle_as_sprite_frames};
|
||||
use crate::godot::sprites::load_sprite_frames;
|
||||
use crate::godot::tile_map::{create_tile_map, TileCollision};
|
||||
use crate::godot::ui::convert_ui;
|
||||
use binrw::BinRead;
|
||||
use godot::engine::global::Error;
|
||||
use godot::engine::image::Format;
|
||||
use godot::engine::resource_loader::CacheMode;
|
||||
use godot::engine::resource_saver::SaverFlags;
|
||||
use godot::engine::utilities::{printerr, prints};
|
||||
use godot::engine::{
|
||||
AtlasTexture, AudioStream, AudioStreamOggVorbis, DirAccess, OggPacketSequence,
|
||||
PlaceholderTexture2D, SpriteFrames,
|
||||
};
|
||||
use godot::engine::{Image, PckPacker};
|
||||
use godot::engine::{ImageTexture, ProjectSettings};
|
||||
use godot::engine::{ResourceFormatLoader, ResourceSaver};
|
||||
use godot::engine::{ResourceFormatLoaderVirtual, ResourceLoader};
|
||||
use godot::prelude::*;
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::str::FromStr;
|
||||
|
||||
const DAT_PATH: &str = "E:\\Games\\Schatzjäger\\data\\datafile.dat";
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=ResourceFormatLoader)]
|
||||
pub struct DatafileLoader {
|
||||
pub datafile_table: HashMap<String, FileEntry>,
|
||||
|
||||
#[base]
|
||||
pub base: Base<ResourceFormatLoader>,
|
||||
}
|
||||
|
||||
fn convert_path(path: &GodotString) -> String {
|
||||
path.to_string()
|
||||
.strip_prefix("datafile://")
|
||||
.map(|it| it.replace('/', "\\"))
|
||||
.expect("Invalid path")
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl DatafileLoader {
|
||||
fn save_to_cache(&self, resource: Gd<Resource>, path: String) {
|
||||
let cache_path = self.get_cache_path(path);
|
||||
match DirAccess::make_dir_recursive_absolute(cache_path.rsplit_once('/').unwrap().0.into())
|
||||
{
|
||||
Error::OK => (),
|
||||
error => printerr(error.to_variant(), &[]),
|
||||
}
|
||||
ResourceSaver::singleton().save(resource, cache_path.into(), SaverFlags::FLAG_NONE);
|
||||
}
|
||||
|
||||
fn get_cache_path(&self, path: String) -> String {
|
||||
format!(
|
||||
"{}/.cache/{}",
|
||||
DAT_PATH
|
||||
.replace('\\', "/")
|
||||
.strip_suffix("datafile.dat")
|
||||
.unwrap(),
|
||||
path.replace('\\', "/")
|
||||
)
|
||||
}
|
||||
|
||||
fn retrieve_cache<T>(&self, path: String) -> Option<Gd<T>>
|
||||
where
|
||||
T: GodotClass + Inherits<Resource>,
|
||||
{
|
||||
let cache_path = self.get_cache_path(path);
|
||||
let type_hint = T::CLASS_NAME;
|
||||
if !ResourceLoader::singleton().exists(cache_path.clone().into(), type_hint.into()) {
|
||||
return None;
|
||||
}
|
||||
ResourceLoader::singleton()
|
||||
.load(
|
||||
cache_path.into(),
|
||||
type_hint.into(),
|
||||
CacheMode::CACHE_MODE_REUSE,
|
||||
)
|
||||
.map(|it| it.cast())
|
||||
}
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl ResourceFormatLoaderVirtual for DatafileLoader {
|
||||
fn init(base: Base<Self::Base>) -> Self {
|
||||
let mut file = File::open(DAT_PATH).unwrap();
|
||||
let datafile_table = Datafile::read(&mut file).unwrap().into_index();
|
||||
|
||||
DatafileLoader {
|
||||
base,
|
||||
datafile_table,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_recognized_extensions(&self) -> PackedStringArray {
|
||||
PackedStringArray::from(&[
|
||||
"xml".into(),
|
||||
"txt".into(),
|
||||
"rle".into(),
|
||||
"bmp".into(),
|
||||
"dat".into(),
|
||||
])
|
||||
}
|
||||
|
||||
fn recognize_path(&self, path: GodotString, _type: StringName) -> bool {
|
||||
path.to_string().starts_with("datafile://")
|
||||
}
|
||||
|
||||
fn get_resource_type(&self, path: GodotString) -> GodotString {
|
||||
if path.to_string().ends_with(".dat") {
|
||||
"PackedScene".into()
|
||||
} else {
|
||||
"Resource".into()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_resource_script_class(&self, _path: GodotString) -> GodotString {
|
||||
GodotString::from_str("").unwrap()
|
||||
}
|
||||
|
||||
fn exists(&self, path: GodotString) -> bool {
|
||||
self.datafile_table
|
||||
.contains_key(convert_path(&path).as_str())
|
||||
}
|
||||
|
||||
fn get_classes_used(&self, _path: GodotString) -> PackedStringArray {
|
||||
PackedStringArray::from(&[])
|
||||
}
|
||||
|
||||
fn load(
|
||||
&self,
|
||||
path: GodotString,
|
||||
_original_path: GodotString,
|
||||
_use_sub_threads: bool,
|
||||
_cache_mode: i64,
|
||||
) -> Variant {
|
||||
let datafile_path = convert_path(&path);
|
||||
if let Some(resource) = self.retrieve_cache::<Resource>(format!(
|
||||
"{}.{}",
|
||||
datafile_path,
|
||||
if datafile_path.ends_with(".xml") || datafile_path.ends_with("dat") {
|
||||
"scn"
|
||||
} else {
|
||||
"res"
|
||||
}
|
||||
)) {
|
||||
return resource.to_variant();
|
||||
}
|
||||
|
||||
if let Some(target) = self.datafile_table.get(datafile_path.as_str()) {
|
||||
let mut file = File::open(DAT_PATH).unwrap();
|
||||
match load_data(target, &mut file) {
|
||||
Ok(DatafileFile::Level(level)) => {
|
||||
let level_id = datafile_path
|
||||
.split_terminator('\\')
|
||||
.find(|i| i.starts_with("level"))
|
||||
.map(|lvl| u32::from_str(lvl.strip_prefix("level").unwrap()).unwrap())
|
||||
.unwrap();
|
||||
let tile_map = create_tile_map(level, level_id);
|
||||
|
||||
self.save_to_cache(tile_map.share().upcast(), format!("{}.scn", datafile_path));
|
||||
tile_map.to_variant()
|
||||
}
|
||||
Ok(DatafileFile::Txt(txt)) => {
|
||||
let game_object = parse_game_object(txt);
|
||||
self.save_to_cache(
|
||||
game_object.share().upcast(),
|
||||
format!("{}.res", datafile_path),
|
||||
);
|
||||
game_object.to_variant()
|
||||
}
|
||||
Ok(DatafileFile::Ui(ui)) => {
|
||||
let ui = convert_ui(ui);
|
||||
let mut scene = PackedScene::new();
|
||||
scene.pack(ui.upcast());
|
||||
|
||||
self.save_to_cache(scene.share().upcast(), format!("{}.scn", datafile_path));
|
||||
scene.to_variant()
|
||||
}
|
||||
Ok(DatafileFile::Vorbis(vorbis)) => {
|
||||
let mut audio = AudioStreamOggVorbis::new();
|
||||
audio.set_loop(true);
|
||||
let mut packet = OggPacketSequence::new();
|
||||
packet.set_packet_data(Array::from(&[Array::from(&[PackedByteArray::from(
|
||||
vorbis.as_slice(),
|
||||
)
|
||||
.to_variant()])]));
|
||||
audio.set_packet_sequence(packet);
|
||||
audio.to_variant()
|
||||
}
|
||||
Ok(DatafileFile::RleSprite(rle)) => load_rle_as_sprite_frames(*rle).to_variant(),
|
||||
Ok(DatafileFile::Sprites(sprites)) => {
|
||||
let sprite_frames = load_sprite_frames(sprites, path);
|
||||
|
||||
self.save_to_cache(
|
||||
sprite_frames.share().upcast(),
|
||||
format!("{}.res", datafile_path),
|
||||
);
|
||||
sprite_frames.to_variant()
|
||||
}
|
||||
Ok(DatafileFile::Bitmap(data)) => {
|
||||
let gd_image = match load_bmp_as_image_texture(data) {
|
||||
Ok(image) => image,
|
||||
Err(err) => return err.to_variant(),
|
||||
};
|
||||
|
||||
if datafile_path.contains("\\fonts\\") {
|
||||
let font = load_bitmap_font(gd_image);
|
||||
|
||||
self.save_to_cache(
|
||||
font.share().upcast(),
|
||||
format!("{}.tres", datafile_path),
|
||||
);
|
||||
font.to_variant()
|
||||
} else {
|
||||
let mut texture = ImageTexture::new();
|
||||
texture.set_image(gd_image);
|
||||
|
||||
self.save_to_cache(
|
||||
texture.share().upcast(),
|
||||
format!("{}.res", datafile_path),
|
||||
);
|
||||
texture.to_variant()
|
||||
}
|
||||
}
|
||||
Ok(DatafileFile::TileCollision(collision)) => {
|
||||
let tile_collision = Gd::new(TileCollision {
|
||||
collision: collision
|
||||
.chars()
|
||||
.filter_map(|c| c.to_digit(10))
|
||||
.map(|d| d as u8)
|
||||
.collect(),
|
||||
});
|
||||
|
||||
// No need to save this to cache, we only use this internally
|
||||
/*self.save_to_cache(
|
||||
tile_collision.share().upcast(),
|
||||
format!("{}.res", datafile_path),
|
||||
);*/
|
||||
tile_collision.to_variant()
|
||||
}
|
||||
Err(formats::Error::UnknownFormat(ext)) => {
|
||||
printerr(format!("Unknown format <{}>", ext).to_variant(), &[]);
|
||||
Error::ERR_FILE_UNRECOGNIZED.to_variant()
|
||||
}
|
||||
Err(formats::Error::Deserialization) => {
|
||||
printerr("Failed to deserialize".to_variant(), &[]);
|
||||
Error::ERR_FILE_CORRUPT.to_variant()
|
||||
}
|
||||
Err(formats::Error::Custom(message)) => {
|
||||
printerr(message.to_variant(), &[]);
|
||||
Error::ERR_BUG.to_variant()
|
||||
}
|
||||
_ => {
|
||||
printerr("Unknown error".to_variant(), &[]);
|
||||
Error::ERR_BUG.to_variant()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printerr("File not found".to_variant(), &[]);
|
||||
Error::ERR_FILE_NOT_FOUND.to_variant()
|
||||
}
|
||||
}
|
||||
}
|
||||
88
rust/src/godot/font.rs
Normal file
88
rust/src/godot/font.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use godot::builtin::{Rect2, Vector2, Vector2i};
|
||||
use godot::engine::{FontFile, Image};
|
||||
use godot::prelude::utilities::{print_verbose, prints};
|
||||
use godot::prelude::{Color, Gd, Share, ToVariant};
|
||||
use std::ops::Index;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
const CHARSET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜabcdefghijklmnopqrstuvwxyzäöüß0123456789,;.:!?\
|
||||
+-*/=<>()[]{}\"$%&#~_’^@|¡¿™©®º¹²³ªÀÁÂÃÅÆÇÈÉÊËÌÍÎÏIÐGÑÒÓÔÕŒØSŠÙÚÛÝÞŸŽàáâãåæçèéêëìíî\
|
||||
ïiðgñòóôõœøsšùúûýþÿž£¥ƒ¤¯¦¬¸¨·§×¢±÷µ«»";
|
||||
|
||||
pub fn load_bitmap_font(image: Gd<Image>) -> Gd<FontFile> {
|
||||
let mut font_chars = CHARSET.as_bytes().iter();
|
||||
|
||||
let mut font_file = FontFile::new();
|
||||
|
||||
let chroma_key = Color {
|
||||
r: 0.0,
|
||||
g: 0.0,
|
||||
b: 0.0,
|
||||
a: 0.0,
|
||||
};
|
||||
|
||||
let mut was_empty_column = true;
|
||||
let mut char_x = 0;
|
||||
let mut char_width = 0;
|
||||
let char_height = image.get_height();
|
||||
let char_y = 0;
|
||||
|
||||
let base_size = Vector2i { x: 16, y: 0 };
|
||||
|
||||
font_file.set_texture_image(0, base_size, 0, image.share());
|
||||
|
||||
for x in 0..image.get_width() {
|
||||
let is_empty_column = (0..image.get_height()).all(|y| image.get_pixel(x, y).a == 0.0);
|
||||
|
||||
if !was_empty_column && is_empty_column {
|
||||
let char = font_chars.next().expect("Font has too many characters!");
|
||||
let glyph = *char as i64;
|
||||
/*let mut glyph = 0i64;
|
||||
for (i, c) in char.bytes().rev().enumerate() {
|
||||
glyph |= (c as i64) << (i * 8);
|
||||
}*/
|
||||
|
||||
let glyph_offset = Vector2 {
|
||||
x: char_x as f32,
|
||||
y: char_y as f32,
|
||||
};
|
||||
let glyph_size = Vector2 {
|
||||
x: char_width as f32,
|
||||
y: char_height as f32,
|
||||
};
|
||||
|
||||
prints(
|
||||
"Glyph".to_variant(),
|
||||
&[
|
||||
(*char as char).to_string().to_variant(),
|
||||
glyph_offset.to_variant(),
|
||||
glyph_size.to_variant(),
|
||||
],
|
||||
);
|
||||
|
||||
// font_file.set_glyph_offset(0, base_size, glyph, glyph_offset);
|
||||
font_file.set_glyph_size(0, base_size, glyph, glyph_size);
|
||||
font_file.set_glyph_uv_rect(
|
||||
0,
|
||||
base_size,
|
||||
glyph,
|
||||
Rect2 {
|
||||
position: glyph_offset,
|
||||
size: glyph_size,
|
||||
},
|
||||
);
|
||||
font_file.set_glyph_texture_idx(0, base_size, glyph, 0);
|
||||
} else if was_empty_column && !is_empty_column {
|
||||
char_x = x;
|
||||
char_width = 0;
|
||||
}
|
||||
|
||||
char_width += 1;
|
||||
was_empty_column = is_empty_column;
|
||||
}
|
||||
|
||||
font_file.set_font_name("menufont".into());
|
||||
// font_file.set_cache_ascent(0, base_size.x, )
|
||||
|
||||
font_file
|
||||
}
|
||||
138
rust/src/godot/game_object.rs
Normal file
138
rust/src/godot/game_object.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
use godot::engine::Resource;
|
||||
use godot::prelude::*;
|
||||
use itertools::Itertools;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Resource, init)]
|
||||
pub struct ObjectScript {
|
||||
#[export]
|
||||
pub dynamic_objects: Array<Gd<ObjectData>>,
|
||||
#[export]
|
||||
pub static_objects: Array<Gd<ObjectData>>,
|
||||
#[base]
|
||||
base: Base<Resource>,
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl ObjectScript {}
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Resource, init)]
|
||||
pub struct ObjectData {
|
||||
#[export]
|
||||
pub class_type: GodotString,
|
||||
#[export]
|
||||
pub resource_type: GodotString,
|
||||
#[export]
|
||||
pub name: GodotString,
|
||||
#[export]
|
||||
pub props: Dictionary,
|
||||
#[export]
|
||||
pub children: Array<Gd<ObjectData>>,
|
||||
#[base]
|
||||
base: Base<Resource>,
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl ObjectData {}
|
||||
|
||||
pub fn parse_game_object(contents: String) -> Gd<ObjectScript> {
|
||||
Gd::<ObjectScript>::with_base(|base| {
|
||||
let mut object_script = ObjectScript {
|
||||
dynamic_objects: Array::new(),
|
||||
static_objects: Array::new(),
|
||||
base,
|
||||
};
|
||||
|
||||
let mut lines = contents
|
||||
.lines()
|
||||
.map(|l| l.trim())
|
||||
.filter(|l| !l.is_empty())
|
||||
.filter(|l| !l.starts_with('#'));
|
||||
|
||||
while let Some(line) = lines.next() {
|
||||
match line {
|
||||
"DYNAMIC OBJECT START" => {
|
||||
object_script.dynamic_objects.push(read_object(&mut lines))
|
||||
}
|
||||
"OBJECT START" => object_script.static_objects.push(read_object(&mut lines)),
|
||||
l => eprintln!("TODO: {}", l),
|
||||
};
|
||||
}
|
||||
|
||||
object_script
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_object<'s, I>(lines: &mut I) -> Gd<ObjectData>
|
||||
where
|
||||
I: Iterator<Item = &'s str>,
|
||||
{
|
||||
let class_type = lines
|
||||
.next()
|
||||
.unwrap()
|
||||
.strip_prefix("class type:")
|
||||
.unwrap()
|
||||
.trim()
|
||||
.trim_matches('"');
|
||||
let (resource_type, name) = lines
|
||||
.next()
|
||||
.unwrap()
|
||||
.splitn(2, ']')
|
||||
.map(|x| x.trim())
|
||||
.collect_tuple::<(&str, &str)>()
|
||||
.unwrap();
|
||||
|
||||
Gd::<ObjectData>::with_base(|base| {
|
||||
let mut object_data = ObjectData {
|
||||
class_type: class_type.into(),
|
||||
resource_type: resource_type
|
||||
.trim_start_matches('[')
|
||||
.trim_end_matches(']')
|
||||
.into(),
|
||||
name: name.trim_matches('"').into(),
|
||||
props: Dictionary::new(),
|
||||
children: Array::new(),
|
||||
base,
|
||||
};
|
||||
|
||||
lines.next();
|
||||
loop {
|
||||
match lines.next().unwrap() {
|
||||
"}" => break,
|
||||
l => {
|
||||
let (_, key, value) = l
|
||||
.splitn(3, '"')
|
||||
.map(|x| x.trim())
|
||||
.collect_tuple::<(&str, &str, &str)>()
|
||||
.unwrap();
|
||||
let values = value
|
||||
.split_whitespace()
|
||||
.map(|s| f32::from_str(s).unwrap())
|
||||
.collect_vec();
|
||||
object_data.props.insert(
|
||||
key,
|
||||
match values.len() {
|
||||
1 => values[0].to_variant(),
|
||||
2 => Vector2 {
|
||||
x: values[0],
|
||||
y: values[1],
|
||||
}
|
||||
.to_variant(),
|
||||
3 => Vector3 {
|
||||
x: values[0],
|
||||
y: values[1],
|
||||
z: values[2],
|
||||
}
|
||||
.to_variant(),
|
||||
_ => panic!(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object_data
|
||||
})
|
||||
}
|
||||
57
rust/src/godot/image.rs
Normal file
57
rust/src/godot/image.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use crate::formats::rle::RleImage;
|
||||
use godot::builtin::{Color, PackedByteArray};
|
||||
use godot::engine::global::Error;
|
||||
use godot::engine::image::Format;
|
||||
use godot::engine::{Image, ImageTexture, SpriteFrames};
|
||||
use godot::obj::Gd;
|
||||
|
||||
const FPS: f64 = 15.0;
|
||||
|
||||
pub fn load_rle_as_sprite_frames(rle: RleImage) -> Gd<SpriteFrames> {
|
||||
let mut frames = SpriteFrames::new();
|
||||
|
||||
frames.set_animation_loop("default".into(), true);
|
||||
frames.set_animation_speed("default".into(), FPS);
|
||||
|
||||
for frame in rle.frames.iter() {
|
||||
let mut image = Image::new();
|
||||
image.set_data(
|
||||
rle.width as i64,
|
||||
rle.height as i64,
|
||||
false,
|
||||
Format::FORMAT_RGBA8,
|
||||
PackedByteArray::from(rle.get_image_data(frame).as_slice()),
|
||||
);
|
||||
image.fix_alpha_edges();
|
||||
|
||||
let mut texture = ImageTexture::new();
|
||||
texture.set_image(image);
|
||||
frames.add_frame("default".into(), texture.upcast(), 1.0, 0);
|
||||
}
|
||||
|
||||
frames
|
||||
}
|
||||
|
||||
pub fn load_bmp_as_image_texture(data: Vec<u8>) -> Result<Gd<Image>, Error> {
|
||||
let mut image = Image::new();
|
||||
|
||||
match image.load_bmp_from_buffer(data.as_slice().into()) {
|
||||
Error::OK => {
|
||||
for x in 0..image.get_width() {
|
||||
for y in 0..image.get_height() {
|
||||
if image.get_pixel(x, y).is_equal_approx(Color {
|
||||
r: 1.0,
|
||||
g: 0.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
}) {
|
||||
image.set_pixel(x, y, Color::TRANSPARENT_BLACK);
|
||||
}
|
||||
}
|
||||
}
|
||||
image.fix_alpha_edges();
|
||||
Ok(image)
|
||||
}
|
||||
error => Err(error),
|
||||
}
|
||||
}
|
||||
7
rust/src/godot/mod.rs
Normal file
7
rust/src/godot/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub mod datafile;
|
||||
pub mod font;
|
||||
pub mod game_object;
|
||||
pub mod image;
|
||||
pub mod sprites;
|
||||
pub mod tile_map;
|
||||
pub mod ui;
|
||||
133
rust/src/godot/sprites.rs
Normal file
133
rust/src/godot/sprites.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use crate::formats::sprites::{CropMode, RenderMode, Sprites};
|
||||
use godot::builtin::{GodotString, Rect2, StringName, ToVariant, Vector2};
|
||||
use godot::engine::utilities::printerr;
|
||||
use godot::engine::{
|
||||
load, AtlasTexture, ImageTexture, PlaceholderTexture2D, ResourceLoader, SpriteFrames,
|
||||
};
|
||||
use godot::obj::{Gd, Share};
|
||||
use godot::prelude::GodotClass;
|
||||
|
||||
const FPS: f64 = 15.0;
|
||||
const SPRITE_EXTENSIONS: &[&str] = &["bmp", "rle"];
|
||||
|
||||
pub fn load_sprite_frames(sprites: Vec<Sprites>, path: GodotString) -> Gd<SpriteFrames> {
|
||||
let dir = path
|
||||
.to_string()
|
||||
.strip_suffix("/sprites.txt")
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let mut sprite_frames = SpriteFrames::new();
|
||||
for sprite in sprites.into_iter() {
|
||||
if let RenderMode::FlipX = sprite.render_mode {
|
||||
continue;
|
||||
}
|
||||
sprite_frames.add_animation(StringName::from(&sprite.name));
|
||||
sprite_frames.set_animation_speed(StringName::from(&sprite.name), FPS);
|
||||
|
||||
match select_from_extensions(&dir, &sprite.file_name) {
|
||||
Some((path, "rle")) => extract_rle_frames(&mut sprite_frames, &sprite, path),
|
||||
Some((path, "bmp")) => extract_bitmap_frames(&mut sprite_frames, &sprite, path),
|
||||
Some(_) | None => {
|
||||
printerr(
|
||||
format!("Missing sprite '{}'", sprite.file_name).to_variant(),
|
||||
&[],
|
||||
);
|
||||
let texture = PlaceholderTexture2D::new();
|
||||
sprite_frames.add_frame(
|
||||
StringName::from(&sprite.name),
|
||||
texture.upcast(),
|
||||
60.0 / FPS,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sprite_frames
|
||||
}
|
||||
|
||||
/// Loads an RLE file as SpriteFrames and extracts
|
||||
/// its frames into `sprite_frames`
|
||||
fn extract_rle_frames(sprite_frames: &mut SpriteFrames, sprite: &Sprites, path: String) {
|
||||
let frames: Gd<SpriteFrames> = load(path);
|
||||
for frame_idx in 0..frames.get_frame_count("default".into()) {
|
||||
sprite_frames.add_frame(
|
||||
StringName::from(&sprite.name),
|
||||
frames
|
||||
.get_frame_texture("default".into(), frame_idx)
|
||||
.unwrap(),
|
||||
60.0 / FPS,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads a bitmap and extracts its frames into `sprite_frames`
|
||||
/// creates an atlas if there are multiple frames.
|
||||
fn extract_bitmap_frames(sprite_frames: &mut SpriteFrames, sprite: &Sprites, path: String) {
|
||||
let texture: Gd<ImageTexture> = load(path);
|
||||
|
||||
let frame_count = if let Some(CropMode::FrameCount(frame_count)) = sprite.frames {
|
||||
frame_count
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
if frame_count > 1 {
|
||||
let height = texture.get_height();
|
||||
let width = texture.get_width();
|
||||
let frame_height = height / frame_count as i64;
|
||||
|
||||
for i in 0..frame_count as i64 {
|
||||
let mut atlas = AtlasTexture::new();
|
||||
atlas.set_atlas(texture.share().upcast());
|
||||
atlas.set_region(Rect2 {
|
||||
position: Vector2 {
|
||||
x: 0.0,
|
||||
y: (i * frame_height) as f32,
|
||||
},
|
||||
size: Vector2 {
|
||||
x: width as f32,
|
||||
y: frame_height as f32,
|
||||
},
|
||||
});
|
||||
|
||||
sprite_frames.add_frame(
|
||||
StringName::from(&sprite.name),
|
||||
atlas.upcast(),
|
||||
60.0 / FPS,
|
||||
0,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
sprite_frames.add_frame(
|
||||
StringName::from(&sprite.name),
|
||||
texture.upcast(),
|
||||
60.0 / FPS,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Selects the extension based on which file exists
|
||||
fn select_from_extensions(dir: &str, file_name: &str) -> Option<(String, &'static str)> {
|
||||
SPRITE_EXTENSIONS
|
||||
.iter()
|
||||
.map(|ext| {
|
||||
(
|
||||
format!("{}/sprites/{}.{}", dir, file_name.to_lowercase(), ext),
|
||||
*ext,
|
||||
)
|
||||
})
|
||||
.find(|(path, ext)| {
|
||||
ResourceLoader::singleton().exists(
|
||||
path.clone().into(),
|
||||
match *ext {
|
||||
"rle" => SpriteFrames::CLASS_NAME.to_string(),
|
||||
"bmp" => ImageTexture::CLASS_NAME.to_string(),
|
||||
_ => panic!(),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
})
|
||||
}
|
||||
139
rust/src/godot/tile_map.rs
Normal file
139
rust/src/godot/tile_map.rs
Normal file
@@ -0,0 +1,139 @@
|
||||
use crate::formats::level::LevelLayer;
|
||||
use godot::engine::global::Error;
|
||||
use godot::engine::utilities::{clampi, printerr};
|
||||
use godot::engine::{load, PackedScene};
|
||||
use godot::engine::{ImageTexture, TileSet};
|
||||
use godot::engine::{TileMap, TileSetAtlasSource};
|
||||
use godot::prelude::*;
|
||||
use godot::prelude::{Gd, PackedByteArray, Share, ToVariant};
|
||||
|
||||
pub fn create_tile_map(layer: LevelLayer, level_id: u32) -> Gd<PackedScene> {
|
||||
let mut tile_set = TileSet::new();
|
||||
tile_set.set_tile_size(Vector2i { x: 32, y: 32 });
|
||||
tile_set.add_physics_layer(0);
|
||||
let mut map = TileMap::new_alloc();
|
||||
map.set_tileset(tile_set.share());
|
||||
map.set_quadrant_size(32);
|
||||
|
||||
for x in 0..layer.width {
|
||||
for y in 0..layer.height {
|
||||
let tile = &layer.tiles[(y * layer.width + x) as usize];
|
||||
if tile.id == 0 {
|
||||
continue;
|
||||
}
|
||||
if !tile_set.has_source(tile.id as i64) {
|
||||
let atlas_id = tile.id as u32 + 1;
|
||||
let atlas = load_atlas(1, atlas_id, layer.tile_count);
|
||||
tile_set.add_source(atlas.share().upcast(), tile.id as i64);
|
||||
add_collision(atlas, level_id, atlas_id);
|
||||
}
|
||||
map.set_cell(
|
||||
0,
|
||||
Vector2i {
|
||||
x: x as i32,
|
||||
y: y as i32,
|
||||
},
|
||||
tile.id as i64,
|
||||
Vector2i {
|
||||
x: clampi(tile.index as i64 % 16, 0, 15) as i32,
|
||||
y: clampi(tile.index as i64 / 16, 0, 15) as i32,
|
||||
},
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut scene = PackedScene::new();
|
||||
let error = scene.pack(map.upcast());
|
||||
match error {
|
||||
Error::OK => (),
|
||||
e => printerr(e.to_variant(), &[]),
|
||||
}
|
||||
scene
|
||||
}
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Resource, init)]
|
||||
pub struct TileCollision {
|
||||
#[export]
|
||||
pub collision: PackedByteArray,
|
||||
}
|
||||
|
||||
#[godot_api]
|
||||
impl TileCollision {}
|
||||
|
||||
fn add_collision(atlas: Gd<TileSetAtlasSource>, level_id: u32, atlas_id: u32) {
|
||||
let tile_collision: Gd<TileCollision> = load(format!(
|
||||
"datafile://data/level{:02}/tile_collision_{:02}.txt",
|
||||
level_id, atlas_id
|
||||
));
|
||||
let width = atlas.get_atlas_grid_size().x;
|
||||
let height = atlas.get_atlas_grid_size().y;
|
||||
|
||||
let tile_width = atlas.get_texture_region_size().x as f32 / 2.0;
|
||||
let tile_height = atlas.get_texture_region_size().y as f32 / 2.0;
|
||||
let collision = &[
|
||||
Vector2 {
|
||||
x: -tile_width,
|
||||
y: -tile_height,
|
||||
},
|
||||
Vector2 {
|
||||
x: -tile_width,
|
||||
y: tile_height,
|
||||
},
|
||||
Vector2 {
|
||||
x: tile_width,
|
||||
y: tile_height,
|
||||
},
|
||||
Vector2 {
|
||||
x: tile_width,
|
||||
y: -tile_height,
|
||||
},
|
||||
];
|
||||
|
||||
for x in 0..width {
|
||||
for y in 0..height {
|
||||
let collision_data = tile_collision
|
||||
.bind()
|
||||
.collision
|
||||
.get((y * width + x) as usize);
|
||||
let mut data = atlas.get_tile_data(Vector2i { x, y }, 0).unwrap();
|
||||
if collision_data & 0x1 != 0 {
|
||||
data.add_collision_polygon(0);
|
||||
data.set_collision_polygon_points(0, 0, PackedVector2Array::from(collision));
|
||||
} else if collision_data & 0xfe != 0 {
|
||||
printerr(
|
||||
format!("Missing collision info for {}", collision_data).to_variant(),
|
||||
&[],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_atlas(set_id: u32, atlas_id: u32, tile_count: u32) -> Gd<TileSetAtlasSource> {
|
||||
let mut atlas = TileSetAtlasSource::new();
|
||||
let tex: Gd<ImageTexture> = load(format!(
|
||||
"datafile://data/set{}/sprites/tiles_{:02}.bmp",
|
||||
set_id, atlas_id,
|
||||
));
|
||||
let region_size = (tile_count as f32).sqrt();
|
||||
debug_assert_eq!(tex.get_width(), tex.get_height());
|
||||
debug_assert_eq!(region_size, region_size.trunc());
|
||||
|
||||
let tile_size = (tex.get_width() / region_size as i64) as i32;
|
||||
|
||||
atlas.set_texture(tex.upcast());
|
||||
atlas.set_texture_region_size(Vector2i {
|
||||
x: tile_size,
|
||||
y: tile_size,
|
||||
});
|
||||
|
||||
for x in 0..region_size as i32 {
|
||||
for y in 0..region_size as i32 {
|
||||
atlas.create_tile(Vector2i { x, y }, Vector2i { x: 1, y: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
atlas
|
||||
}
|
||||
58
rust/src/godot/ui.rs
Normal file
58
rust/src/godot/ui.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use crate::formats::ui_xml::{HorizontalAlign, UiTag};
|
||||
use godot::builtin::{GodotString, Vector2};
|
||||
use godot::engine::global::HorizontalAlignment;
|
||||
use godot::engine::node::InternalMode;
|
||||
use godot::engine::{Button, Container, Control, TextureRect};
|
||||
use godot::prelude::*;
|
||||
|
||||
pub fn convert_ui(ui: UiTag) -> Gd<Control> {
|
||||
match ui {
|
||||
UiTag::Menu(menu) => {
|
||||
let mut gd_menu = Container::new_alloc();
|
||||
for child in menu.children {
|
||||
gd_menu.add_child(
|
||||
convert_ui(child).upcast(),
|
||||
false,
|
||||
InternalMode::INTERNAL_MODE_FRONT,
|
||||
);
|
||||
}
|
||||
gd_menu.upcast()
|
||||
}
|
||||
UiTag::Image(image) => {
|
||||
let mut gd_image = TextureRect::new_alloc();
|
||||
gd_image.set_position(
|
||||
Vector2 {
|
||||
x: image.position[0] as f32,
|
||||
y: image.position[1] as f32,
|
||||
},
|
||||
false,
|
||||
);
|
||||
gd_image.set_size(
|
||||
Vector2 {
|
||||
x: image.size[0] as f32,
|
||||
y: image.size[1] as f32,
|
||||
},
|
||||
false,
|
||||
);
|
||||
gd_image.upcast()
|
||||
}
|
||||
UiTag::TextButton(button) => {
|
||||
let mut gd_button = Button::new_alloc();
|
||||
gd_button.set_position(
|
||||
Vector2 {
|
||||
x: button.position[0] as f32,
|
||||
y: button.position[1] as f32,
|
||||
},
|
||||
false,
|
||||
);
|
||||
gd_button.set_text_alignment(match button.horizontal_align {
|
||||
HorizontalAlign::Center => HorizontalAlignment::HORIZONTAL_ALIGNMENT_CENTER,
|
||||
});
|
||||
if let Some(name) = button.name {
|
||||
gd_button.set_name(GodotString::from(name));
|
||||
}
|
||||
gd_button.set_text(GodotString::from(button.text));
|
||||
gd_button.upcast()
|
||||
}
|
||||
}
|
||||
}
|
||||
40
rust/src/lib.rs
Normal file
40
rust/src/lib.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::godot::datafile::DatafileLoader;
|
||||
use ::godot::engine::class_macros::auto_register_classes;
|
||||
use ::godot::engine::{ResourceFormatLoaderVirtual, ResourceLoader};
|
||||
use ::godot::init::{gdextension, ExtensionLayer};
|
||||
use ::godot::prelude::{ExtensionLibrary, Gd, InitHandle, InitLevel, Share};
|
||||
|
||||
pub mod formats;
|
||||
pub mod godot;
|
||||
|
||||
struct Main {}
|
||||
|
||||
#[gdextension]
|
||||
unsafe impl ExtensionLibrary for Main {
|
||||
fn load_library(handle: &mut InitHandle) -> bool {
|
||||
handle.register_layer(InitLevel::Editor, ResourceLoaderLayer { datafile: None });
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
struct ResourceLoaderLayer {
|
||||
pub datafile: Option<Gd<DatafileLoader>>,
|
||||
}
|
||||
|
||||
impl ExtensionLayer for ResourceLoaderLayer {
|
||||
fn initialize(&mut self) {
|
||||
auto_register_classes();
|
||||
|
||||
self.datafile = Some(Gd::<DatafileLoader>::with_base(DatafileLoader::init));
|
||||
|
||||
ResourceLoader::singleton()
|
||||
.add_resource_format_loader(self.datafile.as_ref().unwrap().share().upcast(), true);
|
||||
}
|
||||
|
||||
fn deinitialize(&mut self) {
|
||||
if let Some(datafile) = &self.datafile {
|
||||
ResourceLoader::singleton().remove_resource_format_loader(datafile.share().upcast());
|
||||
self.datafile = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
130
rust/src/main.rs
Normal file
130
rust/src/main.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
use binrw::{BinRead, NullString};
|
||||
use image::codecs::gif::{GifEncoder, Repeat};
|
||||
use image::{AnimationDecoder, ImageFormat};
|
||||
use mhjnr::formats::datafile::Datafile;
|
||||
use mhjnr::formats::level::level_tile_data_to_image;
|
||||
use mhjnr::formats::rle::RleImage;
|
||||
use mhjnr::formats::sprites::Sprites;
|
||||
use mhjnr::formats::txt::{decrypt_exposed_txt, decrypt_txt};
|
||||
use mhjnr::formats::ui_xml::UiTag;
|
||||
use serde_xml_rs::from_str;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
|
||||
use std::path::Path;
|
||||
|
||||
fn extract(datafile: &Datafile, file: &mut File) {
|
||||
let target = "E:\\Games\\Schatzjäger\\data3";
|
||||
|
||||
for entry in &datafile.files {
|
||||
let file_name = format!("{}\\{}", target, entry.name);
|
||||
fs::create_dir_all(file_name.rsplit_once('\\').unwrap().0).unwrap();
|
||||
|
||||
file.seek(SeekFrom::Start(entry.pos as u64)).unwrap();
|
||||
let mut data = vec![0u8; entry.len as usize];
|
||||
file.read_exact(&mut data).unwrap();
|
||||
|
||||
if entry.name.to_string().ends_with(".txt") {
|
||||
let mut contents = decrypt_txt(data.into_iter()).unwrap();
|
||||
/*if entry
|
||||
.name
|
||||
.to_string()
|
||||
.split('\\')
|
||||
.collect::<Vec<&str>>()
|
||||
.len()
|
||||
== 1
|
||||
{
|
||||
contents = decrypt_exposed_txt(contents).unwrap();
|
||||
}*/
|
||||
File::create(file_name)
|
||||
.unwrap()
|
||||
.write_all(contents.as_bytes())
|
||||
.unwrap();
|
||||
} else if entry.name.to_string().ends_with(".rle") {
|
||||
let image: RleImage = RleImage::read(&mut Cursor::new(data)).unwrap();
|
||||
let mut encoder = GifEncoder::new(
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(format!(
|
||||
"{}.{}",
|
||||
file_name.strip_suffix(".rle").unwrap(),
|
||||
".gif"
|
||||
))
|
||||
.unwrap(),
|
||||
);
|
||||
encoder.set_repeat(Repeat::Infinite).unwrap();
|
||||
encoder.try_encode_frames(image.into_frames()).unwrap();
|
||||
} else {
|
||||
File::create(file_name)
|
||||
.unwrap()
|
||||
.write_all(data.as_slice())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let file_name = Some(NullString::from("data\\loading\\sprites.txt"));
|
||||
let dat_path = "E:\\Games\\Schatzjäger\\data\\datafile.dat";
|
||||
|
||||
let mut file = File::open(dat_path).unwrap();
|
||||
let dat: Datafile = Datafile::read(&mut file).unwrap();
|
||||
println!("{:#?}", dat);
|
||||
|
||||
extract(&dat, &mut file);
|
||||
|
||||
/*if let Some(file_name) = file_name {
|
||||
let target = dat.files.iter().find(|it| it.name == file_name).unwrap();
|
||||
file.seek(SeekFrom::Start(target.pos as u64)).unwrap();
|
||||
let mut data = vec![0u8; target.len as usize];
|
||||
file.read_exact(&mut data).unwrap();
|
||||
|
||||
match Path::new(&file_name.to_string())
|
||||
.extension()
|
||||
.and_then(OsStr::to_str)
|
||||
{
|
||||
Some("xml") => println!(
|
||||
"{:#?}",
|
||||
from_str::<UiTag>(String::from_utf8(data).unwrap().as_str())
|
||||
),
|
||||
Some("txt") => {
|
||||
if false {
|
||||
/*let decr = decrypt_txt(&mut data);
|
||||
let entries: String = decrypt_exposed_txt(decr);*/
|
||||
let decr = decrypt_txt(data.into_iter()).unwrap();
|
||||
println!("{}", &decr);
|
||||
let sprites = Sprites::parse(decr.as_str()).unwrap();
|
||||
println!("{:#?}", sprites);
|
||||
} else {
|
||||
println!("{}", decrypt_txt(data.into_iter()).unwrap())
|
||||
}
|
||||
}
|
||||
Some("rle") => {
|
||||
let image: RleImage = RleImage::read(&mut Cursor::new(data)).unwrap();
|
||||
let path = Path::new(dat_path).with_file_name("res.gif");
|
||||
println!("{:?}", path);
|
||||
let mut encoder = GifEncoder::new(
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.open(path)
|
||||
.unwrap(),
|
||||
);
|
||||
encoder.set_repeat(Repeat::Infinite).unwrap();
|
||||
encoder.try_encode_frames(image.into_frames()).unwrap();
|
||||
}
|
||||
Some("dat") => {
|
||||
let image = level_tile_data_to_image(&data).unwrap();
|
||||
let path = Path::new(dat_path).with_file_name("res.png");
|
||||
println!("{:?}", path);
|
||||
image.save_with_format(path, ImageFormat::Png).unwrap();
|
||||
}
|
||||
Some(ext) => eprintln!("Unknown file extension <{}>", ext),
|
||||
None => eprintln!("Failed to read"),
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
// pub fn decr2()
|
||||
Reference in New Issue
Block a user