added so player can cast Flame spell
BIN
src/assets/audio/sfx/weapons/bow/bow_hit1.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/bow_hit1.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://dgl0rbq5xu58c"
|
||||||
|
path="res://.godot/imported/bow_hit1.mp3-476fb54f8187c5f01a54ae94a3069381.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/bow_hit1.mp3"
|
||||||
|
dest_files=["res://.godot/imported/bow_hit1.mp3-476fb54f8187c5f01a54ae94a3069381.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/weapons/bow/bow_hit2.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/bow_hit2.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://bdxpyn6xks4kx"
|
||||||
|
path="res://.godot/imported/bow_hit2.mp3-7d578bec9a2aa746be41a000c47c524a.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/bow_hit2.mp3"
|
||||||
|
dest_files=["res://.godot/imported/bow_hit2.mp3-7d578bec9a2aa746be41a000c47c524a.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/weapons/bow/bow_hit3.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/bow_hit3.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://xl0c4hddgu6p"
|
||||||
|
path="res://.godot/imported/bow_hit3.mp3-38811bc6d247e5a69ddec5030a1b0c85.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/bow_hit3.mp3"
|
||||||
|
dest_files=["res://.godot/imported/bow_hit3.mp3-38811bc6d247e5a69ddec5030a1b0c85.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/weapons/bow/bow_impact.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/bow_impact.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://cidk2vlmt5gse"
|
||||||
|
path="res://.godot/imported/bow_impact.mp3-a822d7ab29d25d4d6f70ded4c4b90ae1.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/bow_impact.mp3"
|
||||||
|
dest_files=["res://.godot/imported/bow_impact.mp3-a822d7ab29d25d4d6f70ded4c4b90ae1.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/weapons/bow/bow_release2.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/bow_release2.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://b6mwlp2ap0wbj"
|
||||||
|
path="res://.godot/imported/bow_release2.mp3-3716aa2848dcccc35e4091a1798404ab.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/bow_release2.mp3"
|
||||||
|
dest_files=["res://.godot/imported/bow_release2.mp3-3716aa2848dcccc35e4091a1798404ab.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/weapons/bow/bow_release3.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/bow_release3.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://d1ut5lnlch0k2"
|
||||||
|
path="res://.godot/imported/bow_release3.mp3-77df3c50d4604393ae47c3b2fdbe3adb.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/bow_release3.mp3"
|
||||||
|
dest_files=["res://.godot/imported/bow_release3.mp3-77df3c50d4604393ae47c3b2fdbe3adb.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/weapons/bow/bow_release_1.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/bow_release_1.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://b6klanrso0vvq"
|
||||||
|
path="res://.godot/imported/bow_release_1.mp3-4a5c5e9ee81310c6d8c689dca7b70c8d.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/bow_release_1.mp3"
|
||||||
|
dest_files=["res://.godot/imported/bow_release_1.mp3-4a5c5e9ee81310c6d8c689dca7b70c8d.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/weapons/bow/bow_shoot.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/bow_shoot.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://cwgh6e1auq4hi"
|
||||||
|
path="res://.godot/imported/bow_shoot.mp3-96542acce0715d2c60b0b2b6674527c7.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/bow_shoot.mp3"
|
||||||
|
dest_files=["res://.godot/imported/bow_shoot.mp3-96542acce0715d2c60b0b2b6674527c7.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/weapons/bow/bow_shoot2.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/bow_shoot2.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://c3pbc1nlgf2uy"
|
||||||
|
path="res://.godot/imported/bow_shoot2.mp3-4f953e5f72066f9f2e50fdc79968d909.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/bow_shoot2.mp3"
|
||||||
|
dest_files=["res://.godot/imported/bow_shoot2.mp3-4f953e5f72066f9f2e50fdc79968d909.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/weapons/bow/buckle_bow.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/buckle_bow.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://cgya50qrx8gms"
|
||||||
|
path="res://.godot/imported/buckle_bow.mp3-cfc5ff3fd11457403ea20bba39956b64.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/buckle_bow.mp3"
|
||||||
|
dest_files=["res://.godot/imported/buckle_bow.mp3-cfc5ff3fd11457403ea20bba39956b64.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/weapons/bow/namnlös.mp3
Normal file
19
src/assets/audio/sfx/weapons/bow/namnlös.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://fuytnjt12y1r"
|
||||||
|
path="res://.godot/imported/namnlös.mp3-f82b5b1850a2f9f3e25ab453314c001c.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/weapons/bow/namnlös.mp3"
|
||||||
|
dest_files=["res://.godot/imported/namnlös.mp3-f82b5b1850a2f9f3e25ab453314c001c.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/wizard/incantations/exo-splosion.mp3
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://cfppof6cgm56a"
|
||||||
|
path="res://.godot/imported/exo-splosion.mp3-0700dbdecd62fd0dae19f5eb96b3e3d2.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/wizard/incantations/exo-splosion.mp3"
|
||||||
|
dest_files=["res://.godot/imported/exo-splosion.mp3-0700dbdecd62fd0dae19f5eb96b3e3d2.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/wizard/incantations/flame-u.mp3
Normal file
19
src/assets/audio/sfx/wizard/incantations/flame-u.mp3.import
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://b3yy3ohbhettx"
|
||||||
|
path="res://.godot/imported/flame-u.mp3-5a63fc6b70f4d9e856832e86b5c7127e.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/wizard/incantations/flame-u.mp3"
|
||||||
|
dest_files=["res://.godot/imported/flame-u.mp3-5a63fc6b70f4d9e856832e86b5c7127e.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/wizard/incantations/indignation.mp3
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://0xm3gyh8051h"
|
||||||
|
path="res://.godot/imported/indignation.mp3-38f0d18f01596b101ac929b743c845ec.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/wizard/incantations/indignation.mp3"
|
||||||
|
dest_files=["res://.godot/imported/indignation.mp3-38f0d18f01596b101ac929b743c845ec.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
BIN
src/assets/audio/sfx/wizard/incantations/spell_effect.mp3
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="mp3"
|
||||||
|
type="AudioStreamMP3"
|
||||||
|
uid="uid://jri0jevyl5d3"
|
||||||
|
path="res://.godot/imported/spell_effect.mp3-07edd758f03d0cb5fe272a5562ddc335.mp3str"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/audio/sfx/wizard/incantations/spell_effect.mp3"
|
||||||
|
dest_files=["res://.godot/imported/spell_effect.mp3-07edd758f03d0cb5fe272a5562ddc335.mp3str"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
loop=false
|
||||||
|
loop_offset=0
|
||||||
|
bpm=0
|
||||||
|
beat_count=0
|
||||||
|
bar_beats=4
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
BIN
src/assets/gfx/fx/burn.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
40
src/assets/gfx/fx/burn.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://di2yxjrvw7hi0"
|
||||||
|
path="res://.godot/imported/burn.png-d83aa157ca1f58cbb469aa9293eb2e7a.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/burn.png"
|
||||||
|
dest_files=["res://.godot/imported/burn.png-d83aa157ca1f58cbb469aa9293eb2e7a.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
|
||||||
BIN
src/assets/gfx/fx/explosion.png
Normal file
|
After Width: | Height: | Size: 952 B |
40
src/assets/gfx/fx/explosion.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cbfyeow1v5vf"
|
||||||
|
path="res://.godot/imported/explosion.png-866d55c49d5e6cd502d6b8f3c389cf2f.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/explosion.png"
|
||||||
|
dest_files=["res://.godot/imported/explosion.png-866d55c49d5e6cd502d6b8f3c389cf2f.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
|
||||||
BIN
src/assets/gfx/fx/lightning1.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
40
src/assets/gfx/fx/lightning1.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dcnp3xxxkxvou"
|
||||||
|
path="res://.godot/imported/lightning1.png-0e25a600051273da79e7c57c0d4f2c1a.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/lightning1.png"
|
||||||
|
dest_files=["res://.godot/imported/lightning1.png-0e25a600051273da79e7c57c0d4f2c1a.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
|
||||||
BIN
src/assets/gfx/fx/lightning2.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
40
src/assets/gfx/fx/lightning2.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://cbo4vqifr1qbb"
|
||||||
|
path="res://.godot/imported/lightning2.png-33f12035b9169d598e355a94cd5e4260.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/lightning2.png"
|
||||||
|
dest_files=["res://.godot/imported/lightning2.png-33f12035b9169d598e355a94cd5e4260.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
|
||||||
BIN
src/assets/gfx/fx/lightning3.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
40
src/assets/gfx/fx/lightning3.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://d1hq1q3isscm7"
|
||||||
|
path="res://.godot/imported/lightning3.png-1e328886a874a028282238adf48ff44c.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/lightning3.png"
|
||||||
|
dest_files=["res://.godot/imported/lightning3.png-1e328886a874a028282238adf48ff44c.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
|
||||||
BIN
src/assets/gfx/fx/lightning4.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
40
src/assets/gfx/fx/lightning4.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://jur4f4d4yjem"
|
||||||
|
path="res://.godot/imported/lightning4.png-a7d7ab97864dc0c6e131cdf30f1a9515.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/lightning4.png"
|
||||||
|
dest_files=["res://.godot/imported/lightning4.png-a7d7ab97864dc0c6e131cdf30f1a9515.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
|
||||||
BIN
src/assets/gfx/fx/lightning5.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
40
src/assets/gfx/fx/lightning5.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://m5qoox3y452v"
|
||||||
|
path="res://.godot/imported/lightning5.png-499a1138ebdc5b3e011b6ac9d4f23b9a.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/lightning5.png"
|
||||||
|
dest_files=["res://.godot/imported/lightning5.png-499a1138ebdc5b3e011b6ac9d4f23b9a.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
|
||||||
BIN
src/assets/gfx/fx/lightning6.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
40
src/assets/gfx/fx/lightning6.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://ysa0im6rm7m7"
|
||||||
|
path="res://.godot/imported/lightning6.png-fdec43eec00eb3e78dfcd906fe6aba71.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/lightning6.png"
|
||||||
|
dest_files=["res://.godot/imported/lightning6.png-fdec43eec00eb3e78dfcd906fe6aba71.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
|
||||||
BIN
src/assets/gfx/fx/lightning7.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
40
src/assets/gfx/fx/lightning7.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://dv2874j8slec0"
|
||||||
|
path="res://.godot/imported/lightning7.png-6d0213aaa6fd5b90e8a6f42d0cedaff4.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/lightning7.png"
|
||||||
|
dest_files=["res://.godot/imported/lightning7.png-6d0213aaa6fd5b90e8a6f42d0cedaff4.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
|
||||||
BIN
src/assets/gfx/fx/magic/red_star.png
Normal file
|
After Width: | Height: | Size: 304 B |
40
src/assets/gfx/fx/magic/red_star.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://b6jq2xdkwlckv"
|
||||||
|
path="res://.godot/imported/red_star.png-7377430fa454b1d679af156e73fca052.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/magic/red_star.png"
|
||||||
|
dest_files=["res://.godot/imported/red_star.png-7377430fa454b1d679af156e73fca052.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
|
||||||
BIN
src/assets/gfx/fx/sparks_64x64_x4_256.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
40
src/assets/gfx/fx/sparks_64x64_x4_256.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://rh731dlsygsq"
|
||||||
|
path="res://.godot/imported/sparks_64x64_x4_256.png-e0462725852ee1181490a3ea96243748.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/gfx/fx/sparks_64x64_x4_256.png"
|
||||||
|
dest_files=["res://.godot/imported/sparks_64x64_x4_256.png-e0462725852ee1181490a3ea96243748.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: 44 KiB After Width: | Height: | Size: 44 KiB |
86
src/scenes/attack_spell_flame.tscn
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
[gd_scene format=3 uid="uid://b8k3m2n4p5q6r"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" uid="uid://dwthqtcn5sqoj" path="res://assets/gfx/fx/flames/nedladdning (14).png" id="1_52nqi"]
|
||||||
|
[ext_resource type="Script" uid="uid://pufa280hfhiy" path="res://scripts/attack_spell_flame.gd" id="1_flame"]
|
||||||
|
[ext_resource type="Shader" uid="uid://c40fb6mfe76g3" path="res://shaders/fire_light.gdshader" id="3_g5mbl"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://d1ylxthy45aw" path="res://assets/audio/sfx/environment/fireplace/fireplace-01.mp3" id="4_n6h4x"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://dd6xrtcx2vtlx" path="res://assets/audio/sfx/environment/fireplace/fireplace-02.mp3" id="5_2hde6"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://bjlq8la2amdut" path="res://assets/audio/sfx/environment/fireplace/fireplace-03.mp3" id="6_sy6oy"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://cxw5pdksu1o8v" path="res://assets/audio/sfx/environment/fireplace/fireplace-04.mp3" id="7_l6fjl"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://cxtp23isjsfvw" path="res://assets/audio/sfx/environment/fireplace/fireplace-05.mp3" id="8_rlm0w"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://rtn5i86tlqyh" path="res://assets/audio/sfx/environment/fireplace/fireplace-06.mp3" id="9_8o5sr"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://jri0jevyl5d3" path="res://assets/audio/sfx/wizard/incantations/spell_effect.mp3" id="10_2hde6"]
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_rach5"]
|
||||||
|
shader = ExtResource("3_g5mbl")
|
||||||
|
shader_parameter/brightness_multiplier = 3.0
|
||||||
|
|
||||||
|
[sub_resource type="Gradient" id="Gradient_lquwl"]
|
||||||
|
offsets = PackedFloat32Array(0.743, 0.74418604)
|
||||||
|
colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 0)
|
||||||
|
|
||||||
|
[sub_resource type="GradientTexture2D" id="GradientTexture2D_w00nx"]
|
||||||
|
gradient = SubResource("Gradient_lquwl")
|
||||||
|
fill = 1
|
||||||
|
fill_from = Vector2(0.508547, 0.487179)
|
||||||
|
fill_to = Vector2(0.974359, 0.0470085)
|
||||||
|
|
||||||
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_flame"]
|
||||||
|
size = Vector2(16, 16)
|
||||||
|
|
||||||
|
[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_1qtic"]
|
||||||
|
streams_count = 6
|
||||||
|
stream_0/stream = ExtResource("4_n6h4x")
|
||||||
|
stream_1/stream = ExtResource("5_2hde6")
|
||||||
|
stream_2/stream = ExtResource("6_sy6oy")
|
||||||
|
stream_3/stream = ExtResource("7_l6fjl")
|
||||||
|
stream_4/stream = ExtResource("8_rlm0w")
|
||||||
|
stream_5/stream = ExtResource("9_8o5sr")
|
||||||
|
|
||||||
|
[node name="FlameSpell" type="Node2D" unique_id=250449910]
|
||||||
|
script = ExtResource("1_flame")
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=546834923]
|
||||||
|
texture = ExtResource("1_52nqi")
|
||||||
|
hframes = 4
|
||||||
|
vframes = 4
|
||||||
|
frame = 4
|
||||||
|
|
||||||
|
[node name="ExplosionInit" type="Sprite2D" parent="." unique_id=123456789]
|
||||||
|
visible = false
|
||||||
|
texture = ExtResource("1_52nqi")
|
||||||
|
hframes = 4
|
||||||
|
vframes = 4
|
||||||
|
|
||||||
|
[node name="TorchLight" type="PointLight2D" parent="." unique_id=1247002844]
|
||||||
|
modulate = Color(1.353256, 1.353256, 1.353256, 1)
|
||||||
|
z_index = 10
|
||||||
|
material = SubResource("ShaderMaterial_rach5")
|
||||||
|
position = Vector2(0, -1)
|
||||||
|
blend_mode = 2
|
||||||
|
texture = SubResource("GradientTexture2D_w00nx")
|
||||||
|
|
||||||
|
[node name="Area2D" type="Area2D" parent="." unique_id=556563630]
|
||||||
|
collision_layer = 4
|
||||||
|
collision_mask = 3
|
||||||
|
|
||||||
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D" unique_id=520125161]
|
||||||
|
shape = SubResource("RectangleShape2D_flame")
|
||||||
|
debug_color = Color(0.70196074, 0, 0.09064378, 0.41960785)
|
||||||
|
|
||||||
|
[node name="LifetimeTimer" type="Timer" parent="." unique_id=77775231]
|
||||||
|
wait_time = 2.0
|
||||||
|
one_shot = true
|
||||||
|
|
||||||
|
[node name="SfxFire" type="AudioStreamPlayer2D" parent="." unique_id=8963001]
|
||||||
|
stream = SubResource("AudioStreamRandomizer_1qtic")
|
||||||
|
volume_db = 4.996
|
||||||
|
max_distance = 1040.0
|
||||||
|
attenuation = 2.2973964
|
||||||
|
panning_strength = 1.09
|
||||||
|
|
||||||
|
[node name="SfxInit" type="AudioStreamPlayer2D" parent="." unique_id=467371620]
|
||||||
|
stream = ExtResource("10_2hde6")
|
||||||
|
attenuation = 1.5157177
|
||||||
|
panning_strength = 1.04
|
||||||
|
bus = &"Sfx"
|
||||||
14
src/scenes/debuff_burn.tscn
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[gd_scene format=3 uid="uid://debuff_burn_12345"]
|
||||||
|
|
||||||
|
[ext_resource type="Texture2D" path="res://assets/gfx/fx/burn.png" id="1_burn"]
|
||||||
|
[ext_resource type="Script" path="res://scripts/debuff_burn.gd" id="1_script"]
|
||||||
|
|
||||||
|
[node name="DebuffBurn" type="Node2D"]
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||||
|
texture = ExtResource("1_burn")
|
||||||
|
hframes = 4
|
||||||
|
vframes = 4
|
||||||
|
frame = 0
|
||||||
|
z_index = 5
|
||||||
@@ -29,8 +29,13 @@
|
|||||||
[ext_resource type="AudioStream" uid="uid://bdhmel5vyixng" path="res://assets/audio/sfx/player/take_damage/player_damaged_07.wav.mp3" id="26_gl8cc"]
|
[ext_resource type="AudioStream" uid="uid://bdhmel5vyixng" path="res://assets/audio/sfx/player/take_damage/player_damaged_07.wav.mp3" id="26_gl8cc"]
|
||||||
[ext_resource type="AudioStream" uid="uid://4vulahdsj4i2" path="res://assets/audio/sfx/swoosh/throw_01.wav.mp3" id="27_31cv2"]
|
[ext_resource type="AudioStream" uid="uid://4vulahdsj4i2" path="res://assets/audio/sfx/swoosh/throw_01.wav.mp3" id="27_31cv2"]
|
||||||
[ext_resource type="AudioStream" uid="uid://w6yon88kjfml" path="res://assets/audio/sfx/nickes/lift_sfx.ogg" id="28_pf23h"]
|
[ext_resource type="AudioStream" uid="uid://w6yon88kjfml" path="res://assets/audio/sfx/nickes/lift_sfx.ogg" id="28_pf23h"]
|
||||||
[ext_resource type="AudioStream" uid="uid://d4kjyb1olr74s" path="res://assets/audio/sfx/weapons/bow/bow_draw-02.mp3" id="30_gl8cc"]
|
[ext_resource type="AudioStream" uid="uid://b6klanrso0vvq" path="res://assets/audio/sfx/weapons/bow/bow_release_1.mp3" id="30_md1ol"]
|
||||||
[ext_resource type="AudioStream" uid="uid://mbtpqlb5n3gd" path="res://assets/audio/sfx/weapons/bow/bow_no_arrow.mp3" id="31_487ah"]
|
[ext_resource type="AudioStream" uid="uid://mbtpqlb5n3gd" path="res://assets/audio/sfx/weapons/bow/bow_no_arrow.mp3" id="31_487ah"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://b6mwlp2ap0wbj" path="res://assets/audio/sfx/weapons/bow/bow_release2.mp3" id="31_bj30b"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://cgya50qrx8gms" path="res://assets/audio/sfx/weapons/bow/buckle_bow.mp3" id="32_gl8cc"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://d1ut5lnlch0k2" path="res://assets/audio/sfx/weapons/bow/bow_release3.mp3" id="32_jc3p3"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://bvi00vbftbgc5" path="res://assets/audio/sfx/player/ultra_run/shinespark_start.wav" id="35_bj30b"]
|
||||||
|
[ext_resource type="AudioStream" uid="uid://0xm3gyh8051h" path="res://assets/audio/sfx/wizard/incantations/indignation.mp3" id="36_jc3p3"]
|
||||||
|
|
||||||
[sub_resource type="Gradient" id="Gradient_wqfne"]
|
[sub_resource type="Gradient" id="Gradient_wqfne"]
|
||||||
colors = PackedColorArray(0, 0, 0, 1, 1, 0.13732082, 0.092538536, 1)
|
colors = PackedColorArray(0, 0, 0, 1, 1, 0.13732082, 0.092538536, 1)
|
||||||
@@ -275,6 +280,14 @@ stream_4/stream = ExtResource("24_wqfne")
|
|||||||
stream_5/stream = ExtResource("25_wnwbv")
|
stream_5/stream = ExtResource("25_wnwbv")
|
||||||
stream_6/stream = ExtResource("26_gl8cc")
|
stream_6/stream = ExtResource("26_gl8cc")
|
||||||
|
|
||||||
|
[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_bj30b"]
|
||||||
|
playback_mode = 1
|
||||||
|
random_pitch = 1.0123794
|
||||||
|
streams_count = 3
|
||||||
|
stream_0/stream = ExtResource("30_md1ol")
|
||||||
|
stream_1/stream = ExtResource("31_bj30b")
|
||||||
|
stream_2/stream = ExtResource("32_jc3p3")
|
||||||
|
|
||||||
[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_hhpqf"]
|
[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_hhpqf"]
|
||||||
random_pitch = 1.0630184
|
random_pitch = 1.0630184
|
||||||
streams_count = 1
|
streams_count = 1
|
||||||
@@ -457,7 +470,7 @@ shadow_enabled = true
|
|||||||
max_distance = 100.0
|
max_distance = 100.0
|
||||||
|
|
||||||
[node name="SfxBowShoot" type="AudioStreamPlayer2D" parent="." unique_id=340970961]
|
[node name="SfxBowShoot" type="AudioStreamPlayer2D" parent="." unique_id=340970961]
|
||||||
stream = ExtResource("30_gl8cc")
|
stream = SubResource("AudioStreamRandomizer_bj30b")
|
||||||
pitch_scale = 1.33
|
pitch_scale = 1.33
|
||||||
attenuation = 6.7271657
|
attenuation = 6.7271657
|
||||||
|
|
||||||
@@ -465,3 +478,18 @@ attenuation = 6.7271657
|
|||||||
stream = SubResource("AudioStreamRandomizer_hhpqf")
|
stream = SubResource("AudioStreamRandomizer_hhpqf")
|
||||||
max_distance = 1455.0
|
max_distance = 1455.0
|
||||||
attenuation = 7.4642572
|
attenuation = 7.4642572
|
||||||
|
|
||||||
|
[node name="SfxBuckleBow" type="AudioStreamPlayer2D" parent="." unique_id=1991119205]
|
||||||
|
stream = ExtResource("32_gl8cc")
|
||||||
|
attenuation = 7.727478
|
||||||
|
panning_strength = 1.03
|
||||||
|
|
||||||
|
[node name="SfxSpellCharge" type="AudioStreamPlayer2D" parent="." unique_id=282455991]
|
||||||
|
stream = ExtResource("35_bj30b")
|
||||||
|
|
||||||
|
[node name="SfxSpellIncantation" type="AudioStreamPlayer2D" parent="." unique_id=300820616]
|
||||||
|
stream = ExtResource("36_jc3p3")
|
||||||
|
volume_db = 5.729
|
||||||
|
attenuation = 7.727487
|
||||||
|
panning_strength = 1.04
|
||||||
|
bus = &"Sfx"
|
||||||
|
|||||||
215
src/scripts/attack_spell_flame.gd
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
extends Node2D
|
||||||
|
|
||||||
|
# Flame Spell - Spawns at target position and deals damage over time
|
||||||
|
|
||||||
|
@export var damage: float = 15.0
|
||||||
|
@export var damage_interval: float = 0.5 # Deal damage every 0.5 seconds
|
||||||
|
@export var lifetime: float = 2.0 # Spell lasts 2 seconds
|
||||||
|
|
||||||
|
var player_owner: Node = null
|
||||||
|
var hit_targets = {} # Track what we've already hit
|
||||||
|
var first_hit_targets = {} # Track targets that haven't taken initial damage yet
|
||||||
|
var damage_timer: float = 0.0
|
||||||
|
var animation_timer: float = 0.0
|
||||||
|
var current_frame: int = 4 # Start at frame 4 (first burning frame)
|
||||||
|
var is_fading_out: bool = false # True when playing fade-out frames (2, 1, 0)
|
||||||
|
var fade_out_frame_index: int = 0 # Index into fade-out frames [2, 1, 0]
|
||||||
|
var initial_damage_window: float = 0.2 # Initial 0.2 seconds for bonus damage
|
||||||
|
var initial_damage_multiplier: float = 3.0 # Initial damage is 3x base damage
|
||||||
|
var spell_start_time: float = 0.0 # Time when spell started
|
||||||
|
var explosion_init_frame: int = 0 # Current frame of explosion init animation
|
||||||
|
var explosion_init_timer: float = 0.0 # Timer for explosion init animation
|
||||||
|
|
||||||
|
@onready var sprite = $Sprite2D
|
||||||
|
@onready var explosion_init_sprite = $ExplosionInit
|
||||||
|
@onready var hit_area = $Area2D
|
||||||
|
@onready var lifetime_timer = $LifetimeTimer
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
# Connect area signals
|
||||||
|
if hit_area and not hit_area.body_entered.is_connected(_on_body_entered):
|
||||||
|
hit_area.body_entered.connect(_on_body_entered)
|
||||||
|
|
||||||
|
# Start lifetime timer
|
||||||
|
lifetime_timer.wait_time = lifetime
|
||||||
|
lifetime_timer.timeout.connect(_on_lifetime_expired)
|
||||||
|
lifetime_timer.start()
|
||||||
|
|
||||||
|
# Track spell start time
|
||||||
|
spell_start_time = Time.get_ticks_msec() / 1000.0
|
||||||
|
|
||||||
|
# Show explosion init animation first
|
||||||
|
if explosion_init_sprite:
|
||||||
|
explosion_init_sprite.visible = true
|
||||||
|
explosion_init_sprite.frame = 0
|
||||||
|
explosion_init_frame = 0
|
||||||
|
explosion_init_timer = 0.0
|
||||||
|
|
||||||
|
# Hide main sprite during explosion init
|
||||||
|
if sprite:
|
||||||
|
sprite.visible = false
|
||||||
|
|
||||||
|
# Play sounds
|
||||||
|
if has_node("SfxInit"):
|
||||||
|
$SfxInit.play()
|
||||||
|
if has_node("SfxFire"):
|
||||||
|
$SfxFire.play()
|
||||||
|
|
||||||
|
func setup(target_position: Vector2, owner_player: Node, damage_value: float = 15.0):
|
||||||
|
global_position = target_position
|
||||||
|
player_owner = owner_player
|
||||||
|
damage = damage_value
|
||||||
|
|
||||||
|
func _process(delta):
|
||||||
|
damage_timer += delta
|
||||||
|
animation_timer += delta
|
||||||
|
explosion_init_timer += delta
|
||||||
|
|
||||||
|
# Handle explosion init animation (4 frames, then hide)
|
||||||
|
if explosion_init_sprite and explosion_init_sprite.visible:
|
||||||
|
# Play 4 frames of explosion init (0, 1, 2, 3) at ~30 FPS
|
||||||
|
if explosion_init_timer >= 0.1:
|
||||||
|
explosion_init_timer = 0.0
|
||||||
|
explosion_init_frame += 1
|
||||||
|
if explosion_init_frame < 4:
|
||||||
|
explosion_init_sprite.frame = explosion_init_frame
|
||||||
|
else:
|
||||||
|
# Hide explosion init and show main sprite
|
||||||
|
explosion_init_sprite.visible = false
|
||||||
|
if sprite:
|
||||||
|
sprite.visible = true
|
||||||
|
_start_sprite_animation()
|
||||||
|
|
||||||
|
# Check if spell is about to expire (last 0.5 seconds)
|
||||||
|
var time_remaining = lifetime_timer.time_left
|
||||||
|
var fade_out_start_time = 0.5 # Start fade-out 0.5 seconds before expiration
|
||||||
|
|
||||||
|
if not is_fading_out and time_remaining <= fade_out_start_time:
|
||||||
|
# Start fade-out sequence
|
||||||
|
is_fading_out = true
|
||||||
|
fade_out_frame_index = 0
|
||||||
|
animation_timer = 0.0
|
||||||
|
# Fade-out frames: 2, 1, 0
|
||||||
|
var fade_out_frames = [2, 1, 0]
|
||||||
|
current_frame = fade_out_frames[0]
|
||||||
|
if sprite:
|
||||||
|
sprite.frame = current_frame
|
||||||
|
|
||||||
|
# Animate sprite frames (only if explosion init is done)
|
||||||
|
if sprite and sprite.visible:
|
||||||
|
if is_fading_out:
|
||||||
|
# Play fade-out frames (2, 1, 0) - slower transition (0.15s per frame)
|
||||||
|
if animation_timer >= 0.15: # Slower for fade-out
|
||||||
|
animation_timer = 0.0
|
||||||
|
var fade_out_frames = [2, 1, 0]
|
||||||
|
fade_out_frame_index += 1
|
||||||
|
if fade_out_frame_index < fade_out_frames.size():
|
||||||
|
current_frame = fade_out_frames[fade_out_frame_index]
|
||||||
|
sprite.frame = current_frame
|
||||||
|
# If we've played all fade-out frames, keep on frame 0 until expiration
|
||||||
|
elif fade_out_frame_index >= fade_out_frames.size():
|
||||||
|
current_frame = 0
|
||||||
|
sprite.frame = 0
|
||||||
|
else:
|
||||||
|
# Normal animation: cycle through frames 4-15 (~30 FPS)
|
||||||
|
if animation_timer >= 0.03333333:
|
||||||
|
animation_timer = 0.0
|
||||||
|
current_frame += 1
|
||||||
|
if current_frame > 15:
|
||||||
|
current_frame = 4 # Loop back to first burning frame
|
||||||
|
sprite.frame = current_frame
|
||||||
|
|
||||||
|
# Deal periodic damage to targets in area (only if not fading out and explosion init is done)
|
||||||
|
if sprite and sprite.visible and not is_fading_out and damage_timer >= damage_interval:
|
||||||
|
damage_timer = 0.0
|
||||||
|
_deal_periodic_damage()
|
||||||
|
|
||||||
|
func _start_sprite_animation():
|
||||||
|
# Initialize sprite to first burning frame
|
||||||
|
if sprite:
|
||||||
|
sprite.frame = 4 # First frame of burning animation
|
||||||
|
current_frame = 4
|
||||||
|
|
||||||
|
func _deal_periodic_damage():
|
||||||
|
# Get all bodies in the area
|
||||||
|
var bodies = hit_area.get_overlapping_bodies()
|
||||||
|
|
||||||
|
for body in bodies:
|
||||||
|
if body == player_owner:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# CRITICAL: Only the spell owner (authority) should deal damage
|
||||||
|
if player_owner and not player_owner.is_multiplayer_authority():
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if this is the first hit on this target (for initial damage bonus)
|
||||||
|
var is_first_hit = not (body in first_hit_targets)
|
||||||
|
if is_first_hit:
|
||||||
|
first_hit_targets[body] = true
|
||||||
|
|
||||||
|
# Calculate damage - initial damage is much higher for first hit
|
||||||
|
var final_damage = damage
|
||||||
|
var int_bonus_damage = 0.0 # Declare outside if block for use in print statements
|
||||||
|
if is_first_hit:
|
||||||
|
# Initial damage is multiplied and gets INT bonus
|
||||||
|
final_damage = damage * initial_damage_multiplier
|
||||||
|
if player_owner and player_owner.character_stats:
|
||||||
|
var int_stat = player_owner.character_stats.baseStats.int + player_owner.character_stats.get_pass("int")
|
||||||
|
int_bonus_damage = int_stat * 0.5 # 0.5 damage per INT point
|
||||||
|
final_damage += int_bonus_damage
|
||||||
|
|
||||||
|
# Deal damage to players
|
||||||
|
if body.is_in_group("player") and body.has_method("rpc_take_damage"):
|
||||||
|
var attacker_pos = player_owner.global_position if player_owner else global_position
|
||||||
|
var player_peer_id = body.get_multiplayer_authority()
|
||||||
|
|
||||||
|
# Apply burn debuff with 50% chance
|
||||||
|
var apply_burn = randf() < 0.5
|
||||||
|
|
||||||
|
if player_peer_id != 0:
|
||||||
|
if multiplayer.is_server() and player_peer_id == multiplayer.get_unique_id():
|
||||||
|
body.rpc_take_damage(final_damage, attacker_pos, false, apply_burn) # Pass burn flag
|
||||||
|
else:
|
||||||
|
body.rpc_take_damage.rpc_id(player_peer_id, final_damage, attacker_pos, false, apply_burn)
|
||||||
|
else:
|
||||||
|
body.rpc_take_damage.rpc(final_damage, attacker_pos, false, apply_burn)
|
||||||
|
|
||||||
|
if is_first_hit:
|
||||||
|
var int_bonus = int_bonus_damage if player_owner and player_owner.character_stats else 0.0
|
||||||
|
print("Flame spell INITIAL hit player: ", body.name, " for ", final_damage, " damage (base: ", damage, " x ", initial_damage_multiplier, " + INT bonus: ", int_bonus, ")")
|
||||||
|
else:
|
||||||
|
print("Flame spell hit player: ", body.name, " for ", final_damage, " damage!")
|
||||||
|
|
||||||
|
# Deal damage to enemies
|
||||||
|
elif body.is_in_group("enemy") and body.has_method("rpc_take_damage"):
|
||||||
|
var attacker_pos = player_owner.global_position if player_owner else global_position
|
||||||
|
|
||||||
|
# Apply burn debuff with 50% chance
|
||||||
|
var apply_burn = randf() < 0.5
|
||||||
|
|
||||||
|
# Use game_world's _request_enemy_damage for damage, but we need to handle burn separately
|
||||||
|
# Since _request_enemy_damage doesn't support burn, we'll call rpc_take_damage directly
|
||||||
|
var enemy_peer_id = body.get_multiplayer_authority()
|
||||||
|
if enemy_peer_id != 0:
|
||||||
|
if multiplayer.is_server() and enemy_peer_id == multiplayer.get_unique_id():
|
||||||
|
body.rpc_take_damage(final_damage, attacker_pos, false, false, apply_burn) # is_critical=false, is_burn_damage=false, apply_burn_debuff
|
||||||
|
else:
|
||||||
|
body.rpc_take_damage.rpc_id(enemy_peer_id, final_damage, attacker_pos, false, false, apply_burn)
|
||||||
|
else:
|
||||||
|
body.rpc_take_damage.rpc(final_damage, attacker_pos, false, false, apply_burn)
|
||||||
|
|
||||||
|
if is_first_hit:
|
||||||
|
var int_bonus = int_bonus_damage if player_owner and player_owner.character_stats else 0.0
|
||||||
|
print("Flame spell INITIAL hit enemy: ", body.name, " for ", final_damage, " damage (base: ", damage, " x ", initial_damage_multiplier, " + INT bonus: ", int_bonus, ")")
|
||||||
|
else:
|
||||||
|
print("Flame spell hit enemy: ", body.name, " for ", final_damage, " damage!")
|
||||||
|
|
||||||
|
func _on_body_entered(_body):
|
||||||
|
# Track bodies that enter the area (for periodic damage)
|
||||||
|
# Don't add to hit_targets here - we want to deal damage multiple times
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _on_lifetime_expired():
|
||||||
|
# Spell expires - fade out and remove
|
||||||
|
hit_area.set_deferred("monitoring", false)
|
||||||
|
queue_free()
|
||||||
1
src/scripts/attack_spell_flame.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://pufa280hfhiy
|
||||||
29
src/scripts/debuff_burn.gd
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
extends Node2D
|
||||||
|
|
||||||
|
# Burn Debuff Visual - Shows burning animation on player/enemy
|
||||||
|
|
||||||
|
@onready var sprite = $Sprite2D
|
||||||
|
|
||||||
|
var animation_timer: float = 0.0
|
||||||
|
var current_frame: int = 0
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
if sprite:
|
||||||
|
sprite.frame = 0
|
||||||
|
current_frame = 0
|
||||||
|
animation_timer = 0.0
|
||||||
|
# Ensure sprite is visible and positioned correctly
|
||||||
|
sprite.z_index = 5 # Above player/enemy sprites
|
||||||
|
sprite.visible = true
|
||||||
|
print("DebuffBurn: sprite visible: ", sprite.visible, ", z_index: ", sprite.z_index)
|
||||||
|
|
||||||
|
func _process(delta):
|
||||||
|
if not sprite:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Animate through frames 0-15 (4x4 grid) at ~10 FPS
|
||||||
|
animation_timer += delta
|
||||||
|
if animation_timer >= 0.1: # ~10 FPS
|
||||||
|
animation_timer = 0.0
|
||||||
|
current_frame = (current_frame + 1) % 16
|
||||||
|
sprite.frame = current_frame
|
||||||
1
src/scripts/debuff_burn.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cvtox8ek8u7oq
|
||||||
@@ -21,6 +21,13 @@ var knockback_time: float = 0.0
|
|||||||
var knockback_duration: float = 0.3
|
var knockback_duration: float = 0.3
|
||||||
var knockback_force: float = 125.0 # Scaled down for 1x scale
|
var knockback_force: float = 125.0 # Scaled down for 1x scale
|
||||||
|
|
||||||
|
# Burn debuff
|
||||||
|
var burn_debuff_timer: float = 0.0 # Timer for burn debuff
|
||||||
|
var burn_debuff_duration: float = 5.0 # Burn lasts 5 seconds
|
||||||
|
var burn_debuff_damage_per_second: float = 1.0 # 1 HP per second
|
||||||
|
var burn_debuff_visual: Node2D = null # Visual indicator for burn debuff
|
||||||
|
var burn_damage_timer: float = 0.0 # Timer for burn damage ticks
|
||||||
|
|
||||||
# Z-axis for flying enemies
|
# Z-axis for flying enemies
|
||||||
var position_z: float = 0.0
|
var position_z: float = 0.0
|
||||||
var velocity_z: float = 0.0
|
var velocity_z: float = 0.0
|
||||||
@@ -95,6 +102,19 @@ func _physics_process(delta):
|
|||||||
# Only server (authority) runs AI and physics
|
# Only server (authority) runs AI and physics
|
||||||
if multiplayer.has_multiplayer_peer() and not is_multiplayer_authority():
|
if multiplayer.has_multiplayer_peer() and not is_multiplayer_authority():
|
||||||
# Clients only interpolate position (handled by _sync_position)
|
# Clients only interpolate position (handled by _sync_position)
|
||||||
|
# But still update burn visual animation on clients
|
||||||
|
if burn_debuff_visual and is_instance_valid(burn_debuff_visual):
|
||||||
|
if burn_debuff_visual is Sprite2D:
|
||||||
|
var burn_sprite = burn_debuff_visual as Sprite2D
|
||||||
|
var anim_timer = burn_sprite.get_meta("burn_animation_timer", 0.0)
|
||||||
|
anim_timer += delta
|
||||||
|
if anim_timer >= 0.1: # ~10 FPS
|
||||||
|
anim_timer = 0.0
|
||||||
|
var frame = burn_sprite.get_meta("burn_animation_frame", 0)
|
||||||
|
frame = (frame + 1) % 16
|
||||||
|
burn_sprite.frame = frame
|
||||||
|
burn_sprite.set_meta("burn_animation_frame", frame)
|
||||||
|
burn_sprite.set_meta("burn_animation_timer", anim_timer)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Update attack timer
|
# Update attack timer
|
||||||
@@ -125,6 +145,53 @@ func _physics_process(delta):
|
|||||||
# Check collisions with interactable objects
|
# Check collisions with interactable objects
|
||||||
_check_interactable_object_collision()
|
_check_interactable_object_collision()
|
||||||
|
|
||||||
|
# Update burn debuff
|
||||||
|
if burn_debuff_timer > 0.0:
|
||||||
|
burn_debuff_timer -= delta
|
||||||
|
burn_damage_timer += delta
|
||||||
|
|
||||||
|
# Deal burn damage every second (no knockback)
|
||||||
|
if burn_damage_timer >= 1.0:
|
||||||
|
burn_damage_timer = 0.0
|
||||||
|
# Deal burn damage directly (no knockback, no animation)
|
||||||
|
if character_stats:
|
||||||
|
var old_hp = character_stats.hp
|
||||||
|
character_stats.modify_health(-burn_debuff_damage_per_second)
|
||||||
|
current_health = character_stats.hp
|
||||||
|
if character_stats.hp <= 0:
|
||||||
|
character_stats.no_health.emit()
|
||||||
|
var actual_damage = old_hp - character_stats.hp
|
||||||
|
LogManager.log(str(name) + " takes " + str(actual_damage) + " burn damage! Health: " + str(current_health) + "/" + str(character_stats.maxhp), LogManager.CATEGORY_ENEMY)
|
||||||
|
# Show damage number for burn damage
|
||||||
|
_show_damage_number(actual_damage, global_position, false, false, false) # Show burn damage number
|
||||||
|
# Sync burn damage visual to clients
|
||||||
|
if multiplayer.has_multiplayer_peer() and is_inside_tree():
|
||||||
|
var enemy_name = name
|
||||||
|
var enemy_index = get_meta("enemy_index") if has_meta("enemy_index") else -1
|
||||||
|
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||||
|
if game_world and game_world.has_method("_sync_enemy_damage_visual"):
|
||||||
|
game_world._rpc_to_ready_peers("_sync_enemy_damage_visual", [enemy_name, enemy_index, actual_damage, global_position, false])
|
||||||
|
|
||||||
|
# Animate burn visual if it's a sprite (only on authority/server)
|
||||||
|
if is_multiplayer_authority() and burn_debuff_visual and is_instance_valid(burn_debuff_visual):
|
||||||
|
if burn_debuff_visual is Sprite2D:
|
||||||
|
var burn_sprite = burn_debuff_visual as Sprite2D
|
||||||
|
var anim_timer = burn_sprite.get_meta("burn_animation_timer", 0.0)
|
||||||
|
anim_timer += delta
|
||||||
|
if anim_timer >= 0.1: # ~10 FPS
|
||||||
|
anim_timer = 0.0
|
||||||
|
var frame = burn_sprite.get_meta("burn_animation_frame", 0)
|
||||||
|
frame = (frame + 1) % 16
|
||||||
|
burn_sprite.frame = frame
|
||||||
|
burn_sprite.set_meta("burn_animation_frame", frame)
|
||||||
|
burn_sprite.set_meta("burn_animation_timer", anim_timer)
|
||||||
|
|
||||||
|
# Remove burn debuff when timer expires
|
||||||
|
if burn_debuff_timer <= 0.0:
|
||||||
|
burn_debuff_timer = 0.0
|
||||||
|
burn_damage_timer = 0.0
|
||||||
|
_remove_burn_debuff()
|
||||||
|
|
||||||
# Sync position and animation to clients (only server sends)
|
# Sync position and animation to clients (only server sends)
|
||||||
if multiplayer.has_multiplayer_peer() and is_multiplayer_authority():
|
if multiplayer.has_multiplayer_peer() and is_multiplayer_authority():
|
||||||
# Get state value if enemy has a state variable (for bats/slimes)
|
# Get state value if enemy has a state variable (for bats/slimes)
|
||||||
@@ -284,7 +351,7 @@ func _find_nearest_player_to_position(pos: Vector2, max_range: float = 100.0) ->
|
|||||||
|
|
||||||
return nearest
|
return nearest
|
||||||
|
|
||||||
func take_damage(amount: float, from_position: Vector2, is_critical: bool = false):
|
func take_damage(amount: float, from_position: Vector2, is_critical: bool = false, is_burn_damage: bool = false, apply_burn_debuff: bool = false):
|
||||||
# Only process damage on server/authority
|
# Only process damage on server/authority
|
||||||
if not is_multiplayer_authority():
|
if not is_multiplayer_authority():
|
||||||
return
|
return
|
||||||
@@ -335,12 +402,18 @@ func take_damage(amount: float, from_position: Vector2, is_critical: bool = fals
|
|||||||
actual_damage = amount
|
actual_damage = amount
|
||||||
LogManager.log(str(name) + " took " + str(amount) + " damage! Health: " + str(current_health) + " (critical: " + str(is_critical) + ")", LogManager.CATEGORY_ENEMY)
|
LogManager.log(str(name) + " took " + str(amount) + " damage! Health: " + str(current_health) + " (critical: " + str(is_critical) + ")", LogManager.CATEGORY_ENEMY)
|
||||||
|
|
||||||
|
# Only apply knockback if not burn damage
|
||||||
|
if not is_burn_damage:
|
||||||
# Calculate knockback direction (away from attacker)
|
# Calculate knockback direction (away from attacker)
|
||||||
var knockback_direction = (global_position - from_position).normalized()
|
var knockback_direction = (global_position - from_position).normalized()
|
||||||
velocity = knockback_direction * knockback_force
|
velocity = knockback_direction * knockback_force
|
||||||
is_knocked_back = true
|
is_knocked_back = true
|
||||||
knockback_time = 0.0
|
knockback_time = 0.0
|
||||||
|
|
||||||
|
# Apply burn debuff if requested
|
||||||
|
if apply_burn_debuff:
|
||||||
|
_apply_burn_debuff()
|
||||||
|
|
||||||
_on_take_damage(from_position)
|
_on_take_damage(from_position)
|
||||||
|
|
||||||
# Flash red (even if dying, show the hit)
|
# Flash red (even if dying, show the hit)
|
||||||
@@ -379,10 +452,10 @@ func take_damage(amount: float, from_position: Vector2, is_critical: bool = fals
|
|||||||
call_deferred("_notify_doors_enemy_died")
|
call_deferred("_notify_doors_enemy_died")
|
||||||
|
|
||||||
@rpc("any_peer", "reliable")
|
@rpc("any_peer", "reliable")
|
||||||
func rpc_take_damage(amount: float, from_position: Vector2, is_critical: bool = false):
|
func rpc_take_damage(amount: float, from_position: Vector2, is_critical: bool = false, is_burn_damage: bool = false, apply_burn_debuff: bool = false):
|
||||||
# RPC version - only process on server/authority
|
# RPC version - only process on server/authority
|
||||||
if is_multiplayer_authority():
|
if is_multiplayer_authority():
|
||||||
take_damage(amount, from_position, is_critical)
|
take_damage(amount, from_position, is_critical, is_burn_damage, apply_burn_debuff)
|
||||||
|
|
||||||
func _show_damage_number(amount: float, from_position: Vector2, is_critical: bool = false, is_miss: bool = false, is_dodged: bool = false):
|
func _show_damage_number(amount: float, from_position: Vector2, is_critical: bool = false, is_miss: bool = false, is_dodged: bool = false):
|
||||||
# Show damage number (red/orange for crits, cyan for dodge, gray for miss, using dmg_numbers.png font) above enemy
|
# Show damage number (red/orange for crits, cyan for dodge, gray for miss, using dmg_numbers.png font) above enemy
|
||||||
@@ -475,6 +548,91 @@ func _set_animation(_anim_name: String):
|
|||||||
# (e.g., enemy_humanoid.gd uses player-like animation system)
|
# (e.g., enemy_humanoid.gd uses player-like animation system)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
func _apply_burn_debuff():
|
||||||
|
# Apply burn debuff to enemy
|
||||||
|
if burn_debuff_timer > 0.0:
|
||||||
|
# Already burning - refresh duration
|
||||||
|
burn_debuff_timer = burn_debuff_duration
|
||||||
|
burn_damage_timer = 0.0 # Reset damage timer
|
||||||
|
LogManager.log(str(name) + " burn debuff refreshed", LogManager.CATEGORY_ENEMY)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Start burn debuff
|
||||||
|
burn_debuff_timer = burn_debuff_duration
|
||||||
|
burn_damage_timer = 0.0
|
||||||
|
LogManager.log(str(name) + " applied burn debuff (" + str(burn_debuff_duration) + " seconds)", LogManager.CATEGORY_ENEMY)
|
||||||
|
|
||||||
|
# Create visual indicator
|
||||||
|
_create_burn_debuff_visual()
|
||||||
|
|
||||||
|
# Sync burn debuff to clients
|
||||||
|
if multiplayer.has_multiplayer_peer() and is_inside_tree():
|
||||||
|
var enemy_name = name
|
||||||
|
var enemy_index = get_meta("enemy_index") if has_meta("enemy_index") else -1
|
||||||
|
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||||
|
if game_world and game_world.has_method("_sync_enemy_burn_debuff"):
|
||||||
|
game_world._rpc_to_ready_peers("_sync_enemy_burn_debuff", [enemy_name, enemy_index, true])
|
||||||
|
|
||||||
|
func _create_burn_debuff_visual():
|
||||||
|
# Remove existing visual if any
|
||||||
|
if burn_debuff_visual and is_instance_valid(burn_debuff_visual):
|
||||||
|
burn_debuff_visual.queue_free()
|
||||||
|
|
||||||
|
# Load burn debuff scene
|
||||||
|
var burn_debuff_scene = load("res://scenes/debuff_burn.tscn")
|
||||||
|
if burn_debuff_scene:
|
||||||
|
burn_debuff_visual = burn_debuff_scene.instantiate()
|
||||||
|
add_child(burn_debuff_visual)
|
||||||
|
# Position on enemy (centered)
|
||||||
|
burn_debuff_visual.position = Vector2(0, 0)
|
||||||
|
burn_debuff_visual.z_index = 5 # Above enemy sprite
|
||||||
|
LogManager.log(str(name) + " created burn debuff visual", LogManager.CATEGORY_ENEMY)
|
||||||
|
else:
|
||||||
|
# Fallback: create simple sprite if scene doesn't exist
|
||||||
|
var burn_texture = load("res://assets/gfx/fx/burn.png")
|
||||||
|
if burn_texture:
|
||||||
|
var burn_sprite = Sprite2D.new()
|
||||||
|
burn_sprite.name = "BurnDebuffSprite"
|
||||||
|
burn_sprite.texture = burn_texture
|
||||||
|
burn_sprite.hframes = 4
|
||||||
|
burn_sprite.vframes = 4
|
||||||
|
burn_sprite.frame = 0
|
||||||
|
burn_sprite.position = Vector2(0, 0)
|
||||||
|
burn_sprite.z_index = 5 # Above enemy sprite
|
||||||
|
burn_sprite.set_meta("burn_animation_frame", 0)
|
||||||
|
burn_sprite.set_meta("burn_animation_timer", 0.0)
|
||||||
|
add_child(burn_sprite)
|
||||||
|
burn_debuff_visual = burn_sprite
|
||||||
|
|
||||||
|
func _remove_burn_debuff():
|
||||||
|
# Remove burn debuff visual
|
||||||
|
if burn_debuff_visual and is_instance_valid(burn_debuff_visual):
|
||||||
|
burn_debuff_visual.queue_free()
|
||||||
|
burn_debuff_visual = null
|
||||||
|
LogManager.log(str(name) + " burn debuff removed", LogManager.CATEGORY_ENEMY)
|
||||||
|
|
||||||
|
# Sync burn debuff removal to clients
|
||||||
|
if multiplayer.has_multiplayer_peer() and is_inside_tree():
|
||||||
|
var enemy_name = name
|
||||||
|
var enemy_index = get_meta("enemy_index") if has_meta("enemy_index") else -1
|
||||||
|
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||||
|
if game_world and game_world.has_method("_sync_enemy_burn_debuff"):
|
||||||
|
game_world._rpc_to_ready_peers("_sync_enemy_burn_debuff", [enemy_name, enemy_index, false])
|
||||||
|
|
||||||
|
func _sync_burn_debuff(apply: bool):
|
||||||
|
# Client-side sync of burn debuff visual
|
||||||
|
if apply:
|
||||||
|
if burn_debuff_timer <= 0.0:
|
||||||
|
# Only create visual if not already burning
|
||||||
|
_create_burn_debuff_visual()
|
||||||
|
burn_debuff_timer = burn_debuff_duration
|
||||||
|
burn_damage_timer = 0.0
|
||||||
|
else:
|
||||||
|
# Remove visual
|
||||||
|
_remove_burn_debuff()
|
||||||
|
burn_debuff_timer = 0.0
|
||||||
|
burn_damage_timer = 0.0
|
||||||
|
|
||||||
func _die():
|
func _die():
|
||||||
if is_dead:
|
if is_dead:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const REFERENCE_ASPECT: float = 16.0 / 9.0
|
|||||||
# Mouse cursor system
|
# Mouse cursor system
|
||||||
var cursor_sprite: Sprite2D = null # Free movement cursor (frame 0)
|
var cursor_sprite: Sprite2D = null # Free movement cursor (frame 0)
|
||||||
var grid_cursor_sprite: Sprite2D = null # Grid-locked cursor (frame 1)
|
var grid_cursor_sprite: Sprite2D = null # Grid-locked cursor (frame 1)
|
||||||
|
var spell_cursor_sprite: Sprite2D = null # Spell targeting cursor (frame 1, tinted by element)
|
||||||
var cursor_layer: CanvasLayer = null
|
var cursor_layer: CanvasLayer = null
|
||||||
const CURSOR_LAYER_Z: int = 2000 # Very high Z index for cursor
|
const CURSOR_LAYER_Z: int = 2000 # Very high Z index for cursor
|
||||||
var use_mouse_control: bool = true # Enable/disable mouse control
|
var use_mouse_control: bool = true # Enable/disable mouse control
|
||||||
@@ -1214,6 +1215,35 @@ func _sync_enemy_damage_visual(enemy_name: String, enemy_index: int, damage_amou
|
|||||||
# This is okay, just log it
|
# This is okay, just log it
|
||||||
print("GameWorld: Could not find enemy for damage visual sync: name=", enemy_name, " index=", enemy_index)
|
print("GameWorld: Could not find enemy for damage visual sync: name=", enemy_name, " index=", enemy_index)
|
||||||
|
|
||||||
|
@rpc("authority", "reliable")
|
||||||
|
func _sync_enemy_burn_debuff(enemy_name: String, enemy_index: int, apply: bool):
|
||||||
|
# Clients receive enemy burn debuff sync from server
|
||||||
|
# Find the enemy by name or index
|
||||||
|
if multiplayer.is_server():
|
||||||
|
return # Server ignores this (it's the sender)
|
||||||
|
|
||||||
|
var entities_node = get_node_or_null("Entities")
|
||||||
|
if not entities_node:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Try to find enemy by name first, then by index
|
||||||
|
var enemy = null
|
||||||
|
for child in entities_node.get_children():
|
||||||
|
if child.is_in_group("enemy"):
|
||||||
|
if child.name == enemy_name:
|
||||||
|
enemy = child
|
||||||
|
break
|
||||||
|
elif child.has_meta("enemy_index") and child.get_meta("enemy_index") == enemy_index:
|
||||||
|
enemy = child
|
||||||
|
break
|
||||||
|
|
||||||
|
if enemy and enemy.has_method("_sync_burn_debuff"):
|
||||||
|
# Call the enemy's _sync_burn_debuff method directly (not via RPC)
|
||||||
|
enemy._sync_burn_debuff(apply)
|
||||||
|
else:
|
||||||
|
# Enemy not found - might already be freed or never spawned
|
||||||
|
print("GameWorld: Could not find enemy for burn debuff sync: name=", enemy_name, " index=", enemy_index)
|
||||||
|
|
||||||
@rpc("authority", "reliable")
|
@rpc("authority", "reliable")
|
||||||
func _sync_enemy_attack(enemy_name: String, enemy_index: int, direction: int, attack_dir: Vector2):
|
func _sync_enemy_attack(enemy_name: String, enemy_index: int, direction: int, attack_dir: Vector2):
|
||||||
# Clients receive enemy attack sync from server
|
# Clients receive enemy attack sync from server
|
||||||
@@ -1319,7 +1349,7 @@ func _request_enemy_damage(enemy_name: String, enemy_index: int, damage: float,
|
|||||||
|
|
||||||
if enemy and enemy.has_method("rpc_take_damage"):
|
if enemy and enemy.has_method("rpc_take_damage"):
|
||||||
# Call the enemy's rpc_take_damage method directly (it will handle authority check)
|
# Call the enemy's rpc_take_damage method directly (it will handle authority check)
|
||||||
enemy.rpc_take_damage(damage, attacker_position, is_critical)
|
enemy.rpc_take_damage(damage, attacker_position, is_critical, false, false) # is_burn_damage=false, apply_burn_debuff=false
|
||||||
else:
|
else:
|
||||||
# Enemy not found - might already be freed
|
# Enemy not found - might already be freed
|
||||||
print("GameWorld: Could not find enemy for damage request: name=", enemy_name, " index=", enemy_index)
|
print("GameWorld: Could not find enemy for damage request: name=", enemy_name, " index=", enemy_index)
|
||||||
@@ -1764,6 +1794,17 @@ func _init_mouse_cursor():
|
|||||||
grid_cursor_sprite.modulate.a = 0.3 # 30% opacity
|
grid_cursor_sprite.modulate.a = 0.3 # 30% opacity
|
||||||
cursor_layer.add_child(grid_cursor_sprite)
|
cursor_layer.add_child(grid_cursor_sprite)
|
||||||
|
|
||||||
|
# Create spell targeting cursor sprite (frame 1, will be tinted by element)
|
||||||
|
spell_cursor_sprite = Sprite2D.new()
|
||||||
|
spell_cursor_sprite.name = "MouseCursorSpell"
|
||||||
|
spell_cursor_sprite.texture = cursor_texture
|
||||||
|
spell_cursor_sprite.hframes = 2
|
||||||
|
spell_cursor_sprite.vframes = 1
|
||||||
|
spell_cursor_sprite.frame = 1 # Frame 1 = grid-locked
|
||||||
|
spell_cursor_sprite.modulate.a = 0.5 # 50% opacity
|
||||||
|
spell_cursor_sprite.visible = false # Hidden by default
|
||||||
|
cursor_layer.add_child(spell_cursor_sprite)
|
||||||
|
|
||||||
# Hide system cursor
|
# Hide system cursor
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_HIDDEN
|
Input.mouse_mode = Input.MOUSE_MODE_HIDDEN
|
||||||
|
|
||||||
@@ -1777,6 +1818,19 @@ func _update_mouse_cursor(delta: float):
|
|||||||
if not grid_cursor_sprite or not is_instance_valid(grid_cursor_sprite):
|
if not grid_cursor_sprite or not is_instance_valid(grid_cursor_sprite):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not spell_cursor_sprite or not is_instance_valid(spell_cursor_sprite):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if player is charging a spell
|
||||||
|
var is_charging_spell = false
|
||||||
|
var spell_element = "fire" # Default to fire, can be extended later
|
||||||
|
if local_players.size() > 0:
|
||||||
|
var player = local_players[0]
|
||||||
|
if player and is_instance_valid(player) and player.is_local_player:
|
||||||
|
if "is_charging_spell" in player:
|
||||||
|
is_charging_spell = player.is_charging_spell
|
||||||
|
# TODO: Get spell element from player/equipment when element system is added
|
||||||
|
|
||||||
# Update pulse time for grid cursor color animation
|
# Update pulse time for grid cursor color animation
|
||||||
cursor_pulse_time += delta * CURSOR_PULSE_SPEED
|
cursor_pulse_time += delta * CURSOR_PULSE_SPEED
|
||||||
|
|
||||||
@@ -1792,6 +1846,7 @@ func _update_mouse_cursor(delta: float):
|
|||||||
var cursor_scale = camera.zoom.x # Use x zoom (should be same as y)
|
var cursor_scale = camera.zoom.x # Use x zoom (should be same as y)
|
||||||
cursor_sprite.scale = Vector2.ONE * cursor_scale
|
cursor_sprite.scale = Vector2.ONE * cursor_scale
|
||||||
grid_cursor_sprite.scale = Vector2.ONE * cursor_scale
|
grid_cursor_sprite.scale = Vector2.ONE * cursor_scale
|
||||||
|
spell_cursor_sprite.scale = Vector2.ONE * cursor_scale
|
||||||
|
|
||||||
# Check if we should show grid-locked cursor (when mouse is over game world tiles)
|
# Check if we should show grid-locked cursor (when mouse is over game world tiles)
|
||||||
var show_grid_cursor = false
|
var show_grid_cursor = false
|
||||||
@@ -1815,7 +1870,40 @@ func _update_mouse_cursor(delta: float):
|
|||||||
# Update free cursor position (always follows mouse)
|
# Update free cursor position (always follows mouse)
|
||||||
cursor_sprite.position = mouse_pos
|
cursor_sprite.position = mouse_pos
|
||||||
|
|
||||||
# Update grid cursor visibility and pulsing color
|
# Update spell cursor if charging spell
|
||||||
|
if is_charging_spell:
|
||||||
|
# Hide normal grid cursor
|
||||||
|
grid_cursor_sprite.visible = false
|
||||||
|
|
||||||
|
# Show spell cursor at valid spell target position
|
||||||
|
var spell_target_pos = _get_valid_spell_target_position(world_pos)
|
||||||
|
if spell_target_pos != Vector2.ZERO:
|
||||||
|
spell_cursor_sprite.visible = true
|
||||||
|
# Convert world position to screen position
|
||||||
|
var viewport_size = get_viewport().get_visible_rect().size
|
||||||
|
var viewport_center = viewport_size / 2.0
|
||||||
|
var spell_screen_pos = (spell_target_pos - camera.position) * camera.zoom.x + viewport_center
|
||||||
|
spell_cursor_sprite.position = spell_screen_pos
|
||||||
|
|
||||||
|
# Tint by element
|
||||||
|
match spell_element:
|
||||||
|
"fire":
|
||||||
|
spell_cursor_sprite.modulate = Color(1.0, 0.3, 0.3, 0.5) # Red
|
||||||
|
"water", "ice":
|
||||||
|
spell_cursor_sprite.modulate = Color(0.3, 0.5, 1.0, 0.5) # Blue
|
||||||
|
"electric":
|
||||||
|
spell_cursor_sprite.modulate = Color(1.0, 1.0, 0.3, 0.5) # Yellow
|
||||||
|
"earth":
|
||||||
|
spell_cursor_sprite.modulate = Color(0.3, 1.0, 0.3, 0.5) # Green
|
||||||
|
"wind":
|
||||||
|
spell_cursor_sprite.modulate = Color(1.0, 1.0, 1.0, 0.5) # White
|
||||||
|
_:
|
||||||
|
spell_cursor_sprite.modulate = Color(1.0, 0.3, 0.3, 0.5) # Default red
|
||||||
|
else:
|
||||||
|
spell_cursor_sprite.visible = false
|
||||||
|
else:
|
||||||
|
# Show normal grid cursor when not charging spell
|
||||||
|
spell_cursor_sprite.visible = false
|
||||||
grid_cursor_sprite.visible = show_grid_cursor
|
grid_cursor_sprite.visible = show_grid_cursor
|
||||||
if show_grid_cursor:
|
if show_grid_cursor:
|
||||||
# Pulse color: oscillate between normal and brighter color
|
# Pulse color: oscillate between normal and brighter color
|
||||||
@@ -1837,8 +1925,18 @@ func _update_mouse_cursor(delta: float):
|
|||||||
if mouse_in_window:
|
if mouse_in_window:
|
||||||
# Mouse is in window - use mouse for direction control
|
# Mouse is in window - use mouse for direction control
|
||||||
var player_pos = player.global_position
|
var player_pos = player.global_position
|
||||||
# Use grid-locked position if available, otherwise use free mouse position
|
# Use spell cursor position if charging spell, otherwise use grid-locked or free mouse position
|
||||||
var target_world_pos = grid_locked_world_pos if show_grid_cursor else world_pos
|
var target_world_pos = world_pos
|
||||||
|
if is_charging_spell and spell_cursor_sprite.visible:
|
||||||
|
# Use spell cursor position for facing direction
|
||||||
|
var viewport_size = get_viewport().get_visible_rect().size
|
||||||
|
var viewport_center = viewport_size / 2.0
|
||||||
|
# Convert spell cursor screen position back to world position
|
||||||
|
var spell_screen_pos = spell_cursor_sprite.position
|
||||||
|
target_world_pos = (spell_screen_pos - viewport_center) / camera.zoom.x + camera.position
|
||||||
|
elif show_grid_cursor:
|
||||||
|
target_world_pos = grid_locked_world_pos
|
||||||
|
|
||||||
var mouse_direction = (target_world_pos - player_pos).normalized()
|
var mouse_direction = (target_world_pos - player_pos).normalized()
|
||||||
|
|
||||||
# Only update facing if mouse is far enough from player
|
# Only update facing if mouse is far enough from player
|
||||||
@@ -1849,6 +1947,104 @@ func _update_mouse_cursor(delta: float):
|
|||||||
if "mouse_control_active" in player:
|
if "mouse_control_active" in player:
|
||||||
player.mouse_control_active = false
|
player.mouse_control_active = false
|
||||||
|
|
||||||
|
func get_grid_locked_cursor_position() -> Vector2:
|
||||||
|
# Get the grid-locked cursor world position for spell casting
|
||||||
|
if not dungeon_tilemap_layer:
|
||||||
|
return Vector2.ZERO
|
||||||
|
|
||||||
|
var _mouse_pos = get_viewport().get_mouse_position()
|
||||||
|
var world_pos = camera.get_global_mouse_position()
|
||||||
|
|
||||||
|
var tile_pos = dungeon_tilemap_layer.local_to_map(world_pos - dungeon_tilemap_layer.global_position)
|
||||||
|
var tile_data = dungeon_tilemap_layer.get_cell_source_id(tile_pos)
|
||||||
|
if tile_data >= 0: # Valid tile
|
||||||
|
# Return tile center world position
|
||||||
|
return dungeon_tilemap_layer.map_to_local(tile_pos) + dungeon_tilemap_layer.global_position
|
||||||
|
|
||||||
|
return Vector2.ZERO # No valid grid position
|
||||||
|
|
||||||
|
func _get_valid_spell_target_position(target_world_pos: Vector2) -> Vector2:
|
||||||
|
# Get valid spell target position (closest valid floor tile, or in front of wall if blocked)
|
||||||
|
# Returns Vector2.ZERO if no valid position found
|
||||||
|
if not dungeon_tilemap_layer:
|
||||||
|
return Vector2.ZERO
|
||||||
|
|
||||||
|
# Get player position for raycast
|
||||||
|
var player_pos = Vector2.ZERO
|
||||||
|
if local_players.size() > 0:
|
||||||
|
var player = local_players[0]
|
||||||
|
if player and is_instance_valid(player) and player.is_local_player:
|
||||||
|
player_pos = player.global_position
|
||||||
|
|
||||||
|
# Get tile position from world position
|
||||||
|
var tile_pos = dungeon_tilemap_layer.local_to_map(target_world_pos - dungeon_tilemap_layer.global_position)
|
||||||
|
var tile_center = dungeon_tilemap_layer.map_to_local(tile_pos) + dungeon_tilemap_layer.global_position
|
||||||
|
|
||||||
|
# Check if target is on a floor tile and not blocked by wall
|
||||||
|
if _is_valid_spell_target(tile_center, player_pos):
|
||||||
|
return tile_center
|
||||||
|
|
||||||
|
# If target is invalid, find closest valid position along the line from player to target
|
||||||
|
var direction = (target_world_pos - player_pos).normalized()
|
||||||
|
var max_distance = player_pos.distance_to(target_world_pos)
|
||||||
|
var step_size = 16.0 # One tile
|
||||||
|
var steps = int(max_distance / step_size) + 1
|
||||||
|
|
||||||
|
# Search backwards from target towards player to find first valid position
|
||||||
|
for i in range(steps + 1):
|
||||||
|
var check_distance = max_distance - (i * step_size)
|
||||||
|
if check_distance < 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
var check_pos = player_pos + (direction * check_distance)
|
||||||
|
var check_tile_pos = dungeon_tilemap_layer.local_to_map(check_pos - dungeon_tilemap_layer.global_position)
|
||||||
|
var check_tile_center = dungeon_tilemap_layer.map_to_local(check_tile_pos) + dungeon_tilemap_layer.global_position
|
||||||
|
|
||||||
|
if _is_valid_spell_target(check_tile_center, player_pos):
|
||||||
|
return check_tile_center
|
||||||
|
|
||||||
|
return Vector2.ZERO # No valid position found
|
||||||
|
|
||||||
|
func _is_valid_spell_target(target_pos: Vector2, player_pos: Vector2) -> bool:
|
||||||
|
# Check if target position is valid for spell casting (floor tile, not blocked by wall)
|
||||||
|
if dungeon_data.is_empty() or not dungeon_data.has("grid"):
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Check if target is on a floor tile
|
||||||
|
var tile_size = 16
|
||||||
|
var tile_x = int(target_pos.x / tile_size)
|
||||||
|
var tile_y = int(target_pos.y / tile_size)
|
||||||
|
var grid = dungeon_data.grid
|
||||||
|
var map_size = dungeon_data.map_size
|
||||||
|
|
||||||
|
# Check bounds
|
||||||
|
if tile_x < 0 or tile_y < 0 or tile_x >= map_size.x or tile_y >= map_size.y:
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Check if it's a floor tile (grid value 1) or corridor (grid value 3)
|
||||||
|
if grid[tile_x][tile_y] != 1 and grid[tile_x][tile_y] != 3:
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Check if there's a wall between player and target using raycast
|
||||||
|
var space_state = get_world_2d().direct_space_state
|
||||||
|
var query = PhysicsRayQueryParameters2D.new()
|
||||||
|
query.from = player_pos
|
||||||
|
query.to = target_pos
|
||||||
|
query.collision_mask = 64 # Layer 7 = walls (bit 6 = 64)
|
||||||
|
|
||||||
|
# Exclude player if we have a reference
|
||||||
|
if local_players.size() > 0:
|
||||||
|
var player = local_players[0]
|
||||||
|
if player and is_instance_valid(player):
|
||||||
|
query.exclude = [player.get_rid()]
|
||||||
|
|
||||||
|
var result = space_state.intersect_ray(query)
|
||||||
|
if result:
|
||||||
|
# Hit something - wall blocks spell casting
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
func _init_fog_of_war():
|
func _init_fog_of_war():
|
||||||
if dungeon_data.is_empty() or not dungeon_data.has("map_size"):
|
if dungeon_data.is_empty() or not dungeon_data.has("map_size"):
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ enum WeaponType {
|
|||||||
DAGGER,
|
DAGGER,
|
||||||
STAFF,
|
STAFF,
|
||||||
SPEAR,
|
SPEAR,
|
||||||
MACE
|
MACE,
|
||||||
|
SPELLBOOK
|
||||||
}
|
}
|
||||||
|
|
||||||
var use_function = null
|
var use_function = null
|
||||||
|
|||||||
@@ -1218,6 +1218,43 @@ static func _load_all_items():
|
|||||||
"rarity": ItemRarity.CONSUMABLE
|
"rarity": ItemRarity.CONSUMABLE
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# SPELLBOOKS (row 11, columns 13-14)
|
||||||
|
# Sprite 233 = 11 * 20 + 13
|
||||||
|
_register_item("tome_of_frostspike", {
|
||||||
|
"item_name": "Tome of Frostspike",
|
||||||
|
"description": "A spellbook containing frost magic",
|
||||||
|
"item_type": Item.ItemType.Equippable,
|
||||||
|
"equipment_type": Item.EquipmentType.OFFHAND,
|
||||||
|
"weapon_type": Item.WeaponType.SPELLBOOK,
|
||||||
|
"spriteFrame": 11 * 20 + 13, # 233
|
||||||
|
"modifiers": {},
|
||||||
|
"buy_cost": 100,
|
||||||
|
"sell_worth": 30,
|
||||||
|
"weight": 1.5,
|
||||||
|
"rarity": ItemRarity.UNCOMMON,
|
||||||
|
"colorReplacements": [
|
||||||
|
{"old_color": Color(1.0, 1.0, 1.0), "new_color": Color(0.7, 0.9, 1.0)} # Light blue tint for frost
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
# Sprite 234 = 11 * 20 + 14
|
||||||
|
_register_item("tome_of_flames", {
|
||||||
|
"item_name": "Tome of Flames",
|
||||||
|
"description": "A spellbook containing fire magic",
|
||||||
|
"item_type": Item.ItemType.Equippable,
|
||||||
|
"equipment_type": Item.EquipmentType.OFFHAND,
|
||||||
|
"weapon_type": Item.WeaponType.SPELLBOOK,
|
||||||
|
"spriteFrame": 11 * 20 + 14, # 234
|
||||||
|
"modifiers": {},
|
||||||
|
"buy_cost": 100,
|
||||||
|
"sell_worth": 30,
|
||||||
|
"weight": 1.5,
|
||||||
|
"rarity": ItemRarity.UNCOMMON,
|
||||||
|
"colorReplacements": [
|
||||||
|
{"old_color": Color(1.0, 1.0, 1.0), "new_color": Color(1.0, 0.8, 0.6)} # Warm orange tint for fire
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
# Register an item in the database
|
# Register an item in the database
|
||||||
static func _register_item(item_id: String, item_data: Dictionary):
|
static func _register_item(item_id: String, item_data: Dictionary):
|
||||||
item_data["item_id"] = item_id
|
item_data["item_id"] = item_id
|
||||||
|
|||||||
@@ -428,7 +428,7 @@ func _on_server_disconnected():
|
|||||||
reconnection_room_id = room_id
|
reconnection_room_id = room_id
|
||||||
# Get current level from game_world
|
# Get current level from game_world
|
||||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||||
if game_world and game_world.has("current_level"):
|
if game_world and "current_level" in game_world:
|
||||||
reconnection_level = game_world.current_level
|
reconnection_level = game_world.current_level
|
||||||
log_print("NetworkManager: Stored reconnection info - room_id: " + reconnection_room_id + ", level: " + str(reconnection_level))
|
log_print("NetworkManager: Stored reconnection info - room_id: " + reconnection_room_id + ", level: " + str(reconnection_level))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -220,6 +220,8 @@ func _update_disarm_ui() -> void:
|
|||||||
disarm_label.text = "DISARM (" + str(progress_percent) + "%)"
|
disarm_label.text = "DISARM (" + str(progress_percent) + "%)"
|
||||||
|
|
||||||
func _cancel_disarm() -> void:
|
func _cancel_disarm() -> void:
|
||||||
|
if disarming_player and disarming_player.has_method("set") and "is_disarming" in disarming_player:
|
||||||
|
disarming_player.is_disarming = false
|
||||||
disarming_player = null
|
disarming_player = null
|
||||||
disarm_progress = 0.0
|
disarm_progress = 0.0
|
||||||
# Stop disarming sound
|
# Stop disarming sound
|
||||||
@@ -231,6 +233,8 @@ func _cancel_disarm() -> void:
|
|||||||
func _complete_disarm() -> void:
|
func _complete_disarm() -> void:
|
||||||
# Trap successfully disarmed!
|
# Trap successfully disarmed!
|
||||||
is_disarmed = true
|
is_disarmed = true
|
||||||
|
if disarming_player and disarming_player.has_method("set") and "is_disarming" in disarming_player:
|
||||||
|
disarming_player.is_disarming = false
|
||||||
disarming_player = null
|
disarming_player = null
|
||||||
disarm_progress = 0.0
|
disarm_progress = 0.0
|
||||||
|
|
||||||
|
|||||||