Compare commits
8 Commits
534b7f45ca
..
0.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 91d88cd5c7 | |||
| 6dff45c659 | |||
| 2fd41f4f69 | |||
| a147527e91 | |||
| 09d4a60f43 | |||
| c0a022fed4 | |||
| 9458085abb | |||
| 565c132848 |
@@ -9,9 +9,10 @@
|
||||
.idea/
|
||||
|
||||
# Blender render previews (generated by render_anim_preview.py)
|
||||
Blender/preview/
|
||||
art/blender/preview/
|
||||
|
||||
Blender/*.blend1
|
||||
# Blender auto-save backups (.blend1, .blend2, … — Blender rewrites these on every save)
|
||||
art/blender/*.blend[0-9]
|
||||
|
||||
# Test output (generated screenshots, frame strips)
|
||||
tests/output/
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
[gd_resource type="Animation" format=3 uid="uid://bq71flatfddll"]
|
||||
|
||||
[resource]
|
||||
resource_name = "Bear_fx_smash_hit"
|
||||
length = 0.23333333
|
||||
tracks/0/type = "position_3d"
|
||||
tracks/0/imported = true
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath("Bear_fx_smash_hit")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = PackedFloat32Array(0, 1, 0, -0.51246816, 7.1560388, 0.033333335, 1, 0, -0.51246816, 7.1560388, 0.06666667, 1, 1.2735307, -1.4730668, 5.139501, 0.1, 1, 1.3610363, 0.27301335, 5.141279, 0.13333334, 1, 1.3735371, 2.1639488, 5.153728, 0.16666667, 1, 1.3573295, -0.04170864, 5.1945124, 0.2, 1, 1.3061407, -0.3338372, 5.1877995, 0.23333333, 1, 1.2735307, -0.37556988, 5.1810856)
|
||||
tracks/1/type = "rotation_3d"
|
||||
tracks/1/imported = true
|
||||
tracks/1/enabled = true
|
||||
tracks/1/path = NodePath("Bear_fx_smash_hit")
|
||||
tracks/1/interp = 1
|
||||
tracks/1/loop_wrap = true
|
||||
tracks/1/keys = PackedFloat32Array(0, 1, 0, 0, 0, 1, 0.033333335, 1, 0, 0, 0, 1, 0.06666667, 1, 0.045591418, -0.036703378, -0.0016762356, 0.9982843, 0.1, 1, 0.06741002, -0.02538766, 0.014408795, 0.99729824, 0.13333334, 1, 0.05603578, -0.017236687, 0.029419938, 0.99784636, 0.16666667, 1, -0.071329094, -0.014488678, 0.036211126, 0.9966901, 0.23333333, 1, -0.039237745, -0.015645763, 0.035726424, 0.99846846)
|
||||
tracks/2/type = "scale_3d"
|
||||
tracks/2/imported = true
|
||||
tracks/2/enabled = true
|
||||
tracks/2/path = NodePath("Bear_fx_smash_hit")
|
||||
tracks/2/interp = 1
|
||||
tracks/2/loop_wrap = true
|
||||
tracks/2/keys = PackedFloat32Array(0, 1, 0, 0, 0, 0.033333335, 1, 0, 0, 0, 0.06666667, 1, 0.55657715, 0.55657715, 0.5565772, 0.23333333, 1, 0.55657715, 0.55657715, 0.55657715)
|
||||
tracks/3/type = "position_3d"
|
||||
tracks/3/imported = true
|
||||
tracks/3/enabled = true
|
||||
tracks/3/path = NodePath("Bear_fx_jump_smash")
|
||||
tracks/3/interp = 1
|
||||
tracks/3/loop_wrap = true
|
||||
tracks/3/keys = PackedFloat32Array(0, 1, 0, -0.51246816, 0)
|
||||
tracks/4/type = "scale_3d"
|
||||
tracks/4/imported = true
|
||||
tracks/4/enabled = true
|
||||
tracks/4/path = NodePath("Bear_fx_jump_smash")
|
||||
tracks/4/interp = 1
|
||||
tracks/4/loop_wrap = true
|
||||
tracks/4/keys = PackedFloat32Array(0, 1, 0, 0, 0)
|
||||
tracks/5/type = "position_3d"
|
||||
tracks/5/imported = true
|
||||
tracks/5/enabled = true
|
||||
tracks/5/path = NodePath("Bear_fx_claw_attack_R")
|
||||
tracks/5/interp = 1
|
||||
tracks/5/loop_wrap = true
|
||||
tracks/5/keys = PackedFloat32Array(0, 1, -3.188013, 1.0970955, 1.7545929)
|
||||
tracks/6/type = "rotation_3d"
|
||||
tracks/6/imported = true
|
||||
tracks/6/enabled = true
|
||||
tracks/6/path = NodePath("Bear_fx_claw_attack_R")
|
||||
tracks/6/interp = 1
|
||||
tracks/6/loop_wrap = true
|
||||
tracks/6/keys = PackedFloat32Array(0, 1, 0, 0, 0, 1)
|
||||
tracks/7/type = "scale_3d"
|
||||
tracks/7/imported = true
|
||||
tracks/7/enabled = true
|
||||
tracks/7/path = NodePath("Bear_fx_claw_attack_R")
|
||||
tracks/7/interp = 1
|
||||
tracks/7/loop_wrap = true
|
||||
tracks/7/keys = PackedFloat32Array(0, 1, 0, 0, 0)
|
||||
tracks/8/type = "position_3d"
|
||||
tracks/8/imported = true
|
||||
tracks/8/enabled = true
|
||||
tracks/8/path = NodePath("Bear_fx_claw_attack_L")
|
||||
tracks/8/interp = 1
|
||||
tracks/8/loop_wrap = true
|
||||
tracks/8/keys = PackedFloat32Array(0, 1, -3.188013, 1.0970955, 1.7545929)
|
||||
tracks/9/type = "rotation_3d"
|
||||
tracks/9/imported = true
|
||||
tracks/9/enabled = true
|
||||
tracks/9/path = NodePath("Bear_fx_claw_attack_L")
|
||||
tracks/9/interp = 1
|
||||
tracks/9/loop_wrap = true
|
||||
tracks/9/keys = PackedFloat32Array(0, 1, 0, 0, 0, 1)
|
||||
tracks/10/type = "scale_3d"
|
||||
tracks/10/imported = true
|
||||
tracks/10/enabled = true
|
||||
tracks/10/path = NodePath("Bear_fx_claw_attack_L")
|
||||
tracks/10/interp = 1
|
||||
tracks/10/loop_wrap = true
|
||||
tracks/10/keys = PackedFloat32Array(0, 1, 0, 0, 0)
|
||||
tracks/11/type = "method"
|
||||
tracks/11/imported = false
|
||||
tracks/11/enabled = true
|
||||
tracks/11/path = NodePath("..")
|
||||
tracks/11/interp = 1
|
||||
tracks/11/loop_wrap = true
|
||||
tracks/11/keys = {
|
||||
"times": PackedFloat32Array(),
|
||||
"transitions": PackedFloat32Array(),
|
||||
"values": []
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
extends Node3D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group(&"public") # excluded from debug overlays — see publikum_controller.gd
|
||||
var anim := get_node_or_null(^"AnimationPlayer") as AnimationPlayer
|
||||
if anim:
|
||||
anim.play(&"Juubeldab")
|
||||
@@ -1 +0,0 @@
|
||||
uid://wj6tgh8nb0ka
|
||||
@@ -1,5 +0,0 @@
|
||||
extends Node3D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
$AnimationPlayer.play("ArmatureAction")
|
||||
@@ -1 +0,0 @@
|
||||
uid://gt3kfchac3m3
|
||||
|
Before Width: | Height: | Size: 1.3 MiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ch1wqha62vhtb"
|
||||
path="res://.godot/imported/IG_square.png-c38dd1a0a4601b6ff4ffec2910ed2ce0.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/IG_square.png"
|
||||
dest_files=["res://.godot/imported/IG_square.png-c38dd1a0a4601b6ff4ffec2910ed2ce0.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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
|
||||
|
Before Width: | Height: | Size: 598 KiB |
@@ -1,45 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://xtv6u00w4nmy"
|
||||
path.s3tc="res://.godot/imported/Lobby_level_sand.png-bb70b0169c6b5159e020ce143afe9786.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/Lobby_level_sand.png-bb70b0169c6b5159e020ce143afe9786.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "c8b9b7d6205ce80dfcfaa38152b996fe"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/Lobby_level_sand.png"
|
||||
dest_files=["res://.godot/imported/Lobby_level_sand.png-bb70b0169c6b5159e020ce143afe9786.s3tc.ctex", "res://.godot/imported/Lobby_level_sand.png-bb70b0169c6b5159e020ce143afe9786.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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=0
|
||||
|
Before Width: | Height: | Size: 412 KiB |
@@ -1,45 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://csy5f3l5lufp5"
|
||||
path.s3tc="res://.godot/imported/Lobby_level_wall.png-39243638bfc6ea5742b6e2fdc927ae2c.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/Lobby_level_wall.png-39243638bfc6ea5742b6e2fdc927ae2c.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "0e7ed34c3c812d96732bd32f9dca0c93"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/Lobby_level_wall.png"
|
||||
dest_files=["res://.godot/imported/Lobby_level_wall.png-39243638bfc6ea5742b6e2fdc927ae2c.s3tc.ctex", "res://.godot/imported/Lobby_level_wall.png-39243638bfc6ea5742b6e2fdc927ae2c.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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=0
|
||||
|
Before Width: | Height: | Size: 180 KiB |
@@ -1,45 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://ym5273astq46"
|
||||
path.s3tc="res://.godot/imported/Lobby_level_wood.png-4817792c09af3fd5e04ad29e0f9bd0ce.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/Lobby_level_wood.png-4817792c09af3fd5e04ad29e0f9bd0ce.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "23a7a951553ffe271c47693fd71314ca"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/Lobby_level_wood.png"
|
||||
dest_files=["res://.godot/imported/Lobby_level_wood.png-4817792c09af3fd5e04ad29e0f9bd0ce.s3tc.ctex", "res://.godot/imported/Lobby_level_wood.png-4817792c09af3fd5e04ad29e0f9bd0ce.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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=0
|
||||
|
Before Width: | Height: | Size: 47 KiB |
@@ -1,45 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://v0hdo12xule3"
|
||||
path.s3tc="res://.godot/imported/Lobby_underground_rock_B.jpg-c5e86461201f99688c8146fff239a62b.s3tc.ctex"
|
||||
path.etc2="res://.godot/imported/Lobby_underground_rock_B.jpg-c5e86461201f99688c8146fff239a62b.etc2.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "335b739e743244a64c47e2564a9a3652"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/Lobby_underground_rock_B.jpg"
|
||||
dest_files=["res://.godot/imported/Lobby_underground_rock_B.jpg-c5e86461201f99688c8146fff239a62b.s3tc.ctex", "res://.godot/imported/Lobby_underground_rock_B.jpg-c5e86461201f99688c8146fff239a62b.etc2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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=0
|
||||
@@ -1,12 +0,0 @@
|
||||
extends Node3D
|
||||
|
||||
|
||||
@onready var animation_player: AnimationPlayer = $AnimationPlayer
|
||||
|
||||
func _ready():
|
||||
animation_player.play("Taunt")
|
||||
animation_player.animation_finished.connect(_on_animation_finished)
|
||||
|
||||
func _on_animation_finished(anim_name):
|
||||
if anim_name == "Taunt":
|
||||
animation_player.play("Taunt")
|
||||
@@ -1 +0,0 @@
|
||||
uid://dycnoiadljbb1
|
||||
|
Before Width: | Height: | Size: 296 KiB |
@@ -1,40 +0,0 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bxv1uuymonrp6"
|
||||
path="res://.godot/imported/menu_bg.png-3906063022fa3c81090e3c663f8866b0.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/menu_bg.png"
|
||||
dest_files=["res://.godot/imported/menu_bg.png-3906063022fa3c81090e3c663f8866b0.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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
|
||||
@@ -1 +0,0 @@
|
||||
uid://ymaeifakqovf
|
||||
@@ -27,7 +27,12 @@ ui/ hud, mobile_hud, main_menu, ui_theme/fonts, over
|
||||
props/ barrels, torch
|
||||
levels/ game_shell.tscn + level modules
|
||||
tools/ tests/ editor/bake scripts, headless tests
|
||||
Assets/ Animations/ fonts/ HUD/ raw art (not yet consolidated under art/)
|
||||
art/ all raw art, grouped by role (snake_case):
|
||||
characters/ levels/ weapons/ bull/matador/bear, arena/lobby geometry, spear/sword
|
||||
fx/ anim/ textures/ bear_fx, crowd_clap, baked sheets
|
||||
ui/ icons/ fonts/ menu art, ability icons, Cinzel fonts
|
||||
hdri/ materials/ sky HDRIs, shared StandardMaterial3D resources
|
||||
blender/ Siim's .blend working files (Godot-ignored)
|
||||
```
|
||||
|
||||
| Path | Purpose |
|
||||
@@ -38,7 +43,9 @@ Assets/ Animations/ fonts/ HUD/ raw art (not yet consolidated under art/)
|
||||
| `levels/arena_level.tscn` | The arena **level module**: floor + wall colliders, arena meshes, `MatadorSpawner` (with child `Node3D` spawn points placed by the designer), lighting, audience (`PublikNode`). Instanced into the shell's `LevelRoot` |
|
||||
| `levels/bear_level.tscn` | The Bear's Den **level module**: bear-arena mesh, its own floor collider, lighting, and a `BearSpawner` (`bear_spawn.gd`) that places the bear at center. Instanced into the shell's `LevelRoot` |
|
||||
| `actors/player/player.tscn` | Player scene root (`CharacterBody3D`) |
|
||||
| `actors/player/player.gd` | Movement, charge, turning logic |
|
||||
| `actors/player/player.gd` | Movement, charge, turning, and abilities (kick/dash/slam/roll). Ability & movement particle FX are **not** built here — they're authored scenes in `systems/fx/` (`bull_*.tscn`), fired via `Fx.spawn()` (one-shots) / `Fx.attach()` (continuous) |
|
||||
| `actors/player/bull_impact_fx.gd` | Bull's coded, non-particle impact FX (the slam shockwave rings + kick flash-pop — tweened meshes, not CPUParticles3D, so they can't be `systems/fx/` scenes). Created by `player.gd` |
|
||||
| `actors/player/bull_legs.gd` | Bull body-wobble + tail Verlet-chain simulation. Created by `player.gd` |
|
||||
| `camera_spring_arm.gd` | Mouse-look pivot (`Node3D` + `SpringArm3D`) |
|
||||
| `camera_follow.gd` | Smooth camera follow (`Camera3D`) |
|
||||
| `actors/matador/matador.gd` | Matador AI (wander / ragdoll state machine) |
|
||||
@@ -47,10 +54,8 @@ Assets/ Animations/ fonts/ HUD/ raw art (not yet consolidated under art/)
|
||||
| `actors/bear/bear.tscn` / `actors/bear/bear.gd` | Dark-Souls-style bear boss (`CharacterBody3D`). Routine state machine (wind-up → active → recovery) picking telegraphed moves by range band: Double Swipe / Overhead Smash (close), Leap Slam (mid-far), plus Stalk (2-leg walk-in) / Prowl (4-leg lope) positioning. Melee steps in during the swing (`bear_lunge_speed`) + tracks the bull; the leap is frame-synced to JUMP_SMASH so the paws hit ground on the impact frame (`bear_leap_impact_frame`); slams kick up dust + shake. Combos chain, hyper-armour mid-swing + poise (`bear_stagger_cd`), phase 2 enrage under `bear_enrage_frac` HP. Never damages the bull by colliding — only swings/slams. A horns-first bull charge wounds it (`_sweep_bull_ram`: swept prev→now so a tunnelling/point-blank charge still lands, gated by `bear_ram_reach` + `bull_gore_arc`, edge-triggered = one charge per wound). Joins `&"matador"` so bull abilities already hit it. Tuning under the `Bear` DP section |
|
||||
| `actors/bear/bear_attack_hitboxes.tscn` | The bear's attack colliders as a self-contained sub-scene (instanced under `bear_model`): a `LeapSlamHitbox` (Hitbox + cylinder = ground circle) and `SmashHitbox` (Hitbox + box = forward shockwave lane), plus an `AnimationPlayer` whose `leap_slam` / `smash_travel` clips author each collider's size + travel — scrub to tweak, like `Bear_FX`. `actors/bear/bear.gd` only fires the clip, arms the knock-up shaping, and sweeps for overlaps while it runs (see `_setup_attack_hitboxes` / `_start_attack_hitbox`) |
|
||||
| `actors/matador/sword.tscn` / `systems/combat/hitbox.gd` | The matador's sword wrapper (glb + editor-tweakable `BladeHitbox`). `systems/combat/hitbox.gd` (`class_name Hitbox`, Area3D) is the shared attack collider: `activate()`/`deactivate()` gate an active window, `sweep()` catches bodies already inside (AoE), and knock-up can ramp by distance (`knock_up_min_dist`/`_max_dist`) for the bear's outer-ring launch / further-hit-flies-higher |
|
||||
| `levels/run_state.gd` | Run progression autoload (`Run`) — Slay-the-Spire map + player position across fights |
|
||||
| `levels/level_def.gd` | `LevelDef` resource — a level's name / map colour / `scene_path` (always the shell) / `level_scene` (the level module instanced into the shell) / optional `enemy_scene` (arena wave vs. a boss like the bear) |
|
||||
| `levels/map_node.gd` | `MapNode` — one runtime node on the run map (position, level, links) |
|
||||
| `levels/MapScreen.tscn` / `levels/map_screen.gd` | Between-fights map screen; win → pick a node → next level |
|
||||
| `levels/run_state.gd` | Run progression autoload (`Run`) — the lobby hub and which level is being fought, across scene reloads |
|
||||
| `levels/level_def.gd` | `LevelDef` resource — a level's name / `scene_path` (always the shell) / `level_scene` (the level module instanced into the shell) / optional `enemy_scene` (arena wave vs. a boss like the bear) |
|
||||
| `systems/crowd/crowd_marker.gd` | `CrowdMarker` (`@tool Node3D`, `class_name`) — designer drops one per billboard spectator via the editor's Add-Node dialog; picks one of six 60°-spaced facings from the `orientation` dropdown. Draws an editor-only preview (slab + forward arrow, never serialised) and joins the `&"crowd_marker"` group |
|
||||
| `systems/crowd/crowd_billboards.gd` / `systems/crowd/crowd_billboard.gdshader` | `MultiMeshInstance3D` that harvests every `CrowdMarker` (group `&"crowd_marker"`) into one draw call, using each marker's world position + chosen facing. Front/back sprite sheet baked by `tools/bake_crowd_sheet.gd`; falls back to a generated inward-facing ring when no markers are placed |
|
||||
| `systems/debug/debug_params.gd` | Runtime-tunable parameter registry (autoload `DP`). Mobile-relevant flags: `force_touch_controls`, `touch_debug` (also enabled on-device with `?debug=1` in the page URL — shows the live profiler overlay in `systems/input/touch_controls.gd`), and `use_rigid_skin` (default **off** — native GPU skinning everywhere now that the 4.7 web templates fixed the Mali/ANGLE invisible-skinning bug; `systems/render/rigid_skin.gd` is a retained opt-in fallback). Rigid-skin is gated through `Controls.rigid_skin_enabled()`, which a page URL param overrides for on-device A/B with no console: `?skin=1` forces the workaround on (for a device where native skinning still fails), `?noskin=1` forces native off. Turning the workaround off also removes its per-spawn SurfaceTool rebuild, which was the arena's matador-spawn lag |
|
||||
@@ -58,11 +63,11 @@ Assets/ Animations/ fonts/ HUD/ raw art (not yet consolidated under art/)
|
||||
| `ui/web_start_gate.gd` | Autoload `WebStartGate` — "TAP TO PLAY" panel shown on web+touch before play; the tap drives `Controls.request_fullscreen_landscape()` so the fullscreen/orientation churn happens with no in-flight touch to lose. Re-armable: polls browser fullscreen state and returns if the player leaves it (iPhone Safari can't fullscreen, so it stops nagging once a request never takes) |
|
||||
| `ui/mobile_hud.gd` | Mobile-only health readouts (`CanvasLayer`, instanced by `ui/hud.gd` only on touch): big bull pip row top-left + a Souls-style bear boss bar top-centre (discovered via group `&"bear"`, tracks `health_changed`). The desktop bottom pip cluster is hidden on touch |
|
||||
| `ui/orientation_guard.gd` | Autoload `OrientationGuard` — full-screen "rotate your device" prompt shown on touch devices while portrait |
|
||||
| `Assets/` | Raw 3D assets (`.glb`, `.fbx`) |
|
||||
| `Blender/` | Blender source files, animation scripts, FBX exports |
|
||||
| `Blender/create_matador_anims.py` | Creates walk + idle animations and exports FBX |
|
||||
| `Blender/render_anim_preview.py` | Renders animation preview PNGs for visual verification |
|
||||
| `Blender/preview/` | Output directory for animation preview images |
|
||||
| `art/` | All raw art, grouped by role (`characters/ levels/ weapons/ fx/ anim/ textures/ ui/ icons/ fonts/`). GLBs use **Extract Textures** import, so their embedded textures land beside them (e.g. `art/levels/arena_*.png`) named from the GLB — expected, keep them tracked |
|
||||
| `art/blender/` | Blender source files, animation scripts, FBX exports (Godot-ignored via `.gdignore`) |
|
||||
| `art/blender/create_matador_anims.py` | Creates walk + idle animations and exports FBX |
|
||||
| `art/blender/render_anim_preview.py` | Renders animation preview PNGs for visual verification |
|
||||
| `art/blender/preview/` | Output directory for animation preview images |
|
||||
| `tests/` | Headless test scripts (screenshot capture, logic validation) |
|
||||
| `addons/rider-plugin/` | JetBrains Rider IDE integration |
|
||||
|
||||
@@ -96,7 +101,7 @@ Assets/ Animations/ fonts/ HUD/ raw art (not yet consolidated under art/)
|
||||
|
||||
## Animation & bone conventions
|
||||
|
||||
### Blender rig (matador_v02)
|
||||
### Blender rig (matador)
|
||||
- **Armature bones:** `matador` (root) → `COG` → `chest` → `head`, `collarbone_L/R` → `arm_L/R` → `forearm_L/R` → `hand_L/R`, `leg_L/R` → `shin_L/R` → `foot_L/R`
|
||||
- **Rotation mode:** All animated bones use **XYZ Euler** (set in `create_matador_anims.py`)
|
||||
- **Bone-local axes:** Swing (forward/back) is on **local X**. Arm lowering from T-pose is on **local Y** (positive = left arm down, negative = right arm down). Knee bend is **positive X** (shins only bend backward).
|
||||
@@ -114,8 +119,8 @@ After FBX import, animations appear as `Armature|walk` and `Armature|idle` in An
|
||||
|
||||
### Verification workflow
|
||||
1. Edit bones/animations in Blender (`create_matador_anims.py`)
|
||||
2. Run render preview: `blender --background Blender/matador_v02.blend --python Blender/render_anim_preview.py`
|
||||
3. Inspect PNGs in `Blender/preview/` — check bone orientations **before** exporting to Godot
|
||||
2. Run render preview: `blender --background art/blender/matador.blend --python art/blender/render_anim_preview.py`
|
||||
3. Inspect PNGs in `art/blender/preview/` — check bone orientations **before** exporting to Godot
|
||||
4. Export FBX (done automatically by `create_matador_anims.py`)
|
||||
5. Re-import in Godot and test in-game
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[InternetShortcut]
|
||||
URL=https://www.youtube.com/watch?v=ZCb12AHKMfE
|
||||
@@ -1,14 +0,0 @@
|
||||
shader_type spatial;
|
||||
|
||||
void vertex() {
|
||||
// Called for every vertex the material is visible on.
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
// Called for every pixel the material is visible on.
|
||||
}
|
||||
|
||||
//void light() {
|
||||
// // Called for every pixel for every light affecting the material.
|
||||
// // Uncomment to replace the default light processing function with this one.
|
||||
//}
|
||||
@@ -1 +0,0 @@
|
||||
uid://bme1rkldtyrhu
|
||||
@@ -26,11 +26,11 @@ var _phase: Phase = Phase.WINDUP
|
||||
var _bull: CharacterBody3D = null
|
||||
var _skeleton: Skeleton3D = null
|
||||
var _anim_player: AnimationPlayer = null
|
||||
# Separate player baked into Assets/fx/Bear_FX.glb: each clip scales one FX mesh up from 0
|
||||
# Separate player baked into art/fx/bear_fx.glb: each clip scales one FX mesh up from 0
|
||||
# and back at its strike frame (self-hiding), so bear.gd just fires the matching clip.
|
||||
var _fx_player: AnimationPlayer = null
|
||||
var _fx_root: Node = null
|
||||
# Shaped attack colliders live in BearAttackHitboxes.tscn (under bear_model, so they track the
|
||||
# Shaped attack colliders live in bear_attack_hitboxes.tscn (under bear_model, so they track the
|
||||
# bear's facing): a ground-circle cylinder for the leap slam and a forward box lane for the smash.
|
||||
# Their sizes AND their travel/pop motion are authored in that scene's AnimationPlayer (clips
|
||||
# "leap_slam" / "smash_travel") — scrub them there to tweak. bear.gd only fires the clip, sets the
|
||||
@@ -635,7 +635,7 @@ func _try_hit(reach: float) -> bool:
|
||||
return true
|
||||
|
||||
|
||||
# Grab the designer-authored collider sub-scene (BearAttackHitboxes.tscn instanced under
|
||||
# Grab the designer-authored collider sub-scene (bear_attack_hitboxes.tscn instanced under
|
||||
# bear_model) and park both hitboxes deactivated. Shapes + travel are authored in that scene.
|
||||
func _setup_attack_hitboxes() -> void:
|
||||
var root := _mesh.get_node_or_null("AttackHitboxes") if _mesh else null
|
||||
@@ -920,18 +920,6 @@ func _die(hit_dir: Vector3) -> void:
|
||||
velocity = Vector3.ZERO
|
||||
_blood_burst.burst(global_position + Vector3(0.0, 1.4, 0.0), hit_dir)
|
||||
_play(_anim_death)
|
||||
# No death clip on the rig — topple the beast onto its side (about the hit direction,
|
||||
# so it falls the way it was struck) and sink it away, then free.
|
||||
#var fall_axis := Vector3(-hit_dir.z, 0.0, hit_dir.x)
|
||||
#if fall_axis.length() < 0.01:
|
||||
# fall_axis = Vector3.RIGHT
|
||||
#var tween := create_tween()
|
||||
#tween.tween_property(_mesh, "rotation",
|
||||
# _mesh.rotation + fall_axis.normalized() * deg_to_rad(88.0), 0.7)\
|
||||
# .set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_IN)
|
||||
#tween.tween_interval(2.5)
|
||||
#tween.tween_property(_mesh, "position:y", _mesh.position.y - 3.0, 1.0)
|
||||
#tween.tween_callback(queue_free)
|
||||
|
||||
|
||||
# ── Interface shared with the matador (guarded elsewhere via has_method) ─────────────
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[gd_scene format=3 uid="uid://o8ocjfprxa7u"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cn7fhjkro7637" path="res://actors/bear/bear.gd" id="1_beargd"]
|
||||
[ext_resource type="PackedScene" uid="uid://cofv5dbavesis" path="res://Assets/Bear.glb" id="2_bearglb"]
|
||||
[ext_resource type="PackedScene" uid="uid://bdbx0wwa73rhp" path="res://Assets/fx/Bear_FX.glb" id="3_bearfx"]
|
||||
[ext_resource type="Material" uid="uid://blpkmyikjnhbo" path="res://Materials/M_bear_claw_fx.tres" id="4_clawfx"]
|
||||
[ext_resource type="PackedScene" uid="uid://cofv5dbavesis" path="res://art/characters/bear.glb" id="2_bearglb"]
|
||||
[ext_resource type="PackedScene" uid="uid://bdbx0wwa73rhp" path="res://art/fx/bear_fx.glb" id="3_bearfx"]
|
||||
[ext_resource type="Material" uid="uid://blpkmyikjnhbo" path="res://art/materials/bear_claw_fx.tres" id="4_clawfx"]
|
||||
[ext_resource type="PackedScene" path="res://actors/bear/bear_attack_hitboxes.tscn" id="5_hitboxes"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
|
||||
|
||||
@@ -74,7 +74,7 @@ const _ANIM_TAUNTS: Array[StringName] = [&"Taunt", &"Taunt_B", &"Taunt_C"]
|
||||
# reparents from holster to hand at this point in the animation.
|
||||
const _DRAW_GRAB_AT: float = 0.55
|
||||
|
||||
# Wrapper scenes (glb + an editor-tweakable hitbox / collider) — see Sword.tscn / Spear.tscn.
|
||||
# Wrapper scenes (glb + an editor-tweakable hitbox / collider) — see sword.tscn / spear.tscn.
|
||||
const _SWORD_SCENE: PackedScene = preload("res://actors/matador/sword.tscn")
|
||||
const _SPEAR_SCENE: PackedScene = preload("res://actors/matador/spear.tscn")
|
||||
|
||||
@@ -603,7 +603,7 @@ func _tick_attack(delta: float) -> void:
|
||||
if _swing_timer > 0.0:
|
||||
_decelerate(22.0, delta)
|
||||
_play_anim(_ANIM_ATTACK)
|
||||
# Damage off the sword's own BladeHitbox (Sword.tscn), riding the animated blade — gored only
|
||||
# Damage off the sword's own BladeHitbox (sword.tscn), riding the animated blade — gored only
|
||||
# when the thrust reaches it. Arm at the thrust (mat_stab_strike_at) and sweep for the overlap.
|
||||
if _sword_blade_area != null and _swing_phase() >= DP.f("mat_stab_strike_at"):
|
||||
_sword_blade_area.activate()
|
||||
@@ -1189,7 +1189,7 @@ func _spawn_sword() -> void:
|
||||
_drawing = false
|
||||
_apply_sword_grip()
|
||||
|
||||
# The blade hitbox (Sword.tscn, hitbox.gd) is armed only during the ATTACK thrust (see
|
||||
# The blade hitbox (sword.tscn, hitbox.gd) is armed only during the ATTACK thrust (see
|
||||
# _tick_attack); off while merely carried, so brushing a drawn matador never gores the bull.
|
||||
_sword_blade_area = _sword_node.get_node_or_null("BladeHitbox") as Hitbox
|
||||
if _sword_blade_area != null:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[gd_scene format=3 uid="uid://d0te4mwbh33ag"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bvfa7xi0luysk" path="res://actors/matador/matador.gd" id="1_matgd"]
|
||||
[ext_resource type="PackedScene" uid="uid://eu2lqjhowj4t" path="res://Assets/Matador.glb" id="2_matfbx"]
|
||||
[ext_resource type="PackedScene" uid="uid://eu2lqjhowj4t" path="res://art/characters/matador.glb" id="2_matfbx"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
|
||||
radius = 0.25
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene format=3 uid="uid://c8spearwrap01"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://jepc74w8opbu" path="res://Assets/spear.glb" id="1_spearglb"]
|
||||
[ext_resource type="PackedScene" uid="uid://jepc74w8opbu" path="res://art/weapons/spear.glb" id="1_spearglb"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_shaft"]
|
||||
radius = 0.13
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene format=3 uid="uid://c8swordwrap01"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://xyvank512l5n" path="res://Assets/sword.glb" id="1_swordglb"]
|
||||
[ext_resource type="PackedScene" uid="uid://xyvank512l5n" path="res://art/weapons/sword.glb" id="1_swordglb"]
|
||||
[ext_resource type="Script" path="res://systems/combat/hitbox.gd" id="2_hitbox"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_blade"]
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
extends Node
|
||||
## Procedural (non-particle) bull impact FX: the expanding ground-slam shockwave rings and the
|
||||
## kick flash-pop. These are tween-animated MeshInstance3D flashes, not CPUParticles3D, so they
|
||||
## can't be authored as systems/fx/ scenes and live in code here. Created by player.gd; the
|
||||
## transient meshes are parented into the level (the bull's parent) so they hold their world
|
||||
## position while the bull keeps moving.
|
||||
|
||||
var _player: Node3D
|
||||
var _ring_mesh: ArrayMesh = null
|
||||
|
||||
|
||||
func setup(player: Node3D) -> void:
|
||||
_player = player
|
||||
|
||||
|
||||
# Three nested shockwave rings racing outward from a ground slam, each fading as it grows.
|
||||
func slam_rings(origin: Vector3, max_r: float) -> void:
|
||||
if _ring_mesh == null:
|
||||
_ring_mesh = _make_ring_mesh()
|
||||
_launch_ring(origin, max_r, 0.00, 0.55, Color(1.0, 0.70, 0.10, 0.85))
|
||||
_launch_ring(origin, max_r, 0.10, 0.52, Color(1.0, 0.50, 0.10, 0.65))
|
||||
_launch_ring(origin, max_r, 0.20, 0.48, Color(0.9, 0.30, 0.10, 0.50))
|
||||
|
||||
|
||||
# A quick bright flash-pop at the kick's contact point.
|
||||
func kick_flash(origin: Vector3) -> void:
|
||||
var mat := _flat_material(Color(1.0, 0.97, 0.40, 0.0))
|
||||
var sphere := SphereMesh.new()
|
||||
sphere.radius = 1.0
|
||||
sphere.height = 2.0
|
||||
sphere.radial_segments = 8
|
||||
sphere.rings = 4
|
||||
sphere.material = mat
|
||||
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = sphere
|
||||
mi.material_override = mat
|
||||
mi.scale = Vector3(0.05, 0.05, 0.05)
|
||||
_host().add_child(mi)
|
||||
mi.global_position = origin
|
||||
|
||||
var tween := create_tween()
|
||||
tween.set_parallel(true)
|
||||
tween.tween_property(mi, "scale", Vector3(0.7, 0.7, 0.7), 0.13)\
|
||||
.set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_OUT)
|
||||
var fade := func(a: float) -> void: mat.albedo_color.a = a
|
||||
tween.tween_method(fade, 0.92, 0.0, 0.14)\
|
||||
.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
||||
tween.tween_callback(mi.queue_free).set_delay(0.16)
|
||||
|
||||
|
||||
func _launch_ring(origin: Vector3, max_r: float, delay: float, duration: float, col: Color) -> void:
|
||||
var mat := _flat_material(Color(col.r, col.g, col.b, 0.0))
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = _ring_mesh
|
||||
mi.material_override = mat
|
||||
mi.scale = Vector3(0.02, 1.0, 0.02)
|
||||
_host().add_child(mi)
|
||||
mi.global_position = origin
|
||||
|
||||
var tween := create_tween()
|
||||
tween.set_parallel(true)
|
||||
tween.tween_property(mi, "scale", Vector3(max_r, 1.0, max_r), duration)\
|
||||
.set_delay(delay).set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_OUT)
|
||||
var fade := func(a: float) -> void: mat.albedo_color.a = a
|
||||
tween.tween_method(fade, col.a, 0.0, duration)\
|
||||
.set_delay(delay).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
||||
tween.tween_callback(mi.queue_free).set_delay(delay + duration + 0.05)
|
||||
|
||||
|
||||
func _flat_material(color: Color) -> StandardMaterial3D:
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
mat.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||
mat.albedo_color = color
|
||||
return mat
|
||||
|
||||
|
||||
# A flat annulus in the XZ plane (unit radius), scaled up at runtime into the shockwave ring.
|
||||
func _make_ring_mesh() -> ArrayMesh:
|
||||
const INNER_R: float = 0.82
|
||||
const OUTER_R: float = 1.00
|
||||
const SEGMENTS: int = 48
|
||||
|
||||
var verts := PackedVector3Array()
|
||||
var indices := PackedInt32Array()
|
||||
for i: int in SEGMENTS:
|
||||
var a0 := i / float(SEGMENTS) * TAU
|
||||
var a1 := (i + 1) / float(SEGMENTS) * TAU
|
||||
var vi := i * 4
|
||||
verts.append(Vector3(cos(a0) * INNER_R, 0.0, sin(a0) * INNER_R))
|
||||
verts.append(Vector3(cos(a0) * OUTER_R, 0.0, sin(a0) * OUTER_R))
|
||||
verts.append(Vector3(cos(a1) * OUTER_R, 0.0, sin(a1) * OUTER_R))
|
||||
verts.append(Vector3(cos(a1) * INNER_R, 0.0, sin(a1) * INNER_R))
|
||||
indices.append_array([vi, vi + 2, vi + 1, vi, vi + 3, vi + 2])
|
||||
|
||||
var arrays: Array = []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
arrays[Mesh.ARRAY_VERTEX] = verts
|
||||
arrays[Mesh.ARRAY_INDEX] = indices
|
||||
var mesh := ArrayMesh.new()
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
||||
return mesh
|
||||
|
||||
|
||||
func _host() -> Node:
|
||||
if _player != null and _player.get_parent() != null:
|
||||
return _player.get_parent()
|
||||
return _player
|
||||
@@ -0,0 +1 @@
|
||||
uid://sf5etma4kjt0
|
||||
@@ -8,7 +8,7 @@ const HOOF_OFFSETS: Array = [
|
||||
Vector3( 0.30, -0.25, 0.60),
|
||||
]
|
||||
|
||||
# Bull animation clips (Armature|* action names in Assets/bull.fbx). IDLE is the
|
||||
# Bull animation clips (Armature|* action names in art/characters/bull.fbx). IDLE is the
|
||||
# looping base pose; the others are one-shot ability clips that return to it.
|
||||
const _ANIM_IDLE: StringName = &"Armature|IDLE"
|
||||
const _ANIM_KICK: StringName = &"Armature|KICK"
|
||||
@@ -27,12 +27,11 @@ var _legs: Node
|
||||
var _was_on_floor: bool = false
|
||||
var _pre_slide_vel_y: float = 0.0
|
||||
|
||||
var _slam_ring_mesh: ArrayMesh = null
|
||||
# Attack-FX resources are identical every swing, so build them once and reuse. Recreating a fresh
|
||||
# StandardMaterial3D / SphereMesh per attack makes a mobile WebGL driver recompile the shader and
|
||||
# re-upload the mesh each time — a main-thread hitch "when attacking" that never shows on desktop.
|
||||
# Built once and reused: a fresh StandardMaterial3D/SphereMesh per spawn makes a mobile WebGL
|
||||
# driver recompile the shader + re-upload the mesh — a hitch "when attacking" absent on desktop.
|
||||
var _unshaded_mat: StandardMaterial3D = null
|
||||
var _particle_sphere_cache: Dictionary = {} # "radius:seg:ring" -> SphereMesh (shared)
|
||||
var _impact_fx: Node = null # bull_impact_fx.gd — the coded (non-particle) slam rings + kick flash
|
||||
|
||||
# Ability system — kick=0, dash=1, slam=2, roll=3
|
||||
var ability_cd: Array[float] = [0.0, 0.0, 0.0, 0.0]
|
||||
@@ -83,8 +82,12 @@ var _huff_timer: float = 0.0
|
||||
|
||||
enum DustState { NONE, WALK, CHARGE }
|
||||
var _dust_state: DustState = DustState.NONE
|
||||
var _walk_dust_ramp: Gradient = null
|
||||
var _charge_dust_ramp: Gradient = null
|
||||
# Hoof-dust scale/velocity per state (the look — mesh, colour, curve — lives in bull_hoof_dust.tscn).
|
||||
# A charge sprint kicks up bigger, faster dust than a walk.
|
||||
const _WALK_DUST_SCALE: Vector2 = Vector2(0.20, 0.40)
|
||||
const _WALK_DUST_VEL: Vector2 = Vector2(0.8, 1.8)
|
||||
const _CHARGE_DUST_SCALE: Vector2 = Vector2(0.35, 0.70)
|
||||
const _CHARGE_DUST_VEL: Vector2 = Vector2(2.8, 5.5)
|
||||
|
||||
|
||||
const RigidSkin = preload("res://systems/render/rigid_skin.gd")
|
||||
@@ -106,6 +109,7 @@ func _ready() -> void:
|
||||
_max_hp += _bonus_hp
|
||||
_hp += _bonus_hp
|
||||
_cube_scale = cube_guy.scale
|
||||
_setup_impact_fx()
|
||||
_setup_hoof_dust()
|
||||
_setup_legs()
|
||||
_setup_charge_trail()
|
||||
@@ -113,7 +117,6 @@ func _ready() -> void:
|
||||
_setup_audio()
|
||||
_bull_anim = _find_anim_player(cube_guy)
|
||||
_setup_bull_idle()
|
||||
DP.any_changed.connect(_on_dp_changed)
|
||||
|
||||
|
||||
func _setup_legs() -> void:
|
||||
@@ -123,113 +126,35 @@ func _setup_legs() -> void:
|
||||
_legs.call(&"setup", self, cube_guy)
|
||||
|
||||
|
||||
func _setup_impact_fx() -> void:
|
||||
_impact_fx = Node.new()
|
||||
_impact_fx.set_script(load("res://actors/player/bull_impact_fx.gd"))
|
||||
add_child(_impact_fx)
|
||||
_impact_fx.call(&"setup", self)
|
||||
|
||||
|
||||
# The bull's ability/movement particle effects are authored as scenes in systems/fx/ (so Siim can
|
||||
# tune them in the inspector). The continuous ones follow the bull — instanced once here as
|
||||
# children, then toggled via `emitting`. The one-shots are fired on demand through Fx.spawn().
|
||||
func _setup_hoof_dust() -> void:
|
||||
var sphere := _particle_sphere(0.07, 4, 2, _unshaded_material())
|
||||
|
||||
_walk_dust_ramp = Gradient.new()
|
||||
_walk_dust_ramp.set_color(0, Color(0.94, 0.90, 0.83, 1.0))
|
||||
_walk_dust_ramp.set_color(1, Color(0.94, 0.90, 0.83, 0.0))
|
||||
|
||||
# White-beige dust while running
|
||||
_charge_dust_ramp = Gradient.new()
|
||||
_charge_dust_ramp.set_color(0, Color(0.94, 0.90, 0.83, 1.0))
|
||||
_charge_dust_ramp.set_color(1, Color(0.94, 0.90, 0.83, 0.0))
|
||||
|
||||
var scale_curve := Curve.new()
|
||||
scale_curve.add_point(Vector2(0.0, 0.2))
|
||||
scale_curve.add_point(Vector2(0.4, 1.0))
|
||||
scale_curve.add_point(Vector2(1.0, 0.0))
|
||||
|
||||
for offset: Vector3 in HOOF_OFFSETS:
|
||||
var p := CPUParticles3D.new()
|
||||
var p := Fx.attach(&"bull_hoof_dust", cube_guy) as CPUParticles3D
|
||||
if p == null:
|
||||
continue
|
||||
p.position = offset
|
||||
p.emitting = false
|
||||
p.amount = 12
|
||||
p.lifetime = DP.f("dust_lifetime")
|
||||
p.explosiveness = DP.f("dust_explosiveness")
|
||||
p.randomness = 0.6
|
||||
p.local_coords = false
|
||||
p.direction = Vector3(0.0, 1.0, 0.0)
|
||||
p.spread = DP.f("dust_spread")
|
||||
p.gravity = Vector3(0.0, DP.f("dust_gravity_y"), 0.0)
|
||||
p.initial_velocity_min = DP.f("walk_vel_min")
|
||||
p.initial_velocity_max = DP.f("walk_vel_max")
|
||||
p.scale_amount_min = DP.f("walk_scale_min")
|
||||
p.scale_amount_max = DP.f("walk_scale_max")
|
||||
p.mesh = sphere
|
||||
p.color_ramp = _walk_dust_ramp
|
||||
p.scale_amount_curve = scale_curve
|
||||
cube_guy.add_child(p)
|
||||
_hoof_emitters.append(p)
|
||||
|
||||
|
||||
func _setup_charge_trail() -> void:
|
||||
# Blue energy trail — streams backward during a fast run / roll
|
||||
var trail_sphere := _particle_sphere(0.065, 4, 2, _unshaded_material())
|
||||
|
||||
var trail_ramp := Gradient.new()
|
||||
trail_ramp.set_color(0, Color(0.30, 0.65, 1.0, 1.0))
|
||||
trail_ramp.set_color(1, Color(0.30, 0.65, 1.0, 0.0))
|
||||
|
||||
var trail_scale := Curve.new()
|
||||
trail_scale.add_point(Vector2(0.0, 1.0))
|
||||
trail_scale.add_point(Vector2(0.6, 0.8))
|
||||
trail_scale.add_point(Vector2(1.0, 0.0))
|
||||
|
||||
_charge_trail_emitter = CPUParticles3D.new()
|
||||
_charge_trail_emitter.position = Vector3(0.0, 0.25, 0.3)
|
||||
_charge_trail_emitter.emitting = false
|
||||
_charge_trail_emitter.one_shot = false
|
||||
_charge_trail_emitter.amount = 18
|
||||
_charge_trail_emitter.lifetime = 0.30
|
||||
_charge_trail_emitter.randomness = 0.4
|
||||
_charge_trail_emitter.local_coords = false
|
||||
_charge_trail_emitter.direction = Vector3(0.0, 0.2, 1.0).normalized()
|
||||
_charge_trail_emitter.spread = 25.0
|
||||
_charge_trail_emitter.gravity = Vector3(0.0, 2.0, 0.0)
|
||||
_charge_trail_emitter.initial_velocity_min = 4.0
|
||||
_charge_trail_emitter.initial_velocity_max = 9.0
|
||||
_charge_trail_emitter.scale_amount_min = 0.4
|
||||
_charge_trail_emitter.scale_amount_max = 1.2
|
||||
_charge_trail_emitter.mesh = trail_sphere
|
||||
_charge_trail_emitter.color_ramp = trail_ramp
|
||||
_charge_trail_emitter.scale_amount_curve = trail_scale
|
||||
cube_guy.add_child(_charge_trail_emitter)
|
||||
_charge_trail_emitter = Fx.attach(&"bull_charge_trail", cube_guy) as CPUParticles3D
|
||||
if _charge_trail_emitter != null:
|
||||
_charge_trail_emitter.position = Vector3(0.0, 0.25, 0.3)
|
||||
|
||||
|
||||
func _setup_roll_sparks() -> void:
|
||||
# Rammus Powerball ground spray — dirt/sparks flung outward from the spinning
|
||||
# ball. World-space (local_coords off) so they peel off into a trail behind it.
|
||||
var spark := _particle_sphere(0.05, 4, 2, _unshaded_material())
|
||||
|
||||
var ramp := Gradient.new()
|
||||
ramp.set_color(0, Color(1.00, 0.78, 0.32, 1.0))
|
||||
ramp.set_color(1, Color(0.70, 0.42, 0.14, 0.0))
|
||||
|
||||
var sc := Curve.new()
|
||||
sc.add_point(Vector2(0.0, 0.30))
|
||||
sc.add_point(Vector2(0.3, 1.00))
|
||||
sc.add_point(Vector2(1.0, 0.00))
|
||||
|
||||
_roll_spark_emitter = CPUParticles3D.new()
|
||||
_roll_spark_emitter.position = Vector3(0.0, 0.10, 0.0)
|
||||
_roll_spark_emitter.emitting = false
|
||||
_roll_spark_emitter.one_shot = false
|
||||
_roll_spark_emitter.amount = 46
|
||||
_roll_spark_emitter.lifetime = 0.45
|
||||
_roll_spark_emitter.randomness = 0.7
|
||||
_roll_spark_emitter.local_coords = false
|
||||
_roll_spark_emitter.direction = Vector3(0.0, 1.0, 0.0)
|
||||
_roll_spark_emitter.spread = 180.0
|
||||
_roll_spark_emitter.gravity = Vector3(0.0, -6.0, 0.0)
|
||||
_roll_spark_emitter.initial_velocity_min = 3.0
|
||||
_roll_spark_emitter.initial_velocity_max = 8.0
|
||||
_roll_spark_emitter.scale_amount_min = 0.5
|
||||
_roll_spark_emitter.scale_amount_max = 1.3
|
||||
_roll_spark_emitter.mesh = spark
|
||||
_roll_spark_emitter.color_ramp = ramp
|
||||
_roll_spark_emitter.scale_amount_curve = sc
|
||||
cube_guy.add_child(_roll_spark_emitter)
|
||||
_roll_spark_emitter = Fx.attach(&"bull_roll_sparks", cube_guy) as CPUParticles3D
|
||||
if _roll_spark_emitter != null:
|
||||
_roll_spark_emitter.position = Vector3(0.0, 0.10, 0.0)
|
||||
|
||||
|
||||
func _setup_audio() -> void:
|
||||
@@ -251,42 +176,20 @@ func _setup_audio() -> void:
|
||||
add_child(_crowd_player)
|
||||
|
||||
|
||||
# Toggle the hoof emitters and swap their scale/velocity for the walk vs charge feel. The blue
|
||||
# charge trail is the roll/dash ability's, driven from the roll loop, not by dust state.
|
||||
func _set_dust_state(new_state: DustState) -> void:
|
||||
if new_state == _dust_state:
|
||||
return
|
||||
_dust_state = new_state
|
||||
var scale := _CHARGE_DUST_SCALE if new_state == DustState.CHARGE else _WALK_DUST_SCALE
|
||||
var vel := _CHARGE_DUST_VEL if new_state == DustState.CHARGE else _WALK_DUST_VEL
|
||||
for p: CPUParticles3D in _hoof_emitters:
|
||||
p.lifetime = DP.f("dust_lifetime")
|
||||
p.explosiveness = DP.f("dust_explosiveness")
|
||||
p.spread = DP.f("dust_spread")
|
||||
p.gravity = Vector3(0.0, DP.f("dust_gravity_y"), 0.0)
|
||||
match new_state:
|
||||
DustState.NONE:
|
||||
p.emitting = false
|
||||
DustState.WALK:
|
||||
p.color_ramp = _walk_dust_ramp
|
||||
p.scale_amount_min = DP.f("walk_scale_min")
|
||||
p.scale_amount_max = DP.f("walk_scale_max")
|
||||
p.initial_velocity_min = DP.f("walk_vel_min")
|
||||
p.initial_velocity_max = DP.f("walk_vel_max")
|
||||
p.emitting = true
|
||||
DustState.CHARGE:
|
||||
p.color_ramp = _charge_dust_ramp
|
||||
p.scale_amount_min = DP.f("charge_scale_min")
|
||||
p.scale_amount_max = DP.f("charge_scale_max")
|
||||
p.initial_velocity_min = DP.f("charge_vel_min")
|
||||
p.initial_velocity_max = DP.f("charge_vel_max")
|
||||
p.emitting = true
|
||||
# Blue energy trail belongs to the roll/dash ability only, not to fast WASD
|
||||
# running — driven explicitly in the roll loop / _end_roll, not by dust state.
|
||||
|
||||
|
||||
func _on_dp_changed(_key: String, _val: Variant) -> void:
|
||||
if _hoof_emitters.is_empty() or _dust_state == DustState.NONE:
|
||||
return
|
||||
var prev := _dust_state
|
||||
_dust_state = DustState.NONE
|
||||
_set_dust_state(prev)
|
||||
p.scale_amount_min = scale.x
|
||||
p.scale_amount_max = scale.y
|
||||
p.initial_velocity_min = vel.x
|
||||
p.initial_velocity_max = vel.y
|
||||
p.emitting = new_state != DustState.NONE
|
||||
|
||||
|
||||
func _find_anim_player(node: Node) -> AnimationPlayer:
|
||||
@@ -681,13 +584,10 @@ func _hit_matadors_radius(range_m: float, strength: float, up_boost: float = 0.0
|
||||
mat.call(&"apply_ability_hit", (away + Vector3.UP * 0.6).normalized(), strength, up_boost)
|
||||
|
||||
|
||||
# ── Particle helpers ──────────────────────────────────────────────────────────
|
||||
# All FX in this script use unshaded, vertex-coloured, alpha-blended particles;
|
||||
# these three helpers remove the boilerplate each spawn function used to repeat.
|
||||
|
||||
# One shared unshaded material for every particle FX (per-particle colour comes from the
|
||||
# CPUParticles3D colour_ramp via vertex colour, not the material) — built once so the mobile
|
||||
# driver compiles this shader a single time instead of on every attack.
|
||||
# ── Blood-spurt particle helpers ──────────────────────────────────────────────
|
||||
# The ability effects now live in systems/fx/; only the spear-wound blood below is still built in
|
||||
# code. Shared material, built once so the mobile driver compiles this particle shader a single
|
||||
# time (colour comes per-particle from the colour_ramp via vertex colour, not this material).
|
||||
func _unshaded_material() -> StandardMaterial3D:
|
||||
if _unshaded_mat == null:
|
||||
_unshaded_mat = StandardMaterial3D.new()
|
||||
@@ -698,8 +598,7 @@ func _unshaded_material() -> StandardMaterial3D:
|
||||
return _unshaded_mat
|
||||
|
||||
|
||||
# Low-poly sphere sized for a particle mesh (height is always the diameter). Cached per size so
|
||||
# repeat attacks reuse the same mesh (one GPU upload) instead of generating + uploading a new one.
|
||||
# Low-poly particle-sphere mesh (height = diameter), cached per size so repeats reuse one GPU upload.
|
||||
func _particle_sphere(radius: float, segments: int, rings: int, mat: Material) -> SphereMesh:
|
||||
var key := "%.4f:%d:%d" % [radius, segments, rings]
|
||||
var cached: SphereMesh = _particle_sphere_cache.get(key)
|
||||
@@ -724,281 +623,31 @@ func _free_after(node: Node, delay: float) -> void:
|
||||
|
||||
|
||||
func _spawn_slam_fx() -> void:
|
||||
if not _slam_ring_mesh:
|
||||
_slam_ring_mesh = _make_slam_ring_mesh()
|
||||
var max_r := DP.f("slam_range")
|
||||
var origin := global_position + Vector3(0.0, -0.5, 0.0)
|
||||
_launch_slam_ring(origin, max_r, 0.00, 0.55, Color(1.00, 0.70, 0.10, 0.85))
|
||||
_launch_slam_ring(origin, max_r, 0.10, 0.52, Color(1.00, 0.50, 0.10, 0.65))
|
||||
_launch_slam_ring(origin, max_r, 0.20, 0.48, Color(0.90, 0.30, 0.10, 0.50))
|
||||
_spawn_slam_dust(origin)
|
||||
_impact_fx.call(&"slam_rings", origin, DP.f("slam_range"))
|
||||
Fx.spawn(&"bull_slam", Transform3D(Basis(), origin))
|
||||
camera_pivot.call(&"trigger_hit", 0.60)
|
||||
|
||||
|
||||
func _launch_slam_ring(origin: Vector3, max_r: float,
|
||||
delay: float, duration: float, col: Color) -> void:
|
||||
var mat := StandardMaterial3D.new()
|
||||
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
mat.albedo_color = Color(col.r, col.g, col.b, 0.0)
|
||||
mat.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = _slam_ring_mesh
|
||||
mi.material_override = mat
|
||||
mi.scale = Vector3(0.02, 1.0, 0.02)
|
||||
get_parent().add_child(mi)
|
||||
mi.global_position = origin
|
||||
|
||||
var tween := create_tween()
|
||||
tween.set_parallel(true)
|
||||
tween.tween_property(mi, "scale",
|
||||
Vector3(max_r, 1.0, max_r), duration)\
|
||||
.set_delay(delay)\
|
||||
.set_trans(Tween.TRANS_EXPO)\
|
||||
.set_ease(Tween.EASE_OUT)
|
||||
var fade := func(a: float) -> void: mat.albedo_color.a = a
|
||||
tween.tween_method(fade, col.a, 0.0, duration)\
|
||||
.set_delay(delay)\
|
||||
.set_trans(Tween.TRANS_SINE)\
|
||||
.set_ease(Tween.EASE_IN)
|
||||
tween.tween_callback(mi.queue_free).set_delay(delay + duration + 0.05)
|
||||
|
||||
|
||||
func _spawn_slam_dust(origin: Vector3) -> void:
|
||||
var sphere := _particle_sphere(0.09, 4, 2, _unshaded_material())
|
||||
|
||||
var ramp := Gradient.new()
|
||||
ramp.set_color(0, Color(0.88, 0.74, 0.46, 1.0))
|
||||
ramp.set_color(1, Color(0.88, 0.74, 0.46, 0.0))
|
||||
|
||||
var scale_curve := Curve.new()
|
||||
scale_curve.add_point(Vector2(0.0, 0.25))
|
||||
scale_curve.add_point(Vector2(0.35, 1.6))
|
||||
scale_curve.add_point(Vector2(1.00, 0.0))
|
||||
|
||||
var p := CPUParticles3D.new()
|
||||
p.one_shot = true
|
||||
p.explosiveness = 0.85
|
||||
p.amount = 48
|
||||
p.lifetime = 1.1
|
||||
p.randomness = 0.5
|
||||
p.local_coords = false
|
||||
p.direction = Vector3.UP
|
||||
p.spread = 85.0
|
||||
p.gravity = Vector3(0.0, -7.0, 0.0)
|
||||
p.initial_velocity_min = 3.5
|
||||
p.initial_velocity_max = 10.0
|
||||
p.scale_amount_min = 0.5
|
||||
p.scale_amount_max = 2.0
|
||||
p.mesh = sphere
|
||||
p.color_ramp = ramp
|
||||
p.scale_amount_curve = scale_curve
|
||||
get_parent().add_child(p)
|
||||
p.global_position = origin
|
||||
p.restart()
|
||||
_free_after(p, 2.5)
|
||||
|
||||
|
||||
func _spawn_kick_fx(back_dir: Vector3) -> void:
|
||||
var origin := global_position + back_dir * 0.55 + Vector3(0.0, -0.25, 0.0)
|
||||
_spawn_kick_dust(origin, back_dir)
|
||||
_spawn_kick_stones(origin, back_dir)
|
||||
_spawn_kick_sparks(origin, back_dir)
|
||||
_spawn_kick_flash(origin)
|
||||
Fx.spawn(&"bull_kick", _aimed_xform(origin, back_dir))
|
||||
_impact_fx.call(&"kick_flash", origin)
|
||||
camera_pivot.call(&"trigger_hit", 0.35)
|
||||
|
||||
|
||||
func _spawn_kick_dust(origin: Vector3, back_dir: Vector3) -> void:
|
||||
var sphere := _particle_sphere(0.12, 4, 2, _unshaded_material())
|
||||
|
||||
var ramp := Gradient.new()
|
||||
ramp.set_color(0, Color(1.0, 0.78, 0.18, 0.95))
|
||||
ramp.set_color(1, Color(1.0, 0.45, 0.05, 0.0))
|
||||
|
||||
var scale_curve := Curve.new()
|
||||
scale_curve.add_point(Vector2(0.0, 0.3))
|
||||
scale_curve.add_point(Vector2(0.3, 1.5))
|
||||
scale_curve.add_point(Vector2(1.0, 0.0))
|
||||
|
||||
var p := CPUParticles3D.new()
|
||||
p.one_shot = true
|
||||
p.explosiveness = 0.80
|
||||
p.amount = 40
|
||||
p.lifetime = 1.2
|
||||
p.randomness = 0.5
|
||||
p.local_coords = false
|
||||
p.direction = (back_dir + Vector3(0.0, 0.7, 0.0)).normalized()
|
||||
p.spread = 65.0
|
||||
p.gravity = Vector3(0.0, -6.0, 0.0)
|
||||
p.initial_velocity_min = 3.0
|
||||
p.initial_velocity_max = 9.0
|
||||
p.scale_amount_min = 0.5
|
||||
p.scale_amount_max = 2.2
|
||||
p.mesh = sphere
|
||||
p.color_ramp = ramp
|
||||
p.scale_amount_curve = scale_curve
|
||||
get_parent().add_child(p)
|
||||
p.global_position = origin
|
||||
p.restart()
|
||||
_free_after(p, 2.5)
|
||||
|
||||
|
||||
func _spawn_kick_stones(origin: Vector3, back_dir: Vector3) -> void:
|
||||
var sphere := _particle_sphere(0.055, 3, 1, _unshaded_material())
|
||||
|
||||
var ramp := Gradient.new()
|
||||
ramp.set_color(0, Color(0.62, 0.58, 0.52, 1.0))
|
||||
ramp.set_color(1, Color(0.62, 0.58, 0.52, 0.0))
|
||||
|
||||
var scale_curve := Curve.new()
|
||||
scale_curve.add_point(Vector2(0.0, 1.0))
|
||||
scale_curve.add_point(Vector2(0.6, 1.0))
|
||||
scale_curve.add_point(Vector2(1.0, 0.0))
|
||||
|
||||
var p := CPUParticles3D.new()
|
||||
p.one_shot = true
|
||||
p.explosiveness = 0.92
|
||||
p.amount = 14
|
||||
p.lifetime = 0.65
|
||||
p.randomness = 0.6
|
||||
p.local_coords = false
|
||||
p.direction = (back_dir + Vector3(0.0, 0.35, 0.0)).normalized()
|
||||
p.spread = 45.0
|
||||
p.gravity = Vector3(0.0, -9.8, 0.0)
|
||||
p.initial_velocity_min = 5.0
|
||||
p.initial_velocity_max = 14.0
|
||||
p.scale_amount_min = 0.5
|
||||
p.scale_amount_max = 1.8
|
||||
p.mesh = sphere
|
||||
p.color_ramp = ramp
|
||||
p.scale_amount_curve = scale_curve
|
||||
get_parent().add_child(p)
|
||||
p.global_position = origin
|
||||
p.restart()
|
||||
_free_after(p, 2.0)
|
||||
|
||||
|
||||
func _spawn_kick_flash(origin: Vector3) -> void:
|
||||
var mat := _unshaded_material()
|
||||
mat.albedo_color = Color(1.0, 0.97, 0.40, 0.0)
|
||||
|
||||
var sphere := _particle_sphere(1.0, 8, 4, mat)
|
||||
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = sphere
|
||||
mi.material_override = mat
|
||||
mi.scale = Vector3(0.05, 0.05, 0.05)
|
||||
get_parent().add_child(mi)
|
||||
mi.global_position = origin
|
||||
|
||||
var tween := create_tween()
|
||||
tween.set_parallel(true)
|
||||
tween.tween_property(mi, "scale", Vector3(0.7, 0.7, 0.7), 0.13)\
|
||||
.set_trans(Tween.TRANS_EXPO).set_ease(Tween.EASE_OUT)
|
||||
var fade := func(a: float) -> void: mat.albedo_color.a = a
|
||||
tween.tween_method(fade, 0.92, 0.0, 0.14)\
|
||||
.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN)
|
||||
tween.tween_callback(mi.queue_free).set_delay(0.16)
|
||||
|
||||
|
||||
func _spawn_dash_burst() -> void:
|
||||
var box := BoxMesh.new()
|
||||
box.size = Vector3(0.045, 0.045, 0.30)
|
||||
box.material = _unshaded_material()
|
||||
|
||||
var ramp := Gradient.new()
|
||||
ramp.set_color(0, Color(0.50, 0.82, 1.0, 1.0))
|
||||
ramp.set_color(1, Color(0.50, 0.82, 1.0, 0.0))
|
||||
|
||||
var scale_curve := Curve.new()
|
||||
scale_curve.add_point(Vector2(0.0, 1.0))
|
||||
scale_curve.add_point(Vector2(1.0, 0.0))
|
||||
|
||||
var p := CPUParticles3D.new()
|
||||
p.one_shot = true
|
||||
p.explosiveness = 0.92
|
||||
p.amount = 18
|
||||
p.lifetime = 0.22
|
||||
p.randomness = 0.15
|
||||
p.local_coords = false
|
||||
p.direction = _dash_dir
|
||||
p.spread = 15.0
|
||||
p.gravity = Vector3.ZERO
|
||||
p.initial_velocity_min = 18.0
|
||||
p.initial_velocity_max = 35.0
|
||||
p.scale_amount_min = 0.8
|
||||
p.scale_amount_max = 2.8
|
||||
p.mesh = box
|
||||
p.color_ramp = ramp
|
||||
p.scale_amount_curve = scale_curve
|
||||
get_parent().add_child(p)
|
||||
p.global_position = global_position + Vector3(0.0, 0.2, 0.0)
|
||||
p.restart()
|
||||
_free_after(p, 0.6)
|
||||
Fx.spawn(&"bull_dash", _aimed_xform(global_position + Vector3(0.0, 0.2, 0.0), _dash_dir))
|
||||
|
||||
|
||||
func _spawn_kick_sparks(origin: Vector3, back_dir: Vector3) -> void:
|
||||
var sphere := _particle_sphere(0.04, 3, 1, _unshaded_material())
|
||||
|
||||
var ramp := Gradient.new()
|
||||
ramp.set_color(0, Color(1.0, 0.98, 0.60, 1.0))
|
||||
ramp.set_color(1, Color(1.0, 1.0, 1.0, 0.0))
|
||||
|
||||
var scale_curve := Curve.new()
|
||||
scale_curve.add_point(Vector2(0.0, 1.0))
|
||||
scale_curve.add_point(Vector2(0.5, 0.6))
|
||||
scale_curve.add_point(Vector2(1.0, 0.0))
|
||||
|
||||
var p := CPUParticles3D.new()
|
||||
p.one_shot = true
|
||||
p.explosiveness = 0.95
|
||||
p.amount = 22
|
||||
p.lifetime = 0.28
|
||||
p.randomness = 0.4
|
||||
p.local_coords = false
|
||||
p.direction = (back_dir + Vector3(0.0, 0.5, 0.0)).normalized()
|
||||
p.spread = 30.0
|
||||
p.gravity = Vector3(0.0, -14.0, 0.0)
|
||||
p.initial_velocity_min = 8.0
|
||||
p.initial_velocity_max = 18.0
|
||||
p.scale_amount_min = 0.6
|
||||
p.scale_amount_max = 2.0
|
||||
p.mesh = sphere
|
||||
p.color_ramp = ramp
|
||||
p.scale_amount_curve = scale_curve
|
||||
get_parent().add_child(p)
|
||||
p.global_position = origin
|
||||
p.restart()
|
||||
_free_after(p, 1.0)
|
||||
|
||||
|
||||
func _make_slam_ring_mesh() -> ArrayMesh:
|
||||
const INNER_R: float = 0.82
|
||||
const OUTER_R: float = 1.00
|
||||
const SEGMENTS: int = 48
|
||||
|
||||
var verts := PackedVector3Array()
|
||||
var indices := PackedInt32Array()
|
||||
|
||||
for i: int in SEGMENTS:
|
||||
var a0 := i / float(SEGMENTS) * TAU
|
||||
var a1 := (i + 1) / float(SEGMENTS) * TAU
|
||||
var vi := i * 4
|
||||
verts.append(Vector3(cos(a0) * INNER_R, 0.0, sin(a0) * INNER_R))
|
||||
verts.append(Vector3(cos(a0) * OUTER_R, 0.0, sin(a0) * OUTER_R))
|
||||
verts.append(Vector3(cos(a1) * OUTER_R, 0.0, sin(a1) * OUTER_R))
|
||||
verts.append(Vector3(cos(a1) * INNER_R, 0.0, sin(a1) * INNER_R))
|
||||
indices.append_array([vi, vi + 2, vi + 1, vi, vi + 3, vi + 2])
|
||||
|
||||
var arrays: Array = []
|
||||
arrays.resize(Mesh.ARRAY_MAX)
|
||||
arrays[Mesh.ARRAY_VERTEX] = verts
|
||||
arrays[Mesh.ARRAY_INDEX] = indices
|
||||
|
||||
var mesh := ArrayMesh.new()
|
||||
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
||||
return mesh
|
||||
# A transform at `origin` whose local -Z faces `forward` (flattened to horizontal). The kick/dash
|
||||
# effects bake their spray in local space, so aiming the spawn transform points the burst the
|
||||
# right way. Falls back to an unrotated transform when `forward` has no horizontal component.
|
||||
func _aimed_xform(origin: Vector3, forward: Vector3) -> Transform3D:
|
||||
var flat := Vector3(forward.x, 0.0, forward.z)
|
||||
if flat.length_squared() < 0.0001:
|
||||
return Transform3D(Basis(), origin)
|
||||
return Transform3D(Basis(), origin).looking_at(origin + flat.normalized(), Vector3.UP)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
@@ -1112,23 +761,14 @@ func _physics_process(delta: float) -> void:
|
||||
velocity.z = flat_n.z * exit_spd
|
||||
pre_wall_vel = Vector3(velocity.x, 0.0, velocity.z)
|
||||
|
||||
# --- ADD THIS BREAKABLE BARREL DETECTION CODE ---
|
||||
var collision = get_slide_collision(i)
|
||||
var collider = collision.get_collider()
|
||||
|
||||
# Check if the object we hit has the "destroy_barrel" function
|
||||
if collider and collider.has_method("destroy_barrel"):
|
||||
# Optional: Only break if moving fast (e.g., sprinting or dashing)
|
||||
# if velocity.length() > 5.0:
|
||||
collider.destroy_barrel()
|
||||
# Ram straight through a barrel that formed the wall we bounced off.
|
||||
var hit := col.get_collider()
|
||||
if hit != null and hit.has_method(&"destroy_barrel"):
|
||||
hit.call(&"destroy_barrel")
|
||||
|
||||
# ── Landing. Don't bounce ─────────────────────────────────────────────────────
|
||||
if is_on_floor() and not _was_on_floor:
|
||||
velocity.y = 0.0
|
||||
#Bouncing landing below
|
||||
#var impact := -_pre_slide_vel_y
|
||||
#if impact > DP.f("bounce_min_impact") and not _slam_pending:
|
||||
#velocity.y = impact * DP.f("bounce_restitution")
|
||||
|
||||
# ── Slam landing ──────────────────────────────────────────────────────────
|
||||
if _slam_pending and is_on_floor() and not _was_on_floor:
|
||||
@@ -1157,8 +797,6 @@ func _physics_process(delta: float) -> void:
|
||||
if vel_flat.length_squared() > 0.1:
|
||||
_charge_trail_emitter.direction = (-vel_flat.normalized() + Vector3(0.0, 0.25, 0.0)).normalized()
|
||||
|
||||
# ── Hoof sounds ───────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
func get_hp() -> int:
|
||||
return _hp
|
||||
@@ -1274,7 +912,6 @@ func _spawn_blood(hit_pos: Vector3) -> void:
|
||||
_free_after(p, 1.5)
|
||||
|
||||
|
||||
#hit flashing
|
||||
func _hit_flash() -> void:
|
||||
var meshes := find_children("*", "MeshInstance3D", true, false)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[ext_resource type="Script" uid="uid://bn777gguclud5" path="res://actors/player/player.gd" id="1_xhfnw"]
|
||||
[ext_resource type="Script" uid="uid://cpyq2r5cq653g" path="res://actors/player/camera_iso.gd" id="2_camera_iso"]
|
||||
[ext_resource type="PackedScene" uid="uid://bun14xmk20pcj" path="res://Assets/bull.fbx" id="4_e80uo"]
|
||||
[ext_resource type="PackedScene" uid="uid://bun14xmk20pcj" path="res://art/characters/bull.fbx" id="4_e80uo"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_kne1u"]
|
||||
radius = 0.7495117
|
||||
@@ -23,7 +23,7 @@ script = ExtResource("2_camera_iso")
|
||||
[node name="bull" parent="." unique_id=1386495166 instance=ExtResource("4_e80uo")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.655, 0.23)
|
||||
|
||||
[node name="Skeleton3D" parent="bull/Armature" parent_id_path=PackedInt32Array(1386495166, 1942410469) index="0" unique_id=550138996]
|
||||
[node name="Skeleton3D" parent="bull/Armature" parent_id_path=PackedInt32Array(1386495166, 1057197468) index="0" unique_id=840295370]
|
||||
bones/0/position = Vector3(0, 0, 0.009963839)
|
||||
bones/0/rotation = Quaternion(0.7071068, 0, 0, 0.7071067)
|
||||
bones/0/scale = Vector3(1, 1, 1)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
extends Node3D
|
||||
|
||||
var player: Node3D = null
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
# If we don't have the player yet, try to find them in the group
|
||||
if not is_instance_valid(player):
|
||||
var players = get_tree().get_nodes_in_group("player")
|
||||
if players.size() > 0:
|
||||
player = players[0] as Node3D
|
||||
|
||||
# If the player exists, follow their position
|
||||
if is_instance_valid(player):
|
||||
global_position = player.global_position
|
||||
@@ -0,0 +1 @@
|
||||
uid://cmapsaj010q40
|
||||
@@ -0,0 +1,14 @@
|
||||
[gd_scene format=3 uid="uid://4v25hb0qu5l0"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cmapsaj010q40" path="res://actors/player/player_follow_light.gd" id="1_w7wrt"]
|
||||
|
||||
[node name="PlayerFollowLight" type="Node3D" unique_id=1889971494]
|
||||
script = ExtResource("1_w7wrt")
|
||||
|
||||
[node name="SpotLight3D" type="SpotLight3D" parent="." unique_id=1523840927]
|
||||
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 2.6136062, 1.0431258e-07)
|
||||
light_color = Color(0.9999998, 0.8702623, 0.60548127, 1)
|
||||
light_energy = 8.0
|
||||
shadow_enabled = true
|
||||
distance_fade_length = 30.45
|
||||
spot_angle = 72.62192
|
||||
@@ -4,12 +4,12 @@ importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cmxqocsdbgufr"
|
||||
path="res://.godot/imported/mees_plaksutab.glb-0ce4e4ee8c8f92069abfac9352f0f4b9.scn"
|
||||
path="res://.godot/imported/crowd_clap.glb-8bb3f532af711440142a67228d380ce0.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Animations/mees_plaksutab.glb"
|
||||
dest_files=["res://.godot/imported/mees_plaksutab.glb-0ce4e4ee8c8f92069abfac9352f0f4b9.scn"]
|
||||
source_file="res://art/anim/crowd_clap.glb"
|
||||
dest_files=["res://.godot/imported/crowd_clap.glb-8bb3f532af711440142a67228d380ce0.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""
|
||||
Blender 5.1 script: create walk + idle animations for matador_v02.
|
||||
Run: blender --background matador_v02.blend --python create_matador_anims.py
|
||||
Blender 5.1 script: create walk + idle animations for matador.
|
||||
Run: blender --background matador.blend --python create_matador_anims.py
|
||||
"""
|
||||
import bpy, math, os
|
||||
|
||||
@@ -130,7 +130,7 @@ ti.name = 'idle'
|
||||
si = ti.strips.new('idle', 0, idle)
|
||||
|
||||
# ── export FBX ─────────────────────────────────────────────────────────────────
|
||||
fbx_path = os.path.join(os.path.dirname(bpy.data.filepath), 'matador_v02.fbx')
|
||||
fbx_path = os.path.join(os.path.dirname(bpy.data.filepath), 'matador.fbx')
|
||||
bpy.ops.export_scene.fbx(
|
||||
filepath=fbx_path,
|
||||
use_selection=False,
|
||||
@@ -1,8 +1,8 @@
|
||||
"""
|
||||
Blender 5.1 script: render preview frames of matador animations to PNG.
|
||||
Run: blender --background matador_v02.blend --python render_anim_preview.py
|
||||
Run: blender --background matador.blend --python render_anim_preview.py
|
||||
|
||||
Outputs to Blender/preview/ directory:
|
||||
Outputs to art/blender/preview/ directory:
|
||||
walk_frame_00.png .. walk_frame_09.png
|
||||
idle_frame_00.png
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://dnltsqmuqcxh1"
|
||||
path="res://.godot/imported/Barrel.glb-8809615fbcbd5dc5b64a229316967555.scn"
|
||||
uid="uid://cofv5dbavesis"
|
||||
path="res://.godot/imported/bear.glb-75dd9ad1d0d42d959c02624cfc22996d.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/breakables/Barrel.glb"
|
||||
dest_files=["res://.godot/imported/Barrel.glb-8809615fbcbd5dc5b64a229316967555.scn"]
|
||||
source_file="res://art/characters/bear.glb"
|
||||
dest_files=["res://.godot/imported/bear.glb-75dd9ad1d0d42d959c02624cfc22996d.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -4,12 +4,12 @@ importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://bun14xmk20pcj"
|
||||
path="res://.godot/imported/bull.fbx-50f6d03a92c862695bbeaeba563be0db.scn"
|
||||
path="res://.godot/imported/bull.fbx-ffca5aad6fa2fef7c6aaeff23784274e.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/bull.fbx"
|
||||
dest_files=["res://.godot/imported/bull.fbx-50f6d03a92c862695bbeaeba563be0db.scn"]
|
||||
source_file="res://art/characters/bull.fbx"
|
||||
dest_files=["res://.godot/imported/bull.fbx-ffca5aad6fa2fef7c6aaeff23784274e.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -4,12 +4,12 @@ importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://eu2lqjhowj4t"
|
||||
path="res://.godot/imported/Matador.glb-84387af784b13addc4467fc8edace9ff.scn"
|
||||
path="res://.godot/imported/matador.glb-d61955edf618ff60a81602b44843dd9c.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/Matador.glb"
|
||||
dest_files=["res://.godot/imported/Matador.glb-84387af784b13addc4467fc8edace9ff.scn"]
|
||||
source_file="res://art/characters/matador.glb"
|
||||
dest_files=["res://.godot/imported/matador.glb-d61955edf618ff60a81602b44843dd9c.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://fu84fchl1im6"
|
||||
path="res://.godot/imported/Cinzel.ttf-b9b78caa7497d2ec78b922cb77f34333.fontdata"
|
||||
path="res://.godot/imported/Cinzel.ttf-95f91c70d492f60cef14788d0547dff4.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://fonts/Cinzel.ttf"
|
||||
dest_files=["res://.godot/imported/Cinzel.ttf-b9b78caa7497d2ec78b922cb77f34333.fontdata"]
|
||||
source_file="res://art/fonts/Cinzel.ttf"
|
||||
dest_files=["res://.godot/imported/Cinzel.ttf-95f91c70d492f60cef14788d0547dff4.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
importer="font_data_dynamic"
|
||||
type="FontFile"
|
||||
uid="uid://rcfn6xhs7fur"
|
||||
path="res://.godot/imported/CinzelDecorative-Bold.ttf-fd8dd0c7ef2ceb6acaeb4b78a3c0a47b.fontdata"
|
||||
path="res://.godot/imported/CinzelDecorative-Bold.ttf-71cca077ba75a6c829f53e2548a2580d.fontdata"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://fonts/CinzelDecorative-Bold.ttf"
|
||||
dest_files=["res://.godot/imported/CinzelDecorative-Bold.ttf-fd8dd0c7ef2ceb6acaeb4b78a3c0a47b.fontdata"]
|
||||
source_file="res://art/fonts/CinzelDecorative-Bold.ttf"
|
||||
dest_files=["res://.godot/imported/CinzelDecorative-Bold.ttf-71cca077ba75a6c829f53e2548a2580d.fontdata"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cwmo5vh738x6w"
|
||||
path="res://.godot/imported/Lobby_level.glb-a7e896ecf7377d9530a80d5e63140cc3.scn"
|
||||
uid="uid://bdbx0wwa73rhp"
|
||||
path="res://.godot/imported/bear_fx.glb-ec36223896f7444a4dbe06fe70380d22.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/Lobby_level.glb"
|
||||
dest_files=["res://.godot/imported/Lobby_level.glb-a7e896ecf7377d9530a80d5e63140cc3.scn"]
|
||||
source_file="res://art/fx/bear_fx.glb"
|
||||
dest_files=["res://.godot/imported/bear_fx.glb-ec36223896f7444a4dbe06fe70380d22.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bxm7c6ldrdiq5"
|
||||
path.bptc="res://.godot/imported/citrus_orchard_road_puresky_4k.hdr-262868e8787abf05f7044da72beefce7.bptc.ctex"
|
||||
path.astc="res://.godot/imported/citrus_orchard_road_puresky_4k.hdr-262868e8787abf05f7044da72beefce7.astc.ctex"
|
||||
path.bptc="res://.godot/imported/citrus_orchard_road_puresky_4k.hdr-227da4c15c620e5b14eb76c27dcd326f.bptc.ctex"
|
||||
path.astc="res://.godot/imported/citrus_orchard_road_puresky_4k.hdr-227da4c15c620e5b14eb76c27dcd326f.astc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
@@ -12,8 +12,8 @@ metadata={
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://HDRI/citrus_orchard_road_puresky_4k.hdr"
|
||||
dest_files=["res://.godot/imported/citrus_orchard_road_puresky_4k.hdr-262868e8787abf05f7044da72beefce7.bptc.ctex", "res://.godot/imported/citrus_orchard_road_puresky_4k.hdr-262868e8787abf05f7044da72beefce7.astc.ctex"]
|
||||
source_file="res://art/hdri/citrus_orchard_road_puresky_4k.hdr"
|
||||
dest_files=["res://.godot/imported/citrus_orchard_road_puresky_4k.hdr-227da4c15c620e5b14eb76c27dcd326f.bptc.ctex", "res://.godot/imported/citrus_orchard_road_puresky_4k.hdr-227da4c15c620e5b14eb76c27dcd326f.astc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://3syhacswepyj"
|
||||
path.bptc="res://.godot/imported/kloppenheim_06_puresky_4k.hdr-026f0eabdfce50035e2f8417ce3e0060.bptc.ctex"
|
||||
path.astc="res://.godot/imported/kloppenheim_06_puresky_4k.hdr-026f0eabdfce50035e2f8417ce3e0060.astc.ctex"
|
||||
path.bptc="res://.godot/imported/kloppenheim_06_puresky_4k.hdr-8cc5d2aeb82da4bd42ed9b64393cbf3a.bptc.ctex"
|
||||
path.astc="res://.godot/imported/kloppenheim_06_puresky_4k.hdr-8cc5d2aeb82da4bd42ed9b64393cbf3a.astc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||
"vram_texture": true
|
||||
@@ -12,8 +12,8 @@ metadata={
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://HDRI/kloppenheim_06_puresky_4k.hdr"
|
||||
dest_files=["res://.godot/imported/kloppenheim_06_puresky_4k.hdr-026f0eabdfce50035e2f8417ce3e0060.bptc.ctex", "res://.godot/imported/kloppenheim_06_puresky_4k.hdr-026f0eabdfce50035e2f8417ce3e0060.astc.ctex"]
|
||||
source_file="res://art/hdri/kloppenheim_06_puresky_4k.hdr"
|
||||
dest_files=["res://.godot/imported/kloppenheim_06_puresky_4k.hdr-8cc5d2aeb82da4bd42ed9b64393cbf3a.bptc.ctex", "res://.godot/imported/kloppenheim_06_puresky_4k.hdr-8cc5d2aeb82da4bd42ed9b64393cbf3a.astc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
@@ -3,15 +3,15 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cyscxku0nw055"
|
||||
path="res://.godot/imported/DASH.png-02cbc39ec03a0c79526d7dc333659c48.ctex"
|
||||
path="res://.godot/imported/dash.png-f29ea607d0f55bfdf36de8ed38bf1a09.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://HUD/DASH.png"
|
||||
dest_files=["res://.godot/imported/DASH.png-02cbc39ec03a0c79526d7dc333659c48.ctex"]
|
||||
source_file="res://art/icons/dash.png"
|
||||
dest_files=["res://.godot/imported/dash.png-f29ea607d0f55bfdf36de8ed38bf1a09.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
@@ -3,15 +3,15 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bdnc3jw1ykydi"
|
||||
path="res://.godot/imported/KICK.png-5482be80e8176ed7a0ae69a48ca1f5ed.ctex"
|
||||
path="res://.godot/imported/kick.png-8a80f588c63b84c72b33cb0d47b50412.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://HUD/KICK.png"
|
||||
dest_files=["res://.godot/imported/KICK.png-5482be80e8176ed7a0ae69a48ca1f5ed.ctex"]
|
||||
source_file="res://art/icons/kick.png"
|
||||
dest_files=["res://.godot/imported/kick.png-8a80f588c63b84c72b33cb0d47b50412.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 204 KiB |
@@ -3,15 +3,15 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d25i88cixy4pn"
|
||||
path="res://.godot/imported/ROLL.png-306dafcb4b6c13eded9a925aa6d2f62a.ctex"
|
||||
path="res://.godot/imported/roll.png-dc2c913f1361acd63205c54ed9ce4acf.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://HUD/ROLL.png"
|
||||
dest_files=["res://.godot/imported/ROLL.png-306dafcb4b6c13eded9a925aa6d2f62a.ctex"]
|
||||
source_file="res://art/icons/roll.png"
|
||||
dest_files=["res://.godot/imported/roll.png-dc2c913f1361acd63205c54ed9ce4acf.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
@@ -3,15 +3,15 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bxplqrp4mc5yh"
|
||||
path="res://.godot/imported/SLAM.png-b5036a739251be1240a24080e28d7a8e.ctex"
|
||||
path="res://.godot/imported/slam.png-5cede6108fccd992c3819a9617ff12aa.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://HUD/SLAM.png"
|
||||
dest_files=["res://.godot/imported/SLAM.png-b5036a739251be1240a24080e28d7a8e.ctex"]
|
||||
source_file="res://art/icons/slam.png"
|
||||
dest_files=["res://.godot/imported/slam.png-5cede6108fccd992c3819a9617ff12aa.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -4,12 +4,12 @@ importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cl54dt38yj2x"
|
||||
path="res://.godot/imported/Arena.glb-bf6b057edaab2fa44513d7f705517361.scn"
|
||||
path="res://.godot/imported/arena.glb-264984213b895d6f3700ac45a3723e1b.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://Assets/Arena.glb"
|
||||
dest_files=["res://.godot/imported/Arena.glb-bf6b057edaab2fa44513d7f705517361.scn"]
|
||||
source_file="res://art/levels/arena.glb"
|
||||
dest_files=["res://.godot/imported/arena.glb-264984213b895d6f3700ac45a3723e1b.scn"]
|
||||
|
||||
[params]
|
||||
|
||||